Version 2.9.0-19.0.dev
Merge commit 'b5470ae013d24a6f6b5fd4bb7fb803ec24682add' into dev
diff --git a/.dart_tool/package_config.json b/.dart_tool/package_config.json
index cd9ba19..a62e37d 100644
--- a/.dart_tool/package_config.json
+++ b/.dart_tool/package_config.json
@@ -11,7 +11,7 @@
"constraint, update this by running tools/generate_package_config.dart."
],
"configVersion": 2,
- "generated": "2020-06-17T09:39:51.245406",
+ "generated": "2020-06-23T13:49:13.378284",
"generator": "tools/generate_package_config.dart",
"packages": [
{
@@ -380,7 +380,8 @@
{
"name": "meta",
"rootUri": "../pkg/meta",
- "packageUri": "lib/"
+ "packageUri": "lib/",
+ "languageVersion": "2.9"
},
{
"name": "mime",
@@ -559,7 +560,7 @@
"name": "stack_trace",
"rootUri": "../third_party/pkg/stack_trace",
"packageUri": "lib/",
- "languageVersion": "1.23"
+ "languageVersion": "2.0"
},
{
"name": "stagehand",
@@ -676,6 +677,12 @@
"languageVersion": "2.6"
},
{
+ "name": "vm_snapshot_analysis",
+ "rootUri": "../pkg/vm_snapshot_analysis",
+ "packageUri": "lib/",
+ "languageVersion": "2.8"
+ },
+ {
"name": "watcher",
"rootUri": "../third_party/pkg/watcher",
"packageUri": "lib/",
diff --git a/.packages b/.packages
index 368824f..2b486aa 100644
--- a/.packages
+++ b/.packages
@@ -112,6 +112,7 @@
usage:third_party/pkg/usage/lib
vm:pkg/vm/lib
vm_service:pkg/vm_service/lib
+vm_snapshot_analysis:pkg/vm_snapshot_analysis/lib
watcher:third_party/pkg/watcher/lib
web_components:third_party/pkg/web_components/lib
web_socket_channel:third_party/pkg/web_socket_channel/lib
diff --git a/DEPS b/DEPS
index 6ebc4f9..9335624 100644
--- a/DEPS
+++ b/DEPS
@@ -39,16 +39,16 @@
# Checked-in SDK version. The checked-in SDK is a Dart SDK distribution in a
# cipd package used to run Dart scripts in the build and test infrastructure.
- "sdk_tag": "version:2.9.0-16.0.dev",
+ "sdk_tag": "version:2.9.0-18.0.dev",
# co19 is a cipd package. Use update.sh in tests/co19[_2] to update these
# hashes. It requires access to the dart-build-access group, which EngProd
# has.
- "co19_rev": "b071a4729a57ee47dcc7330ab9ea3566ef58e5c2",
+ "co19_rev": "443e4b3cd3953e9d8feef85bb712a93f6dac0858",
"co19_2_rev": "620c1148c8b7a3d7f74afacf348c46f109eb64f2",
# The internal benchmarks to use. See go/dart-benchmarks-internal
- "benchmarks_internal_rev": "478f5a8be0bad2b1bc3c1c9cf150486ec77516e1",
+ "benchmarks_internal_rev": "8a7ce78ada809beb62fcfdf2f8e7a400a7bc5ed3",
"checkout_benchmarks_internal": False,
# As Flutter does, we use Fuchsia's GN and Clang toolchain. These revision
@@ -142,7 +142,7 @@
"source_maps-0.9.4_rev": "38524",
"source_maps_rev": "87b4fd9027378bbd51b02e9d7df794eee8a82b7a",
"source_span_tag": "1.7.0",
- "stack_trace_tag": "1.9.3",
+ "stack_trace_tag": "07569831f17844ef90214a179db211954a7f7324",
"stagehand_tag": "v3.3.7",
"stream_channel_tag": "70433d577be02c48cb16d72d65654f3b4d82c6ed",
"string_scanner_rev": "a918e7371af6b6e73bfd534ff9da6084741c1f99",
diff --git a/benchmarks/BigIntParsePrint/dart/BigIntParsePrint.dart b/benchmarks/BigIntParsePrint/dart/BigIntParsePrint.dart
index b7ed19e..f9e05e7 100644
--- a/benchmarks/BigIntParsePrint/dart/BigIntParsePrint.dart
+++ b/benchmarks/BigIntParsePrint/dart/BigIntParsePrint.dart
@@ -168,10 +168,11 @@
@override
void run() {
+ final one = BigInt.one;
for (final b0 in values) {
// Instances might cache `toString()`, so use arithmetic to create a new
// instance to try to protect against measuring a cached string.
- final b = b0 + BigInt.one;
+ final b = b0 + one;
final s = b.toString();
sink1 = s;
sink2 = b;
@@ -206,10 +207,11 @@
@override
void run() {
+ final one = Int64.ONE;
for (final b0 in values) {
// Instances might cache `toString()`, so use arithmetic to create a new
// instance to try to protect against measuring a cached string.
- final b = b0 + Int64.ONE;
+ final b = b0 + one;
final s = b.toStringUnsigned();
sink1 = s;
sink2 = b;
diff --git a/benchmarks/BigIntParsePrint/dart2/BigIntParsePrint.dart b/benchmarks/BigIntParsePrint/dart2/BigIntParsePrint.dart
index b7ed19e..f9e05e7 100644
--- a/benchmarks/BigIntParsePrint/dart2/BigIntParsePrint.dart
+++ b/benchmarks/BigIntParsePrint/dart2/BigIntParsePrint.dart
@@ -168,10 +168,11 @@
@override
void run() {
+ final one = BigInt.one;
for (final b0 in values) {
// Instances might cache `toString()`, so use arithmetic to create a new
// instance to try to protect against measuring a cached string.
- final b = b0 + BigInt.one;
+ final b = b0 + one;
final s = b.toString();
sink1 = s;
sink2 = b;
@@ -206,10 +207,11 @@
@override
void run() {
+ final one = Int64.ONE;
for (final b0 in values) {
// Instances might cache `toString()`, so use arithmetic to create a new
// instance to try to protect against measuring a cached string.
- final b = b0 + Int64.ONE;
+ final b = b0 + one;
final s = b.toStringUnsigned();
sink1 = s;
sink2 = b;
diff --git a/benchmarks/Calls/dart/Calls.dart b/benchmarks/Calls/dart/Calls.dart
index ac78efe..5b94b2a 100644
--- a/benchmarks/Calls/dart/Calls.dart
+++ b/benchmarks/Calls/dart/Calls.dart
@@ -4,7 +4,7 @@
// Micro-benchmarks for sync/sync*/async/async* functionality.
-import "dart:async";
+import 'dart:async';
const int iterationLimitAsync = 200;
const int sumOfIterationLimitAsync =
@@ -14,35 +14,35 @@
const int sumOfIterationLimitSync =
iterationLimitSync * (iterationLimitSync - 1) ~/ 2;
-main() async {
+Future main() async {
final target = Target();
final target2 = Target2();
final target3 = Target3();
// Ensure the call sites will have another target in the ICData.
- performAwaitCallsClosureTargetPolymorphic(returnAsync);
- performAwaitCallsClosureTargetPolymorphic(returnFuture);
- performAwaitCallsClosureTargetPolymorphic(returnFutureOr);
- performAwaitAsyncCallsInstanceTargetPolymorphic(target);
- performAwaitAsyncCallsInstanceTargetPolymorphic(target2);
- performAwaitAsyncCallsInstanceTargetPolymorphic(target3);
- performAwaitFutureCallsInstanceTargetPolymorphic(target);
- performAwaitFutureCallsInstanceTargetPolymorphic(target2);
- performAwaitFutureCallsInstanceTargetPolymorphic(target3);
- performAwaitFutureOrCallsInstanceTargetPolymorphic(target);
- performAwaitFutureOrCallsInstanceTargetPolymorphic(target2);
- performAwaitFutureOrCallsInstanceTargetPolymorphic(target3);
+ await performAwaitCallsClosureTargetPolymorphic(returnAsync);
+ await performAwaitCallsClosureTargetPolymorphic(returnFuture);
+ await performAwaitCallsClosureTargetPolymorphic(returnFutureOr);
+ await performAwaitAsyncCallsInstanceTargetPolymorphic(target);
+ await performAwaitAsyncCallsInstanceTargetPolymorphic(target2);
+ await performAwaitAsyncCallsInstanceTargetPolymorphic(target3);
+ await performAwaitFutureCallsInstanceTargetPolymorphic(target);
+ await performAwaitFutureCallsInstanceTargetPolymorphic(target2);
+ await performAwaitFutureCallsInstanceTargetPolymorphic(target3);
+ await performAwaitFutureOrCallsInstanceTargetPolymorphic(target);
+ await performAwaitFutureOrCallsInstanceTargetPolymorphic(target2);
+ await performAwaitFutureOrCallsInstanceTargetPolymorphic(target3);
performSyncCallsInstanceTargetPolymorphic(target);
performSyncCallsInstanceTargetPolymorphic(target2);
performSyncCallsInstanceTargetPolymorphic(target3);
- performAwaitAsyncCallsInstanceTargetPolymorphicManyAwaits(target);
- performAwaitAsyncCallsInstanceTargetPolymorphicManyAwaits(target2);
- performAwaitAsyncCallsInstanceTargetPolymorphicManyAwaits(target3);
+ await performAwaitAsyncCallsInstanceTargetPolymorphicManyAwaits(target);
+ await performAwaitAsyncCallsInstanceTargetPolymorphicManyAwaits(target2);
+ await performAwaitAsyncCallsInstanceTargetPolymorphicManyAwaits(target3);
- performAwaitForIterationPolymorphic(generateNumbersAsyncStar);
- performAwaitForIterationPolymorphic(generateNumbersAsyncStar2);
- performAwaitForIterationPolymorphic(generateNumbersManualAsync);
- performAwaitForIterationPolymorphic(generateNumbersAsyncStarManyYields);
+ await performAwaitForIterationPolymorphic(generateNumbersAsyncStar);
+ await performAwaitForIterationPolymorphic(generateNumbersAsyncStar2);
+ await performAwaitForIterationPolymorphic(generateNumbersManualAsync);
+ await performAwaitForIterationPolymorphic(generateNumbersAsyncStarManyYields);
performSyncIterationPolymorphic(generateNumbersSyncStar);
performSyncIterationPolymorphic(generateNumbersSyncStar2);
performSyncIterationPolymorphic(generateNumbersManual);
@@ -105,7 +105,7 @@
@pragma('vm:never-inline')
@pragma('dart2js:noInline')
Future<int> performAwaitCallsClosureTargetPolymorphic(
- FutureOr<int> fun(int count)) async {
+ FutureOr<int> Function(int) fun) async {
int sum = 0;
for (int i = 0; i < iterationLimitAsync; ++i) {
sum += await fun(i);
@@ -286,7 +286,7 @@
@pragma('vm:never-inline')
@pragma('dart2js:noInline')
Future<int> performAwaitForIterationPolymorphic(
- Stream<int> fun(int count)) async {
+ Stream<int> Function(int) fun) async {
int sum = 0;
await for (int value in fun(iterationLimitAsync)) {
sum += value;
@@ -297,7 +297,7 @@
@pragma('vm:never-inline')
@pragma('dart2js:noInline')
-int performSyncCallsClosureTarget(int fun(int count)) {
+int performSyncCallsClosureTarget(int Function(int) fun) {
int sum = 0;
for (int i = 0; i < iterationLimitSync; ++i) {
sum += fun(i);
@@ -330,7 +330,7 @@
@pragma('vm:never-inline')
@pragma('dart2js:noInline')
-int performSyncIterationPolymorphic(Iterable<int> fun(int count)) {
+int performSyncIterationPolymorphic(Iterable<int> Function(int) fun) {
int sum = 0;
for (int value in fun(iterationLimitSync)) {
sum += value;
@@ -371,7 +371,7 @@
@pragma('dart2js:noInline')
Stream<int> generateNumbersManualAsync(int limit) {
int current = 0;
- StreamController<int> controller = StreamController(sync: true);
+ final controller = StreamController<int>(sync: true);
void emit() {
while (true) {
if (controller.isPaused || !controller.hasListener) return;
@@ -746,36 +746,44 @@
}
class Target2 extends Target {
+ @override
@pragma('vm:never-inline')
@pragma('dart2js:noInline')
FutureOr<int> returnFutureOr(int i) => i;
+ @override
@pragma('vm:never-inline')
@pragma('dart2js:noInline')
Future<int> returnFuture(int i) => Future.value(i);
+ @override
@pragma('vm:never-inline')
@pragma('dart2js:noInline')
Future<int> returnAsync(int i) async => i;
+ @override
@pragma('vm:never-inline')
@pragma('dart2js:noInline')
int returnSync(int i) => i;
}
class Target3 extends Target {
+ @override
@pragma('vm:never-inline')
@pragma('dart2js:noInline')
FutureOr<int> returnFutureOr(int i) => i;
+ @override
@pragma('vm:never-inline')
@pragma('dart2js:noInline')
Future<int> returnFuture(int i) => Future.value(i);
+ @override
@pragma('vm:never-inline')
@pragma('dart2js:noInline')
Future<int> returnAsync(int i) async => i;
+ @override
@pragma('vm:never-inline')
@pragma('dart2js:noInline')
int returnSync(int i) => i;
@@ -815,7 +823,7 @@
final double nsPerCall = measureFor(const Duration(seconds: 2));
// Report result.
- print("$name(RunTimeRaw): $nsPerCall ns.");
+ print('$name(RunTimeRaw): $nsPerCall ns.');
}
}
@@ -850,6 +858,6 @@
final double nsPerCall = await measureFor(const Duration(seconds: 2));
// Report result.
- print("$name(RunTimeRaw): $nsPerCall ns.");
+ print('$name(RunTimeRaw): $nsPerCall ns.');
}
}
diff --git a/benchmarks/Calls/dart2/Calls.dart b/benchmarks/Calls/dart2/Calls.dart
index ac78efe..5b94b2a 100644
--- a/benchmarks/Calls/dart2/Calls.dart
+++ b/benchmarks/Calls/dart2/Calls.dart
@@ -4,7 +4,7 @@
// Micro-benchmarks for sync/sync*/async/async* functionality.
-import "dart:async";
+import 'dart:async';
const int iterationLimitAsync = 200;
const int sumOfIterationLimitAsync =
@@ -14,35 +14,35 @@
const int sumOfIterationLimitSync =
iterationLimitSync * (iterationLimitSync - 1) ~/ 2;
-main() async {
+Future main() async {
final target = Target();
final target2 = Target2();
final target3 = Target3();
// Ensure the call sites will have another target in the ICData.
- performAwaitCallsClosureTargetPolymorphic(returnAsync);
- performAwaitCallsClosureTargetPolymorphic(returnFuture);
- performAwaitCallsClosureTargetPolymorphic(returnFutureOr);
- performAwaitAsyncCallsInstanceTargetPolymorphic(target);
- performAwaitAsyncCallsInstanceTargetPolymorphic(target2);
- performAwaitAsyncCallsInstanceTargetPolymorphic(target3);
- performAwaitFutureCallsInstanceTargetPolymorphic(target);
- performAwaitFutureCallsInstanceTargetPolymorphic(target2);
- performAwaitFutureCallsInstanceTargetPolymorphic(target3);
- performAwaitFutureOrCallsInstanceTargetPolymorphic(target);
- performAwaitFutureOrCallsInstanceTargetPolymorphic(target2);
- performAwaitFutureOrCallsInstanceTargetPolymorphic(target3);
+ await performAwaitCallsClosureTargetPolymorphic(returnAsync);
+ await performAwaitCallsClosureTargetPolymorphic(returnFuture);
+ await performAwaitCallsClosureTargetPolymorphic(returnFutureOr);
+ await performAwaitAsyncCallsInstanceTargetPolymorphic(target);
+ await performAwaitAsyncCallsInstanceTargetPolymorphic(target2);
+ await performAwaitAsyncCallsInstanceTargetPolymorphic(target3);
+ await performAwaitFutureCallsInstanceTargetPolymorphic(target);
+ await performAwaitFutureCallsInstanceTargetPolymorphic(target2);
+ await performAwaitFutureCallsInstanceTargetPolymorphic(target3);
+ await performAwaitFutureOrCallsInstanceTargetPolymorphic(target);
+ await performAwaitFutureOrCallsInstanceTargetPolymorphic(target2);
+ await performAwaitFutureOrCallsInstanceTargetPolymorphic(target3);
performSyncCallsInstanceTargetPolymorphic(target);
performSyncCallsInstanceTargetPolymorphic(target2);
performSyncCallsInstanceTargetPolymorphic(target3);
- performAwaitAsyncCallsInstanceTargetPolymorphicManyAwaits(target);
- performAwaitAsyncCallsInstanceTargetPolymorphicManyAwaits(target2);
- performAwaitAsyncCallsInstanceTargetPolymorphicManyAwaits(target3);
+ await performAwaitAsyncCallsInstanceTargetPolymorphicManyAwaits(target);
+ await performAwaitAsyncCallsInstanceTargetPolymorphicManyAwaits(target2);
+ await performAwaitAsyncCallsInstanceTargetPolymorphicManyAwaits(target3);
- performAwaitForIterationPolymorphic(generateNumbersAsyncStar);
- performAwaitForIterationPolymorphic(generateNumbersAsyncStar2);
- performAwaitForIterationPolymorphic(generateNumbersManualAsync);
- performAwaitForIterationPolymorphic(generateNumbersAsyncStarManyYields);
+ await performAwaitForIterationPolymorphic(generateNumbersAsyncStar);
+ await performAwaitForIterationPolymorphic(generateNumbersAsyncStar2);
+ await performAwaitForIterationPolymorphic(generateNumbersManualAsync);
+ await performAwaitForIterationPolymorphic(generateNumbersAsyncStarManyYields);
performSyncIterationPolymorphic(generateNumbersSyncStar);
performSyncIterationPolymorphic(generateNumbersSyncStar2);
performSyncIterationPolymorphic(generateNumbersManual);
@@ -105,7 +105,7 @@
@pragma('vm:never-inline')
@pragma('dart2js:noInline')
Future<int> performAwaitCallsClosureTargetPolymorphic(
- FutureOr<int> fun(int count)) async {
+ FutureOr<int> Function(int) fun) async {
int sum = 0;
for (int i = 0; i < iterationLimitAsync; ++i) {
sum += await fun(i);
@@ -286,7 +286,7 @@
@pragma('vm:never-inline')
@pragma('dart2js:noInline')
Future<int> performAwaitForIterationPolymorphic(
- Stream<int> fun(int count)) async {
+ Stream<int> Function(int) fun) async {
int sum = 0;
await for (int value in fun(iterationLimitAsync)) {
sum += value;
@@ -297,7 +297,7 @@
@pragma('vm:never-inline')
@pragma('dart2js:noInline')
-int performSyncCallsClosureTarget(int fun(int count)) {
+int performSyncCallsClosureTarget(int Function(int) fun) {
int sum = 0;
for (int i = 0; i < iterationLimitSync; ++i) {
sum += fun(i);
@@ -330,7 +330,7 @@
@pragma('vm:never-inline')
@pragma('dart2js:noInline')
-int performSyncIterationPolymorphic(Iterable<int> fun(int count)) {
+int performSyncIterationPolymorphic(Iterable<int> Function(int) fun) {
int sum = 0;
for (int value in fun(iterationLimitSync)) {
sum += value;
@@ -371,7 +371,7 @@
@pragma('dart2js:noInline')
Stream<int> generateNumbersManualAsync(int limit) {
int current = 0;
- StreamController<int> controller = StreamController(sync: true);
+ final controller = StreamController<int>(sync: true);
void emit() {
while (true) {
if (controller.isPaused || !controller.hasListener) return;
@@ -746,36 +746,44 @@
}
class Target2 extends Target {
+ @override
@pragma('vm:never-inline')
@pragma('dart2js:noInline')
FutureOr<int> returnFutureOr(int i) => i;
+ @override
@pragma('vm:never-inline')
@pragma('dart2js:noInline')
Future<int> returnFuture(int i) => Future.value(i);
+ @override
@pragma('vm:never-inline')
@pragma('dart2js:noInline')
Future<int> returnAsync(int i) async => i;
+ @override
@pragma('vm:never-inline')
@pragma('dart2js:noInline')
int returnSync(int i) => i;
}
class Target3 extends Target {
+ @override
@pragma('vm:never-inline')
@pragma('dart2js:noInline')
FutureOr<int> returnFutureOr(int i) => i;
+ @override
@pragma('vm:never-inline')
@pragma('dart2js:noInline')
Future<int> returnFuture(int i) => Future.value(i);
+ @override
@pragma('vm:never-inline')
@pragma('dart2js:noInline')
Future<int> returnAsync(int i) async => i;
+ @override
@pragma('vm:never-inline')
@pragma('dart2js:noInline')
int returnSync(int i) => i;
@@ -815,7 +823,7 @@
final double nsPerCall = measureFor(const Duration(seconds: 2));
// Report result.
- print("$name(RunTimeRaw): $nsPerCall ns.");
+ print('$name(RunTimeRaw): $nsPerCall ns.');
}
}
@@ -850,6 +858,6 @@
final double nsPerCall = await measureFor(const Duration(seconds: 2));
// Report result.
- print("$name(RunTimeRaw): $nsPerCall ns.");
+ print('$name(RunTimeRaw): $nsPerCall ns.');
}
}
diff --git a/benchmarks/Example/dart/Example.dart b/benchmarks/Example/dart/Example.dart
index 451124b..bf0f06f 100644
--- a/benchmarks/Example/dart/Example.dart
+++ b/benchmarks/Example/dart/Example.dart
@@ -5,18 +5,21 @@
import 'package:benchmark_harness/benchmark_harness.dart';
class Example extends BenchmarkBase {
- const Example() : super("Example");
+ const Example() : super('Example');
// The benchmark code.
+ @override
void run() {}
// Not measured setup code executed prior to the benchmark runs.
+ @override
void setup() {}
// Not measures teardown code executed after the benchark runs.
+ @override
void teardown() {}
}
-main() {
+void main() {
const Example().report();
}
diff --git a/benchmarks/Example/dart2/Example.dart b/benchmarks/Example/dart2/Example.dart
index 451124b..bf0f06f 100644
--- a/benchmarks/Example/dart2/Example.dart
+++ b/benchmarks/Example/dart2/Example.dart
@@ -5,18 +5,21 @@
import 'package:benchmark_harness/benchmark_harness.dart';
class Example extends BenchmarkBase {
- const Example() : super("Example");
+ const Example() : super('Example');
// The benchmark code.
+ @override
void run() {}
// Not measured setup code executed prior to the benchmark runs.
+ @override
void setup() {}
// Not measures teardown code executed after the benchark runs.
+ @override
void teardown() {}
}
-main() {
+void main() {
const Example().report();
}
diff --git a/benchmarks/Isolate/dart/Isolate.dart b/benchmarks/Isolate/dart/Isolate.dart
index 465d56a..b703340 100644
--- a/benchmarks/Isolate/dart/Isolate.dart
+++ b/benchmarks/Isolate/dart/Isolate.dart
@@ -8,11 +8,10 @@
import 'package:benchmark_harness/benchmark_harness.dart'
show PrintEmitter, ScoreEmitter;
-import 'package:meta/meta.dart';
class SendReceiveBytes extends AsyncBenchmarkBase {
SendReceiveBytes(String name,
- {@required int this.size, @required bool this.useTransferable})
+ {required this.size, required this.useTransferable})
: super(name);
@override
@@ -33,7 +32,7 @@
final bool useTransferable;
final int size;
- SendReceiveHelper helper;
+ late SendReceiveHelper helper;
}
// Identical to BenchmarkBase from package:benchmark_harness but async.
@@ -86,10 +85,10 @@
// Measures how long sending and receiving of [size]-length Uint8List takes.
class SendReceiveHelper {
- SendReceiveHelper(this.size, {@required bool this.useTransferable});
+ SendReceiveHelper(this.size, {required this.useTransferable});
Future<void> setup() async {
- data = new Uint8List(size);
+ data = Uint8List(size);
port = ReceivePort();
inbox = StreamIterator<dynamic>(port);
@@ -121,24 +120,24 @@
}
}
- Uint8List data;
- ReceivePort port;
- StreamIterator<dynamic> inbox;
- SendPort outbox;
- Isolate worker;
- Completer<bool> workerCompleted;
- ReceivePort workerExitedPort;
+ late Uint8List data;
+ late ReceivePort port;
+ late StreamIterator<dynamic> inbox;
+ late SendPort outbox;
+ late Isolate worker;
+ late Completer<bool> workerCompleted;
+ late ReceivePort workerExitedPort;
final int size;
final bool useTransferable;
}
-packageList(Uint8List data, bool useTransferable) =>
+Object packageList(Uint8List data, bool useTransferable) =>
useTransferable ? TransferableTypedData.fromList(<Uint8List>[data]) : data;
Future<void> isolate(StartMessage startMessage) async {
final port = ReceivePort();
final inbox = StreamIterator<dynamic>(port);
- final data = Uint8List.view(new Uint8List(startMessage.size).buffer);
+ final data = Uint8List.view(Uint8List(startMessage.size).buffer);
startMessage.sendPort.send(port.sendPort);
while (true) {
@@ -163,22 +162,22 @@
final String name;
}
-final List<SizeName> sizes = <SizeName>[
- SizeName(1 * 1024, "1KB"),
- SizeName(10 * 1024, "10KB"),
- SizeName(100 * 1024, "100KB"),
- SizeName(1 * 1024 * 1024, "1MB"),
- SizeName(10 * 1024 * 1024, "10MB"),
- SizeName(100 * 1024 * 1024, "100MB")
+const List<SizeName> sizes = <SizeName>[
+ SizeName(1 * 1024, '1KB'),
+ SizeName(10 * 1024, '10KB'),
+ SizeName(100 * 1024, '100KB'),
+ SizeName(1 * 1024 * 1024, '1MB'),
+ SizeName(10 * 1024 * 1024, '10MB'),
+ SizeName(100 * 1024 * 1024, '100MB')
];
Future<void> main() async {
- for (SizeName sizeName in sizes) {
- await SendReceiveBytes("Isolate.SendReceiveBytes${sizeName.name}",
+ for (final sizeName in sizes) {
+ await SendReceiveBytes('Isolate.SendReceiveBytes${sizeName.name}',
size: sizeName.size, useTransferable: false)
.report();
await SendReceiveBytes(
- "Isolate.SendReceiveBytesTransferable${sizeName.name}",
+ 'Isolate.SendReceiveBytesTransferable${sizeName.name}',
size: sizeName.size,
useTransferable: true)
.report();
diff --git a/benchmarks/Isolate/dart2/Isolate.dart b/benchmarks/Isolate/dart2/Isolate.dart
index 465d56a..aba6224 100644
--- a/benchmarks/Isolate/dart2/Isolate.dart
+++ b/benchmarks/Isolate/dart2/Isolate.dart
@@ -12,7 +12,7 @@
class SendReceiveBytes extends AsyncBenchmarkBase {
SendReceiveBytes(String name,
- {@required int this.size, @required bool this.useTransferable})
+ {@required this.size, @required this.useTransferable})
: super(name);
@override
@@ -86,10 +86,10 @@
// Measures how long sending and receiving of [size]-length Uint8List takes.
class SendReceiveHelper {
- SendReceiveHelper(this.size, {@required bool this.useTransferable});
+ SendReceiveHelper(this.size, {@required this.useTransferable});
Future<void> setup() async {
- data = new Uint8List(size);
+ data = Uint8List(size);
port = ReceivePort();
inbox = StreamIterator<dynamic>(port);
@@ -132,13 +132,13 @@
final bool useTransferable;
}
-packageList(Uint8List data, bool useTransferable) =>
+Object packageList(Uint8List data, bool useTransferable) =>
useTransferable ? TransferableTypedData.fromList(<Uint8List>[data]) : data;
Future<void> isolate(StartMessage startMessage) async {
final port = ReceivePort();
final inbox = StreamIterator<dynamic>(port);
- final data = Uint8List.view(new Uint8List(startMessage.size).buffer);
+ final data = Uint8List.view(Uint8List(startMessage.size).buffer);
startMessage.sendPort.send(port.sendPort);
while (true) {
@@ -163,22 +163,22 @@
final String name;
}
-final List<SizeName> sizes = <SizeName>[
- SizeName(1 * 1024, "1KB"),
- SizeName(10 * 1024, "10KB"),
- SizeName(100 * 1024, "100KB"),
- SizeName(1 * 1024 * 1024, "1MB"),
- SizeName(10 * 1024 * 1024, "10MB"),
- SizeName(100 * 1024 * 1024, "100MB")
+const List<SizeName> sizes = <SizeName>[
+ SizeName(1 * 1024, '1KB'),
+ SizeName(10 * 1024, '10KB'),
+ SizeName(100 * 1024, '100KB'),
+ SizeName(1 * 1024 * 1024, '1MB'),
+ SizeName(10 * 1024 * 1024, '10MB'),
+ SizeName(100 * 1024 * 1024, '100MB')
];
Future<void> main() async {
- for (SizeName sizeName in sizes) {
- await SendReceiveBytes("Isolate.SendReceiveBytes${sizeName.name}",
+ for (final sizeName in sizes) {
+ await SendReceiveBytes('Isolate.SendReceiveBytes${sizeName.name}',
size: sizeName.size, useTransferable: false)
.report();
await SendReceiveBytes(
- "Isolate.SendReceiveBytesTransferable${sizeName.name}",
+ 'Isolate.SendReceiveBytesTransferable${sizeName.name}',
size: sizeName.size,
useTransferable: true)
.report();
diff --git a/benchmarks/IsolateJson/dart/IsolateJson.dart b/benchmarks/IsolateJson/dart/IsolateJson.dart
index 1fa4919..5b5a9ee 100644
--- a/benchmarks/IsolateJson/dart/IsolateJson.dart
+++ b/benchmarks/IsolateJson/dart/IsolateJson.dart
@@ -9,15 +9,14 @@
import 'dart:typed_data';
import 'package:benchmark_harness/benchmark_harness.dart' show BenchmarkBase;
-import 'package:meta/meta.dart';
import 'runtime/tests/vm/dart/export_sendAndExit_helper.dart' show sendAndExit;
class JsonDecodingBenchmark {
JsonDecodingBenchmark(this.name,
- {@required this.sample,
- @required this.numTasks,
- @required this.useSendAndExit});
+ {required this.sample,
+ required this.numTasks,
+ required this.useSendAndExit});
Future<void> report() async {
final stopwatch = Stopwatch()..start();
@@ -30,7 +29,7 @@
await Future.wait(decodedFutures);
}
- print("$name(RunTime): ${stopwatch.elapsedMicroseconds} us.");
+ print('$name(RunTime): ${stopwatch.elapsedMicroseconds} us.');
}
final String name;
@@ -45,7 +44,7 @@
for (int i = 0; i < size; i++) {
map['$i'] = list;
}
- return utf8.encode(json.encode(map));
+ return utf8.encode(json.encode(map)) as Uint8List;
}
class JsonDecodeRequest {
@@ -73,7 +72,7 @@
workerExitedPort.close();
workerErroredPort.close();
await inbox.moveNext();
- final decodedJson = inbox.current;
+ final decodedJson = inbox.current as Map;
port.close();
return decodedJson;
}
@@ -89,7 +88,7 @@
class SyncJsonDecodingBenchmark extends BenchmarkBase {
SyncJsonDecodingBenchmark(String name,
- {@required this.sample, @required this.iterations})
+ {required this.sample, required this.iterations})
: super(name);
@override
@@ -116,43 +115,46 @@
Future<void> main() async {
final jsonString =
File('benchmarks/IsolateJson/dart/sample.json').readAsStringSync();
- final json250KB = utf8.encode(jsonString); // 294356 bytes
+ final json250KB = utf8.encode(jsonString) as Uint8List; // 294356 bytes
final decoded = json.decode(utf8.decode(json250KB));
final decoded1MB = <dynamic, dynamic>{
- "1": decoded["1"],
- "2": decoded["1"],
- "3": decoded["1"],
- "4": decoded["1"],
+ '1': decoded['1'],
+ '2': decoded['1'],
+ '3': decoded['1'],
+ '4': decoded['1'],
};
- final json1MB = utf8.encode(json.encode(decoded1MB)); // 1177397 bytes
- decoded["1"] = (decoded["1"] as List).sublist(0, 200);
- final json100KB = utf8.encode(json.encode(decoded)); // 104685 bytes
- decoded["1"] = (decoded["1"] as List).sublist(0, 100);
- final json50KB = utf8.encode(json.encode(decoded)); // 51760 bytes
+ final json1MB =
+ utf8.encode(json.encode(decoded1MB)) as Uint8List; // 1177397 bytes
+ decoded['1'] = (decoded['1'] as List).sublist(0, 200);
+ final json100KB =
+ utf8.encode(json.encode(decoded)) as Uint8List; // 104685 bytes
+ decoded['1'] = (decoded['1'] as List).sublist(0, 100);
+ final json50KB =
+ utf8.encode(json.encode(decoded)) as Uint8List; // 51760 bytes
final configs = <BenchmarkConfig>[
- BenchmarkConfig("50KB", json50KB),
- BenchmarkConfig("100KB", json100KB),
- BenchmarkConfig("250KB", json250KB),
- BenchmarkConfig("1MB", json1MB),
+ BenchmarkConfig('50KB', json50KB),
+ BenchmarkConfig('100KB', json100KB),
+ BenchmarkConfig('250KB', json250KB),
+ BenchmarkConfig('1MB', json1MB),
];
- for (BenchmarkConfig config in configs) {
+ for (final config in configs) {
for (final iterations in <int>[1, 4]) {
await JsonDecodingBenchmark(
- "IsolateJson.Decode${config.suffix}x$iterations",
+ 'IsolateJson.Decode${config.suffix}x$iterations',
useSendAndExit: false,
sample: config.sample,
numTasks: iterations)
.report();
await JsonDecodingBenchmark(
- "IsolateJson.SendAndExit_Decode${config.suffix}x$iterations",
+ 'IsolateJson.SendAndExit_Decode${config.suffix}x$iterations',
useSendAndExit: true,
sample: config.sample,
numTasks: iterations)
.report();
SyncJsonDecodingBenchmark(
- "IsolateJson.SyncDecode${config.suffix}x$iterations",
+ 'IsolateJson.SyncDecode${config.suffix}x$iterations',
sample: config.sample,
iterations: iterations)
.report();
diff --git a/benchmarks/IsolateJson/dart2/IsolateJson.dart b/benchmarks/IsolateJson/dart2/IsolateJson.dart
index 459c133..4e64477 100644
--- a/benchmarks/IsolateJson/dart2/IsolateJson.dart
+++ b/benchmarks/IsolateJson/dart2/IsolateJson.dart
@@ -30,7 +30,7 @@
await Future.wait(decodedFutures);
}
- print("$name(RunTime): ${stopwatch.elapsedMicroseconds} us.");
+ print('$name(RunTime): ${stopwatch.elapsedMicroseconds} us.');
}
final String name;
@@ -119,40 +119,40 @@
final json250KB = utf8.encode(jsonString); // 294356 bytes
final decoded = json.decode(utf8.decode(json250KB));
final decoded1MB = <dynamic, dynamic>{
- "1": decoded["1"],
- "2": decoded["1"],
- "3": decoded["1"],
- "4": decoded["1"],
+ '1': decoded['1'],
+ '2': decoded['1'],
+ '3': decoded['1'],
+ '4': decoded['1'],
};
final json1MB = utf8.encode(json.encode(decoded1MB)); // 1177397 bytes
- decoded["1"] = (decoded["1"] as List).sublist(0, 200);
+ decoded['1'] = (decoded['1'] as List).sublist(0, 200);
final json100KB = utf8.encode(json.encode(decoded)); // 104685 bytes
- decoded["1"] = (decoded["1"] as List).sublist(0, 100);
+ decoded['1'] = (decoded['1'] as List).sublist(0, 100);
final json50KB = utf8.encode(json.encode(decoded)); // 51760 bytes
final configs = <BenchmarkConfig>[
- BenchmarkConfig("50KB", json50KB),
- BenchmarkConfig("100KB", json100KB),
- BenchmarkConfig("250KB", json250KB),
- BenchmarkConfig("1MB", json1MB),
+ BenchmarkConfig('50KB', json50KB),
+ BenchmarkConfig('100KB', json100KB),
+ BenchmarkConfig('250KB', json250KB),
+ BenchmarkConfig('1MB', json1MB),
];
- for (BenchmarkConfig config in configs) {
+ for (final config in configs) {
for (final iterations in <int>[1, 4]) {
await JsonDecodingBenchmark(
- "IsolateJson.Decode${config.suffix}x$iterations",
+ 'IsolateJson.Decode${config.suffix}x$iterations',
useSendAndExit: false,
sample: config.sample,
numTasks: iterations)
.report();
await JsonDecodingBenchmark(
- "IsolateJson.SendAndExit_Decode${config.suffix}x$iterations",
+ 'IsolateJson.SendAndExit_Decode${config.suffix}x$iterations',
useSendAndExit: true,
sample: config.sample,
numTasks: iterations)
.report();
SyncJsonDecodingBenchmark(
- "IsolateJson.SyncDecode${config.suffix}x$iterations",
+ 'IsolateJson.SyncDecode${config.suffix}x$iterations',
sample: config.sample,
iterations: iterations)
.report();
diff --git a/benchmarks/IsolateSpawn/dart/IsolateSpawn.dart b/benchmarks/IsolateSpawn/dart/IsolateSpawn.dart
index 5742568..6212abe 100644
--- a/benchmarks/IsolateSpawn/dart/IsolateSpawn.dart
+++ b/benchmarks/IsolateSpawn/dart/IsolateSpawn.dart
@@ -6,8 +6,6 @@
import 'dart:isolate';
import 'dart:math';
-import 'package:meta/meta.dart';
-
import 'package:compiler/src/dart2js.dart' as dart2js_main;
class SpawnLatency {
@@ -16,17 +14,17 @@
Future<ResultMessageLatency> run() async {
final completerResult = Completer();
final receivePort = ReceivePort()..listen(completerResult.complete);
- final Completer<DateTime> isolateExitedCompleter = Completer<DateTime>();
+ final isolateExitedCompleter = Completer<DateTime>();
final onExitReceivePort = ReceivePort()
..listen((_) {
isolateExitedCompleter.complete(DateTime.now());
});
- final DateTime beforeSpawn = DateTime.now();
+ final beforeSpawn = DateTime.now();
await Isolate.spawn(
isolateCompiler, StartMessageLatency(receivePort.sendPort, beforeSpawn),
onExit: onExitReceivePort.sendPort,
onError: onExitReceivePort.sendPort);
- final DateTime afterSpawn = DateTime.now();
+ final afterSpawn = DateTime.now();
final ResultMessageLatency result = await completerResult.future;
receivePort.close();
@@ -42,13 +40,13 @@
Future<AggregatedResultMessageLatency> measureFor(int minimumMillis) async {
final minimumMicros = minimumMillis * 1000;
final watch = Stopwatch()..start();
- final Metric toAfterIsolateSpawnUs = LatencyMetric("${name}ToAfterSpawn");
- final Metric toStartRunningCodeUs = LatencyMetric("${name}ToStartRunning");
+ final Metric toAfterIsolateSpawnUs = LatencyMetric('${name}ToAfterSpawn');
+ final Metric toStartRunningCodeUs = LatencyMetric('${name}ToStartRunning');
final Metric toFinishRunningCodeUs =
- LatencyMetric("${name}ToFinishRunning");
- final Metric toExitUs = LatencyMetric("${name}ToExit");
+ LatencyMetric('${name}ToFinishRunning');
+ final Metric toExitUs = LatencyMetric('${name}ToExit');
while (watch.elapsedMicroseconds < minimumMicros) {
- final ResultMessageLatency result = await run();
+ final result = await run();
toAfterIsolateSpawnUs.add(result.timeToIsolateSpawnUs);
toStartRunningCodeUs.add(result.timeToStartRunningCodeUs);
toFinishRunningCodeUs.add(result.timeToFinishRunningCodeUs);
@@ -64,16 +62,16 @@
}
Future<void> report() async {
- final AggregatedResultMessageLatency result = await measure();
+ final result = await measure();
print(result);
}
final String name;
- RawReceivePort receivePort;
+ late RawReceivePort receivePort;
}
class Metric {
- Metric({@required this.prefix, @required this.suffix});
+ Metric({required this.prefix, required this.suffix});
void add(int value) {
if (value > max) {
@@ -87,9 +85,10 @@
double _average() => sum / count;
double _rms() => sqrt(sumOfSquares / count);
- toString() => "$prefix): ${_average()}$suffix\n"
- "${prefix}Max): $max$suffix\n"
- "${prefix}RMS): ${_rms()}$suffix";
+ @override
+ String toString() => '$prefix): ${_average()}$suffix\n'
+ '${prefix}Max): $max$suffix\n'
+ '${prefix}RMS): ${_rms()}$suffix';
final String prefix;
final String suffix;
@@ -100,7 +99,7 @@
}
class LatencyMetric extends Metric {
- LatencyMetric(String name) : super(prefix: "$name(Latency", suffix: " us.");
+ LatencyMetric(String name) : super(prefix: '$name(Latency', suffix: ' us.');
}
class StartMessageLatency {
@@ -112,16 +111,14 @@
class ResultMessageLatency {
ResultMessageLatency(
- {this.timeToStartRunningCodeUs,
- this.timeToFinishRunningCodeUs,
- this.deltaHeap});
+ {required this.timeToStartRunningCodeUs,
+ required this.timeToFinishRunningCodeUs});
final int timeToStartRunningCodeUs;
final int timeToFinishRunningCodeUs;
- final int deltaHeap;
- int timeToIsolateSpawnUs;
- int timeToExitUs;
+ late int timeToIsolateSpawnUs;
+ late int timeToExitUs;
}
class AggregatedResultMessageLatency {
@@ -132,10 +129,11 @@
this.toExitUs,
);
- String toString() => """$toAfterIsolateSpawnUs
+ @override
+ String toString() => '''$toAfterIsolateSpawnUs
$toStartRunningCodeUs
$toFinishRunningCodeUs
-$toExitUs""";
+$toExitUs''';
final Metric toAfterIsolateSpawnUs;
final Metric toStartRunningCodeUs;
@@ -144,15 +142,15 @@
}
Future<void> isolateCompiler(StartMessageLatency start) async {
- final DateTime timeRunningCodeUs = DateTime.now();
+ final timeRunningCodeUs = DateTime.now();
await runZoned(
() => dart2js_main.internalMain(<String>[
- "benchmarks/IsolateSpawn/dart/helloworld.dart",
+ 'benchmarks/IsolateSpawn/dart/helloworld.dart',
'--libraries-spec=sdk/lib/libraries.json'
]),
zoneSpecification: ZoneSpecification(
print: (Zone self, ZoneDelegate parent, Zone zone, String line) {}));
- final DateTime timeFinishRunningCodeUs = DateTime.now();
+ final timeFinishRunningCodeUs = DateTime.now();
start.sendPort.send(ResultMessageLatency(
timeToStartRunningCodeUs:
timeRunningCodeUs.difference(start.spawned).inMicroseconds,
@@ -161,5 +159,5 @@
}
Future<void> main() async {
- await SpawnLatency("IsolateSpawn.Dart2JS").report();
+ await SpawnLatency('IsolateSpawn.Dart2JS').report();
}
diff --git a/benchmarks/IsolateSpawn/dart2/IsolateSpawn.dart b/benchmarks/IsolateSpawn/dart2/IsolateSpawn.dart
index e8ec25c..a754441 100644
--- a/benchmarks/IsolateSpawn/dart2/IsolateSpawn.dart
+++ b/benchmarks/IsolateSpawn/dart2/IsolateSpawn.dart
@@ -16,17 +16,17 @@
Future<ResultMessageLatency> run() async {
final completerResult = Completer();
final receivePort = ReceivePort()..listen(completerResult.complete);
- final Completer<DateTime> isolateExitedCompleter = Completer<DateTime>();
+ final isolateExitedCompleter = Completer<DateTime>();
final onExitReceivePort = ReceivePort()
..listen((_) {
isolateExitedCompleter.complete(DateTime.now());
});
- final DateTime beforeSpawn = DateTime.now();
+ final beforeSpawn = DateTime.now();
await Isolate.spawn(
isolateCompiler, StartMessageLatency(receivePort.sendPort, beforeSpawn),
onExit: onExitReceivePort.sendPort,
onError: onExitReceivePort.sendPort);
- final DateTime afterSpawn = DateTime.now();
+ final afterSpawn = DateTime.now();
final ResultMessageLatency result = await completerResult.future;
receivePort.close();
@@ -42,13 +42,13 @@
Future<AggregatedResultMessageLatency> measureFor(int minimumMillis) async {
final minimumMicros = minimumMillis * 1000;
final watch = Stopwatch()..start();
- final Metric toAfterIsolateSpawnUs = LatencyMetric("${name}ToAfterSpawn");
- final Metric toStartRunningCodeUs = LatencyMetric("${name}ToStartRunning");
+ final Metric toAfterIsolateSpawnUs = LatencyMetric('${name}ToAfterSpawn');
+ final Metric toStartRunningCodeUs = LatencyMetric('${name}ToStartRunning');
final Metric toFinishRunningCodeUs =
- LatencyMetric("${name}ToFinishRunning");
- final Metric toExitUs = LatencyMetric("${name}ToExit");
+ LatencyMetric('${name}ToFinishRunning');
+ final Metric toExitUs = LatencyMetric('${name}ToExit');
while (watch.elapsedMicroseconds < minimumMicros) {
- final ResultMessageLatency result = await run();
+ final result = await run();
toAfterIsolateSpawnUs.add(result.timeToIsolateSpawnUs);
toStartRunningCodeUs.add(result.timeToStartRunningCodeUs);
toFinishRunningCodeUs.add(result.timeToFinishRunningCodeUs);
@@ -64,7 +64,7 @@
}
Future<void> report() async {
- final AggregatedResultMessageLatency result = await measure();
+ final result = await measure();
print(result);
}
@@ -87,9 +87,10 @@
double _average() => sum / count;
double _rms() => sqrt(sumOfSquares / count);
- toString() => "$prefix): ${_average()}$suffix\n"
- "${prefix}Max): $max$suffix\n"
- "${prefix}RMS): ${_rms()}$suffix";
+ @override
+ String toString() => '$prefix): ${_average()}$suffix\n'
+ '${prefix}Max): $max$suffix\n'
+ '${prefix}RMS): ${_rms()}$suffix';
final String prefix;
final String suffix;
@@ -100,7 +101,7 @@
}
class LatencyMetric extends Metric {
- LatencyMetric(String name) : super(prefix: "$name(Latency", suffix: " us.");
+ LatencyMetric(String name) : super(prefix: '$name(Latency', suffix: ' us.');
}
class StartMessageLatency {
@@ -132,10 +133,11 @@
this.toExitUs,
);
- String toString() => """$toAfterIsolateSpawnUs
+ @override
+ String toString() => '''$toAfterIsolateSpawnUs
$toStartRunningCodeUs
$toFinishRunningCodeUs
-$toExitUs""";
+$toExitUs''';
final Metric toAfterIsolateSpawnUs;
final Metric toStartRunningCodeUs;
@@ -144,15 +146,15 @@
}
Future<void> isolateCompiler(StartMessageLatency start) async {
- final DateTime timeRunningCodeUs = DateTime.now();
+ final timeRunningCodeUs = DateTime.now();
await runZoned(
() => dart2js_main.internalMain(<String>[
- "benchmarks/IsolateSpawn/dart2/helloworld.dart",
+ 'benchmarks/IsolateSpawn/dart/helloworld.dart',
'--libraries-spec=sdk/lib/libraries.json'
]),
zoneSpecification: ZoneSpecification(
print: (Zone self, ZoneDelegate parent, Zone zone, String line) {}));
- final DateTime timeFinishRunningCodeUs = DateTime.now();
+ final timeFinishRunningCodeUs = DateTime.now();
start.sendPort.send(ResultMessageLatency(
timeToStartRunningCodeUs:
timeRunningCodeUs.difference(start.spawned).inMicroseconds,
@@ -161,5 +163,5 @@
}
Future<void> main() async {
- await SpawnLatency("IsolateSpawn.Dart2JS").report();
+ await SpawnLatency('IsolateSpawn.Dart2JS').report();
}
diff --git a/benchmarks/IsolateSpawnMemory/dart/IsolateSpawnMemory.dart b/benchmarks/IsolateSpawnMemory/dart/IsolateSpawnMemory.dart
index 322f1af..1f7b009 100644
--- a/benchmarks/IsolateSpawnMemory/dart/IsolateSpawnMemory.dart
+++ b/benchmarks/IsolateSpawnMemory/dart/IsolateSpawnMemory.dart
@@ -96,11 +96,11 @@
final doneDiffHeap =
math.max(0, doneHeap - beforeHeap) ~/ numberOfBenchmarks;
- print("${name}RssOnStart(MemoryUse): $readyDiffRss");
- print("${name}RssOnEnd(MemoryUse): $doneDiffRss");
- print("${name}HeapOnStart(MemoryUse): $readyDiffHeap");
- print("${name}HeapOnEnd(MemoryUse): $doneDiffHeap");
- print("${name}PeakProcessRss(MemoryUse): $maxProcessRss");
+ print('${name}RssOnStart(MemoryUse): $readyDiffRss');
+ print('${name}RssOnEnd(MemoryUse): $doneDiffRss');
+ print('${name}HeapOnStart(MemoryUse): $readyDiffHeap');
+ print('${name}HeapOnEnd(MemoryUse): $doneDiffHeap');
+ print('${name}PeakProcessRss(MemoryUse): $maxProcessRss');
}
final String name;
@@ -117,7 +117,7 @@
await runZoned(
() => dart2js_main.internalMain(<String>[
- "benchmarks/IsolateSpawnMemory/dart/helloworld.dart",
+ 'benchmarks/IsolateSpawnMemory/dart/helloworld.dart',
'--libraries-spec=sdk/lib/libraries.json'
]),
zoneSpecification: ZoneSpecification(
@@ -133,13 +133,11 @@
}
Future<int> currentHeapUsage(String wsUri) async {
- final vm_service.VmService vmService =
- await vm_service_io.vmServiceConnectUri(wsUri);
+ final vmService = await vm_service_io.vmServiceConnectUri(wsUri);
final groupIds = await getGroupIds(vmService);
int sum = 0;
for (final groupId in groupIds) {
- final vm_service.MemoryUsage usage =
- await vmService.getIsolateGroupMemoryUsage(groupId);
+ final usage = await vmService.getIsolateGroupMemoryUsage(groupId);
sum += usage.heapUsage + usage.externalUsage;
}
vmService.dispose();
@@ -150,11 +148,10 @@
// Only if we successfully reach the end will we set 0 exit code.
exitCode = 255;
- final ServiceProtocolInfo info = await Service.controlWebServer(enable: true);
- final Uri observatoryUri = info.serverUri;
- final String wsUri =
- 'ws://${observatoryUri.authority}${observatoryUri.path}ws';
- await SpawnMemory("IsolateSpawnMemory.Dart2JSDelta", wsUri).report();
+ final info = await Service.controlWebServer(enable: true);
+ final observatoryUri = info.serverUri!;
+ final wsUri = 'ws://${observatoryUri.authority}${observatoryUri.path}ws';
+ await SpawnMemory('IsolateSpawnMemory.Dart2JSDelta', wsUri).report();
// Only if we successfully reach the end will we set 0 exit code.
exitCode = 0;
@@ -174,10 +171,9 @@
Future<List<String>> getGroupIds(vm_service.VmService vmService) async {
final groupIds = <String>{};
final vm = await vmService.getVM();
- for (vm_service.IsolateGroupRef groupRef in vm.isolateGroups) {
- final vm_service.IsolateGroup group =
- await vmService.getIsolateGroup(groupRef.id);
- for (vm_service.IsolateRef isolateRef in group.isolates) {
+ for (final groupRef in vm.isolateGroups) {
+ final group = await vmService.getIsolateGroup(groupRef.id);
+ for (final isolateRef in group.isolates) {
final isolateOrSentinel = await vmService.getIsolate(isolateRef.id);
if (isolateOrSentinel is vm_service.Isolate) {
groupIds.add(groupRef.id);
@@ -185,7 +181,7 @@
}
}
if (groupIds.isEmpty) {
- throw "Could not find main isolate";
+ throw 'Could not find main isolate';
}
return groupIds.toList();
}
diff --git a/benchmarks/IsolateSpawnMemory/dart2/IsolateSpawnMemory.dart b/benchmarks/IsolateSpawnMemory/dart2/IsolateSpawnMemory.dart
index 609a8c2..5a13d96 100644
--- a/benchmarks/IsolateSpawnMemory/dart2/IsolateSpawnMemory.dart
+++ b/benchmarks/IsolateSpawnMemory/dart2/IsolateSpawnMemory.dart
@@ -96,11 +96,11 @@
final doneDiffHeap =
math.max(0, doneHeap - beforeHeap) ~/ numberOfBenchmarks;
- print("${name}RssOnStart(MemoryUse): $readyDiffRss");
- print("${name}RssOnEnd(MemoryUse): $doneDiffRss");
- print("${name}HeapOnStart(MemoryUse): $readyDiffHeap");
- print("${name}HeapOnEnd(MemoryUse): $doneDiffHeap");
- print("${name}PeakProcessRss(MemoryUse): $maxProcessRss");
+ print('${name}RssOnStart(MemoryUse): $readyDiffRss');
+ print('${name}RssOnEnd(MemoryUse): $doneDiffRss');
+ print('${name}HeapOnStart(MemoryUse): $readyDiffHeap');
+ print('${name}HeapOnEnd(MemoryUse): $doneDiffHeap');
+ print('${name}PeakProcessRss(MemoryUse): $maxProcessRss');
}
final String name;
@@ -117,7 +117,7 @@
await runZoned(
() => dart2js_main.internalMain(<String>[
- "benchmarks/IsolateSpawnMemory/dart2/helloworld.dart",
+ 'benchmarks/IsolateSpawnMemory/dart/helloworld.dart',
'--libraries-spec=sdk/lib/libraries.json'
]),
zoneSpecification: ZoneSpecification(
@@ -133,13 +133,11 @@
}
Future<int> currentHeapUsage(String wsUri) async {
- final vm_service.VmService vmService =
- await vm_service_io.vmServiceConnectUri(wsUri);
+ final vmService = await vm_service_io.vmServiceConnectUri(wsUri);
final groupIds = await getGroupIds(vmService);
int sum = 0;
for (final groupId in groupIds) {
- final vm_service.MemoryUsage usage =
- await vmService.getIsolateGroupMemoryUsage(groupId);
+ final usage = await vmService.getIsolateGroupMemoryUsage(groupId);
sum += usage.heapUsage + usage.externalUsage;
}
vmService.dispose();
@@ -150,11 +148,10 @@
// Only if we successfully reach the end will we set 0 exit code.
exitCode = 255;
- final ServiceProtocolInfo info = await Service.controlWebServer(enable: true);
- final Uri observatoryUri = info.serverUri;
- final String wsUri =
- 'ws://${observatoryUri.authority}${observatoryUri.path}ws';
- await SpawnMemory("IsolateSpawnMemory.Dart2JSDelta", wsUri).report();
+ final info = await Service.controlWebServer(enable: true);
+ final observatoryUri = info.serverUri;
+ final wsUri = 'ws://${observatoryUri.authority}${observatoryUri.path}ws';
+ await SpawnMemory('IsolateSpawnMemory.Dart2JSDelta', wsUri).report();
// Only if we successfully reach the end will we set 0 exit code.
exitCode = 0;
@@ -174,10 +171,9 @@
Future<List<String>> getGroupIds(vm_service.VmService vmService) async {
final groupIds = <String>{};
final vm = await vmService.getVM();
- for (vm_service.IsolateGroupRef groupRef in vm.isolateGroups) {
- final vm_service.IsolateGroup group =
- await vmService.getIsolateGroup(groupRef.id);
- for (vm_service.IsolateRef isolateRef in group.isolates) {
+ for (final groupRef in vm.isolateGroups) {
+ final group = await vmService.getIsolateGroup(groupRef.id);
+ for (final isolateRef in group.isolates) {
final isolateOrSentinel = await vmService.getIsolate(isolateRef.id);
if (isolateOrSentinel is vm_service.Isolate) {
groupIds.add(groupRef.id);
@@ -185,7 +181,7 @@
}
}
if (groupIds.isEmpty) {
- throw "Could not find main isolate";
+ throw 'Could not find main isolate';
}
return groupIds.toList();
}
diff --git a/benchmarks/analysis_options.yaml b/benchmarks/analysis_options.yaml
index 8099cf0..e7100d3 100644
--- a/benchmarks/analysis_options.yaml
+++ b/benchmarks/analysis_options.yaml
@@ -1,11 +1,15 @@
-include: package:pedantic/analysis_options.yaml
+# 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.
+
#analyzer:
# strong-mode:
# implicit-casts: false
linter:
rules:
- #- always_declare_return_types
- #- annotate_overrides
+ - always_declare_return_types
+ - always_require_non_null_named_parameters
+ - annotate_overrides
- avoid_empty_else
- avoid_function_literals_in_foreach_calls
- avoid_init_to_null
@@ -19,11 +23,13 @@
- avoid_types_as_parameter_names
- avoid_unused_constructor_parameters
- await_only_futures
+ - camel_case_extensions
- camel_case_types
- cancel_subscriptions
- comment_references
#- constant_identifier_names
- control_flow_in_finally
+ - curly_braces_in_flow_control_structures
- directives_ordering
- empty_catches
- empty_constructor_bodies
@@ -35,7 +41,7 @@
- library_names
- library_prefixes
- list_remove_unrelated_type
- - literal_only_boolean_expressions
+ #- literal_only_boolean_expressions
- no_adjacent_strings_in_list
- no_duplicate_case_values
#- non_constant_identifier_names
@@ -52,14 +58,18 @@
- prefer_const_constructors
- prefer_contains
- prefer_equal_for_default_values
- - prefer_generic_function_type_aliases
- prefer_final_fields
- prefer_final_locals
+ - prefer_for_elements_to_map_fromIterable
+ - prefer_generic_function_type_aliases
+ - prefer_if_null_operators
- prefer_initializing_formals
- prefer_interpolation_to_compose_strings
- prefer_is_empty
- prefer_is_not_empty
- #- prefer_single_quotes
+ - prefer_iterable_whereType
+ - prefer_single_quotes
+ - prefer_spread_collections
#- prefer_typing_uninitialized_variables
- recursive_getters
- slash_for_doc_comments
@@ -67,16 +77,17 @@
- throw_in_finally
- type_init_formals
- unawaited_futures
- - unnecessary_await_in_return
+ #- unnecessary_await_in_return
- unnecessary_brace_in_string_interps
- unnecessary_const
- unnecessary_getters_setters
- unnecessary_lambdas
- unnecessary_new
- unnecessary_null_aware_assignments
+ - unnecessary_null_in_if_null_operators
- unnecessary_parenthesis
- unnecessary_statements
- #- unnecessary_this
+ - unnecessary_this
- unrelated_type_equality_checks
- use_function_type_syntax_for_parameters
- use_rethrow_when_possible
diff --git a/pkg/_js_interop_checks/pubspec.yaml b/pkg/_js_interop_checks/pubspec.yaml
index abca579..336d433 100644
--- a/pkg/_js_interop_checks/pubspec.yaml
+++ b/pkg/_js_interop_checks/pubspec.yaml
@@ -1,4 +1,5 @@
name: _js_interop_checks
+# This package is not intended for consumption on pub.dev. DO NOT publish.
publish_to: none
environment:
diff --git a/pkg/analysis_server/lib/src/cider/completion.dart b/pkg/analysis_server/lib/src/cider/completion.dart
index bd1f8d5..51d7bfb 100644
--- a/pkg/analysis_server/lib/src/cider/completion.dart
+++ b/pkg/analysis_server/lib/src/cider/completion.dart
@@ -14,6 +14,7 @@
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/element/element.dart' show LibraryElement;
import 'package:analyzer/src/dart/analysis/performance_logger.dart';
+import 'package:analyzer/src/dart/micro/performance.dart';
import 'package:analyzer/src/dart/micro/resolve_file.dart';
import 'package:analyzer/src/dartdoc/dartdoc_directive_info.dart';
import 'package:analyzer/src/test_utilities/function_ast_visitor.dart';
@@ -33,6 +34,9 @@
final CiderCompletionCache _cache;
final FileResolver _fileResolver;
+ final CiderOperationPerformanceImpl _performanceRoot =
+ CiderOperationPerformanceImpl('<root>');
+
DartCompletionRequestImpl _dartCompletionRequest;
/// Paths of imported libraries for which suggestions were (re)computed
@@ -55,91 +59,109 @@
@required int line,
@required int column,
}) async {
- var getFileTimer = Stopwatch()..start();
- var fileContext = _logger.run('Get file $path', () {
- try {
- return _fileResolver.getFileContext(path);
- } finally {
- getFileTimer.stop();
- }
- });
-
- var file = fileContext.file;
-
- var lineInfo = file.lineInfo;
- var offset = lineInfo.getOffsetOfLine(line) + column;
-
- var resolutionTimer = Stopwatch()..start();
- var resolvedUnit = _fileResolver.resolve(path);
- resolutionTimer.stop();
-
- var completionRequest = CompletionRequestImpl(
- resolvedUnit,
- offset,
- false,
- CompletionPerformance(),
- );
- var dartdocDirectiveInfo = DartdocDirectiveInfo();
-
- var suggestionsTimer = Stopwatch()..start();
- var suggestions = await _logger.runAsync('Compute suggestions', () async {
- var includedElementKinds = <ElementKind>{};
- var includedElementNames = <String>{};
- var includedSuggestionRelevanceTags = <IncludedSuggestionRelevanceTag>[];
-
- var manager = DartCompletionManager(
- dartdocDirectiveInfo: dartdocDirectiveInfo,
- includedElementKinds: includedElementKinds,
- includedElementNames: includedElementNames,
- includedSuggestionRelevanceTags: includedSuggestionRelevanceTags,
- );
-
- return await manager.computeSuggestions(
- completionRequest,
- enableUriContributor: false,
- );
- });
- suggestionsTimer.stop();
-
- _dartCompletionRequest = await DartCompletionRequestImpl.from(
- completionRequest,
- dartdocDirectiveInfo,
- );
-
- var importsTimer = Stopwatch();
- if (_dartCompletionRequest.includeIdentifiers) {
- _logger.run('Add imported suggestions', () {
- importsTimer.start();
- suggestions.addAll(
- _importedLibrariesSuggestions(
- resolvedUnit.libraryElement,
- ),
+ return _performanceRoot.runAsync('completion', (performance) async {
+ var fileContext = _logger.run('Get file $path', () {
+ return _fileResolver.getFileContext(
+ path: path,
+ performance: performance,
);
- importsTimer.stop();
});
- }
- var filter = _FilterSort(
- _dartCompletionRequest,
- suggestions,
- );
+ var file = fileContext.file;
- _logger.run('Filter suggestions', () {
- suggestions = filter.perform();
+ var lineInfo = file.lineInfo;
+ var offset = lineInfo.getOffsetOfLine(line) + column;
+
+ var resolvedUnit = performance.run('resolution', (performance) {
+ return _fileResolver.resolve2(
+ path: path,
+ performance: performance,
+ );
+ });
+
+ var completionRequest = CompletionRequestImpl(
+ resolvedUnit,
+ offset,
+ false,
+ CompletionPerformance(),
+ );
+ var dartdocDirectiveInfo = DartdocDirectiveInfo();
+
+ var suggestions = await performance.runAsync(
+ 'suggestions',
+ (performance) async {
+ var result = await _logger.runAsync('Compute suggestions', () async {
+ var includedElementKinds = <ElementKind>{};
+ var includedElementNames = <String>{};
+ var includedSuggestionRelevanceTags =
+ <IncludedSuggestionRelevanceTag>[];
+
+ var manager = DartCompletionManager(
+ dartdocDirectiveInfo: dartdocDirectiveInfo,
+ includedElementKinds: includedElementKinds,
+ includedElementNames: includedElementNames,
+ includedSuggestionRelevanceTags: includedSuggestionRelevanceTags,
+ );
+
+ return await manager.computeSuggestions(
+ completionRequest,
+ enableUriContributor: false,
+ );
+ });
+
+ for (var operation in completionRequest.performance.operations) {
+ performance.addChildFixed(
+ operation.name,
+ operation.elapsed,
+ );
+ }
+
+ return result;
+ },
+ );
+
+ _dartCompletionRequest = await DartCompletionRequestImpl.from(
+ completionRequest,
+ dartdocDirectiveInfo,
+ );
+
+ performance.run('imports', (performance) {
+ if (_dartCompletionRequest.includeIdentifiers) {
+ _logger.run('Add imported suggestions', () {
+ suggestions.addAll(
+ _importedLibrariesSuggestions(
+ resolvedUnit.libraryElement,
+ ),
+ );
+ });
+ }
+ });
+
+ var filter = _FilterSort(
+ _dartCompletionRequest,
+ suggestions,
+ );
+
+ performance.run('filter', (performance) {
+ _logger.run('Filter suggestions', () {
+ suggestions = filter.perform();
+ });
+ });
+
+ var result = CiderCompletionResult._(
+ suggestions: suggestions,
+ performance: CiderCompletionPerformance._(
+ file: performance.getChild('fileContext').elapsed,
+ imports: performance.getChild('imports').elapsed,
+ resolution: performance.getChild('resolution').elapsed,
+ suggestions: performance.getChild('suggestions').elapsed,
+ operations: _performanceRoot.children.first,
+ ),
+ prefixStart: CiderPosition(line, column - filter._pattern.length),
+ );
+
+ return result;
});
-
- var result = CiderCompletionResult._(
- suggestions: suggestions,
- performance: CiderCompletionPerformance(
- file: getFileTimer.elapsed,
- imports: importsTimer.elapsed,
- resolution: resolutionTimer.elapsed,
- suggestions: suggestionsTimer.elapsed,
- ),
- prefixStart: CiderPosition(line, column - filter._pattern.length),
- );
-
- return result;
}
@Deprecated('Use compute')
@@ -151,6 +173,17 @@
return compute(path: path, line: line, column: column);
}
+ /// Prepare for computing completions in files from the [pathList].
+ ///
+ /// This method might be called when we are finishing a large initial
+ /// analysis, so spending additionally a fraction of this time to make
+ /// any subsequent completion seem fast is a reasonable trade-off.
+ Future<void> warmUp(List<String> pathList) async {
+ for (var path in pathList) {
+ await compute(path: path, line: 0, column: 0);
+ }
+ }
+
/// Return suggestions from libraries imported into the [target].
///
/// TODO(scheglov) Implement show / hide combinators.
@@ -214,11 +247,15 @@
/// The elapsed time to compute suggestions.
final Duration suggestions;
- CiderCompletionPerformance({
+ /// The tree of operation performances.
+ final CiderOperationPerformance operations;
+
+ CiderCompletionPerformance._({
@required this.file,
@required this.imports,
@required this.resolution,
@required this.suggestions,
+ @required this.operations,
});
}
diff --git a/pkg/analysis_server/lib/src/cider/fixes.dart b/pkg/analysis_server/lib/src/cider/fixes.dart
index 5a19221..29205e0 100644
--- a/pkg/analysis_server/lib/src/cider/fixes.dart
+++ b/pkg/analysis_server/lib/src/cider/fixes.dart
@@ -34,7 +34,7 @@
/// Compute quick fixes for errors on the line with the [offset].
Future<List<CiderErrorFixes>> compute(String path, int offset) async {
var result = <CiderErrorFixes>[];
- var resolvedUnit = _fileResolver.resolve(path);
+ var resolvedUnit = _fileResolver.resolve2(path: path);
var lineInfo = resolvedUnit.lineInfo;
var requestLine = lineInfo.getLocation(offset).lineNumber;
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart
index 5fe7fa0..c83138a 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart
@@ -362,26 +362,38 @@
}
/// Return the pattern to match suggestions against, from the identifier
- /// to the left of the caret. Return the empty string if cannot find the
- /// identifier.
+ /// to the left of (or spanning) the caret. Return the empty string if cannot
+ /// find the identifier.
+ ///
+ /// If the caret is within the identifier, the returned pattern will be truncated
+ /// to the position of the caret. For example at:
+ ///
+ /// new MyClass^Foo
+ ///
+ /// will return "MyClass" as the search pattern.
String _prefixMatchingPattern(DartCompletionRequestImpl request) {
- final nodeAtOffsetVisitor =
- _IdentifierEndingAtOffsetVisitor(request.offset);
- request.target.containingNode.accept(nodeAtOffsetVisitor);
+ final nodeSpanningOffsetVisitor =
+ _IdentifierSpanningOffsetVisitor(request.offset);
+ request.target.containingNode.accept(nodeSpanningOffsetVisitor);
+ final node = nodeSpanningOffsetVisitor.matchingNode;
- return nodeAtOffsetVisitor.matchingNode?.name ?? '';
+ final prefix = node != null && request.offset - node.offset < node.length
+ ? node.name.substring(0, request.offset - node.offset)
+ : node?.name;
+
+ return prefix ?? '';
}
}
-/// An AST visitor to locate a [SimpleIdentifier] that ends at the provided offset.
-class _IdentifierEndingAtOffsetVisitor extends RecursiveAstVisitor<void> {
+/// An AST visitor to locate a [SimpleIdentifier] that spans the provided offset.
+class _IdentifierSpanningOffsetVisitor extends RecursiveAstVisitor<void> {
final int offset;
SimpleIdentifier _matchingNode;
- _IdentifierEndingAtOffsetVisitor(this.offset);
+ _IdentifierSpanningOffsetVisitor(this.offset);
SimpleIdentifier get matchingNode => _matchingNode;
@override
void visitSimpleIdentifier(SimpleIdentifier node) {
- if (node.end == offset) {
+ if (node.offset <= offset && node.end >= offset) {
_matchingNode = node;
}
}
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/feature_computer.dart b/pkg/analysis_server/lib/src/services/completion/dart/feature_computer.dart
index e9857ad..944f8cc 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/feature_computer.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/feature_computer.dart
@@ -29,6 +29,40 @@
import 'package:analyzer/src/dart/resolver/body_inference_context.dart';
import 'package:analyzer_plugin/utilities/range_factory.dart';
+const List<String> intNames = ['i', 'j', 'index', 'length'];
+const List<String> listNames = ['list', 'items'];
+const List<String> numNames = ['height', 'width'];
+const List<String> stringNames = [
+ 'key',
+ 'text',
+ 'url',
+ 'uri',
+ 'name',
+ 'str',
+ 'string'
+];
+
+DartType impliedDartTypeWithName(TypeProvider typeProvider, String name) {
+ if (typeProvider == null || name == null || name.isEmpty) {
+ return null;
+ }
+ if (intNames.contains(name)) {
+ return typeProvider.intType;
+ } else if (numNames.contains(name)) {
+ return typeProvider.numType;
+ } else if (listNames.contains(name)) {
+ return typeProvider.listType2(typeProvider.dynamicType);
+ } else if (stringNames.contains(name)) {
+ return typeProvider.stringType;
+ } else if (name == 'iterator') {
+ return typeProvider.iterableDynamicType;
+ } else if (name == 'map') {
+ return typeProvider.mapType2(
+ typeProvider.dynamicType, typeProvider.dynamicType);
+ }
+ return null;
+}
+
/// Convert a relevance score (assumed to be between `0.0` and `1.0` inclusive)
/// to a relevance value between `0` and `1000`. If the score is outside that
/// range, return the [defaultValue].
@@ -225,6 +259,33 @@
return 0.0;
}
+ /// Return the value of the _keyword_ feature for the [keyword] when
+ /// completing at the given [completionLocation].
+ double keywordFeature(String keyword, String completionLocation) {
+ if (completionLocation == null) {
+ return -1.0;
+ }
+ var locationTable = keywordRelevance[completionLocation];
+ if (locationTable == null) {
+ return -1.0;
+ }
+ var range = locationTable[keyword];
+ if (range == null) {
+ // We sometimes suggest multiple tokens where a keyword is allowed, such
+ // as 'async*'. In those cases a valid keyword is always first followed by
+ // a non-alphabetic character. Try stripping off everything after the
+ // keyword and indexing into the table again.
+ var index = keyword.indexOf(RegExp('[^a-z]'));
+ if (index > 0) {
+ range = locationTable[keyword.substring(0, index)];
+ }
+ }
+ if (range == null) {
+ return 0.0;
+ }
+ return range.upper;
+ }
+
/// Return the value of the _starts with dollar_ feature.
double startsWithDollarFeature(String name) =>
name.startsWith('\$') ? 0.0 : 1.0;
@@ -726,7 +787,8 @@
if (node.equals != null && node.equals.end <= offset) {
var parent = node.parent;
if (parent is VariableDeclarationList) {
- return parent.type?.type;
+ return parent.type?.type ??
+ impliedDartTypeWithName(typeProvider, node.name?.name);
}
}
return null;
@@ -736,8 +798,10 @@
DartType visitVariableDeclarationList(VariableDeclarationList node) {
for (var varDecl in node.variables) {
if (varDecl != null && varDecl.contains(offset)) {
- if (varDecl.equals.end <= offset) {
- return node.type?.type;
+ var equals = varDecl.equals;
+ if (equals != null && equals.end <= offset) {
+ return node.type?.type ??
+ impliedDartTypeWithName(typeProvider, varDecl.name?.name);
}
}
}
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/relevance_tables.g.dart b/pkg/analysis_server/lib/src/services/completion/dart/relevance_tables.g.dart
index e461203..943669e 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/relevance_tables.g.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/relevance_tables.g.dart
@@ -15,132 +15,132 @@
/// ranges of the relevance of those element kinds in those locations.
const elementKindRelevance = {
'Annotation_name': {
- ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.000, upper: 0.042),
+ ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.000, upper: 0.041),
ElementKind.TOP_LEVEL_VARIABLE:
- ProbabilityRange(lower: 0.042, upper: 1.000),
+ ProbabilityRange(lower: 0.041, upper: 1.000),
},
'ArgumentList_annotation_named': {
- ElementKind.METHOD: ProbabilityRange(lower: 0.000, upper: 0.979),
- ElementKind.CLASS: ProbabilityRange(lower: 0.979, upper: 0.984),
- ElementKind.FUNCTION: ProbabilityRange(lower: 0.984, upper: 1.000),
+ ElementKind.METHOD: ProbabilityRange(lower: 0.000, upper: 0.026),
+ ElementKind.CLASS: ProbabilityRange(lower: 0.026, upper: 0.060),
+ ElementKind.FUNCTION: ProbabilityRange(lower: 0.060, upper: 0.159),
},
'ArgumentList_annotation_unnamed': {
- ElementKind.FUNCTION: ProbabilityRange(lower: 0.000, upper: 1.000),
+ ElementKind.FUNCTION: ProbabilityRange(lower: 0.375, upper: 1.000),
},
'ArgumentList_constructorRedirect_named': {
- ElementKind.METHOD: ProbabilityRange(lower: 0.000, upper: 0.041),
- ElementKind.FIELD: ProbabilityRange(lower: 0.041, upper: 0.044),
- ElementKind.ENUM: ProbabilityRange(lower: 0.044, upper: 0.047),
- ElementKind.CLASS: ProbabilityRange(lower: 0.047, upper: 0.050),
- ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.050, upper: 0.064),
- ElementKind.PARAMETER: ProbabilityRange(lower: 0.064, upper: 1.000),
+ ElementKind.METHOD: ProbabilityRange(lower: 0.000, upper: 0.001),
+ ElementKind.FIELD: ProbabilityRange(lower: 0.008, upper: 0.011),
+ ElementKind.ENUM: ProbabilityRange(lower: 0.011, upper: 0.014),
+ ElementKind.CLASS: ProbabilityRange(lower: 0.014, upper: 0.017),
+ ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.017, upper: 0.031),
+ ElementKind.PARAMETER: ProbabilityRange(lower: 0.031, upper: 1.000),
},
'ArgumentList_constructorRedirect_unnamed': {
- ElementKind.FUNCTION: ProbabilityRange(lower: 0.000, upper: 0.199),
- ElementKind.FIELD: ProbabilityRange(lower: 0.199, upper: 0.201),
- ElementKind.METHOD: ProbabilityRange(lower: 0.201, upper: 0.206),
- ElementKind.ENUM: ProbabilityRange(lower: 0.206, upper: 0.212),
- ElementKind.CLASS: ProbabilityRange(lower: 0.212, upper: 0.230),
- ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.230, upper: 0.279),
- ElementKind.PARAMETER: ProbabilityRange(lower: 0.279, upper: 1.000),
+ ElementKind.FUNCTION: ProbabilityRange(lower: 0.000, upper: 0.003),
+ ElementKind.FIELD: ProbabilityRange(lower: 0.003, upper: 0.005),
+ ElementKind.METHOD: ProbabilityRange(lower: 0.011, upper: 0.016),
+ ElementKind.ENUM: ProbabilityRange(lower: 0.016, upper: 0.024),
+ ElementKind.CLASS: ProbabilityRange(lower: 0.041, upper: 0.062),
+ ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.062, upper: 0.122),
+ ElementKind.PARAMETER: ProbabilityRange(lower: 0.122, upper: 1.000),
},
'ArgumentList_constructor_named': {
- ElementKind.METHOD: ProbabilityRange(lower: 0.000, upper: 0.492),
- ElementKind.PREFIX: ProbabilityRange(lower: 0.492, upper: 0.493),
- ElementKind.FUNCTION: ProbabilityRange(lower: 0.493, upper: 0.495),
+ ElementKind.METHOD: ProbabilityRange(lower: 0.000, upper: 0.002),
+ ElementKind.PREFIX: ProbabilityRange(lower: 0.002, upper: 0.004),
+ ElementKind.FUNCTION: ProbabilityRange(lower: 0.007, upper: 0.011),
ElementKind.TOP_LEVEL_VARIABLE:
- ProbabilityRange(lower: 0.495, upper: 0.504),
- ElementKind.ENUM: ProbabilityRange(lower: 0.504, upper: 0.516),
- ElementKind.PARAMETER: ProbabilityRange(lower: 0.516, upper: 0.545),
- ElementKind.FIELD: ProbabilityRange(lower: 0.545, upper: 0.574),
- ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.574, upper: 0.642),
- ElementKind.CLASS: ProbabilityRange(lower: 0.642, upper: 0.731),
- ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.731, upper: 1.000),
+ ProbabilityRange(lower: 0.042, upper: 0.059),
+ ElementKind.ENUM: ProbabilityRange(lower: 0.059, upper: 0.082),
+ ElementKind.PARAMETER: ProbabilityRange(lower: 0.082, upper: 0.135),
+ ElementKind.FIELD: ProbabilityRange(lower: 0.135, upper: 0.192),
+ ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.192, upper: 0.321),
+ ElementKind.CLASS: ProbabilityRange(lower: 0.321, upper: 0.492),
+ ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.492, upper: 1.000),
},
'ArgumentList_constructor_unnamed': {
- ElementKind.PREFIX: ProbabilityRange(lower: 0.000, upper: 0.617),
- ElementKind.METHOD: ProbabilityRange(lower: 0.617, upper: 0.622),
- ElementKind.ENUM: ProbabilityRange(lower: 0.622, upper: 0.630),
+ ElementKind.PREFIX: ProbabilityRange(lower: 0.001, upper: 0.004),
+ ElementKind.METHOD: ProbabilityRange(lower: 0.020, upper: 0.031),
+ ElementKind.ENUM: ProbabilityRange(lower: 0.049, upper: 0.070),
ElementKind.TOP_LEVEL_VARIABLE:
- ProbabilityRange(lower: 0.630, upper: 0.641),
- ElementKind.FUNCTION: ProbabilityRange(lower: 0.641, upper: 0.656),
- ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.656, upper: 0.694),
- ElementKind.FIELD: ProbabilityRange(lower: 0.694, upper: 0.734),
- ElementKind.PARAMETER: ProbabilityRange(lower: 0.734, upper: 0.811),
- ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.811, upper: 0.905),
- ElementKind.CLASS: ProbabilityRange(lower: 0.905, upper: 1.000),
+ ProbabilityRange(lower: 0.070, upper: 0.095),
+ ElementKind.FUNCTION: ProbabilityRange(lower: 0.124, upper: 0.162),
+ ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.162, upper: 0.255),
+ ElementKind.FIELD: ProbabilityRange(lower: 0.255, upper: 0.351),
+ ElementKind.PARAMETER: ProbabilityRange(lower: 0.351, upper: 0.539),
+ ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.539, upper: 0.768),
+ ElementKind.CLASS: ProbabilityRange(lower: 0.768, upper: 1.000),
},
'ArgumentList_function_named': {
- ElementKind.FUNCTION: ProbabilityRange(lower: 0.000, upper: 0.616),
- ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.616, upper: 0.648),
- ElementKind.PARAMETER: ProbabilityRange(lower: 0.648, upper: 0.792),
- ElementKind.FIELD: ProbabilityRange(lower: 0.792, upper: 1.000),
+ ElementKind.FUNCTION: ProbabilityRange(lower: 0.038, upper: 0.077),
+ ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.077, upper: 0.154),
+ ElementKind.PARAMETER: ProbabilityRange(lower: 0.154, upper: 0.500),
+ ElementKind.FIELD: ProbabilityRange(lower: 0.500, upper: 1.000),
},
'ArgumentList_function_unnamed': {
ElementKind.TOP_LEVEL_VARIABLE:
- ProbabilityRange(lower: 0.000, upper: 0.216),
- ElementKind.METHOD: ProbabilityRange(lower: 0.216, upper: 0.222),
- ElementKind.FUNCTION: ProbabilityRange(lower: 0.222, upper: 0.229),
- ElementKind.ENUM: ProbabilityRange(lower: 0.229, upper: 0.239),
- ElementKind.CLASS: ProbabilityRange(lower: 0.239, upper: 0.257),
- ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.257, upper: 0.282),
- ElementKind.FIELD: ProbabilityRange(lower: 0.282, upper: 0.347),
- ElementKind.PARAMETER: ProbabilityRange(lower: 0.347, upper: 0.583),
- ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.583, upper: 1.000),
+ ProbabilityRange(lower: 0.003, upper: 0.005),
+ ElementKind.METHOD: ProbabilityRange(lower: 0.010, upper: 0.016),
+ ElementKind.FUNCTION: ProbabilityRange(lower: 0.024, upper: 0.032),
+ ElementKind.ENUM: ProbabilityRange(lower: 0.041, upper: 0.052),
+ ElementKind.CLASS: ProbabilityRange(lower: 0.052, upper: 0.074),
+ ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.074, upper: 0.103),
+ ElementKind.FIELD: ProbabilityRange(lower: 0.171, upper: 0.246),
+ ElementKind.PARAMETER: ProbabilityRange(lower: 0.246, upper: 0.519),
+ ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.519, upper: 1.000),
},
'ArgumentList_method_named': {
- ElementKind.PREFIX: ProbabilityRange(lower: 0.000, upper: 0.549),
- ElementKind.FUNCTION: ProbabilityRange(lower: 0.549, upper: 0.552),
- ElementKind.METHOD: ProbabilityRange(lower: 0.552, upper: 0.556),
+ ElementKind.PREFIX: ProbabilityRange(lower: 0.002, upper: 0.005),
+ ElementKind.FUNCTION: ProbabilityRange(lower: 0.005, upper: 0.011),
+ ElementKind.METHOD: ProbabilityRange(lower: 0.011, upper: 0.017),
ElementKind.TOP_LEVEL_VARIABLE:
- ProbabilityRange(lower: 0.556, upper: 0.566),
- ElementKind.ENUM: ProbabilityRange(lower: 0.566, upper: 0.579),
- ElementKind.CLASS: ProbabilityRange(lower: 0.579, upper: 0.637),
- ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.637, upper: 0.696),
- ElementKind.FIELD: ProbabilityRange(lower: 0.696, upper: 0.764),
- ElementKind.PARAMETER: ProbabilityRange(lower: 0.764, upper: 0.854),
- ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.854, upper: 1.000),
+ ProbabilityRange(lower: 0.017, upper: 0.033),
+ ElementKind.ENUM: ProbabilityRange(lower: 0.033, upper: 0.055),
+ ElementKind.CLASS: ProbabilityRange(lower: 0.114, upper: 0.209),
+ ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.209, upper: 0.306),
+ ElementKind.FIELD: ProbabilityRange(lower: 0.306, upper: 0.417),
+ ElementKind.PARAMETER: ProbabilityRange(lower: 0.417, upper: 0.564),
+ ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.760, upper: 1.000),
},
'ArgumentList_method_unnamed': {
- ElementKind.MIXIN: ProbabilityRange(lower: 0.000, upper: 0.388),
- ElementKind.TYPE_PARAMETER: ProbabilityRange(lower: 0.388, upper: 0.388),
- ElementKind.PREFIX: ProbabilityRange(lower: 0.388, upper: 0.389),
- ElementKind.ENUM: ProbabilityRange(lower: 0.389, upper: 0.395),
- ElementKind.METHOD: ProbabilityRange(lower: 0.395, upper: 0.403),
- ElementKind.FUNCTION: ProbabilityRange(lower: 0.403, upper: 0.426),
- ElementKind.CLASS: ProbabilityRange(lower: 0.426, upper: 0.453),
+ ElementKind.MIXIN: ProbabilityRange(lower: 0.000, upper: 0.000),
+ ElementKind.TYPE_PARAMETER: ProbabilityRange(lower: 0.000, upper: 0.000),
+ ElementKind.PREFIX: ProbabilityRange(lower: 0.002, upper: 0.004),
+ ElementKind.ENUM: ProbabilityRange(lower: 0.025, upper: 0.035),
+ ElementKind.METHOD: ProbabilityRange(lower: 0.035, upper: 0.047),
+ ElementKind.FUNCTION: ProbabilityRange(lower: 0.060, upper: 0.096),
+ ElementKind.CLASS: ProbabilityRange(lower: 0.096, upper: 0.138),
ElementKind.TOP_LEVEL_VARIABLE:
- ProbabilityRange(lower: 0.453, upper: 0.484),
- ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.484, upper: 0.542),
- ElementKind.FIELD: ProbabilityRange(lower: 0.542, upper: 0.633),
- ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.633, upper: 0.747),
- ElementKind.PARAMETER: ProbabilityRange(lower: 0.747, upper: 1.000),
+ ProbabilityRange(lower: 0.138, upper: 0.188),
+ ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.188, upper: 0.279),
+ ElementKind.FIELD: ProbabilityRange(lower: 0.279, upper: 0.422),
+ ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.422, upper: 0.601),
+ ElementKind.PARAMETER: ProbabilityRange(lower: 0.601, upper: 1.000),
},
'ArgumentList_widgetConstructor_named': {
- ElementKind.PREFIX: ProbabilityRange(lower: 0.000, upper: 0.353),
- ElementKind.FUNCTION: ProbabilityRange(lower: 0.353, upper: 0.355),
+ ElementKind.PREFIX: ProbabilityRange(lower: 0.000, upper: 0.001),
+ ElementKind.FUNCTION: ProbabilityRange(lower: 0.006, upper: 0.009),
ElementKind.TOP_LEVEL_VARIABLE:
- ProbabilityRange(lower: 0.355, upper: 0.363),
- ElementKind.METHOD: ProbabilityRange(lower: 0.363, upper: 0.376),
- ElementKind.PARAMETER: ProbabilityRange(lower: 0.376, upper: 0.395),
- ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.395, upper: 0.430),
- ElementKind.ENUM: ProbabilityRange(lower: 0.430, upper: 0.490),
- ElementKind.FIELD: ProbabilityRange(lower: 0.490, upper: 0.552),
- ElementKind.CLASS: ProbabilityRange(lower: 0.552, upper: 0.636),
- ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.636, upper: 1.000),
+ ProbabilityRange(lower: 0.019, upper: 0.031),
+ ElementKind.METHOD: ProbabilityRange(lower: 0.047, upper: 0.066),
+ ElementKind.PARAMETER: ProbabilityRange(lower: 0.066, upper: 0.093),
+ ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.138, upper: 0.187),
+ ElementKind.ENUM: ProbabilityRange(lower: 0.187, upper: 0.273),
+ ElementKind.FIELD: ProbabilityRange(lower: 0.273, upper: 0.361),
+ ElementKind.CLASS: ProbabilityRange(lower: 0.361, upper: 0.482),
+ ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.482, upper: 1.000),
},
'ArgumentList_widgetConstructor_unnamed': {
- ElementKind.ENUM: ProbabilityRange(lower: 0.000, upper: 0.462),
- ElementKind.PREFIX: ProbabilityRange(lower: 0.462, upper: 0.464),
- ElementKind.FUNCTION: ProbabilityRange(lower: 0.464, upper: 0.470),
- ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.470, upper: 0.476),
+ ElementKind.ENUM: ProbabilityRange(lower: 0.004, upper: 0.008),
+ ElementKind.PREFIX: ProbabilityRange(lower: 0.008, upper: 0.012),
+ ElementKind.FUNCTION: ProbabilityRange(lower: 0.018, upper: 0.029),
+ ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.029, upper: 0.040),
ElementKind.TOP_LEVEL_VARIABLE:
- ProbabilityRange(lower: 0.476, upper: 0.485),
- ElementKind.METHOD: ProbabilityRange(lower: 0.485, upper: 0.501),
- ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.501, upper: 0.552),
- ElementKind.PARAMETER: ProbabilityRange(lower: 0.552, upper: 0.622),
- ElementKind.FIELD: ProbabilityRange(lower: 0.622, upper: 0.757),
- ElementKind.CLASS: ProbabilityRange(lower: 0.757, upper: 1.000),
+ ProbabilityRange(lower: 0.040, upper: 0.057),
+ ElementKind.METHOD: ProbabilityRange(lower: 0.057, upper: 0.087),
+ ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.087, upper: 0.179),
+ ElementKind.PARAMETER: ProbabilityRange(lower: 0.179, upper: 0.307),
+ ElementKind.FIELD: ProbabilityRange(lower: 0.307, upper: 0.555),
+ ElementKind.CLASS: ProbabilityRange(lower: 0.555, upper: 1.000),
},
'AsExpression_type': {
ElementKind.FUNCTION_TYPE_ALIAS:
@@ -157,210 +157,210 @@
ElementKind.PARAMETER: ProbabilityRange(lower: 0.000, upper: 1.000),
},
'AssertStatement_condition': {
- ElementKind.PREFIX: ProbabilityRange(lower: 0.000, upper: 0.144),
+ ElementKind.PREFIX: ProbabilityRange(lower: 0.002, upper: 0.004),
ElementKind.TOP_LEVEL_VARIABLE:
- ProbabilityRange(lower: 0.144, upper: 0.150),
- ElementKind.METHOD: ProbabilityRange(lower: 0.150, upper: 0.185),
- ElementKind.FUNCTION: ProbabilityRange(lower: 0.185, upper: 0.236),
- ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.236, upper: 0.352),
- ElementKind.PARAMETER: ProbabilityRange(lower: 0.352, upper: 0.650),
- ElementKind.FIELD: ProbabilityRange(lower: 0.650, upper: 1.000),
+ ProbabilityRange(lower: 0.009, upper: 0.015),
+ ElementKind.METHOD: ProbabilityRange(lower: 0.015, upper: 0.054),
+ ElementKind.FUNCTION: ProbabilityRange(lower: 0.054, upper: 0.110),
+ ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.166, upper: 0.292),
+ ElementKind.PARAMETER: ProbabilityRange(lower: 0.292, upper: 0.617),
+ ElementKind.FIELD: ProbabilityRange(lower: 0.617, upper: 1.000),
},
'AssertStatement_message': {
ElementKind.METHOD: ProbabilityRange(lower: 0.000, upper: 1.000),
},
'AssignmentExpression_rightHandSide': {
- ElementKind.PREFIX: ProbabilityRange(lower: 0.000, upper: 0.306),
+ ElementKind.PREFIX: ProbabilityRange(lower: 0.000, upper: 0.002),
ElementKind.TOP_LEVEL_VARIABLE:
- ProbabilityRange(lower: 0.306, upper: 0.315),
- ElementKind.FUNCTION: ProbabilityRange(lower: 0.315, upper: 0.326),
- ElementKind.METHOD: ProbabilityRange(lower: 0.326, upper: 0.338),
- ElementKind.ENUM: ProbabilityRange(lower: 0.338, upper: 0.358),
- ElementKind.CLASS: ProbabilityRange(lower: 0.358, upper: 0.403),
- ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.403, upper: 0.497),
- ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.497, upper: 0.608),
- ElementKind.FIELD: ProbabilityRange(lower: 0.608, upper: 0.739),
- ElementKind.PARAMETER: ProbabilityRange(lower: 0.739, upper: 1.000),
+ ProbabilityRange(lower: 0.005, upper: 0.016),
+ ElementKind.FUNCTION: ProbabilityRange(lower: 0.016, upper: 0.029),
+ ElementKind.METHOD: ProbabilityRange(lower: 0.029, upper: 0.043),
+ ElementKind.ENUM: ProbabilityRange(lower: 0.066, upper: 0.090),
+ ElementKind.CLASS: ProbabilityRange(lower: 0.233, upper: 0.287),
+ ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.287, upper: 0.400),
+ ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.400, upper: 0.533),
+ ElementKind.FIELD: ProbabilityRange(lower: 0.533, upper: 0.689),
+ ElementKind.PARAMETER: ProbabilityRange(lower: 0.689, upper: 1.000),
},
'AwaitExpression_expression': {
- ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.000, upper: 0.022),
- ElementKind.PREFIX: ProbabilityRange(lower: 0.022, upper: 0.049),
+ ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.009, upper: 0.019),
+ ElementKind.PREFIX: ProbabilityRange(lower: 0.019, upper: 0.046),
ElementKind.TOP_LEVEL_VARIABLE:
- ProbabilityRange(lower: 0.049, upper: 0.086),
- ElementKind.METHOD: ProbabilityRange(lower: 0.086, upper: 0.181),
- ElementKind.FUNCTION: ProbabilityRange(lower: 0.181, upper: 0.329),
- ElementKind.CLASS: ProbabilityRange(lower: 0.329, upper: 0.487),
- ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.487, upper: 0.645),
- ElementKind.PARAMETER: ProbabilityRange(lower: 0.645, upper: 0.809),
- ElementKind.FIELD: ProbabilityRange(lower: 0.809, upper: 1.000),
+ ProbabilityRange(lower: 0.046, upper: 0.083),
+ ElementKind.METHOD: ProbabilityRange(lower: 0.083, upper: 0.178),
+ ElementKind.FUNCTION: ProbabilityRange(lower: 0.178, upper: 0.327),
+ ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.327, upper: 0.485),
+ ElementKind.CLASS: ProbabilityRange(lower: 0.485, upper: 0.645),
+ ElementKind.PARAMETER: ProbabilityRange(lower: 0.645, upper: 0.808),
+ ElementKind.FIELD: ProbabilityRange(lower: 0.808, upper: 1.000),
},
'BinaryExpression_!=_rightOperand': {
- ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.000, upper: 0.874),
- ElementKind.PREFIX: ProbabilityRange(lower: 0.874, upper: 0.875),
- ElementKind.METHOD: ProbabilityRange(lower: 0.875, upper: 0.875),
+ ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.000, upper: 0.000),
+ ElementKind.PREFIX: ProbabilityRange(lower: 0.000, upper: 0.001),
+ ElementKind.METHOD: ProbabilityRange(lower: 0.001, upper: 0.001),
ElementKind.TOP_LEVEL_VARIABLE:
- ProbabilityRange(lower: 0.875, upper: 0.878),
- ElementKind.CLASS: ProbabilityRange(lower: 0.878, upper: 0.889),
- ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.889, upper: 0.906),
- ElementKind.ENUM: ProbabilityRange(lower: 0.906, upper: 0.924),
- ElementKind.FIELD: ProbabilityRange(lower: 0.924, upper: 0.957),
- ElementKind.PARAMETER: ProbabilityRange(lower: 0.957, upper: 1.000),
+ ProbabilityRange(lower: 0.007, upper: 0.010),
+ ElementKind.CLASS: ProbabilityRange(lower: 0.010, upper: 0.022),
+ ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.022, upper: 0.040),
+ ElementKind.ENUM: ProbabilityRange(lower: 0.040, upper: 0.059),
+ ElementKind.FIELD: ProbabilityRange(lower: 0.059, upper: 0.094),
+ ElementKind.PARAMETER: ProbabilityRange(lower: 0.094, upper: 0.140),
},
'BinaryExpression_%_rightOperand': {
- ElementKind.ENUM: ProbabilityRange(lower: 0.000, upper: 0.722),
- ElementKind.MIXIN: ProbabilityRange(lower: 0.722, upper: 0.728),
+ ElementKind.ENUM: ProbabilityRange(lower: 0.000, upper: 0.012),
+ ElementKind.MIXIN: ProbabilityRange(lower: 0.012, upper: 0.035),
ElementKind.TOP_LEVEL_VARIABLE:
- ProbabilityRange(lower: 0.728, upper: 0.745),
- ElementKind.CLASS: ProbabilityRange(lower: 0.745, upper: 0.772),
- ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.772, upper: 0.805),
- ElementKind.PARAMETER: ProbabilityRange(lower: 0.805, upper: 0.868),
- ElementKind.FIELD: ProbabilityRange(lower: 0.868, upper: 1.000),
+ ProbabilityRange(lower: 0.035, upper: 0.094),
+ ElementKind.CLASS: ProbabilityRange(lower: 0.094, upper: 0.188),
+ ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.188, upper: 0.306),
+ ElementKind.PARAMETER: ProbabilityRange(lower: 0.306, upper: 0.529),
+ ElementKind.FIELD: ProbabilityRange(lower: 0.529, upper: 1.000),
},
'BinaryExpression_&&_rightOperand': {
- ElementKind.PREFIX: ProbabilityRange(lower: 0.000, upper: 0.107),
- ElementKind.METHOD: ProbabilityRange(lower: 0.107, upper: 0.112),
+ ElementKind.PREFIX: ProbabilityRange(lower: 0.000, upper: 0.001),
+ ElementKind.METHOD: ProbabilityRange(lower: 0.005, upper: 0.010),
ElementKind.TOP_LEVEL_VARIABLE:
- ProbabilityRange(lower: 0.112, upper: 0.117),
- ElementKind.FUNCTION: ProbabilityRange(lower: 0.117, upper: 0.128),
- ElementKind.CLASS: ProbabilityRange(lower: 0.128, upper: 0.142),
- ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.142, upper: 0.264),
- ElementKind.PARAMETER: ProbabilityRange(lower: 0.264, upper: 0.445),
- ElementKind.FIELD: ProbabilityRange(lower: 0.445, upper: 1.000),
+ ProbabilityRange(lower: 0.010, upper: 0.018),
+ ElementKind.FUNCTION: ProbabilityRange(lower: 0.018, upper: 0.029),
+ ElementKind.CLASS: ProbabilityRange(lower: 0.029, upper: 0.045),
+ ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.045, upper: 0.181),
+ ElementKind.PARAMETER: ProbabilityRange(lower: 0.181, upper: 0.382),
+ ElementKind.FIELD: ProbabilityRange(lower: 0.382, upper: 1.000),
},
'BinaryExpression_&_rightOperand': {
- ElementKind.CLASS: ProbabilityRange(lower: 0.000, upper: 0.285),
- ElementKind.FIELD: ProbabilityRange(lower: 0.285, upper: 0.390),
- ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.390, upper: 0.494),
- ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.494, upper: 0.703),
- ElementKind.PARAMETER: ProbabilityRange(lower: 0.703, upper: 1.000),
+ ElementKind.CLASS: ProbabilityRange(lower: 0.023, upper: 0.068),
+ ElementKind.FIELD: ProbabilityRange(lower: 0.068, upper: 0.205),
+ ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.205, upper: 0.341),
+ ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.341, upper: 0.614),
+ ElementKind.PARAMETER: ProbabilityRange(lower: 0.614, upper: 1.000),
},
'BinaryExpression_*_rightOperand': {
- ElementKind.MIXIN: ProbabilityRange(lower: 0.000, upper: 0.622),
- ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.622, upper: 0.624),
- ElementKind.METHOD: ProbabilityRange(lower: 0.624, upper: 0.627),
- ElementKind.CLASS: ProbabilityRange(lower: 0.627, upper: 0.636),
- ElementKind.FUNCTION: ProbabilityRange(lower: 0.636, upper: 0.651),
- ElementKind.PREFIX: ProbabilityRange(lower: 0.651, upper: 0.672),
+ ElementKind.MIXIN: ProbabilityRange(lower: 0.000, upper: 0.001),
+ ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.004, upper: 0.010),
+ ElementKind.METHOD: ProbabilityRange(lower: 0.010, upper: 0.018),
+ ElementKind.CLASS: ProbabilityRange(lower: 0.018, upper: 0.041),
+ ElementKind.FUNCTION: ProbabilityRange(lower: 0.041, upper: 0.081),
+ ElementKind.PREFIX: ProbabilityRange(lower: 0.081, upper: 0.136),
ElementKind.TOP_LEVEL_VARIABLE:
- ProbabilityRange(lower: 0.672, upper: 0.702),
- ElementKind.PARAMETER: ProbabilityRange(lower: 0.702, upper: 0.751),
- ElementKind.FIELD: ProbabilityRange(lower: 0.751, upper: 0.870),
- ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.870, upper: 1.000),
+ ProbabilityRange(lower: 0.136, upper: 0.214),
+ ElementKind.PARAMETER: ProbabilityRange(lower: 0.214, upper: 0.344),
+ ElementKind.FIELD: ProbabilityRange(lower: 0.344, upper: 0.658),
+ ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.658, upper: 1.000),
},
'BinaryExpression_+_rightOperand': {
- ElementKind.PREFIX: ProbabilityRange(lower: 0.000, upper: 0.508),
- ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.508, upper: 0.510),
- ElementKind.METHOD: ProbabilityRange(lower: 0.510, upper: 0.517),
- ElementKind.FUNCTION: ProbabilityRange(lower: 0.517, upper: 0.532),
- ElementKind.CLASS: ProbabilityRange(lower: 0.532, upper: 0.556),
+ ElementKind.PREFIX: ProbabilityRange(lower: 0.000, upper: 0.003),
+ ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.007, upper: 0.012),
+ ElementKind.METHOD: ProbabilityRange(lower: 0.012, upper: 0.025),
+ ElementKind.FUNCTION: ProbabilityRange(lower: 0.025, upper: 0.056),
+ ElementKind.CLASS: ProbabilityRange(lower: 0.056, upper: 0.104),
ElementKind.TOP_LEVEL_VARIABLE:
- ProbabilityRange(lower: 0.556, upper: 0.582),
- ElementKind.PARAMETER: ProbabilityRange(lower: 0.582, upper: 0.701),
- ElementKind.FIELD: ProbabilityRange(lower: 0.701, upper: 0.829),
- ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.829, upper: 1.000),
+ ProbabilityRange(lower: 0.104, upper: 0.156),
+ ElementKind.PARAMETER: ProbabilityRange(lower: 0.156, upper: 0.396),
+ ElementKind.FIELD: ProbabilityRange(lower: 0.396, upper: 0.655),
+ ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.655, upper: 1.000),
},
'BinaryExpression_-_rightOperand': {
- ElementKind.PREFIX: ProbabilityRange(lower: 0.000, upper: 0.485),
- ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.485, upper: 0.488),
- ElementKind.FUNCTION: ProbabilityRange(lower: 0.488, upper: 0.490),
- ElementKind.METHOD: ProbabilityRange(lower: 0.490, upper: 0.499),
- ElementKind.CLASS: ProbabilityRange(lower: 0.499, upper: 0.515),
+ ElementKind.PREFIX: ProbabilityRange(lower: 0.001, upper: 0.005),
+ ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.005, upper: 0.010),
+ ElementKind.FUNCTION: ProbabilityRange(lower: 0.010, upper: 0.014),
+ ElementKind.METHOD: ProbabilityRange(lower: 0.021, upper: 0.038),
+ ElementKind.CLASS: ProbabilityRange(lower: 0.038, upper: 0.068),
ElementKind.TOP_LEVEL_VARIABLE:
- ProbabilityRange(lower: 0.515, upper: 0.542),
- ElementKind.PARAMETER: ProbabilityRange(lower: 0.542, upper: 0.633),
- ElementKind.FIELD: ProbabilityRange(lower: 0.633, upper: 0.802),
- ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.802, upper: 1.000),
+ ProbabilityRange(lower: 0.068, upper: 0.120),
+ ElementKind.PARAMETER: ProbabilityRange(lower: 0.120, upper: 0.295),
+ ElementKind.FIELD: ProbabilityRange(lower: 0.295, upper: 0.619),
+ ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.619, upper: 1.000),
},
'BinaryExpression_/_rightOperand': {
- ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.000, upper: 0.768),
- ElementKind.METHOD: ProbabilityRange(lower: 0.768, upper: 0.768),
- ElementKind.PREFIX: ProbabilityRange(lower: 0.768, upper: 0.771),
- ElementKind.FUNCTION: ProbabilityRange(lower: 0.771, upper: 0.777),
+ ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.000, upper: 0.002),
+ ElementKind.METHOD: ProbabilityRange(lower: 0.002, upper: 0.004),
+ ElementKind.PREFIX: ProbabilityRange(lower: 0.006, upper: 0.017),
+ ElementKind.FUNCTION: ProbabilityRange(lower: 0.017, upper: 0.042),
ElementKind.TOP_LEVEL_VARIABLE:
- ProbabilityRange(lower: 0.777, upper: 0.785),
- ElementKind.CLASS: ProbabilityRange(lower: 0.785, upper: 0.793),
- ElementKind.PARAMETER: ProbabilityRange(lower: 0.793, upper: 0.830),
- ElementKind.FIELD: ProbabilityRange(lower: 0.830, upper: 0.912),
- ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.912, upper: 1.000),
+ ProbabilityRange(lower: 0.042, upper: 0.075),
+ ElementKind.CLASS: ProbabilityRange(lower: 0.075, upper: 0.112),
+ ElementKind.PARAMETER: ProbabilityRange(lower: 0.112, upper: 0.268),
+ ElementKind.FIELD: ProbabilityRange(lower: 0.268, upper: 0.624),
+ ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.624, upper: 1.000),
},
'BinaryExpression_<<_rightOperand': {
- ElementKind.PARAMETER: ProbabilityRange(lower: 0.000, upper: 0.756),
- ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.756, upper: 0.854),
- ElementKind.FIELD: ProbabilityRange(lower: 0.854, upper: 1.000),
+ ElementKind.PARAMETER: ProbabilityRange(lower: 0.000, upper: 0.167),
+ ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.167, upper: 0.500),
+ ElementKind.FIELD: ProbabilityRange(lower: 0.500, upper: 1.000),
},
'BinaryExpression_<=_rightOperand': {
- ElementKind.CLASS: ProbabilityRange(lower: 0.000, upper: 0.609),
+ ElementKind.CLASS: ProbabilityRange(lower: 0.000, upper: 0.015),
ElementKind.TOP_LEVEL_VARIABLE:
- ProbabilityRange(lower: 0.609, upper: 0.621),
- ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.621, upper: 0.733),
- ElementKind.PARAMETER: ProbabilityRange(lower: 0.733, upper: 0.861),
- ElementKind.FIELD: ProbabilityRange(lower: 0.861, upper: 1.000),
+ ProbabilityRange(lower: 0.037, upper: 0.067),
+ ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.067, upper: 0.343),
+ ElementKind.PARAMETER: ProbabilityRange(lower: 0.343, upper: 0.657),
+ ElementKind.FIELD: ProbabilityRange(lower: 0.657, upper: 1.000),
},
'BinaryExpression_<_rightOperand': {
- ElementKind.ENUM: ProbabilityRange(lower: 0.000, upper: 0.424),
- ElementKind.METHOD: ProbabilityRange(lower: 0.424, upper: 0.426),
- ElementKind.FUNCTION: ProbabilityRange(lower: 0.426, upper: 0.427),
- ElementKind.CLASS: ProbabilityRange(lower: 0.427, upper: 0.439),
+ ElementKind.ENUM: ProbabilityRange(lower: 0.000, upper: 0.002),
+ ElementKind.METHOD: ProbabilityRange(lower: 0.002, upper: 0.005),
+ ElementKind.FUNCTION: ProbabilityRange(lower: 0.005, upper: 0.007),
+ ElementKind.CLASS: ProbabilityRange(lower: 0.023, upper: 0.042),
ElementKind.TOP_LEVEL_VARIABLE:
- ProbabilityRange(lower: 0.439, upper: 0.461),
- ElementKind.PARAMETER: ProbabilityRange(lower: 0.461, upper: 0.580),
- ElementKind.FIELD: ProbabilityRange(lower: 0.580, upper: 0.785),
- ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.785, upper: 1.000),
+ ProbabilityRange(lower: 0.042, upper: 0.083),
+ ElementKind.PARAMETER: ProbabilityRange(lower: 0.083, upper: 0.284),
+ ElementKind.FIELD: ProbabilityRange(lower: 0.284, upper: 0.635),
+ ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.635, upper: 1.000),
},
'BinaryExpression_==_rightOperand': {
- ElementKind.UNKNOWN: ProbabilityRange(lower: 0.000, upper: 0.568),
- ElementKind.FUNCTION: ProbabilityRange(lower: 0.568, upper: 0.568),
- ElementKind.METHOD: ProbabilityRange(lower: 0.568, upper: 0.568),
- ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.568, upper: 0.569),
- ElementKind.PREFIX: ProbabilityRange(lower: 0.569, upper: 0.569),
+ ElementKind.UNKNOWN: ProbabilityRange(lower: 0.000, upper: 0.000),
+ ElementKind.FUNCTION: ProbabilityRange(lower: 0.000, upper: 0.000),
+ ElementKind.METHOD: ProbabilityRange(lower: 0.000, upper: 0.000),
+ ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.000, upper: 0.001),
+ ElementKind.PREFIX: ProbabilityRange(lower: 0.001, upper: 0.002),
ElementKind.TOP_LEVEL_VARIABLE:
- ProbabilityRange(lower: 0.569, upper: 0.577),
- ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.577, upper: 0.601),
- ElementKind.CLASS: ProbabilityRange(lower: 0.601, upper: 0.639),
- ElementKind.FIELD: ProbabilityRange(lower: 0.639, upper: 0.679),
- ElementKind.ENUM: ProbabilityRange(lower: 0.679, upper: 0.807),
- ElementKind.PARAMETER: ProbabilityRange(lower: 0.807, upper: 1.000),
+ ProbabilityRange(lower: 0.008, upper: 0.018),
+ ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.055, upper: 0.085),
+ ElementKind.CLASS: ProbabilityRange(lower: 0.085, upper: 0.133),
+ ElementKind.FIELD: ProbabilityRange(lower: 0.133, upper: 0.183),
+ ElementKind.ENUM: ProbabilityRange(lower: 0.183, upper: 0.342),
+ ElementKind.PARAMETER: ProbabilityRange(lower: 0.342, upper: 0.583),
},
'BinaryExpression_>=_rightOperand': {
- ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.000, upper: 0.714),
- ElementKind.CLASS: ProbabilityRange(lower: 0.714, upper: 0.731),
+ ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.000, upper: 0.006),
+ ElementKind.CLASS: ProbabilityRange(lower: 0.018, upper: 0.077),
ElementKind.TOP_LEVEL_VARIABLE:
- ProbabilityRange(lower: 0.731, upper: 0.761),
- ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.761, upper: 0.821),
- ElementKind.PARAMETER: ProbabilityRange(lower: 0.821, upper: 0.884),
- ElementKind.FIELD: ProbabilityRange(lower: 0.884, upper: 1.000),
+ ProbabilityRange(lower: 0.077, upper: 0.179),
+ ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.179, upper: 0.387),
+ ElementKind.PARAMETER: ProbabilityRange(lower: 0.387, upper: 0.601),
+ ElementKind.FIELD: ProbabilityRange(lower: 0.601, upper: 1.000),
},
'BinaryExpression_>>_rightOperand': {
ElementKind.PARAMETER: ProbabilityRange(lower: 0.000, upper: 1.000),
},
'BinaryExpression_>_rightOperand': {
- ElementKind.PREFIX: ProbabilityRange(lower: 0.000, upper: 0.815),
- ElementKind.FUNCTION: ProbabilityRange(lower: 0.815, upper: 0.815),
- ElementKind.ENUM: ProbabilityRange(lower: 0.815, upper: 0.817),
- ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.817, upper: 0.820),
- ElementKind.CLASS: ProbabilityRange(lower: 0.820, upper: 0.828),
+ ElementKind.PREFIX: ProbabilityRange(lower: 0.000, upper: 0.004),
+ ElementKind.FUNCTION: ProbabilityRange(lower: 0.004, upper: 0.007),
+ ElementKind.ENUM: ProbabilityRange(lower: 0.007, upper: 0.014),
+ ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.014, upper: 0.029),
+ ElementKind.CLASS: ProbabilityRange(lower: 0.068, upper: 0.112),
ElementKind.TOP_LEVEL_VARIABLE:
- ProbabilityRange(lower: 0.828, upper: 0.850),
- ElementKind.PARAMETER: ProbabilityRange(lower: 0.850, upper: 0.890),
- ElementKind.FIELD: ProbabilityRange(lower: 0.890, upper: 0.939),
- ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.939, upper: 1.000),
+ ProbabilityRange(lower: 0.112, upper: 0.227),
+ ElementKind.PARAMETER: ProbabilityRange(lower: 0.227, upper: 0.432),
+ ElementKind.FIELD: ProbabilityRange(lower: 0.432, upper: 0.687),
+ ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.687, upper: 1.000),
},
'BinaryExpression_??_rightOperand': {
- ElementKind.PREFIX: ProbabilityRange(lower: 0.000, upper: 0.665),
- ElementKind.METHOD: ProbabilityRange(lower: 0.665, upper: 0.668),
- ElementKind.FUNCTION: ProbabilityRange(lower: 0.668, upper: 0.673),
+ ElementKind.PREFIX: ProbabilityRange(lower: 0.000, upper: 0.000),
+ ElementKind.METHOD: ProbabilityRange(lower: 0.002, upper: 0.009),
+ ElementKind.FUNCTION: ProbabilityRange(lower: 0.017, upper: 0.027),
ElementKind.TOP_LEVEL_VARIABLE:
- ProbabilityRange(lower: 0.673, upper: 0.680),
- ElementKind.ENUM: ProbabilityRange(lower: 0.680, upper: 0.692),
- ElementKind.FIELD: ProbabilityRange(lower: 0.692, upper: 0.728),
- ElementKind.PARAMETER: ProbabilityRange(lower: 0.728, upper: 0.769),
- ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.769, upper: 0.817),
- ElementKind.CLASS: ProbabilityRange(lower: 0.817, upper: 0.871),
- ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.871, upper: 1.000),
+ ProbabilityRange(lower: 0.027, upper: 0.042),
+ ElementKind.ENUM: ProbabilityRange(lower: 0.058, upper: 0.083),
+ ElementKind.FIELD: ProbabilityRange(lower: 0.108, upper: 0.183),
+ ElementKind.PARAMETER: ProbabilityRange(lower: 0.183, upper: 0.269),
+ ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.367, upper: 0.467),
+ ElementKind.CLASS: ProbabilityRange(lower: 0.467, upper: 0.578),
+ ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.731, upper: 1.000),
},
'BinaryExpression_^_rightOperand': {
- ElementKind.PARAMETER: ProbabilityRange(lower: 0.000, upper: 0.118),
- ElementKind.FIELD: ProbabilityRange(lower: 0.118, upper: 1.000),
+ ElementKind.PARAMETER: ProbabilityRange(lower: 0.000, upper: 0.026),
+ ElementKind.FIELD: ProbabilityRange(lower: 0.026, upper: 1.000),
},
'BinaryExpression_|_rightOperand': {
ElementKind.PARAMETER: ProbabilityRange(lower: 0.000, upper: 0.143),
@@ -368,185 +368,185 @@
ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.429, upper: 1.000),
},
'BinaryExpression_||_rightOperand': {
+ ElementKind.METHOD: ProbabilityRange(lower: 0.004, upper: 0.008),
+ ElementKind.FUNCTION: ProbabilityRange(lower: 0.008, upper: 0.013),
ElementKind.TOP_LEVEL_VARIABLE:
- ProbabilityRange(lower: 0.000, upper: 0.141),
- ElementKind.METHOD: ProbabilityRange(lower: 0.141, upper: 0.145),
- ElementKind.FUNCTION: ProbabilityRange(lower: 0.145, upper: 0.149),
- ElementKind.CLASS: ProbabilityRange(lower: 0.149, upper: 0.169),
- ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.169, upper: 0.382),
- ElementKind.FIELD: ProbabilityRange(lower: 0.382, upper: 0.666),
- ElementKind.PARAMETER: ProbabilityRange(lower: 0.666, upper: 1.000),
+ ProbabilityRange(lower: 0.018, upper: 0.027),
+ ElementKind.CLASS: ProbabilityRange(lower: 0.027, upper: 0.050),
+ ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.050, upper: 0.294),
+ ElementKind.FIELD: ProbabilityRange(lower: 0.294, upper: 0.618),
+ ElementKind.PARAMETER: ProbabilityRange(lower: 0.618, upper: 1.000),
},
'BinaryExpression_~/_rightOperand': {
ElementKind.TOP_LEVEL_VARIABLE:
- ProbabilityRange(lower: 0.000, upper: 0.629),
- ElementKind.CLASS: ProbabilityRange(lower: 0.629, upper: 0.642),
- ElementKind.MIXIN: ProbabilityRange(lower: 0.642, upper: 0.654),
- ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.654, upper: 0.692),
- ElementKind.PARAMETER: ProbabilityRange(lower: 0.692, upper: 0.799),
- ElementKind.FIELD: ProbabilityRange(lower: 0.799, upper: 1.000),
+ ProbabilityRange(lower: 0.000, upper: 0.017),
+ ElementKind.CLASS: ProbabilityRange(lower: 0.017, upper: 0.050),
+ ElementKind.MIXIN: ProbabilityRange(lower: 0.050, upper: 0.083),
+ ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.083, upper: 0.183),
+ ElementKind.PARAMETER: ProbabilityRange(lower: 0.183, upper: 0.467),
+ ElementKind.FIELD: ProbabilityRange(lower: 0.467, upper: 1.000),
},
'Block_statement': {
- ElementKind.TYPE_PARAMETER: ProbabilityRange(lower: 0.000, upper: 0.537),
+ ElementKind.TYPE_PARAMETER: ProbabilityRange(lower: 0.000, upper: 0.000),
ElementKind.FUNCTION_TYPE_ALIAS:
- ProbabilityRange(lower: 0.537, upper: 0.537),
- ElementKind.UNKNOWN: ProbabilityRange(lower: 0.537, upper: 0.537),
- ElementKind.ENUM: ProbabilityRange(lower: 0.537, upper: 0.538),
- ElementKind.MIXIN: ProbabilityRange(lower: 0.538, upper: 0.538),
- ElementKind.PREFIX: ProbabilityRange(lower: 0.538, upper: 0.540),
+ ProbabilityRange(lower: 0.000, upper: 0.000),
+ ElementKind.UNKNOWN: ProbabilityRange(lower: 0.000, upper: 0.001),
+ ElementKind.ENUM: ProbabilityRange(lower: 0.001, upper: 0.001),
+ ElementKind.MIXIN: ProbabilityRange(lower: 0.002, upper: 0.003),
+ ElementKind.PREFIX: ProbabilityRange(lower: 0.005, upper: 0.006),
ElementKind.TOP_LEVEL_VARIABLE:
- ProbabilityRange(lower: 0.540, upper: 0.554),
- ElementKind.PARAMETER: ProbabilityRange(lower: 0.554, upper: 0.577),
- ElementKind.METHOD: ProbabilityRange(lower: 0.577, upper: 0.627),
- ElementKind.FUNCTION: ProbabilityRange(lower: 0.627, upper: 0.687),
- ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.687, upper: 0.770),
- ElementKind.CLASS: ProbabilityRange(lower: 0.770, upper: 0.858),
- ElementKind.FIELD: ProbabilityRange(lower: 0.858, upper: 1.000),
+ ProbabilityRange(lower: 0.062, upper: 0.077),
+ ElementKind.PARAMETER: ProbabilityRange(lower: 0.093, upper: 0.116),
+ ElementKind.METHOD: ProbabilityRange(lower: 0.171, upper: 0.221),
+ ElementKind.FUNCTION: ProbabilityRange(lower: 0.221, upper: 0.282),
+ ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.282, upper: 0.364),
+ ElementKind.CLASS: ProbabilityRange(lower: 0.364, upper: 0.453),
+ ElementKind.FIELD: ProbabilityRange(lower: 0.661, upper: 0.802),
},
'CatchClause_exceptionType': {
ElementKind.CLASS: ProbabilityRange(lower: 0.000, upper: 1.000),
},
'ClassDeclaration_member': {
- ElementKind.MIXIN: ProbabilityRange(lower: 0.000, upper: 0.424),
- ElementKind.PREFIX: ProbabilityRange(lower: 0.424, upper: 0.425),
- ElementKind.UNKNOWN: ProbabilityRange(lower: 0.425, upper: 0.426),
- ElementKind.TYPE_PARAMETER: ProbabilityRange(lower: 0.426, upper: 0.426),
+ ElementKind.MIXIN: ProbabilityRange(lower: 0.000, upper: 0.000),
+ ElementKind.PREFIX: ProbabilityRange(lower: 0.000, upper: 0.000),
+ ElementKind.UNKNOWN: ProbabilityRange(lower: 0.000, upper: 0.001),
+ ElementKind.TYPE_PARAMETER: ProbabilityRange(lower: 0.001, upper: 0.002),
ElementKind.FUNCTION_TYPE_ALIAS:
- ProbabilityRange(lower: 0.426, upper: 0.428),
- ElementKind.ENUM: ProbabilityRange(lower: 0.428, upper: 0.433),
- ElementKind.CLASS: ProbabilityRange(lower: 0.433, upper: 1.000),
+ ProbabilityRange(lower: 0.002, upper: 0.004),
+ ElementKind.ENUM: ProbabilityRange(lower: 0.010, upper: 0.015),
+ ElementKind.CLASS: ProbabilityRange(lower: 0.442, upper: 1.000),
},
'ClassTypeAlias_superclass': {
- ElementKind.CLASS: ProbabilityRange(lower: 0.000, upper: 1.000),
+ ElementKind.CLASS: ProbabilityRange(lower: 0.000, upper: 0.500),
},
'CommentReference_identifier': {
- ElementKind.UNKNOWN: ProbabilityRange(lower: 0.000, upper: 0.001),
+ ElementKind.UNKNOWN: ProbabilityRange(lower: 0.000, upper: 0.000),
ElementKind.FUNCTION_TYPE_ALIAS:
- ProbabilityRange(lower: 0.001, upper: 0.002),
+ ProbabilityRange(lower: 0.000, upper: 0.002),
ElementKind.TYPE_PARAMETER: ProbabilityRange(lower: 0.002, upper: 0.004),
ElementKind.PREFIX: ProbabilityRange(lower: 0.004, upper: 0.008),
ElementKind.TOP_LEVEL_VARIABLE:
ProbabilityRange(lower: 0.008, upper: 0.012),
- ElementKind.FUNCTION: ProbabilityRange(lower: 0.012, upper: 0.026),
- ElementKind.METHOD: ProbabilityRange(lower: 0.026, upper: 0.060),
+ ElementKind.FUNCTION: ProbabilityRange(lower: 0.012, upper: 0.025),
+ ElementKind.METHOD: ProbabilityRange(lower: 0.025, upper: 0.060),
ElementKind.PARAMETER: ProbabilityRange(lower: 0.060, upper: 0.118),
- ElementKind.ENUM: ProbabilityRange(lower: 0.118, upper: 0.181),
- ElementKind.FIELD: ProbabilityRange(lower: 0.181, upper: 0.456),
+ ElementKind.ENUM: ProbabilityRange(lower: 0.118, upper: 0.180),
+ ElementKind.FIELD: ProbabilityRange(lower: 0.180, upper: 0.456),
ElementKind.CLASS: ProbabilityRange(lower: 0.456, upper: 1.000),
},
'CompilationUnit_declaration': {
- ElementKind.PREFIX: ProbabilityRange(lower: 0.000, upper: 0.818),
- ElementKind.UNKNOWN: ProbabilityRange(lower: 0.818, upper: 0.819),
- ElementKind.TYPE_PARAMETER: ProbabilityRange(lower: 0.819, upper: 0.820),
- ElementKind.ENUM: ProbabilityRange(lower: 0.820, upper: 0.822),
+ ElementKind.PREFIX: ProbabilityRange(lower: 0.000, upper: 0.000),
+ ElementKind.UNKNOWN: ProbabilityRange(lower: 0.000, upper: 0.000),
+ ElementKind.TYPE_PARAMETER: ProbabilityRange(lower: 0.000, upper: 0.001),
+ ElementKind.ENUM: ProbabilityRange(lower: 0.001, upper: 0.003),
ElementKind.FUNCTION_TYPE_ALIAS:
- ProbabilityRange(lower: 0.822, upper: 0.836),
- ElementKind.CLASS: ProbabilityRange(lower: 0.836, upper: 1.000),
+ ProbabilityRange(lower: 0.024, upper: 0.036),
+ ElementKind.CLASS: ProbabilityRange(lower: 0.205, upper: 0.342),
},
'ConditionalExpression_elseExpression': {
- ElementKind.PREFIX: ProbabilityRange(lower: 0.000, upper: 0.369),
- ElementKind.FUNCTION: ProbabilityRange(lower: 0.369, upper: 0.378),
+ ElementKind.PREFIX: ProbabilityRange(lower: 0.002, upper: 0.005),
+ ElementKind.FUNCTION: ProbabilityRange(lower: 0.030, upper: 0.041),
ElementKind.TOP_LEVEL_VARIABLE:
- ProbabilityRange(lower: 0.378, upper: 0.388),
- ElementKind.METHOD: ProbabilityRange(lower: 0.388, upper: 0.407),
- ElementKind.ENUM: ProbabilityRange(lower: 0.407, upper: 0.431),
- ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.431, upper: 0.487),
- ElementKind.PARAMETER: ProbabilityRange(lower: 0.487, upper: 0.554),
- ElementKind.FIELD: ProbabilityRange(lower: 0.554, upper: 0.655),
- ElementKind.CLASS: ProbabilityRange(lower: 0.655, upper: 0.790),
- ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.790, upper: 1.000),
+ ProbabilityRange(lower: 0.041, upper: 0.055),
+ ElementKind.METHOD: ProbabilityRange(lower: 0.071, upper: 0.096),
+ ElementKind.ENUM: ProbabilityRange(lower: 0.096, upper: 0.128),
+ ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.128, upper: 0.201),
+ ElementKind.PARAMETER: ProbabilityRange(lower: 0.201, upper: 0.289),
+ ElementKind.FIELD: ProbabilityRange(lower: 0.412, upper: 0.544),
+ ElementKind.CLASS: ProbabilityRange(lower: 0.544, upper: 0.722),
+ ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.722, upper: 1.000),
},
'ConditionalExpression_thenExpression': {
- ElementKind.PREFIX: ProbabilityRange(lower: 0.000, upper: 0.390),
- ElementKind.FUNCTION: ProbabilityRange(lower: 0.390, upper: 0.397),
+ ElementKind.PREFIX: ProbabilityRange(lower: 0.001, upper: 0.004),
+ ElementKind.FUNCTION: ProbabilityRange(lower: 0.024, upper: 0.034),
ElementKind.TOP_LEVEL_VARIABLE:
- ProbabilityRange(lower: 0.397, upper: 0.408),
- ElementKind.METHOD: ProbabilityRange(lower: 0.408, upper: 0.429),
- ElementKind.ENUM: ProbabilityRange(lower: 0.429, upper: 0.456),
- ElementKind.PARAMETER: ProbabilityRange(lower: 0.456, upper: 0.506),
- ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.506, upper: 0.562),
- ElementKind.FIELD: ProbabilityRange(lower: 0.562, upper: 0.633),
- ElementKind.CLASS: ProbabilityRange(lower: 0.633, upper: 0.776),
- ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.776, upper: 1.000),
+ ProbabilityRange(lower: 0.034, upper: 0.048),
+ ElementKind.METHOD: ProbabilityRange(lower: 0.069, upper: 0.097),
+ ElementKind.ENUM: ProbabilityRange(lower: 0.097, upper: 0.132),
+ ElementKind.PARAMETER: ProbabilityRange(lower: 0.132, upper: 0.198),
+ ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.198, upper: 0.272),
+ ElementKind.FIELD: ProbabilityRange(lower: 0.272, upper: 0.367),
+ ElementKind.CLASS: ProbabilityRange(lower: 0.513, upper: 0.703),
+ ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.703, upper: 1.000),
},
'ConstructorDeclaration_initializer': {
- ElementKind.FIELD: ProbabilityRange(lower: 0.000, upper: 1.000),
+ ElementKind.FIELD: ProbabilityRange(lower: 0.181, upper: 0.448),
},
'ConstructorDeclaration_returnType': {
ElementKind.CLASS: ProbabilityRange(lower: 0.000, upper: 1.000),
},
'ConstructorFieldInitializer_expression': {
ElementKind.TOP_LEVEL_VARIABLE:
- ProbabilityRange(lower: 0.000, upper: 0.183),
- ElementKind.FIELD: ProbabilityRange(lower: 0.183, upper: 0.185),
- ElementKind.METHOD: ProbabilityRange(lower: 0.185, upper: 0.188),
- ElementKind.ENUM: ProbabilityRange(lower: 0.188, upper: 0.196),
- ElementKind.FUNCTION: ProbabilityRange(lower: 0.196, upper: 0.221),
- ElementKind.CLASS: ProbabilityRange(lower: 0.221, upper: 0.274),
- ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.274, upper: 0.399),
- ElementKind.PARAMETER: ProbabilityRange(lower: 0.399, upper: 1.000),
+ ProbabilityRange(lower: 0.000, upper: 0.002),
+ ElementKind.FIELD: ProbabilityRange(lower: 0.002, upper: 0.003),
+ ElementKind.METHOD: ProbabilityRange(lower: 0.005, upper: 0.010),
+ ElementKind.ENUM: ProbabilityRange(lower: 0.017, upper: 0.026),
+ ElementKind.FUNCTION: ProbabilityRange(lower: 0.037, upper: 0.067),
+ ElementKind.CLASS: ProbabilityRange(lower: 0.067, upper: 0.130),
+ ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.130, upper: 0.280),
+ ElementKind.PARAMETER: ProbabilityRange(lower: 0.280, upper: 1.000),
},
'DefaultFormalParameter_defaultValue': {
- ElementKind.PREFIX: ProbabilityRange(lower: 0.000, upper: 0.694),
- ElementKind.METHOD: ProbabilityRange(lower: 0.694, upper: 0.695),
- ElementKind.FUNCTION: ProbabilityRange(lower: 0.695, upper: 0.696),
- ElementKind.FIELD: ProbabilityRange(lower: 0.696, upper: 0.702),
+ ElementKind.PREFIX: ProbabilityRange(lower: 0.001, upper: 0.002),
+ ElementKind.METHOD: ProbabilityRange(lower: 0.002, upper: 0.002),
+ ElementKind.FUNCTION: ProbabilityRange(lower: 0.002, upper: 0.004),
+ ElementKind.FIELD: ProbabilityRange(lower: 0.004, upper: 0.014),
ElementKind.TOP_LEVEL_VARIABLE:
- ProbabilityRange(lower: 0.702, upper: 0.713),
- ElementKind.ENUM: ProbabilityRange(lower: 0.713, upper: 0.760),
- ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.760, upper: 0.827),
- ElementKind.CLASS: ProbabilityRange(lower: 0.827, upper: 1.000),
+ ProbabilityRange(lower: 0.014, upper: 0.031),
+ ElementKind.ENUM: ProbabilityRange(lower: 0.031, upper: 0.107),
+ ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.199, upper: 0.307),
+ ElementKind.CLASS: ProbabilityRange(lower: 0.719, upper: 1.000),
},
'DoStatement_condition': {
- ElementKind.PARAMETER: ProbabilityRange(lower: 0.000, upper: 0.333),
- ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.333, upper: 1.000),
+ ElementKind.PARAMETER: ProbabilityRange(lower: 0.000, upper: 0.143),
+ ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.143, upper: 1.000),
},
'Expression': {
- ElementKind.UNKNOWN: ProbabilityRange(lower: 0.000, upper: 0.386),
+ ElementKind.UNKNOWN: ProbabilityRange(lower: 0.000, upper: 0.000),
ElementKind.FUNCTION_TYPE_ALIAS:
- ProbabilityRange(lower: 0.386, upper: 0.386),
- ElementKind.TYPE_PARAMETER: ProbabilityRange(lower: 0.386, upper: 0.387),
- ElementKind.MIXIN: ProbabilityRange(lower: 0.387, upper: 0.387),
- ElementKind.PREFIX: ProbabilityRange(lower: 0.387, upper: 0.388),
- ElementKind.ENUM: ProbabilityRange(lower: 0.388, upper: 0.404),
- ElementKind.METHOD: ProbabilityRange(lower: 0.404, upper: 0.420),
- ElementKind.FUNCTION: ProbabilityRange(lower: 0.420, upper: 0.437),
+ ProbabilityRange(lower: 0.000, upper: 0.000),
+ ElementKind.TYPE_PARAMETER: ProbabilityRange(lower: 0.000, upper: 0.000),
+ ElementKind.MIXIN: ProbabilityRange(lower: 0.000, upper: 0.000),
+ ElementKind.PREFIX: ProbabilityRange(lower: 0.000, upper: 0.003),
+ ElementKind.ENUM: ProbabilityRange(lower: 0.003, upper: 0.029),
+ ElementKind.METHOD: ProbabilityRange(lower: 0.029, upper: 0.055),
+ ElementKind.FUNCTION: ProbabilityRange(lower: 0.055, upper: 0.083),
ElementKind.TOP_LEVEL_VARIABLE:
- ProbabilityRange(lower: 0.437, upper: 0.470),
- ElementKind.CLASS: ProbabilityRange(lower: 0.470, upper: 0.542),
- ElementKind.PARAMETER: ProbabilityRange(lower: 0.542, upper: 0.626),
- ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.626, upper: 0.722),
- ElementKind.FIELD: ProbabilityRange(lower: 0.722, upper: 0.835),
- ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.835, upper: 1.000),
+ ProbabilityRange(lower: 0.083, upper: 0.137),
+ ElementKind.CLASS: ProbabilityRange(lower: 0.137, upper: 0.255),
+ ElementKind.PARAMETER: ProbabilityRange(lower: 0.255, upper: 0.391),
+ ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.391, upper: 0.547),
+ ElementKind.FIELD: ProbabilityRange(lower: 0.547, upper: 0.731),
+ ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.731, upper: 1.000),
},
'ExpressionFunctionBody_expression': {
- ElementKind.MIXIN: ProbabilityRange(lower: 0.000, upper: 0.162),
- ElementKind.TYPE_PARAMETER: ProbabilityRange(lower: 0.162, upper: 0.163),
- ElementKind.ENUM: ProbabilityRange(lower: 0.163, upper: 0.164),
- ElementKind.PREFIX: ProbabilityRange(lower: 0.164, upper: 0.171),
+ ElementKind.MIXIN: ProbabilityRange(lower: 0.000, upper: 0.000),
+ ElementKind.TYPE_PARAMETER: ProbabilityRange(lower: 0.000, upper: 0.000),
+ ElementKind.ENUM: ProbabilityRange(lower: 0.004, upper: 0.005),
+ ElementKind.PREFIX: ProbabilityRange(lower: 0.027, upper: 0.034),
ElementKind.TOP_LEVEL_VARIABLE:
- ProbabilityRange(lower: 0.171, upper: 0.184),
- ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.184, upper: 0.211),
- ElementKind.FUNCTION: ProbabilityRange(lower: 0.211, upper: 0.244),
- ElementKind.CLASS: ProbabilityRange(lower: 0.244, upper: 0.286),
- ElementKind.METHOD: ProbabilityRange(lower: 0.286, upper: 0.332),
- ElementKind.PARAMETER: ProbabilityRange(lower: 0.332, upper: 0.497),
- ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.497, upper: 0.731),
- ElementKind.FIELD: ProbabilityRange(lower: 0.731, upper: 1.000),
+ ProbabilityRange(lower: 0.045, upper: 0.061),
+ ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.061, upper: 0.092),
+ ElementKind.FUNCTION: ProbabilityRange(lower: 0.092, upper: 0.130),
+ ElementKind.CLASS: ProbabilityRange(lower: 0.130, upper: 0.179),
+ ElementKind.METHOD: ProbabilityRange(lower: 0.179, upper: 0.231),
+ ElementKind.PARAMETER: ProbabilityRange(lower: 0.231, upper: 0.421),
+ ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.421, upper: 0.691),
+ ElementKind.FIELD: ProbabilityRange(lower: 0.691, upper: 1.000),
},
'ExpressionStatement_expression': {
- ElementKind.ENUM: ProbabilityRange(lower: 0.000, upper: 0.132),
- ElementKind.MIXIN: ProbabilityRange(lower: 0.132, upper: 0.134),
- ElementKind.PREFIX: ProbabilityRange(lower: 0.134, upper: 0.136),
- ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.136, upper: 0.139),
+ ElementKind.ENUM: ProbabilityRange(lower: 0.000, upper: 0.000),
+ ElementKind.MIXIN: ProbabilityRange(lower: 0.000, upper: 0.002),
+ ElementKind.PREFIX: ProbabilityRange(lower: 0.002, upper: 0.004),
+ ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.006, upper: 0.009),
ElementKind.TOP_LEVEL_VARIABLE:
- ProbabilityRange(lower: 0.139, upper: 0.168),
- ElementKind.PARAMETER: ProbabilityRange(lower: 0.168, upper: 0.218),
- ElementKind.CLASS: ProbabilityRange(lower: 0.218, upper: 0.283),
- ElementKind.METHOD: ProbabilityRange(lower: 0.283, upper: 0.388),
- ElementKind.FUNCTION: ProbabilityRange(lower: 0.388, upper: 0.510),
- ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.510, upper: 0.709),
- ElementKind.FIELD: ProbabilityRange(lower: 0.709, upper: 1.000),
+ ProbabilityRange(lower: 0.054, upper: 0.084),
+ ElementKind.PARAMETER: ProbabilityRange(lower: 0.117, upper: 0.168),
+ ElementKind.CLASS: ProbabilityRange(lower: 0.218, upper: 0.284),
+ ElementKind.METHOD: ProbabilityRange(lower: 0.284, upper: 0.389),
+ ElementKind.FUNCTION: ProbabilityRange(lower: 0.389, upper: 0.511),
+ ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.511, upper: 0.710),
+ ElementKind.FIELD: ProbabilityRange(lower: 0.710, upper: 1.000),
},
'ExtendsClause_superclass': {
ElementKind.PREFIX: ProbabilityRange(lower: 0.000, upper: 0.000),
@@ -558,103 +558,103 @@
ElementKind.CLASS: ProbabilityRange(lower: 0.105, upper: 1.000),
},
'ExtensionDeclaration_member': {
- ElementKind.TYPE_PARAMETER: ProbabilityRange(lower: 0.000, upper: 0.120),
+ ElementKind.TYPE_PARAMETER: ProbabilityRange(lower: 0.040, upper: 0.120),
ElementKind.CLASS: ProbabilityRange(lower: 0.120, upper: 1.000),
},
'FieldDeclaration_fields': {
- ElementKind.UNKNOWN: ProbabilityRange(lower: 0.000, upper: 0.617),
- ElementKind.PREFIX: ProbabilityRange(lower: 0.617, upper: 0.618),
- ElementKind.TYPE_PARAMETER: ProbabilityRange(lower: 0.618, upper: 0.619),
+ ElementKind.UNKNOWN: ProbabilityRange(lower: 0.000, upper: 0.001),
+ ElementKind.PREFIX: ProbabilityRange(lower: 0.001, upper: 0.002),
+ ElementKind.TYPE_PARAMETER: ProbabilityRange(lower: 0.002, upper: 0.003),
ElementKind.FUNCTION_TYPE_ALIAS:
- ProbabilityRange(lower: 0.619, upper: 0.620),
- ElementKind.ENUM: ProbabilityRange(lower: 0.620, upper: 0.628),
- ElementKind.CLASS: ProbabilityRange(lower: 0.628, upper: 1.000),
+ ProbabilityRange(lower: 0.003, upper: 0.004),
+ ElementKind.ENUM: ProbabilityRange(lower: 0.004, upper: 0.011),
+ ElementKind.CLASS: ProbabilityRange(lower: 0.119, upper: 0.491),
},
'ForEachPartsWithDeclaration_iterable': {
- ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.000, upper: 0.016),
- ElementKind.ENUM: ProbabilityRange(lower: 0.016, upper: 0.018),
- ElementKind.FUNCTION: ProbabilityRange(lower: 0.018, upper: 0.022),
- ElementKind.PREFIX: ProbabilityRange(lower: 0.022, upper: 0.029),
- ElementKind.CLASS: ProbabilityRange(lower: 0.029, upper: 0.037),
+ ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.001, upper: 0.003),
+ ElementKind.ENUM: ProbabilityRange(lower: 0.003, upper: 0.005),
+ ElementKind.FUNCTION: ProbabilityRange(lower: 0.005, upper: 0.009),
+ ElementKind.PREFIX: ProbabilityRange(lower: 0.009, upper: 0.016),
+ ElementKind.CLASS: ProbabilityRange(lower: 0.016, upper: 0.023),
ElementKind.TOP_LEVEL_VARIABLE:
- ProbabilityRange(lower: 0.037, upper: 0.076),
- ElementKind.FIELD: ProbabilityRange(lower: 0.076, upper: 0.287),
- ElementKind.PARAMETER: ProbabilityRange(lower: 0.287, upper: 0.606),
- ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.606, upper: 1.000),
+ ProbabilityRange(lower: 0.031, upper: 0.072),
+ ElementKind.FIELD: ProbabilityRange(lower: 0.072, upper: 0.283),
+ ElementKind.PARAMETER: ProbabilityRange(lower: 0.283, upper: 0.602),
+ ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.602, upper: 1.000),
},
'ForEachPartsWithDeclaration_loopVariable': {
- ElementKind.TYPE_PARAMETER: ProbabilityRange(lower: 0.000, upper: 0.703),
+ ElementKind.TYPE_PARAMETER: ProbabilityRange(lower: 0.000, upper: 0.001),
ElementKind.FUNCTION_TYPE_ALIAS:
- ProbabilityRange(lower: 0.703, upper: 0.704),
- ElementKind.UNKNOWN: ProbabilityRange(lower: 0.704, upper: 0.705),
- ElementKind.PREFIX: ProbabilityRange(lower: 0.705, upper: 0.708),
- ElementKind.ENUM: ProbabilityRange(lower: 0.708, upper: 0.712),
- ElementKind.CLASS: ProbabilityRange(lower: 0.712, upper: 1.000),
+ ProbabilityRange(lower: 0.001, upper: 0.003),
+ ElementKind.UNKNOWN: ProbabilityRange(lower: 0.003, upper: 0.004),
+ ElementKind.PREFIX: ProbabilityRange(lower: 0.004, upper: 0.006),
+ ElementKind.ENUM: ProbabilityRange(lower: 0.006, upper: 0.010),
+ ElementKind.CLASS: ProbabilityRange(lower: 0.010, upper: 0.298),
},
'ForElement_body': {
- ElementKind.FUNCTION: ProbabilityRange(lower: 0.000, upper: 0.172),
- ElementKind.PARAMETER: ProbabilityRange(lower: 0.172, upper: 0.221),
- ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.221, upper: 0.290),
- ElementKind.METHOD: ProbabilityRange(lower: 0.290, upper: 0.366),
- ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.366, upper: 1.000),
+ ElementKind.FUNCTION: ProbabilityRange(lower: 0.038, upper: 0.083),
+ ElementKind.PARAMETER: ProbabilityRange(lower: 0.083, upper: 0.136),
+ ElementKind.METHOD: ProbabilityRange(lower: 0.136, upper: 0.220),
+ ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.220, upper: 0.303),
+ ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.303, upper: 1.000),
},
'ForElement_forLoopParts': {
- ElementKind.ENUM: ProbabilityRange(lower: 0.000, upper: 0.634),
- ElementKind.CLASS: ProbabilityRange(lower: 0.634, upper: 1.000),
+ ElementKind.ENUM: ProbabilityRange(lower: 0.000, upper: 0.007),
+ ElementKind.CLASS: ProbabilityRange(lower: 0.630, upper: 1.000),
},
'ForParts_condition': {
- ElementKind.FIELD: ProbabilityRange(lower: 0.000, upper: 0.004),
+ ElementKind.FIELD: ProbabilityRange(lower: 0.000, upper: 0.001),
ElementKind.TOP_LEVEL_VARIABLE:
- ProbabilityRange(lower: 0.004, upper: 0.024),
- ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.024, upper: 1.000),
+ ProbabilityRange(lower: 0.004, upper: 0.023),
+ ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.023, upper: 1.000),
},
'ForParts_updater': {
ElementKind.TOP_LEVEL_VARIABLE:
- ProbabilityRange(lower: 0.000, upper: 0.068),
- ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.068, upper: 1.000),
+ ProbabilityRange(lower: 0.000, upper: 0.020),
+ ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.020, upper: 1.000),
},
'ForStatement_body': {
- ElementKind.METHOD: ProbabilityRange(lower: 0.000, upper: 0.981),
- ElementKind.FIELD: ProbabilityRange(lower: 0.981, upper: 0.986),
- ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.986, upper: 1.000),
+ ElementKind.METHOD: ProbabilityRange(lower: 0.065, upper: 0.194),
+ ElementKind.FIELD: ProbabilityRange(lower: 0.194, upper: 0.387),
+ ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.387, upper: 1.000),
},
'ForStatement_forLoopParts': {
- ElementKind.TYPE_PARAMETER: ProbabilityRange(lower: 0.000, upper: 0.536),
+ ElementKind.TYPE_PARAMETER: ProbabilityRange(lower: 0.000, upper: 0.001),
ElementKind.FUNCTION_TYPE_ALIAS:
- ProbabilityRange(lower: 0.536, upper: 0.537),
- ElementKind.UNKNOWN: ProbabilityRange(lower: 0.537, upper: 0.538),
- ElementKind.ENUM: ProbabilityRange(lower: 0.538, upper: 0.539),
- ElementKind.PREFIX: ProbabilityRange(lower: 0.539, upper: 0.541),
- ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.541, upper: 0.551),
+ ProbabilityRange(lower: 0.001, upper: 0.001),
+ ElementKind.UNKNOWN: ProbabilityRange(lower: 0.001, upper: 0.002),
+ ElementKind.ENUM: ProbabilityRange(lower: 0.002, upper: 0.004),
+ ElementKind.PREFIX: ProbabilityRange(lower: 0.004, upper: 0.005),
+ ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.005, upper: 0.015),
ElementKind.TOP_LEVEL_VARIABLE:
- ProbabilityRange(lower: 0.551, upper: 0.561),
+ ProbabilityRange(lower: 0.015, upper: 0.025),
ElementKind.CLASS: ProbabilityRange(lower: 0.561, upper: 1.000),
},
'FormalParameterList_parameter': {
- ElementKind.MIXIN: ProbabilityRange(lower: 0.000, upper: 0.251),
- ElementKind.PREFIX: ProbabilityRange(lower: 0.251, upper: 0.252),
- ElementKind.TYPE_PARAMETER: ProbabilityRange(lower: 0.252, upper: 0.255),
+ ElementKind.MIXIN: ProbabilityRange(lower: 0.000, upper: 0.000),
+ ElementKind.PREFIX: ProbabilityRange(lower: 0.001, upper: 0.003),
+ ElementKind.TYPE_PARAMETER: ProbabilityRange(lower: 0.003, upper: 0.005),
ElementKind.FUNCTION_TYPE_ALIAS:
- ProbabilityRange(lower: 0.255, upper: 0.263),
- ElementKind.ENUM: ProbabilityRange(lower: 0.263, upper: 0.276),
- ElementKind.UNKNOWN: ProbabilityRange(lower: 0.276, upper: 0.293),
- ElementKind.CLASS: ProbabilityRange(lower: 0.293, upper: 1.000),
+ ProbabilityRange(lower: 0.005, upper: 0.013),
+ ElementKind.ENUM: ProbabilityRange(lower: 0.013, upper: 0.024),
+ ElementKind.UNKNOWN: ProbabilityRange(lower: 0.038, upper: 0.054),
+ ElementKind.CLASS: ProbabilityRange(lower: 0.360, upper: 1.000),
},
'FunctionDeclaration_returnType': {
- ElementKind.PREFIX: ProbabilityRange(lower: 0.000, upper: 0.284),
- ElementKind.UNKNOWN: ProbabilityRange(lower: 0.284, upper: 0.286),
- ElementKind.TYPE_PARAMETER: ProbabilityRange(lower: 0.286, upper: 0.290),
- ElementKind.ENUM: ProbabilityRange(lower: 0.290, upper: 0.301),
+ ElementKind.PREFIX: ProbabilityRange(lower: 0.000, upper: 0.000),
+ ElementKind.UNKNOWN: ProbabilityRange(lower: 0.000, upper: 0.002),
+ ElementKind.TYPE_PARAMETER: ProbabilityRange(lower: 0.002, upper: 0.006),
+ ElementKind.ENUM: ProbabilityRange(lower: 0.006, upper: 0.017),
ElementKind.FUNCTION_TYPE_ALIAS:
- ProbabilityRange(lower: 0.301, upper: 0.354),
+ ProbabilityRange(lower: 0.017, upper: 0.070),
ElementKind.CLASS: ProbabilityRange(lower: 0.354, upper: 1.000),
},
'GenericTypeAlias_functionType': {
ElementKind.FUNCTION_TYPE_ALIAS:
- ProbabilityRange(lower: 0.000, upper: 0.590),
- ElementKind.PREFIX: ProbabilityRange(lower: 0.590, upper: 0.610),
- ElementKind.TYPE_PARAMETER: ProbabilityRange(lower: 0.610, upper: 0.660),
- ElementKind.CLASS: ProbabilityRange(lower: 0.660, upper: 1.000),
+ ProbabilityRange(lower: 0.000, upper: 0.020),
+ ElementKind.PREFIX: ProbabilityRange(lower: 0.020, upper: 0.040),
+ ElementKind.TYPE_PARAMETER: ProbabilityRange(lower: 0.040, upper: 0.090),
+ ElementKind.CLASS: ProbabilityRange(lower: 0.140, upper: 0.480),
},
'HideCombinator_hiddenName': {
ElementKind.ENUM: ProbabilityRange(lower: 0.000, upper: 0.059),
@@ -662,97 +662,97 @@
},
'IfElement_condition': {
ElementKind.TOP_LEVEL_VARIABLE:
- ProbabilityRange(lower: 0.000, upper: 0.158),
- ElementKind.FUNCTION: ProbabilityRange(lower: 0.158, upper: 0.163),
- ElementKind.CLASS: ProbabilityRange(lower: 0.163, upper: 0.179),
- ElementKind.PARAMETER: ProbabilityRange(lower: 0.179, upper: 0.339),
- ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.339, upper: 0.633),
- ElementKind.FIELD: ProbabilityRange(lower: 0.633, upper: 1.000),
+ ProbabilityRange(lower: 0.000, upper: 0.005),
+ ElementKind.FUNCTION: ProbabilityRange(lower: 0.011, upper: 0.016),
+ ElementKind.CLASS: ProbabilityRange(lower: 0.016, upper: 0.035),
+ ElementKind.PARAMETER: ProbabilityRange(lower: 0.035, upper: 0.223),
+ ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.223, upper: 0.569),
+ ElementKind.FIELD: ProbabilityRange(lower: 0.569, upper: 1.000),
},
'IfElement_elseElement': {
- ElementKind.METHOD: ProbabilityRange(lower: 0.000, upper: 0.346),
- ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.346, upper: 1.000),
+ ElementKind.METHOD: ProbabilityRange(lower: 0.043, upper: 0.087),
+ ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.261, upper: 1.000),
},
'IfElement_thenElement': {
- ElementKind.FUNCTION: ProbabilityRange(lower: 0.000, upper: 0.215),
- ElementKind.PARAMETER: ProbabilityRange(lower: 0.215, upper: 0.222),
- ElementKind.CLASS: ProbabilityRange(lower: 0.222, upper: 0.230),
- ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.230, upper: 0.242),
- ElementKind.FIELD: ProbabilityRange(lower: 0.242, upper: 0.257),
- ElementKind.METHOD: ProbabilityRange(lower: 0.257, upper: 0.284),
- ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.284, upper: 1.000),
+ ElementKind.FUNCTION: ProbabilityRange(lower: 0.000, upper: 0.003),
+ ElementKind.PARAMETER: ProbabilityRange(lower: 0.003, upper: 0.011),
+ ElementKind.CLASS: ProbabilityRange(lower: 0.022, upper: 0.033),
+ ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.033, upper: 0.047),
+ ElementKind.FIELD: ProbabilityRange(lower: 0.063, upper: 0.082),
+ ElementKind.METHOD: ProbabilityRange(lower: 0.082, upper: 0.115),
+ ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.115, upper: 1.000),
},
'IfStatement_condition': {
- ElementKind.TYPE_PARAMETER: ProbabilityRange(lower: 0.000, upper: 0.118),
- ElementKind.MIXIN: ProbabilityRange(lower: 0.118, upper: 0.118),
- ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.118, upper: 0.118),
- ElementKind.ENUM: ProbabilityRange(lower: 0.118, upper: 0.118),
- ElementKind.PREFIX: ProbabilityRange(lower: 0.118, upper: 0.119),
+ ElementKind.TYPE_PARAMETER: ProbabilityRange(lower: 0.000, upper: 0.000),
+ ElementKind.MIXIN: ProbabilityRange(lower: 0.000, upper: 0.000),
+ ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.000, upper: 0.000),
+ ElementKind.ENUM: ProbabilityRange(lower: 0.001, upper: 0.001),
+ ElementKind.PREFIX: ProbabilityRange(lower: 0.002, upper: 0.002),
ElementKind.TOP_LEVEL_VARIABLE:
- ProbabilityRange(lower: 0.119, upper: 0.127),
- ElementKind.METHOD: ProbabilityRange(lower: 0.127, upper: 0.137),
- ElementKind.CLASS: ProbabilityRange(lower: 0.137, upper: 0.153),
- ElementKind.FUNCTION: ProbabilityRange(lower: 0.153, upper: 0.175),
- ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.175, upper: 0.397),
- ElementKind.PARAMETER: ProbabilityRange(lower: 0.397, upper: 0.696),
- ElementKind.FIELD: ProbabilityRange(lower: 0.696, upper: 1.000),
+ ProbabilityRange(lower: 0.002, upper: 0.013),
+ ElementKind.METHOD: ProbabilityRange(lower: 0.013, upper: 0.023),
+ ElementKind.CLASS: ProbabilityRange(lower: 0.023, upper: 0.041),
+ ElementKind.FUNCTION: ProbabilityRange(lower: 0.084, upper: 0.107),
+ ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.107, upper: 0.347),
+ ElementKind.PARAMETER: ProbabilityRange(lower: 0.347, upper: 0.672),
+ ElementKind.FIELD: ProbabilityRange(lower: 0.672, upper: 1.000),
},
'IfStatement_elseStatement': {
ElementKind.TOP_LEVEL_VARIABLE:
- ProbabilityRange(lower: 0.000, upper: 0.968),
- ElementKind.PARAMETER: ProbabilityRange(lower: 0.968, upper: 0.969),
- ElementKind.FUNCTION: ProbabilityRange(lower: 0.969, upper: 0.970),
- ElementKind.CLASS: ProbabilityRange(lower: 0.970, upper: 0.973),
- ElementKind.FIELD: ProbabilityRange(lower: 0.973, upper: 0.982),
- ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.982, upper: 0.991),
- ElementKind.METHOD: ProbabilityRange(lower: 0.991, upper: 1.000),
+ ProbabilityRange(lower: 0.004, upper: 0.007),
+ ElementKind.PARAMETER: ProbabilityRange(lower: 0.007, upper: 0.009),
+ ElementKind.FUNCTION: ProbabilityRange(lower: 0.009, upper: 0.012),
+ ElementKind.CLASS: ProbabilityRange(lower: 0.012, upper: 0.021),
+ ElementKind.FIELD: ProbabilityRange(lower: 0.021, upper: 0.045),
+ ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.045, upper: 0.069),
+ ElementKind.METHOD: ProbabilityRange(lower: 0.069, upper: 0.095),
},
'IfStatement_thenStatement': {
- ElementKind.MIXIN: ProbabilityRange(lower: 0.000, upper: 0.935),
+ ElementKind.MIXIN: ProbabilityRange(lower: 0.002, upper: 0.003),
ElementKind.TOP_LEVEL_VARIABLE:
- ProbabilityRange(lower: 0.935, upper: 0.936),
- ElementKind.FUNCTION: ProbabilityRange(lower: 0.936, upper: 0.937),
- ElementKind.CLASS: ProbabilityRange(lower: 0.937, upper: 0.939),
- ElementKind.METHOD: ProbabilityRange(lower: 0.939, upper: 0.948),
- ElementKind.FIELD: ProbabilityRange(lower: 0.948, upper: 0.965),
- ElementKind.PARAMETER: ProbabilityRange(lower: 0.965, upper: 0.981),
- ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.981, upper: 1.000),
+ ProbabilityRange(lower: 0.013, upper: 0.019),
+ ElementKind.FUNCTION: ProbabilityRange(lower: 0.019, upper: 0.025),
+ ElementKind.CLASS: ProbabilityRange(lower: 0.058, upper: 0.069),
+ ElementKind.METHOD: ProbabilityRange(lower: 0.069, upper: 0.134),
+ ElementKind.FIELD: ProbabilityRange(lower: 0.134, upper: 0.243),
+ ElementKind.PARAMETER: ProbabilityRange(lower: 0.243, upper: 0.355),
+ ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.355, upper: 0.480),
},
'ImplementsClause_interface': {
ElementKind.PREFIX: ProbabilityRange(lower: 0.000, upper: 0.003),
ElementKind.CLASS: ProbabilityRange(lower: 0.003, upper: 1.000),
},
'IndexExpression_index': {
- ElementKind.PREFIX: ProbabilityRange(lower: 0.000, upper: 0.655),
- ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.655, upper: 0.655),
- ElementKind.TYPE_PARAMETER: ProbabilityRange(lower: 0.655, upper: 0.656),
- ElementKind.FUNCTION: ProbabilityRange(lower: 0.656, upper: 0.656),
- ElementKind.METHOD: ProbabilityRange(lower: 0.656, upper: 0.657),
- ElementKind.ENUM: ProbabilityRange(lower: 0.657, upper: 0.659),
- ElementKind.CLASS: ProbabilityRange(lower: 0.659, upper: 0.664),
+ ElementKind.PREFIX: ProbabilityRange(lower: 0.000, upper: 0.000),
+ ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.002, upper: 0.003),
+ ElementKind.TYPE_PARAMETER: ProbabilityRange(lower: 0.003, upper: 0.004),
+ ElementKind.FUNCTION: ProbabilityRange(lower: 0.004, upper: 0.006),
+ ElementKind.METHOD: ProbabilityRange(lower: 0.006, upper: 0.008),
+ ElementKind.ENUM: ProbabilityRange(lower: 0.008, upper: 0.015),
+ ElementKind.CLASS: ProbabilityRange(lower: 0.015, upper: 0.029),
ElementKind.TOP_LEVEL_VARIABLE:
- ProbabilityRange(lower: 0.664, upper: 0.670),
- ElementKind.FIELD: ProbabilityRange(lower: 0.670, upper: 0.761),
- ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.761, upper: 0.863),
- ElementKind.PARAMETER: ProbabilityRange(lower: 0.863, upper: 1.000),
+ ProbabilityRange(lower: 0.029, upper: 0.046),
+ ElementKind.FIELD: ProbabilityRange(lower: 0.046, upper: 0.307),
+ ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.307, upper: 0.605),
+ ElementKind.PARAMETER: ProbabilityRange(lower: 0.605, upper: 1.000),
},
'InstanceCreationExpression_constructorName': {
ElementKind.PREFIX: ProbabilityRange(lower: 0.000, upper: 0.006),
ElementKind.CLASS: ProbabilityRange(lower: 0.006, upper: 1.000),
},
'InterpolationExpression_expression': {
- ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.000, upper: 0.035),
- ElementKind.TYPE_PARAMETER: ProbabilityRange(lower: 0.035, upper: 0.035),
- ElementKind.PREFIX: ProbabilityRange(lower: 0.035, upper: 0.036),
- ElementKind.ENUM: ProbabilityRange(lower: 0.036, upper: 0.037),
- ElementKind.METHOD: ProbabilityRange(lower: 0.037, upper: 0.044),
- ElementKind.FUNCTION: ProbabilityRange(lower: 0.044, upper: 0.074),
+ ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.000, upper: 0.001),
+ ElementKind.TYPE_PARAMETER: ProbabilityRange(lower: 0.001, upper: 0.001),
+ ElementKind.PREFIX: ProbabilityRange(lower: 0.001, upper: 0.002),
+ ElementKind.ENUM: ProbabilityRange(lower: 0.002, upper: 0.002),
+ ElementKind.METHOD: ProbabilityRange(lower: 0.002, upper: 0.010),
+ ElementKind.FUNCTION: ProbabilityRange(lower: 0.032, upper: 0.062),
ElementKind.TOP_LEVEL_VARIABLE:
- ProbabilityRange(lower: 0.074, upper: 0.133),
- ElementKind.CLASS: ProbabilityRange(lower: 0.133, upper: 0.257),
- ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.257, upper: 0.447),
- ElementKind.PARAMETER: ProbabilityRange(lower: 0.447, upper: 0.693),
- ElementKind.FIELD: ProbabilityRange(lower: 0.693, upper: 1.000),
+ ProbabilityRange(lower: 0.062, upper: 0.121),
+ ElementKind.CLASS: ProbabilityRange(lower: 0.121, upper: 0.247),
+ ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.247, upper: 0.440),
+ ElementKind.PARAMETER: ProbabilityRange(lower: 0.440, upper: 0.689),
+ ElementKind.FIELD: ProbabilityRange(lower: 0.689, upper: 1.000),
},
'IsExpression_type': {
ElementKind.MIXIN: ProbabilityRange(lower: 0.000, upper: 0.001),
@@ -764,45 +764,45 @@
ElementKind.CLASS: ProbabilityRange(lower: 0.013, upper: 1.000),
},
'ListLiteral_element': {
- ElementKind.PREFIX: ProbabilityRange(lower: 0.000, upper: 0.324),
- ElementKind.FUNCTION: ProbabilityRange(lower: 0.324, upper: 0.326),
+ ElementKind.PREFIX: ProbabilityRange(lower: 0.000, upper: 0.001),
+ ElementKind.FUNCTION: ProbabilityRange(lower: 0.004, upper: 0.008),
ElementKind.TOP_LEVEL_VARIABLE:
- ProbabilityRange(lower: 0.326, upper: 0.329),
- ElementKind.ENUM: ProbabilityRange(lower: 0.329, upper: 0.333),
- ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.333, upper: 0.340),
- ElementKind.FIELD: ProbabilityRange(lower: 0.340, upper: 0.350),
- ElementKind.PARAMETER: ProbabilityRange(lower: 0.350, upper: 0.361),
- ElementKind.METHOD: ProbabilityRange(lower: 0.361, upper: 0.379),
- ElementKind.CLASS: ProbabilityRange(lower: 0.379, upper: 0.406),
- ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.406, upper: 1.000),
+ ProbabilityRange(lower: 0.008, upper: 0.011),
+ ElementKind.ENUM: ProbabilityRange(lower: 0.017, upper: 0.023),
+ ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.023, upper: 0.033),
+ ElementKind.FIELD: ProbabilityRange(lower: 0.033, upper: 0.047),
+ ElementKind.PARAMETER: ProbabilityRange(lower: 0.047, upper: 0.063),
+ ElementKind.METHOD: ProbabilityRange(lower: 0.082, upper: 0.107),
+ ElementKind.CLASS: ProbabilityRange(lower: 0.107, upper: 0.147),
+ ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.147, upper: 1.000),
},
'MapLiteralEntry_value': {
- ElementKind.PREFIX: ProbabilityRange(lower: 0.000, upper: 0.814),
- ElementKind.METHOD: ProbabilityRange(lower: 0.814, upper: 0.814),
- ElementKind.ENUM: ProbabilityRange(lower: 0.814, upper: 0.816),
- ElementKind.FUNCTION: ProbabilityRange(lower: 0.816, upper: 0.818),
+ ElementKind.PREFIX: ProbabilityRange(lower: 0.005, upper: 0.008),
+ ElementKind.METHOD: ProbabilityRange(lower: 0.008, upper: 0.011),
+ ElementKind.ENUM: ProbabilityRange(lower: 0.019, upper: 0.028),
+ ElementKind.FUNCTION: ProbabilityRange(lower: 0.038, upper: 0.049),
ElementKind.TOP_LEVEL_VARIABLE:
- ProbabilityRange(lower: 0.818, upper: 0.823),
- ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.823, upper: 0.828),
- ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.828, upper: 0.833),
- ElementKind.PARAMETER: ProbabilityRange(lower: 0.833, upper: 0.854),
- ElementKind.FIELD: ProbabilityRange(lower: 0.854, upper: 0.884),
- ElementKind.CLASS: ProbabilityRange(lower: 0.884, upper: 1.000),
+ ProbabilityRange(lower: 0.049, upper: 0.073),
+ ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.073, upper: 0.099),
+ ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.099, upper: 0.128),
+ ElementKind.PARAMETER: ProbabilityRange(lower: 0.128, upper: 0.234),
+ ElementKind.FIELD: ProbabilityRange(lower: 0.234, upper: 0.393),
+ ElementKind.CLASS: ProbabilityRange(lower: 0.393, upper: 1.000),
},
'MethodDeclaration_returnType': {
- ElementKind.MIXIN: ProbabilityRange(lower: 0.000, upper: 0.169),
- ElementKind.PREFIX: ProbabilityRange(lower: 0.169, upper: 0.169),
- ElementKind.UNKNOWN: ProbabilityRange(lower: 0.169, upper: 0.171),
- ElementKind.TYPE_PARAMETER: ProbabilityRange(lower: 0.171, upper: 0.172),
+ ElementKind.MIXIN: ProbabilityRange(lower: 0.000, upper: 0.000),
+ ElementKind.PREFIX: ProbabilityRange(lower: 0.000, upper: 0.000),
+ ElementKind.UNKNOWN: ProbabilityRange(lower: 0.000, upper: 0.002),
+ ElementKind.TYPE_PARAMETER: ProbabilityRange(lower: 0.002, upper: 0.004),
ElementKind.FUNCTION_TYPE_ALIAS:
- ProbabilityRange(lower: 0.172, upper: 0.175),
- ElementKind.ENUM: ProbabilityRange(lower: 0.175, upper: 0.179),
+ ProbabilityRange(lower: 0.004, upper: 0.006),
+ ElementKind.ENUM: ProbabilityRange(lower: 0.006, upper: 0.010),
ElementKind.CLASS: ProbabilityRange(lower: 0.179, upper: 1.000),
},
'MixinDeclaration_member': {
- ElementKind.TYPE_PARAMETER: ProbabilityRange(lower: 0.000, upper: 0.116),
- ElementKind.ENUM: ProbabilityRange(lower: 0.116, upper: 0.121),
- ElementKind.UNKNOWN: ProbabilityRange(lower: 0.121, upper: 0.128),
+ ElementKind.TYPE_PARAMETER: ProbabilityRange(lower: 0.000, upper: 0.002),
+ ElementKind.ENUM: ProbabilityRange(lower: 0.002, upper: 0.007),
+ ElementKind.UNKNOWN: ProbabilityRange(lower: 0.007, upper: 0.014),
ElementKind.CLASS: ProbabilityRange(lower: 0.128, upper: 1.000),
},
'OnClause_superclassConstraint': {
@@ -810,31 +810,31 @@
ElementKind.CLASS: ProbabilityRange(lower: 0.392, upper: 1.000),
},
'ParenthesizedExpression_expression': {
- ElementKind.MIXIN: ProbabilityRange(lower: 0.000, upper: 0.218),
- ElementKind.ENUM: ProbabilityRange(lower: 0.218, upper: 0.219),
- ElementKind.PREFIX: ProbabilityRange(lower: 0.219, upper: 0.221),
- ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.221, upper: 0.224),
- ElementKind.FUNCTION: ProbabilityRange(lower: 0.224, upper: 0.228),
- ElementKind.CLASS: ProbabilityRange(lower: 0.228, upper: 0.274),
- ElementKind.METHOD: ProbabilityRange(lower: 0.274, upper: 0.331),
+ ElementKind.MIXIN: ProbabilityRange(lower: 0.000, upper: 0.000),
+ ElementKind.ENUM: ProbabilityRange(lower: 0.001, upper: 0.002),
+ ElementKind.PREFIX: ProbabilityRange(lower: 0.002, upper: 0.004),
+ ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.004, upper: 0.008),
+ ElementKind.FUNCTION: ProbabilityRange(lower: 0.008, upper: 0.013),
+ ElementKind.CLASS: ProbabilityRange(lower: 0.051, upper: 0.107),
+ ElementKind.METHOD: ProbabilityRange(lower: 0.107, upper: 0.177),
ElementKind.TOP_LEVEL_VARIABLE:
- ProbabilityRange(lower: 0.331, upper: 0.403),
- ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.403, upper: 0.586),
- ElementKind.FIELD: ProbabilityRange(lower: 0.586, upper: 0.786),
- ElementKind.PARAMETER: ProbabilityRange(lower: 0.786, upper: 1.000),
+ ProbabilityRange(lower: 0.177, upper: 0.267),
+ ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.267, upper: 0.491),
+ ElementKind.FIELD: ProbabilityRange(lower: 0.491, upper: 0.737),
+ ElementKind.PARAMETER: ProbabilityRange(lower: 0.737, upper: 1.000),
},
'PrefixExpression_!_operand': {
- ElementKind.FUNCTION: ProbabilityRange(lower: 0.000, upper: 0.062),
- ElementKind.METHOD: ProbabilityRange(lower: 0.062, upper: 0.090),
+ ElementKind.FUNCTION: ProbabilityRange(lower: 0.015, upper: 0.028),
+ ElementKind.METHOD: ProbabilityRange(lower: 0.028, upper: 0.057),
ElementKind.TOP_LEVEL_VARIABLE:
- ProbabilityRange(lower: 0.090, upper: 0.124),
- ElementKind.CLASS: ProbabilityRange(lower: 0.124, upper: 0.173),
- ElementKind.PARAMETER: ProbabilityRange(lower: 0.173, upper: 0.360),
- ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.360, upper: 0.616),
- ElementKind.FIELD: ProbabilityRange(lower: 0.616, upper: 1.000),
+ ProbabilityRange(lower: 0.057, upper: 0.093),
+ ElementKind.CLASS: ProbabilityRange(lower: 0.093, upper: 0.144),
+ ElementKind.PARAMETER: ProbabilityRange(lower: 0.144, upper: 0.338),
+ ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.338, upper: 0.603),
+ ElementKind.FIELD: ProbabilityRange(lower: 0.603, upper: 1.000),
},
'PrefixExpression_++_operand': {
- ElementKind.PARAMETER: ProbabilityRange(lower: 0.000, upper: 0.035),
+ ElementKind.PARAMETER: ProbabilityRange(lower: 0.000, upper: 0.018),
ElementKind.FIELD: ProbabilityRange(lower: 0.035, upper: 0.351),
ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.351, upper: 1.000),
},
@@ -844,15 +844,15 @@
ElementKind.FIELD: ProbabilityRange(lower: 0.313, upper: 1.000),
},
'PrefixExpression_-_operand': {
- ElementKind.METHOD: ProbabilityRange(lower: 0.000, upper: 0.830),
- ElementKind.FUNCTION: ProbabilityRange(lower: 0.830, upper: 0.832),
- ElementKind.PREFIX: ProbabilityRange(lower: 0.832, upper: 0.839),
- ElementKind.PARAMETER: ProbabilityRange(lower: 0.839, upper: 0.857),
+ ElementKind.METHOD: ProbabilityRange(lower: 0.000, upper: 0.006),
+ ElementKind.FUNCTION: ProbabilityRange(lower: 0.006, upper: 0.022),
+ ElementKind.PREFIX: ProbabilityRange(lower: 0.022, upper: 0.061),
+ ElementKind.PARAMETER: ProbabilityRange(lower: 0.061, upper: 0.166),
ElementKind.TOP_LEVEL_VARIABLE:
- ProbabilityRange(lower: 0.857, upper: 0.877),
- ElementKind.CLASS: ProbabilityRange(lower: 0.877, upper: 0.902),
- ElementKind.FIELD: ProbabilityRange(lower: 0.902, upper: 0.930),
- ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.930, upper: 1.000),
+ ProbabilityRange(lower: 0.166, upper: 0.282),
+ ElementKind.CLASS: ProbabilityRange(lower: 0.282, upper: 0.431),
+ ElementKind.FIELD: ProbabilityRange(lower: 0.431, upper: 0.591),
+ ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.591, upper: 1.000),
},
'PrefixExpression_~_operand': {
ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.000, upper: 1.000),
@@ -862,31 +862,31 @@
ElementKind.FIELD: ProbabilityRange(lower: 0.002, upper: 1.000),
},
'ReturnStatement_expression': {
- ElementKind.TYPE_PARAMETER: ProbabilityRange(lower: 0.000, upper: 0.243),
- ElementKind.PREFIX: ProbabilityRange(lower: 0.243, upper: 0.246),
- ElementKind.ENUM: ProbabilityRange(lower: 0.246, upper: 0.254),
+ ElementKind.TYPE_PARAMETER: ProbabilityRange(lower: 0.000, upper: 0.000),
+ ElementKind.PREFIX: ProbabilityRange(lower: 0.003, upper: 0.008),
+ ElementKind.ENUM: ProbabilityRange(lower: 0.020, upper: 0.029),
ElementKind.TOP_LEVEL_VARIABLE:
- ProbabilityRange(lower: 0.254, upper: 0.262),
- ElementKind.FUNCTION: ProbabilityRange(lower: 0.262, upper: 0.279),
- ElementKind.METHOD: ProbabilityRange(lower: 0.279, upper: 0.303),
- ElementKind.FIELD: ProbabilityRange(lower: 0.303, upper: 0.352),
- ElementKind.PARAMETER: ProbabilityRange(lower: 0.352, upper: 0.406),
- ElementKind.CLASS: ProbabilityRange(lower: 0.406, upper: 0.466),
- ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.466, upper: 0.564),
- ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.564, upper: 1.000),
+ ProbabilityRange(lower: 0.029, upper: 0.038),
+ ElementKind.FUNCTION: ProbabilityRange(lower: 0.070, upper: 0.090),
+ ElementKind.METHOD: ProbabilityRange(lower: 0.142, upper: 0.171),
+ ElementKind.FIELD: ProbabilityRange(lower: 0.171, upper: 0.229),
+ ElementKind.PARAMETER: ProbabilityRange(lower: 0.229, upper: 0.292),
+ ElementKind.CLASS: ProbabilityRange(lower: 0.292, upper: 0.365),
+ ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.365, upper: 0.481),
+ ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.481, upper: 1.000),
},
'SetOrMapLiteral_element': {
- ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.000, upper: 0.974),
- ElementKind.METHOD: ProbabilityRange(lower: 0.974, upper: 0.974),
- ElementKind.FUNCTION: ProbabilityRange(lower: 0.974, upper: 0.974),
- ElementKind.PREFIX: ProbabilityRange(lower: 0.974, upper: 0.974),
- ElementKind.PARAMETER: ProbabilityRange(lower: 0.974, upper: 0.975),
+ ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.000, upper: 0.002),
+ ElementKind.METHOD: ProbabilityRange(lower: 0.002, upper: 0.005),
+ ElementKind.FUNCTION: ProbabilityRange(lower: 0.005, upper: 0.009),
+ ElementKind.PREFIX: ProbabilityRange(lower: 0.015, upper: 0.022),
+ ElementKind.PARAMETER: ProbabilityRange(lower: 0.040, upper: 0.051),
ElementKind.TOP_LEVEL_VARIABLE:
- ProbabilityRange(lower: 0.975, upper: 0.976),
- ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.976, upper: 0.979),
- ElementKind.ENUM: ProbabilityRange(lower: 0.979, upper: 0.985),
- ElementKind.FIELD: ProbabilityRange(lower: 0.985, upper: 0.991),
- ElementKind.CLASS: ProbabilityRange(lower: 0.991, upper: 1.000),
+ ProbabilityRange(lower: 0.051, upper: 0.107),
+ ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.107, upper: 0.231),
+ ElementKind.ENUM: ProbabilityRange(lower: 0.231, upper: 0.423),
+ ElementKind.FIELD: ProbabilityRange(lower: 0.423, upper: 0.653),
+ ElementKind.CLASS: ProbabilityRange(lower: 0.653, upper: 1.000),
},
'ShowCombinator_shownName': {
ElementKind.FUNCTION_TYPE_ALIAS:
@@ -898,77 +898,77 @@
ElementKind.CLASS: ProbabilityRange(lower: 0.568, upper: 1.000),
},
'SpreadElement_expression': {
- ElementKind.ENUM: ProbabilityRange(lower: 0.000, upper: 0.335),
- ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.335, upper: 0.349),
- ElementKind.CLASS: ProbabilityRange(lower: 0.349, upper: 0.379),
- ElementKind.FUNCTION: ProbabilityRange(lower: 0.379, upper: 0.446),
+ ElementKind.ENUM: ProbabilityRange(lower: 0.000, upper: 0.005),
+ ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.022, upper: 0.044),
+ ElementKind.CLASS: ProbabilityRange(lower: 0.044, upper: 0.087),
+ ElementKind.FUNCTION: ProbabilityRange(lower: 0.087, upper: 0.186),
ElementKind.TOP_LEVEL_VARIABLE:
- ProbabilityRange(lower: 0.446, upper: 0.520),
- ElementKind.METHOD: ProbabilityRange(lower: 0.520, upper: 0.610),
- ElementKind.PARAMETER: ProbabilityRange(lower: 0.610, upper: 0.710),
- ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.710, upper: 0.814),
- ElementKind.FIELD: ProbabilityRange(lower: 0.814, upper: 1.000),
+ ProbabilityRange(lower: 0.186, upper: 0.295),
+ ElementKind.METHOD: ProbabilityRange(lower: 0.295, upper: 0.426),
+ ElementKind.PARAMETER: ProbabilityRange(lower: 0.426, upper: 0.574),
+ ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.574, upper: 0.727),
+ ElementKind.FIELD: ProbabilityRange(lower: 0.727, upper: 1.000),
},
'Statement': {
- ElementKind.TYPE_PARAMETER: ProbabilityRange(lower: 0.000, upper: 0.599),
+ ElementKind.TYPE_PARAMETER: ProbabilityRange(lower: 0.000, upper: 0.000),
ElementKind.FUNCTION_TYPE_ALIAS:
- ProbabilityRange(lower: 0.599, upper: 0.599),
- ElementKind.UNKNOWN: ProbabilityRange(lower: 0.599, upper: 0.600),
- ElementKind.ENUM: ProbabilityRange(lower: 0.600, upper: 0.600),
- ElementKind.MIXIN: ProbabilityRange(lower: 0.600, upper: 0.601),
- ElementKind.PREFIX: ProbabilityRange(lower: 0.601, upper: 0.602),
+ ProbabilityRange(lower: 0.000, upper: 0.000),
+ ElementKind.UNKNOWN: ProbabilityRange(lower: 0.000, upper: 0.001),
+ ElementKind.ENUM: ProbabilityRange(lower: 0.001, upper: 0.002),
+ ElementKind.MIXIN: ProbabilityRange(lower: 0.002, upper: 0.003),
+ ElementKind.PREFIX: ProbabilityRange(lower: 0.003, upper: 0.006),
ElementKind.TOP_LEVEL_VARIABLE:
- ProbabilityRange(lower: 0.602, upper: 0.613),
- ElementKind.PARAMETER: ProbabilityRange(lower: 0.613, upper: 0.634),
- ElementKind.METHOD: ProbabilityRange(lower: 0.634, upper: 0.677),
- ElementKind.FUNCTION: ProbabilityRange(lower: 0.677, upper: 0.727),
- ElementKind.CLASS: ProbabilityRange(lower: 0.727, upper: 0.800),
- ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.800, upper: 0.881),
- ElementKind.FIELD: ProbabilityRange(lower: 0.881, upper: 1.000),
+ ProbabilityRange(lower: 0.006, upper: 0.037),
+ ElementKind.PARAMETER: ProbabilityRange(lower: 0.037, upper: 0.088),
+ ElementKind.METHOD: ProbabilityRange(lower: 0.088, upper: 0.195),
+ ElementKind.FUNCTION: ProbabilityRange(lower: 0.195, upper: 0.319),
+ ElementKind.CLASS: ProbabilityRange(lower: 0.319, upper: 0.502),
+ ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.502, upper: 0.705),
+ ElementKind.FIELD: ProbabilityRange(lower: 0.705, upper: 1.000),
},
'SwitchCase_expression': {
- ElementKind.FIELD: ProbabilityRange(lower: 0.000, upper: 0.508),
+ ElementKind.FIELD: ProbabilityRange(lower: 0.009, upper: 0.016),
ElementKind.TOP_LEVEL_VARIABLE:
- ProbabilityRange(lower: 0.508, upper: 0.525),
- ElementKind.CLASS: ProbabilityRange(lower: 0.525, upper: 0.668),
- ElementKind.ENUM: ProbabilityRange(lower: 0.668, upper: 1.000),
+ ProbabilityRange(lower: 0.016, upper: 0.049),
+ ElementKind.CLASS: ProbabilityRange(lower: 0.049, upper: 0.336),
+ ElementKind.ENUM: ProbabilityRange(lower: 0.336, upper: 1.000),
},
'SwitchMember_statement': {
- ElementKind.UNKNOWN: ProbabilityRange(lower: 0.000, upper: 0.683),
- ElementKind.MIXIN: ProbabilityRange(lower: 0.683, upper: 0.683),
+ ElementKind.UNKNOWN: ProbabilityRange(lower: 0.000, upper: 0.000),
+ ElementKind.MIXIN: ProbabilityRange(lower: 0.000, upper: 0.000),
ElementKind.TOP_LEVEL_VARIABLE:
- ProbabilityRange(lower: 0.683, upper: 0.685),
- ElementKind.PARAMETER: ProbabilityRange(lower: 0.685, upper: 0.691),
- ElementKind.CLASS: ProbabilityRange(lower: 0.691, upper: 0.703),
- ElementKind.FUNCTION: ProbabilityRange(lower: 0.703, upper: 0.715),
- ElementKind.METHOD: ProbabilityRange(lower: 0.715, upper: 0.730),
- ElementKind.FIELD: ProbabilityRange(lower: 0.730, upper: 0.759),
- ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.759, upper: 1.000),
+ ProbabilityRange(lower: 0.013, upper: 0.017),
+ ElementKind.PARAMETER: ProbabilityRange(lower: 0.027, upper: 0.033),
+ ElementKind.CLASS: ProbabilityRange(lower: 0.045, upper: 0.056),
+ ElementKind.FUNCTION: ProbabilityRange(lower: 0.056, upper: 0.069),
+ ElementKind.METHOD: ProbabilityRange(lower: 0.069, upper: 0.085),
+ ElementKind.FIELD: ProbabilityRange(lower: 0.109, upper: 0.138),
+ ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.138, upper: 0.380),
},
'SwitchStatement_expression': {
- ElementKind.TYPE_PARAMETER: ProbabilityRange(lower: 0.000, upper: 0.010),
- ElementKind.FUNCTION: ProbabilityRange(lower: 0.010, upper: 0.012),
- ElementKind.METHOD: ProbabilityRange(lower: 0.012, upper: 0.016),
+ ElementKind.TYPE_PARAMETER: ProbabilityRange(lower: 0.000, upper: 0.001),
+ ElementKind.FUNCTION: ProbabilityRange(lower: 0.001, upper: 0.003),
+ ElementKind.METHOD: ProbabilityRange(lower: 0.006, upper: 0.010),
ElementKind.TOP_LEVEL_VARIABLE:
ProbabilityRange(lower: 0.016, upper: 0.023),
ElementKind.CLASS: ProbabilityRange(lower: 0.023, upper: 0.039),
- ElementKind.FIELD: ProbabilityRange(lower: 0.039, upper: 0.212),
- ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.212, upper: 0.516),
- ElementKind.PARAMETER: ProbabilityRange(lower: 0.516, upper: 1.000),
+ ElementKind.FIELD: ProbabilityRange(lower: 0.039, upper: 0.211),
+ ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.211, upper: 0.517),
+ ElementKind.PARAMETER: ProbabilityRange(lower: 0.517, upper: 1.000),
},
'ThrowExpression_expression': {
- ElementKind.PARAMETER: ProbabilityRange(lower: 0.000, upper: 0.834),
- ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.834, upper: 0.835),
- ElementKind.CLASS: ProbabilityRange(lower: 0.835, upper: 0.837),
- ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.837, upper: 1.000),
+ ElementKind.PARAMETER: ProbabilityRange(lower: 0.001, upper: 0.004),
+ ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.004, upper: 0.010),
+ ElementKind.CLASS: ProbabilityRange(lower: 0.010, upper: 0.022),
+ ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.022, upper: 1.000),
},
'TypeArgumentList_argument': {
- ElementKind.MIXIN: ProbabilityRange(lower: 0.000, upper: 0.021),
+ ElementKind.MIXIN: ProbabilityRange(lower: 0.000, upper: 0.000),
ElementKind.FUNCTION_TYPE_ALIAS:
- ProbabilityRange(lower: 0.021, upper: 0.024),
- ElementKind.PREFIX: ProbabilityRange(lower: 0.024, upper: 0.031),
- ElementKind.ENUM: ProbabilityRange(lower: 0.031, upper: 0.039),
- ElementKind.TYPE_PARAMETER: ProbabilityRange(lower: 0.039, upper: 0.053),
+ ProbabilityRange(lower: 0.000, upper: 0.003),
+ ElementKind.PREFIX: ProbabilityRange(lower: 0.003, upper: 0.010),
+ ElementKind.ENUM: ProbabilityRange(lower: 0.010, upper: 0.018),
+ ElementKind.TYPE_PARAMETER: ProbabilityRange(lower: 0.018, upper: 0.032),
ElementKind.UNKNOWN: ProbabilityRange(lower: 0.053, upper: 0.164),
ElementKind.CLASS: ProbabilityRange(lower: 0.164, upper: 1.000),
},
@@ -978,7 +978,7 @@
ElementKind.CLASS: ProbabilityRange(lower: 0.039, upper: 1.000),
},
'VariableDeclarationList_type': {
- ElementKind.MIXIN: ProbabilityRange(lower: 0.000, upper: 0.002),
+ ElementKind.MIXIN: ProbabilityRange(lower: 0.000, upper: 0.000),
ElementKind.TYPE_PARAMETER: ProbabilityRange(lower: 0.002, upper: 0.003),
ElementKind.PREFIX: ProbabilityRange(lower: 0.003, upper: 0.007),
ElementKind.UNKNOWN: ProbabilityRange(lower: 0.007, upper: 0.015),
@@ -988,41 +988,572 @@
ElementKind.CLASS: ProbabilityRange(lower: 0.047, upper: 1.000),
},
'VariableDeclaration_initializer': {
- ElementKind.MIXIN: ProbabilityRange(lower: 0.000, upper: 0.452),
- ElementKind.TYPE_PARAMETER: ProbabilityRange(lower: 0.452, upper: 0.452),
- ElementKind.PREFIX: ProbabilityRange(lower: 0.452, upper: 0.455),
- ElementKind.ENUM: ProbabilityRange(lower: 0.455, upper: 0.459),
- ElementKind.METHOD: ProbabilityRange(lower: 0.459, upper: 0.471),
+ ElementKind.MIXIN: ProbabilityRange(lower: 0.000, upper: 0.000),
+ ElementKind.TYPE_PARAMETER: ProbabilityRange(lower: 0.000, upper: 0.000),
+ ElementKind.PREFIX: ProbabilityRange(lower: 0.004, upper: 0.010),
+ ElementKind.ENUM: ProbabilityRange(lower: 0.010, upper: 0.016),
+ ElementKind.METHOD: ProbabilityRange(lower: 0.023, upper: 0.041),
ElementKind.TOP_LEVEL_VARIABLE:
- ProbabilityRange(lower: 0.471, upper: 0.489),
- ElementKind.FUNCTION: ProbabilityRange(lower: 0.489, upper: 0.515),
- ElementKind.PARAMETER: ProbabilityRange(lower: 0.515, upper: 0.578),
- ElementKind.FIELD: ProbabilityRange(lower: 0.578, upper: 0.641),
- ElementKind.CLASS: ProbabilityRange(lower: 0.641, upper: 0.712),
- ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.712, upper: 0.794),
- ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.794, upper: 1.000),
+ ProbabilityRange(lower: 0.041, upper: 0.069),
+ ElementKind.FUNCTION: ProbabilityRange(lower: 0.128, upper: 0.167),
+ ElementKind.PARAMETER: ProbabilityRange(lower: 0.167, upper: 0.261),
+ ElementKind.FIELD: ProbabilityRange(lower: 0.261, upper: 0.357),
+ ElementKind.CLASS: ProbabilityRange(lower: 0.459, upper: 0.565),
+ ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.565, upper: 0.689),
+ ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.689, upper: 1.000),
},
'WhileStatement_body': {
- ElementKind.METHOD: ProbabilityRange(lower: 0.000, upper: 0.985),
- ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.985, upper: 1.000),
+ ElementKind.METHOD: ProbabilityRange(lower: 0.000, upper: 0.200),
+ ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.200, upper: 1.000),
},
'WhileStatement_condition': {
- ElementKind.PARAMETER: ProbabilityRange(lower: 0.000, upper: 0.083),
- ElementKind.FIELD: ProbabilityRange(lower: 0.083, upper: 0.109),
- ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.109, upper: 1.000),
+ ElementKind.PARAMETER: ProbabilityRange(lower: 0.000, upper: 0.008),
+ ElementKind.TOP_LEVEL_VARIABLE:
+ ProbabilityRange(lower: 0.008, upper: 0.023),
+ ElementKind.FIELD: ProbabilityRange(lower: 0.023, upper: 0.050),
+ ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.085, upper: 1.000),
},
'WithClause_mixinType': {
ElementKind.CLASS: ProbabilityRange(lower: 0.000, upper: 0.282),
ElementKind.MIXIN: ProbabilityRange(lower: 0.282, upper: 1.000),
},
'YieldStatement_expression': {
- ElementKind.FUNCTION: ProbabilityRange(lower: 0.000, upper: 0.065),
+ ElementKind.FUNCTION: ProbabilityRange(lower: 0.000, upper: 0.003),
ElementKind.TOP_LEVEL_VARIABLE:
- ProbabilityRange(lower: 0.065, upper: 0.068),
- ElementKind.FIELD: ProbabilityRange(lower: 0.068, upper: 0.086),
- ElementKind.PARAMETER: ProbabilityRange(lower: 0.086, upper: 0.122),
- ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.122, upper: 0.161),
- ElementKind.METHOD: ProbabilityRange(lower: 0.161, upper: 0.309),
- ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.309, upper: 1.000),
+ ProbabilityRange(lower: 0.003, upper: 0.006),
+ ElementKind.FIELD: ProbabilityRange(lower: 0.009, upper: 0.028),
+ ElementKind.PARAMETER: ProbabilityRange(lower: 0.073, upper: 0.110),
+ ElementKind.LOCAL_VARIABLE: ProbabilityRange(lower: 0.110, upper: 0.150),
+ ElementKind.METHOD: ProbabilityRange(lower: 0.150, upper: 0.300),
+ ElementKind.CONSTRUCTOR: ProbabilityRange(lower: 0.300, upper: 1.000),
+ },
+};
+
+/// A table keyed by completion location and keyword whose values are the
+/// ranges of the relevance of those keywords in those locations.
+const keywordRelevance = {
+ 'ArgumentList_annotation_named': {
+ 'false': ProbabilityRange(lower: 0.159, upper: 0.517),
+ 'true': ProbabilityRange(lower: 0.517, upper: 1.000),
+ },
+ 'ArgumentList_annotation_unnamed': {
+ 'const': ProbabilityRange(lower: 0.000, upper: 0.375),
+ },
+ 'ArgumentList_constructorRedirect_named': {
+ 'null': ProbabilityRange(lower: 0.001, upper: 0.002),
+ 'const': ProbabilityRange(lower: 0.002, upper: 0.003),
+ 'true': ProbabilityRange(lower: 0.003, upper: 0.005),
+ 'false': ProbabilityRange(lower: 0.005, upper: 0.008),
+ },
+ 'ArgumentList_constructorRedirect_unnamed': {
+ 'const': ProbabilityRange(lower: 0.005, upper: 0.011),
+ 'null': ProbabilityRange(lower: 0.024, upper: 0.041),
+ },
+ 'ArgumentList_constructor_named': {
+ 'super': ProbabilityRange(lower: 0.000, upper: 0.000),
+ 'await': ProbabilityRange(lower: 0.000, upper: 0.000),
+ 'null': ProbabilityRange(lower: 0.004, upper: 0.007),
+ 'false': ProbabilityRange(lower: 0.011, upper: 0.016),
+ 'this': ProbabilityRange(lower: 0.016, upper: 0.023),
+ 'true': ProbabilityRange(lower: 0.023, upper: 0.031),
+ 'const': ProbabilityRange(lower: 0.031, upper: 0.042),
+ },
+ 'ArgumentList_constructor_unnamed': {
+ 'await': ProbabilityRange(lower: 0.000, upper: 0.001),
+ 'true': ProbabilityRange(lower: 0.004, upper: 0.008),
+ 'false': ProbabilityRange(lower: 0.008, upper: 0.014),
+ 'this': ProbabilityRange(lower: 0.014, upper: 0.020),
+ 'null': ProbabilityRange(lower: 0.031, upper: 0.049),
+ 'const': ProbabilityRange(lower: 0.095, upper: 0.124),
+ },
+ 'ArgumentList_function_named': {
+ 'true': ProbabilityRange(lower: 0.000, upper: 0.038),
+ },
+ 'ArgumentList_function_unnamed': {
+ 'await': ProbabilityRange(lower: 0.000, upper: 0.001),
+ 'null': ProbabilityRange(lower: 0.001, upper: 0.003),
+ 'false': ProbabilityRange(lower: 0.005, upper: 0.010),
+ 'const': ProbabilityRange(lower: 0.016, upper: 0.024),
+ 'true': ProbabilityRange(lower: 0.032, upper: 0.041),
+ 'this': ProbabilityRange(lower: 0.103, upper: 0.171),
+ },
+ 'ArgumentList_method_named': {
+ 'null': ProbabilityRange(lower: 0.000, upper: 0.001),
+ 'this': ProbabilityRange(lower: 0.001, upper: 0.002),
+ 'false': ProbabilityRange(lower: 0.055, upper: 0.079),
+ 'true': ProbabilityRange(lower: 0.079, upper: 0.114),
+ 'const': ProbabilityRange(lower: 0.564, upper: 0.760),
+ },
+ 'ArgumentList_method_unnamed': {
+ 'super': ProbabilityRange(lower: 0.000, upper: 0.000),
+ 'await': ProbabilityRange(lower: 0.000, upper: 0.002),
+ 'null': ProbabilityRange(lower: 0.004, upper: 0.008),
+ 'false': ProbabilityRange(lower: 0.008, upper: 0.013),
+ 'true': ProbabilityRange(lower: 0.013, upper: 0.018),
+ 'const': ProbabilityRange(lower: 0.018, upper: 0.025),
+ 'this': ProbabilityRange(lower: 0.047, upper: 0.060),
+ },
+ 'ArgumentList_widgetConstructor_named': {
+ 'super': ProbabilityRange(lower: 0.000, upper: 0.000),
+ 'null': ProbabilityRange(lower: 0.001, upper: 0.003),
+ 'this': ProbabilityRange(lower: 0.003, upper: 0.006),
+ 'false': ProbabilityRange(lower: 0.009, upper: 0.019),
+ 'true': ProbabilityRange(lower: 0.031, upper: 0.047),
+ 'const': ProbabilityRange(lower: 0.093, upper: 0.138),
+ },
+ 'ArgumentList_widgetConstructor_unnamed': {
+ 'const': ProbabilityRange(lower: 0.000, upper: 0.000),
+ 'true': ProbabilityRange(lower: 0.000, upper: 0.001),
+ 'false': ProbabilityRange(lower: 0.001, upper: 0.002),
+ 'null': ProbabilityRange(lower: 0.002, upper: 0.004),
+ 'this': ProbabilityRange(lower: 0.012, upper: 0.018),
+ },
+ 'AssertStatement_condition': {
+ 'this': ProbabilityRange(lower: 0.000, upper: 0.002),
+ 'await': ProbabilityRange(lower: 0.004, upper: 0.009),
+ 'false': ProbabilityRange(lower: 0.110, upper: 0.166),
+ },
+ 'AssignmentExpression_rightHandSide': {
+ 'super': ProbabilityRange(lower: 0.000, upper: 0.000),
+ 'const': ProbabilityRange(lower: 0.002, upper: 0.005),
+ 'await': ProbabilityRange(lower: 0.043, upper: 0.066),
+ 'null': ProbabilityRange(lower: 0.090, upper: 0.116),
+ 'false': ProbabilityRange(lower: 0.116, upper: 0.153),
+ 'true': ProbabilityRange(lower: 0.153, upper: 0.192),
+ 'this': ProbabilityRange(lower: 0.192, upper: 0.233),
+ },
+ 'AwaitExpression_expression': {
+ 'null': ProbabilityRange(lower: 0.000, upper: 0.000),
+ 'const': ProbabilityRange(lower: 0.000, upper: 0.000),
+ 'super': ProbabilityRange(lower: 0.000, upper: 0.000),
+ 'this': ProbabilityRange(lower: 0.000, upper: 0.009),
+ },
+ 'BinaryExpression_!=_rightOperand': {
+ 'false': ProbabilityRange(lower: 0.001, upper: 0.002),
+ 'this': ProbabilityRange(lower: 0.002, upper: 0.004),
+ 'true': ProbabilityRange(lower: 0.004, upper: 0.007),
+ 'null': ProbabilityRange(lower: 0.140, upper: 1.000),
+ },
+ 'BinaryExpression_&&_rightOperand': {
+ 'false': ProbabilityRange(lower: 0.000, upper: 0.000),
+ 'const': ProbabilityRange(lower: 0.001, upper: 0.002),
+ 'await': ProbabilityRange(lower: 0.002, upper: 0.003),
+ 'this': ProbabilityRange(lower: 0.003, upper: 0.005),
+ },
+ 'BinaryExpression_&_rightOperand': {
+ 'const': ProbabilityRange(lower: 0.000, upper: 0.023),
+ },
+ 'BinaryExpression_*_rightOperand': {
+ 'this': ProbabilityRange(lower: 0.001, upper: 0.004),
+ },
+ 'BinaryExpression_+_rightOperand': {
+ 'const': ProbabilityRange(lower: 0.000, upper: 0.000),
+ 'this': ProbabilityRange(lower: 0.003, upper: 0.007),
+ },
+ 'BinaryExpression_-_rightOperand': {
+ 'const': ProbabilityRange(lower: 0.000, upper: 0.001),
+ 'this': ProbabilityRange(lower: 0.014, upper: 0.021),
+ },
+ 'BinaryExpression_/_rightOperand': {
+ 'this': ProbabilityRange(lower: 0.004, upper: 0.006),
+ },
+ 'BinaryExpression_<=_rightOperand': {
+ 'this': ProbabilityRange(lower: 0.015, upper: 0.037),
+ },
+ 'BinaryExpression_<_rightOperand': {
+ 'this': ProbabilityRange(lower: 0.007, upper: 0.023),
+ },
+ 'BinaryExpression_==_rightOperand': {
+ 'this': ProbabilityRange(lower: 0.002, upper: 0.008),
+ 'false': ProbabilityRange(lower: 0.018, upper: 0.032),
+ 'true': ProbabilityRange(lower: 0.032, upper: 0.055),
+ 'null': ProbabilityRange(lower: 0.583, upper: 1.000),
+ },
+ 'BinaryExpression_>=_rightOperand': {
+ 'this': ProbabilityRange(lower: 0.006, upper: 0.018),
+ },
+ 'BinaryExpression_>_rightOperand': {
+ 'const': ProbabilityRange(lower: 0.029, upper: 0.043),
+ 'this': ProbabilityRange(lower: 0.043, upper: 0.068),
+ },
+ 'BinaryExpression_??_rightOperand': {
+ 'super': ProbabilityRange(lower: 0.000, upper: 0.001),
+ 'await': ProbabilityRange(lower: 0.001, upper: 0.002),
+ 'const': ProbabilityRange(lower: 0.009, upper: 0.017),
+ 'null': ProbabilityRange(lower: 0.042, upper: 0.058),
+ 'true': ProbabilityRange(lower: 0.083, upper: 0.108),
+ 'false': ProbabilityRange(lower: 0.269, upper: 0.367),
+ 'this': ProbabilityRange(lower: 0.578, upper: 0.731),
+ },
+ 'BinaryExpression_||_rightOperand': {
+ 'await': ProbabilityRange(lower: 0.000, upper: 0.004),
+ 'this': ProbabilityRange(lower: 0.013, upper: 0.018),
+ },
+ 'BlockFunctionBody_start': {
+ 'sync': ProbabilityRange(lower: 0.000, upper: 0.003),
+ 'async': ProbabilityRange(lower: 0.003, upper: 1.000),
+ },
+ 'Block_statement': {
+ 'do': ProbabilityRange(lower: 0.000, upper: 0.000),
+ 'void': ProbabilityRange(lower: 0.001, upper: 0.001),
+ 'break': ProbabilityRange(lower: 0.001, upper: 0.002),
+ 'continue': ProbabilityRange(lower: 0.003, upper: 0.004),
+ 'rethrow': ProbabilityRange(lower: 0.004, upper: 0.005),
+ 'while': ProbabilityRange(lower: 0.006, upper: 0.008),
+ 'yield': ProbabilityRange(lower: 0.008, upper: 0.010),
+ 'assert': ProbabilityRange(lower: 0.010, upper: 0.014),
+ 'const': ProbabilityRange(lower: 0.014, upper: 0.017),
+ 'switch': ProbabilityRange(lower: 0.017, upper: 0.024),
+ 'try': ProbabilityRange(lower: 0.024, upper: 0.032),
+ 'for': ProbabilityRange(lower: 0.032, upper: 0.041),
+ 'this': ProbabilityRange(lower: 0.041, upper: 0.050),
+ 'throw': ProbabilityRange(lower: 0.050, upper: 0.062),
+ 'super': ProbabilityRange(lower: 0.077, upper: 0.093),
+ 'await': ProbabilityRange(lower: 0.116, upper: 0.141),
+ 'var': ProbabilityRange(lower: 0.141, upper: 0.171),
+ 'final': ProbabilityRange(lower: 0.453, upper: 0.548),
+ 'if': ProbabilityRange(lower: 0.548, upper: 0.661),
+ 'return': ProbabilityRange(lower: 0.802, upper: 1.000),
+ },
+ 'BooleanLiteral_start': {
+ 'false': ProbabilityRange(lower: 0.000, upper: 0.484),
+ 'true': ProbabilityRange(lower: 0.484, upper: 1.000),
+ },
+ 'ClassDeclaration_extends': {
+ 'implements': ProbabilityRange(lower: 0.000, upper: 0.350),
+ 'with': ProbabilityRange(lower: 0.350, upper: 1.000),
+ },
+ 'ClassDeclaration_member': {
+ 'operator': ProbabilityRange(lower: 0.000, upper: 0.000),
+ 'get': ProbabilityRange(lower: 0.004, upper: 0.005),
+ 'var': ProbabilityRange(lower: 0.005, upper: 0.010),
+ 'factory': ProbabilityRange(lower: 0.015, upper: 0.031),
+ 'set': ProbabilityRange(lower: 0.031, upper: 0.051),
+ 'const': ProbabilityRange(lower: 0.051, upper: 0.078),
+ 'void': ProbabilityRange(lower: 0.078, upper: 0.150),
+ 'static': ProbabilityRange(lower: 0.150, upper: 0.230),
+ 'final': ProbabilityRange(lower: 0.230, upper: 0.442),
+ },
+ 'ClassDeclaration_name': {
+ 'with': ProbabilityRange(lower: 0.000, upper: 0.008),
+ 'implements': ProbabilityRange(lower: 0.008, upper: 0.141),
+ 'extends': ProbabilityRange(lower: 0.141, upper: 1.000),
+ },
+ 'ClassDeclaration_with': {
+ 'implements': ProbabilityRange(lower: 0.000, upper: 1.000),
+ },
+ 'ClassTypeAlias_superclass': {
+ 'with': ProbabilityRange(lower: 0.500, upper: 1.000),
+ },
+ 'CompilationUnit_declaration': {
+ 'mixin': ProbabilityRange(lower: 0.003, upper: 0.005),
+ 'var': ProbabilityRange(lower: 0.005, upper: 0.013),
+ 'typedef': ProbabilityRange(lower: 0.013, upper: 0.024),
+ 'enum': ProbabilityRange(lower: 0.036, upper: 0.057),
+ 'final': ProbabilityRange(lower: 0.057, upper: 0.081),
+ 'void': ProbabilityRange(lower: 0.081, upper: 0.127),
+ 'const': ProbabilityRange(lower: 0.127, upper: 0.205),
+ 'class': ProbabilityRange(lower: 0.342, upper: 1.000),
+ },
+ 'CompilationUnit_directive': {
+ 'library': ProbabilityRange(lower: 0.000, upper: 0.001),
+ 'part': ProbabilityRange(lower: 0.001, upper: 0.015),
+ 'export': ProbabilityRange(lower: 0.015, upper: 0.037),
+ 'import': ProbabilityRange(lower: 0.037, upper: 1.000),
+ },
+ 'ConditionalExpression_elseExpression': {
+ 'await': ProbabilityRange(lower: 0.000, upper: 0.001),
+ 'throw': ProbabilityRange(lower: 0.001, upper: 0.002),
+ 'true': ProbabilityRange(lower: 0.005, upper: 0.009),
+ 'false': ProbabilityRange(lower: 0.009, upper: 0.019),
+ 'this': ProbabilityRange(lower: 0.019, upper: 0.030),
+ 'const': ProbabilityRange(lower: 0.055, upper: 0.071),
+ 'null': ProbabilityRange(lower: 0.289, upper: 0.412),
+ },
+ 'ConditionalExpression_thenExpression': {
+ 'super': ProbabilityRange(lower: 0.000, upper: 0.000),
+ 'await': ProbabilityRange(lower: 0.000, upper: 0.001),
+ 'this': ProbabilityRange(lower: 0.004, upper: 0.009),
+ 'false': ProbabilityRange(lower: 0.009, upper: 0.016),
+ 'true': ProbabilityRange(lower: 0.016, upper: 0.024),
+ 'const': ProbabilityRange(lower: 0.048, upper: 0.069),
+ 'null': ProbabilityRange(lower: 0.367, upper: 0.513),
+ },
+ 'ConstructorDeclaration_initializer': {
+ 'this': ProbabilityRange(lower: 0.000, upper: 0.033),
+ 'assert': ProbabilityRange(lower: 0.033, upper: 0.181),
+ 'super': ProbabilityRange(lower: 0.448, upper: 1.000),
+ },
+ 'ConstructorFieldInitializer_expression': {
+ 'true': ProbabilityRange(lower: 0.003, upper: 0.005),
+ 'false': ProbabilityRange(lower: 0.010, upper: 0.017),
+ 'null': ProbabilityRange(lower: 0.026, upper: 0.037),
+ },
+ 'DefaultFormalParameter_defaultValue': {
+ 'null': ProbabilityRange(lower: 0.000, upper: 0.001),
+ 'true': ProbabilityRange(lower: 0.107, upper: 0.199),
+ 'const': ProbabilityRange(lower: 0.307, upper: 0.452),
+ 'false': ProbabilityRange(lower: 0.452, upper: 0.719),
+ },
+ 'ExpressionFunctionBody_expression': {
+ 'throw': ProbabilityRange(lower: 0.000, upper: 0.001),
+ 'super': ProbabilityRange(lower: 0.001, upper: 0.002),
+ 'await': ProbabilityRange(lower: 0.002, upper: 0.004),
+ 'const': ProbabilityRange(lower: 0.005, upper: 0.008),
+ 'true': ProbabilityRange(lower: 0.008, upper: 0.013),
+ 'this': ProbabilityRange(lower: 0.013, upper: 0.019),
+ 'false': ProbabilityRange(lower: 0.019, upper: 0.027),
+ 'null': ProbabilityRange(lower: 0.034, upper: 0.045),
+ },
+ 'ExpressionFunctionBody_start': {
+ 'async': ProbabilityRange(lower: 0.000, upper: 1.000),
+ },
+ 'ExpressionStatement_expression': {
+ 'const': ProbabilityRange(lower: 0.000, upper: 0.000),
+ 'rethrow': ProbabilityRange(lower: 0.004, upper: 0.006),
+ 'this': ProbabilityRange(lower: 0.009, upper: 0.029),
+ 'throw': ProbabilityRange(lower: 0.029, upper: 0.054),
+ 'super': ProbabilityRange(lower: 0.084, upper: 0.117),
+ 'await': ProbabilityRange(lower: 0.168, upper: 0.218),
+ },
+ 'ExtensionDeclaration_member': {
+ 'void': ProbabilityRange(lower: 0.000, upper: 0.040),
+ },
+ 'FieldDeclaration_fields': {
+ 'void': ProbabilityRange(lower: 0.000, upper: 0.000),
+ 'var': ProbabilityRange(lower: 0.011, upper: 0.024),
+ 'const': ProbabilityRange(lower: 0.024, upper: 0.119),
+ 'final': ProbabilityRange(lower: 0.491, upper: 1.000),
+ },
+ 'ForEachPartsWithDeclaration_iterable': {
+ 'await': ProbabilityRange(lower: 0.000, upper: 0.001),
+ 'this': ProbabilityRange(lower: 0.023, upper: 0.031),
+ },
+ 'ForEachPartsWithDeclaration_loopVariable': {
+ 'var': ProbabilityRange(lower: 0.298, upper: 0.628),
+ 'final': ProbabilityRange(lower: 0.628, upper: 1.000),
+ },
+ 'ForElement_body': {
+ 'for': ProbabilityRange(lower: 0.000, upper: 0.008),
+ 'if': ProbabilityRange(lower: 0.008, upper: 0.038),
+ },
+ 'ForElement_forLoopParts': {
+ 'var': ProbabilityRange(lower: 0.007, upper: 0.274),
+ 'final': ProbabilityRange(lower: 0.274, upper: 0.630),
+ },
+ 'ForParts_condition': {
+ 'true': ProbabilityRange(lower: 0.001, upper: 0.004),
+ },
+ 'ForStatement_body': {
+ 'for': ProbabilityRange(lower: 0.000, upper: 0.032),
+ 'if': ProbabilityRange(lower: 0.032, upper: 0.065),
+ },
+ 'ForStatement_forLoopParts': {
+ 'final': ProbabilityRange(lower: 0.025, upper: 0.199),
+ 'var': ProbabilityRange(lower: 0.199, upper: 0.561),
+ },
+ 'FormalParameterList_parameter': {
+ 'covariant': ProbabilityRange(lower: 0.000, upper: 0.000),
+ 'final': ProbabilityRange(lower: 0.000, upper: 0.001),
+ 'var': ProbabilityRange(lower: 0.001, upper: 0.001),
+ 'void': ProbabilityRange(lower: 0.024, upper: 0.038),
+ 'this': ProbabilityRange(lower: 0.054, upper: 0.360),
+ },
+ 'FunctionDeclaration_returnType': {
+ 'void': ProbabilityRange(lower: 0.070, upper: 0.354),
+ },
+ 'GenericTypeAlias_functionType': {
+ 'Function': ProbabilityRange(lower: 0.090, upper: 0.140),
+ 'void': ProbabilityRange(lower: 0.480, upper: 1.000),
+ },
+ 'IfElement_condition': {
+ 'this': ProbabilityRange(lower: 0.005, upper: 0.011),
+ },
+ 'IfElement_elseElement': {
+ 'for': ProbabilityRange(lower: 0.000, upper: 0.043),
+ 'if': ProbabilityRange(lower: 0.087, upper: 0.261),
+ },
+ 'IfElement_thenElement': {
+ 'for': ProbabilityRange(lower: 0.011, upper: 0.022),
+ 'const': ProbabilityRange(lower: 0.047, upper: 0.063),
+ },
+ 'IfStatement_condition': {
+ 'const': ProbabilityRange(lower: 0.000, upper: 0.000),
+ 'super': ProbabilityRange(lower: 0.000, upper: 0.000),
+ 'null': ProbabilityRange(lower: 0.000, upper: 0.001),
+ 'true': ProbabilityRange(lower: 0.001, upper: 0.002),
+ 'await': ProbabilityRange(lower: 0.041, upper: 0.061),
+ 'this': ProbabilityRange(lower: 0.061, upper: 0.084),
+ },
+ 'IfStatement_elseStatement': {
+ 'await': ProbabilityRange(lower: 0.000, upper: 0.001),
+ 'this': ProbabilityRange(lower: 0.001, upper: 0.002),
+ 'throw': ProbabilityRange(lower: 0.002, upper: 0.003),
+ 'rethrow': ProbabilityRange(lower: 0.003, upper: 0.004),
+ 'return': ProbabilityRange(lower: 0.095, upper: 0.145),
+ 'if': ProbabilityRange(lower: 0.145, upper: 1.000),
+ },
+ 'IfStatement_thenStatement': {
+ 'super': ProbabilityRange(lower: 0.000, upper: 0.000),
+ 'rethrow': ProbabilityRange(lower: 0.000, upper: 0.001),
+ 'for': ProbabilityRange(lower: 0.001, upper: 0.001),
+ 'while': ProbabilityRange(lower: 0.001, upper: 0.002),
+ 'if': ProbabilityRange(lower: 0.003, upper: 0.006),
+ 'break': ProbabilityRange(lower: 0.006, upper: 0.009),
+ 'this': ProbabilityRange(lower: 0.009, upper: 0.013),
+ 'await': ProbabilityRange(lower: 0.025, upper: 0.031),
+ 'continue': ProbabilityRange(lower: 0.031, upper: 0.038),
+ 'yield': ProbabilityRange(lower: 0.038, upper: 0.048),
+ 'throw': ProbabilityRange(lower: 0.048, upper: 0.058),
+ 'return': ProbabilityRange(lower: 0.480, upper: 1.000),
+ },
+ 'ImportDirective_combinator': {
+ 'hide': ProbabilityRange(lower: 0.000, upper: 0.106),
+ 'show': ProbabilityRange(lower: 0.106, upper: 1.000),
+ },
+ 'ImportDirective_deferred': {
+ 'as': ProbabilityRange(lower: 0.000, upper: 1.000),
+ },
+ 'ImportDirective_prefix': {
+ 'if': ProbabilityRange(lower: 0.000, upper: 0.059),
+ 'show': ProbabilityRange(lower: 0.059, upper: 1.000),
+ },
+ 'ImportDirective_uri': {
+ 'deferred': ProbabilityRange(lower: 0.000, upper: 0.005),
+ 'if': ProbabilityRange(lower: 0.005, upper: 0.017),
+ 'hide': ProbabilityRange(lower: 0.017, upper: 0.036),
+ 'show': ProbabilityRange(lower: 0.036, upper: 0.175),
+ 'as': ProbabilityRange(lower: 0.175, upper: 1.000),
+ },
+ 'IndexExpression_index': {
+ 'await': ProbabilityRange(lower: 0.000, upper: 0.001),
+ 'this': ProbabilityRange(lower: 0.001, upper: 0.002),
+ },
+ 'InterpolationExpression_expression': {
+ 'false': ProbabilityRange(lower: 0.000, upper: 0.000),
+ 'await': ProbabilityRange(lower: 0.000, upper: 0.000),
+ 'this': ProbabilityRange(lower: 0.010, upper: 0.032),
+ },
+ 'ListLiteral_element': {
+ 'super': ProbabilityRange(lower: 0.000, upper: 0.000),
+ 'null': ProbabilityRange(lower: 0.000, upper: 0.000),
+ 'await': ProbabilityRange(lower: 0.000, upper: 0.000),
+ 'this': ProbabilityRange(lower: 0.000, upper: 0.000),
+ 'false': ProbabilityRange(lower: 0.001, upper: 0.002),
+ 'true': ProbabilityRange(lower: 0.002, upper: 0.003),
+ 'for': ProbabilityRange(lower: 0.003, upper: 0.004),
+ 'if': ProbabilityRange(lower: 0.011, upper: 0.017),
+ 'const': ProbabilityRange(lower: 0.063, upper: 0.082),
+ },
+ 'MapLiteralEntry_value': {
+ 'this': ProbabilityRange(lower: 0.000, upper: 0.002),
+ 'true': ProbabilityRange(lower: 0.002, upper: 0.005),
+ 'false': ProbabilityRange(lower: 0.011, upper: 0.015),
+ 'const': ProbabilityRange(lower: 0.015, upper: 0.019),
+ 'null': ProbabilityRange(lower: 0.028, upper: 0.038),
+ },
+ 'MethodDeclaration_returnType': {
+ 'void': ProbabilityRange(lower: 0.010, upper: 0.179),
+ },
+ 'MixinDeclaration_member': {
+ 'static': ProbabilityRange(lower: 0.014, upper: 0.024),
+ 'void': ProbabilityRange(lower: 0.024, upper: 0.040),
+ 'set': ProbabilityRange(lower: 0.040, upper: 0.077),
+ 'final': ProbabilityRange(lower: 0.077, upper: 0.128),
+ },
+ 'MixinDeclaration_name': {
+ 'implements': ProbabilityRange(lower: 0.000, upper: 0.083),
+ 'on': ProbabilityRange(lower: 0.083, upper: 1.000),
+ },
+ 'ParenthesizedExpression_expression': {
+ 'true': ProbabilityRange(lower: 0.000, upper: 0.001),
+ 'this': ProbabilityRange(lower: 0.013, upper: 0.023),
+ 'await': ProbabilityRange(lower: 0.023, upper: 0.051),
+ },
+ 'PrefixExpression_!_operand': {
+ 'this': ProbabilityRange(lower: 0.000, upper: 0.004),
+ 'await': ProbabilityRange(lower: 0.004, upper: 0.015),
+ },
+ 'PrefixExpression_++_operand': {
+ 'this': ProbabilityRange(lower: 0.018, upper: 0.035),
+ },
+ 'ReturnStatement_expression': {
+ 'const': ProbabilityRange(lower: 0.000, upper: 0.003),
+ 'super': ProbabilityRange(lower: 0.008, upper: 0.012),
+ 'await': ProbabilityRange(lower: 0.012, upper: 0.020),
+ 'this': ProbabilityRange(lower: 0.038, upper: 0.050),
+ 'false': ProbabilityRange(lower: 0.050, upper: 0.070),
+ 'null': ProbabilityRange(lower: 0.090, upper: 0.113),
+ 'true': ProbabilityRange(lower: 0.113, upper: 0.142),
+ },
+ 'SetOrMapLiteral_element': {
+ 'const': ProbabilityRange(lower: 0.009, upper: 0.015),
+ 'for': ProbabilityRange(lower: 0.022, upper: 0.031),
+ 'if': ProbabilityRange(lower: 0.031, upper: 0.040),
+ },
+ 'SpreadElement_expression': {
+ 'this': ProbabilityRange(lower: 0.005, upper: 0.022),
+ },
+ 'SwitchCase_expression': {
+ 'true': ProbabilityRange(lower: 0.000, upper: 0.005),
+ 'false': ProbabilityRange(lower: 0.005, upper: 0.009),
+ },
+ 'SwitchMember_statement': {
+ 'rethrow': ProbabilityRange(lower: 0.000, upper: 0.000),
+ 'try': ProbabilityRange(lower: 0.000, upper: 0.001),
+ 'continue': ProbabilityRange(lower: 0.001, upper: 0.001),
+ 'super': ProbabilityRange(lower: 0.001, upper: 0.002),
+ 'var': ProbabilityRange(lower: 0.002, upper: 0.003),
+ 'yield': ProbabilityRange(lower: 0.003, upper: 0.005),
+ 'switch': ProbabilityRange(lower: 0.005, upper: 0.006),
+ 'assert': ProbabilityRange(lower: 0.006, upper: 0.008),
+ 'this': ProbabilityRange(lower: 0.008, upper: 0.011),
+ 'await': ProbabilityRange(lower: 0.011, upper: 0.013),
+ 'for': ProbabilityRange(lower: 0.017, upper: 0.022),
+ 'throw': ProbabilityRange(lower: 0.022, upper: 0.027),
+ 'final': ProbabilityRange(lower: 0.033, upper: 0.045),
+ 'if': ProbabilityRange(lower: 0.085, upper: 0.109),
+ 'return': ProbabilityRange(lower: 0.380, upper: 0.642),
+ 'break': ProbabilityRange(lower: 0.642, upper: 1.000),
+ },
+ 'SwitchStatement_expression': {
+ 'await': ProbabilityRange(lower: 0.003, upper: 0.006),
+ 'this': ProbabilityRange(lower: 0.010, upper: 0.016),
+ },
+ 'ThrowExpression_expression': {
+ 'const': ProbabilityRange(lower: 0.000, upper: 0.001),
+ },
+ 'TryStatement_catch': {
+ 'catch': ProbabilityRange(lower: 0.000, upper: 0.238),
+ 'finally': ProbabilityRange(lower: 0.238, upper: 0.595),
+ 'on': ProbabilityRange(lower: 0.595, upper: 1.000),
+ },
+ 'TryStatement_try': {
+ 'finally': ProbabilityRange(lower: 0.000, upper: 0.022),
+ 'on': ProbabilityRange(lower: 0.022, upper: 0.183),
+ 'catch': ProbabilityRange(lower: 0.183, upper: 1.000),
+ },
+ 'TypeArgumentList_argument': {
+ 'void': ProbabilityRange(lower: 0.032, upper: 0.053),
+ },
+ 'VariableDeclarationList_type': {
+ 'void': ProbabilityRange(lower: 0.000, upper: 0.002),
+ },
+ 'VariableDeclaration_initializer': {
+ 'super': ProbabilityRange(lower: 0.000, upper: 0.000),
+ 'null': ProbabilityRange(lower: 0.000, upper: 0.001),
+ 'this': ProbabilityRange(lower: 0.001, upper: 0.004),
+ 'true': ProbabilityRange(lower: 0.016, upper: 0.023),
+ 'false': ProbabilityRange(lower: 0.069, upper: 0.098),
+ 'const': ProbabilityRange(lower: 0.098, upper: 0.128),
+ 'await': ProbabilityRange(lower: 0.357, upper: 0.459),
+ },
+ 'WhileStatement_condition': {
+ 'true': ProbabilityRange(lower: 0.050, upper: 0.085),
+ },
+ 'YieldStatement_expression': {
+ 'null': ProbabilityRange(lower: 0.006, upper: 0.009),
+ 'const': ProbabilityRange(lower: 0.028, upper: 0.049),
+ 'await': ProbabilityRange(lower: 0.049, upper: 0.073),
},
};
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/suggestion_builder.dart b/pkg/analysis_server/lib/src/services/completion/dart/suggestion_builder.dart
index 7d74ff0..adcd138 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/suggestion_builder.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/suggestion_builder.dart
@@ -607,8 +607,13 @@
/// Add a suggestion for a [keyword]. The [offset] is the offset from the
/// beginning of the keyword where the cursor will be left.
void suggestKeyword(String keyword, {int offset, @required int relevance}) {
- // TODO(brianwilkerson) Use the location at which the keyword is being
- // inserted to compute the relevance.
+ if (request.useNewRelevance) {
+ // TODO(brianwilkerson) The default value should probably be a constant.
+ relevance = toRelevance(
+ request.featureComputer
+ .keywordFeature(keyword, request.opType.completionLocation),
+ 800);
+ }
_add(CompletionSuggestion(CompletionSuggestionKind.KEYWORD, relevance,
keyword, offset ?? keyword.length, 0, false, false));
}
diff --git a/pkg/analysis_server/lib/src/services/correction/assist_internal.dart b/pkg/analysis_server/lib/src/services/correction/assist_internal.dart
index 1aeb413..f0074a511 100644
--- a/pkg/analysis_server/lib/src/services/correction/assist_internal.dart
+++ b/pkg/analysis_server/lib/src/services/correction/assist_internal.dart
@@ -3,8 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
import 'dart:async';
-import 'dart:collection';
-import 'dart:math';
import 'package:analysis_server/plugin/edit/assist/assist_core.dart';
import 'package:analysis_server/plugin/edit/assist/assist_dart.dart';
@@ -12,6 +10,7 @@
import 'package:analysis_server/src/services/correction/base_processor.dart';
import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart';
import 'package:analysis_server/src/services/correction/dart/add_diagnostic_property_reference.dart';
+import 'package:analysis_server/src/services/correction/dart/add_not_null_assert.dart';
import 'package:analysis_server/src/services/correction/dart/add_return_type.dart';
import 'package:analysis_server/src/services/correction/dart/add_type_annotation.dart';
import 'package:analysis_server/src/services/correction/dart/assign_to_local_variable.dart';
@@ -23,52 +22,60 @@
import 'package:analysis_server/src/services/correction/dart/convert_into_async_body.dart';
import 'package:analysis_server/src/services/correction/dart/convert_into_block_body.dart';
import 'package:analysis_server/src/services/correction/dart/convert_into_final_field.dart';
+import 'package:analysis_server/src/services/correction/dart/convert_into_for_index.dart';
import 'package:analysis_server/src/services/correction/dart/convert_into_getter.dart';
+import 'package:analysis_server/src/services/correction/dart/convert_into_is_not.dart';
+import 'package:analysis_server/src/services/correction/dart/convert_into_is_not_empty.dart';
import 'package:analysis_server/src/services/correction/dart/convert_map_from_iterable_to_for_literal.dart';
import 'package:analysis_server/src/services/correction/dart/convert_part_of_to_uri.dart';
import 'package:analysis_server/src/services/correction/dart/convert_quotes.dart';
import 'package:analysis_server/src/services/correction/dart/convert_to_expression_function_body.dart';
+import 'package:analysis_server/src/services/correction/dart/convert_to_field_parameter.dart';
import 'package:analysis_server/src/services/correction/dart/convert_to_generic_function_syntax.dart';
import 'package:analysis_server/src/services/correction/dart/convert_to_int_literal.dart';
import 'package:analysis_server/src/services/correction/dart/convert_to_list_literal.dart';
import 'package:analysis_server/src/services/correction/dart/convert_to_map_literal.dart';
+import 'package:analysis_server/src/services/correction/dart/convert_to_multiline_string.dart';
+import 'package:analysis_server/src/services/correction/dart/convert_to_normal_parameter.dart';
import 'package:analysis_server/src/services/correction/dart/convert_to_null_aware.dart';
import 'package:analysis_server/src/services/correction/dart/convert_to_package_import.dart';
import 'package:analysis_server/src/services/correction/dart/convert_to_relative_import.dart';
import 'package:analysis_server/src/services/correction/dart/convert_to_set_literal.dart';
+import 'package:analysis_server/src/services/correction/dart/encapsulate_field.dart';
import 'package:analysis_server/src/services/correction/dart/exchange_operands.dart';
+import 'package:analysis_server/src/services/correction/dart/flutter_convert_to_children.dart';
+import 'package:analysis_server/src/services/correction/dart/flutter_convert_to_stateful_widget.dart';
+import 'package:analysis_server/src/services/correction/dart/flutter_move_down.dart';
+import 'package:analysis_server/src/services/correction/dart/flutter_move_up.dart';
+import 'package:analysis_server/src/services/correction/dart/flutter_remove_widget.dart';
+import 'package:analysis_server/src/services/correction/dart/flutter_swap_with_child.dart';
+import 'package:analysis_server/src/services/correction/dart/flutter_swap_with_parent.dart';
+import 'package:analysis_server/src/services/correction/dart/flutter_wrap.dart';
+import 'package:analysis_server/src/services/correction/dart/flutter_wrap_generic.dart';
+import 'package:analysis_server/src/services/correction/dart/flutter_wrap_stream_builder.dart';
+import 'package:analysis_server/src/services/correction/dart/import_add_show.dart';
import 'package:analysis_server/src/services/correction/dart/inline_invocation.dart';
+import 'package:analysis_server/src/services/correction/dart/introduce_local_cast_type.dart';
+import 'package:analysis_server/src/services/correction/dart/invert_if_statement.dart';
+import 'package:analysis_server/src/services/correction/dart/join_if_with_inner.dart';
+import 'package:analysis_server/src/services/correction/dart/join_if_with_outer.dart';
+import 'package:analysis_server/src/services/correction/dart/join_variable_declaration.dart';
import 'package:analysis_server/src/services/correction/dart/remove_type_annotation.dart';
+import 'package:analysis_server/src/services/correction/dart/replace_conditional_with_if_else.dart';
+import 'package:analysis_server/src/services/correction/dart/replace_if_else_with_conditional.dart';
import 'package:analysis_server/src/services/correction/dart/replace_with_var.dart';
import 'package:analysis_server/src/services/correction/dart/shadow_field.dart';
import 'package:analysis_server/src/services/correction/dart/sort_child_property_last.dart';
import 'package:analysis_server/src/services/correction/dart/split_and_condition.dart';
+import 'package:analysis_server/src/services/correction/dart/split_variable_declaration.dart';
+import 'package:analysis_server/src/services/correction/dart/surround_with.dart';
import 'package:analysis_server/src/services/correction/dart/use_curly_braces.dart';
import 'package:analysis_server/src/services/correction/fix_internal.dart';
-import 'package:analysis_server/src/services/correction/name_suggestion.dart';
-import 'package:analysis_server/src/services/correction/selection_analyzer.dart';
-import 'package:analysis_server/src/services/correction/statement_analyzer.dart';
-import 'package:analysis_server/src/services/correction/util.dart';
-import 'package:analysis_server/src/services/search/hierarchy.dart';
-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/dart/element/element.dart';
-import 'package:analyzer/dart/element/type.dart';
-import 'package:analyzer/src/dart/ast/token.dart';
-import 'package:analyzer/src/dart/ast/utilities.dart';
import 'package:analyzer/src/generated/java_core.dart';
-import 'package:analyzer/src/generated/source.dart';
-import 'package:analyzer_plugin/protocol/protocol_common.dart' hide Element;
import 'package:analyzer_plugin/src/utilities/change_builder/change_builder_dart.dart';
import 'package:analyzer_plugin/utilities/assist/assist.dart'
hide AssistContributor;
import 'package:analyzer_plugin/utilities/change_builder/change_builder_dart.dart';
-import 'package:analyzer_plugin/utilities/range_factory.dart';
-import 'package:meta/meta.dart';
-
-typedef _SimpleIdentifierVisitor = void Function(SimpleIdentifier node);
/// The computer for Dart assists.
class AssistProcessor extends BaseProcessor {
@@ -80,6 +87,7 @@
/// A list of the generators used to produce assists.
static const List<ProducerGenerator> generators = [
AddDiagnosticPropertyReference.newInstance,
+ AddNotNullAssert.newInstance,
AddReturnType.newInstance,
AddTypeAnnotation.newInstance,
AssignToLocalVariable.newInstance,
@@ -91,10 +99,16 @@
ConvertIntoAsyncBody.newInstance,
ConvertIntoBlockBody.newInstance,
ConvertIntoFinalField.newInstance,
+ ConvertIntoForIndex.newInstance,
ConvertIntoGetter.newInstance,
+ ConvertIntoIsNot.newInstance,
+ ConvertIntoIsNotEmpty.newInstance,
ConvertMapFromIterableToForLiteral.newInstance,
ConvertPartOfToUri.newInstance,
ConvertToDoubleQuotes.newInstance,
+ ConvertToFieldParameter.newInstance,
+ ConvertToMutilineString.newInstance,
+ ConvertToNormalParameter.newInstance,
ConvertToSingleQuotes.newInstance,
ConvertToExpressionFunctionBody.newInstance,
ConvertToGenericFunctionSyntax.newInstance,
@@ -105,16 +119,41 @@
ConvertToPackageImport.newInstance,
ConvertToRelativeImport.newInstance,
ConvertToSetLiteral.newInstance,
+ EncapsulateField.newInstance,
ExchangeOperands.newInstance,
+ FlutterConvertToChildren.newInstance,
+ FlutterConvertToStatefulWidget.newInstance,
+ FlutterMoveDown.newInstance,
+ FlutterMoveUp.newInstance,
+ FlutterRemoveWidget.newInstance,
+ FlutterSwapWithChild.newInstance,
+ FlutterSwapWithParent.newInstance,
+ FlutterWrapGeneric.newInstance,
+ FlutterWrapStreamBuilder.newInstance,
+ ImportAddShow.newInstance,
InlineInvocation.newInstance,
+ IntroduceLocalCastType.newInstance,
+ InvertIfStatement.newInstance,
+ JoinIfWithInner.newInstance,
+ JoinIfWithOuter.newInstance,
+ JoinVariableDeclaration.newInstance,
RemoveTypeAnnotation.newInstance,
+ ReplaceConditionalWithIfElse.newInstance,
+ ReplaceIfElseWithConditional.newInstance,
ReplaceWithVar.newInstance,
ShadowField.newInstance,
SortChildPropertyLast.newInstance,
SplitAndCondition.newInstance,
+ SplitVariableDeclaration.newInstance,
UseCurlyBraces.newInstance,
];
+ /// A list of the multi-generators used to produce assists.
+ static const List<MultiProducerGenerator> multiGenerators = [
+ FlutterWrap.newInstance,
+ SurroundWith.newInstance,
+ ];
+
final DartAssistContext context;
final List<Assist> assists = <Assist>[];
@@ -131,39 +170,6 @@
if (!setupCompute()) {
return assists;
}
- await _addProposal_addNotNullAssert();
- await _addProposal_convertToFieldParameter();
- await _addProposal_convertToForIndexLoop();
- await _addProposal_convertToIsNot_onIs();
- await _addProposal_convertToIsNot_onNot();
- await _addProposal_convertToIsNotEmpty();
- await _addProposal_convertToMultilineString();
- await _addProposal_convertToNormalParameter();
- await _addProposal_encapsulateField();
- await _addProposal_flutterConvertToChildren();
- await _addProposal_flutterConvertToStatefulWidget();
- await _addProposal_flutterMoveWidgetDown();
- await _addProposal_flutterMoveWidgetUp();
- await _addProposal_flutterRemoveWidget_singleChild();
- await _addProposal_flutterRemoveWidget_multipleChildren();
- await _addProposal_flutterSwapWithChild();
- await _addProposal_flutterSwapWithParent();
- await _addProposal_flutterWrapStreamBuilder();
- await _addProposal_flutterWrapWidget();
- await _addProposal_flutterWrapWidgets();
- await _addProposal_importAddShow();
- await _addProposal_introduceLocalTestedType();
- await _addProposal_invertIf();
- await _addProposal_joinIfStatementInner();
- await _addProposal_joinIfStatementOuter();
- await _addProposal_joinVariableDeclaration_onAssignment();
- await _addProposal_joinVariableDeclaration_onDeclaration();
- await _addProposal_reparentFlutterList();
- await _addProposal_replaceConditionalWithIfElse();
- await _addProposal_replaceIfElseWithConditional();
- await _addProposal_splitVariableDeclaration();
- await _addProposal_surroundWith();
-
await _addFromProducers();
return assists;
@@ -189,7 +195,7 @@
Future<void> compute(CorrectionProducer producer) async {
producer.configure(context);
- var builder = _newDartChangeBuilder();
+ var builder = DartChangeBuilderImpl.forWorkspace(context.workspace);
await producer.compute(builder);
_addAssistFromBuilder(builder, producer.assistKind,
@@ -243,2077 +249,21 @@
var producer = generator();
producer.configure(context);
- var builder = _newDartChangeBuilder();
+ var builder = DartChangeBuilderImpl.forWorkspace(context.workspace);
await producer.compute(builder);
-
_addAssistFromBuilder(builder, producer.assistKind,
args: producer.assistArguments);
}
}
- }
-
- Future<void> _addProposal_addNotNullAssert() async {
- final identifier = node;
- if (identifier is SimpleIdentifier) {
- if (identifier.parent is FormalParameter) {
- final exp = identifier.parent.thisOrAncestorMatching(
- (node) => node is FunctionExpression || node is MethodDeclaration);
- var body;
- if (exp is FunctionExpression) {
- body = exp.body;
- } else if (exp is MethodDeclaration) {
- body = exp.body;
- }
- if (body is BlockFunctionBody) {
- var blockBody = body;
- // Check for an obvious pre-existing assertion.
- for (var statement in blockBody.block.statements) {
- if (statement is AssertStatement) {
- final condition = statement.condition;
- if (condition is BinaryExpression) {
- final leftOperand = condition.leftOperand;
- if (leftOperand is SimpleIdentifier) {
- if (leftOperand.staticElement == identifier.staticElement &&
- condition.operator.type == TokenType.BANG_EQ &&
- condition.rightOperand is NullLiteral) {
- return;
- }
- }
- }
- }
- }
-
- final changeBuilder = _newDartChangeBuilder();
- await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) {
- final id = identifier.name;
- final prefix = utils.getNodePrefix(exp);
- final indent = utils.getIndent(1);
- // todo (pq): follow-ups:
- // 1. if the end token is on the same line as the body
- // we should add an `eol` before the assert as well.
- // 2. also, consider asking the block for the list of statements and
- // adding the statement to the beginning of the list, special casing
- // when there are no statements (or when there's a single statement
- // and the whole block is on the same line).
- var offset = min(utils.getLineNext(blockBody.beginToken.offset),
- blockBody.endToken.offset);
- builder.addSimpleInsertion(
- offset, '$prefix${indent}assert($id != null);$eol');
- _addAssistFromBuilder(
- changeBuilder, DartAssistKind.ADD_NOT_NULL_ASSERT);
- });
- }
- }
- }
- }
-
- Future<void> _addProposal_convertToFieldParameter() async {
- if (node == null) {
- return;
- }
- // prepare ConstructorDeclaration
- var constructor = node.thisOrAncestorOfType<ConstructorDeclaration>();
- if (constructor == null) {
- return;
- }
- var parameterList = constructor.parameters;
- List<ConstructorInitializer> initializers = constructor.initializers;
- // prepare parameter
- SimpleFormalParameter parameter;
- if (node.parent is SimpleFormalParameter &&
- node.parent.parent is FormalParameterList &&
- node.parent.parent.parent is ConstructorDeclaration) {
- parameter = node.parent;
- }
- if (node is SimpleIdentifier &&
- node.parent is ConstructorFieldInitializer) {
- var name = (node as SimpleIdentifier).name;
- ConstructorFieldInitializer initializer = node.parent;
- if (initializer.expression == node) {
- for (var formalParameter in parameterList.parameters) {
- if (formalParameter is SimpleFormalParameter &&
- formalParameter.identifier.name == name) {
- parameter = formalParameter;
- }
- }
- }
- }
- // analyze parameter
- if (parameter != null) {
- var parameterName = parameter.identifier.name;
- var parameterElement = parameter.declaredElement;
- // check number of references
- {
- var numOfReferences = 0;
- AstVisitor visitor =
- _SimpleIdentifierRecursiveAstVisitor((SimpleIdentifier node) {
- if (node.staticElement == parameterElement) {
- numOfReferences++;
- }
- });
- for (var initializer in initializers) {
- initializer.accept(visitor);
- }
- if (numOfReferences != 1) {
- return;
- }
- }
- // find the field initializer
- ConstructorFieldInitializer parameterInitializer;
- for (var initializer in initializers) {
- if (initializer is ConstructorFieldInitializer) {
- var expression = initializer.expression;
- if (expression is SimpleIdentifier &&
- expression.name == parameterName) {
- parameterInitializer = initializer;
- }
- }
- }
- if (parameterInitializer == null) {
- return;
- }
- var fieldName = parameterInitializer.fieldName.name;
-
- var changeBuilder = _newDartChangeBuilder();
- await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) {
- // replace parameter
- builder.addSimpleReplacement(range.node(parameter), 'this.$fieldName');
- // remove initializer
- var initializerIndex = initializers.indexOf(parameterInitializer);
- if (initializers.length == 1) {
- builder
- .addDeletion(range.endEnd(parameterList, parameterInitializer));
- } else {
- if (initializerIndex == 0) {
- var next = initializers[initializerIndex + 1];
- builder.addDeletion(range.startStart(parameterInitializer, next));
- } else {
- var prev = initializers[initializerIndex - 1];
- builder.addDeletion(range.endEnd(prev, parameterInitializer));
- }
- }
- });
- _addAssistFromBuilder(
- changeBuilder, DartAssistKind.CONVERT_TO_FIELD_PARAMETER);
- }
- }
-
- Future<void> _addProposal_convertToForIndexLoop() async {
- // find enclosing ForEachStatement
- var forEachStatement = node.thisOrAncestorMatching(
- (node) => node is ForStatement && node.forLoopParts is ForEachParts)
- as ForStatement;
- if (forEachStatement == null) {
- return;
- }
- ForEachParts forEachParts = forEachStatement.forLoopParts;
- if (selectionOffset < forEachStatement.offset ||
- forEachStatement.rightParenthesis.end < selectionOffset) {
- return;
- }
- // loop should declare variable
- var loopVariable = forEachParts is ForEachPartsWithDeclaration
- ? forEachParts.loopVariable
- : null;
- if (loopVariable == null) {
- return;
- }
- // iterable should be VariableElement
- String listName;
- var iterable = forEachParts.iterable;
- if (iterable is SimpleIdentifier &&
- iterable.staticElement is VariableElement) {
- listName = iterable.name;
- } else {
- return;
- }
- // iterable should be List
- {
- var iterableType = iterable.staticType;
- if (iterableType is! InterfaceType ||
- iterableType.element != typeProvider.listElement) {
- return;
- }
- }
- // body should be Block
- if (forEachStatement.body is! Block) {
- return;
- }
- Block body = forEachStatement.body;
- // prepare a name for the index variable
- String indexName;
- {
- var conflicts =
- utils.findPossibleLocalVariableConflicts(forEachStatement.offset);
- if (!conflicts.contains('i')) {
- indexName = 'i';
- } else if (!conflicts.contains('j')) {
- indexName = 'j';
- } else if (!conflicts.contains('k')) {
- indexName = 'k';
- } else {
- return;
- }
- }
- // prepare environment
- var prefix = utils.getNodePrefix(forEachStatement);
- var indent = utils.getIndent(1);
- var firstBlockLine = utils.getLineContentEnd(body.leftBracket.end);
- // add change
- var changeBuilder = _newDartChangeBuilder();
- await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) {
- // TODO(brianwilkerson) Create linked positions for the loop variable.
- builder.addSimpleReplacement(
- range.startEnd(forEachStatement, forEachStatement.rightParenthesis),
- 'for (int $indexName = 0; $indexName < $listName.length; $indexName++)');
- builder.addSimpleInsertion(firstBlockLine,
- '$prefix$indent$loopVariable = $listName[$indexName];$eol');
- });
- _addAssistFromBuilder(changeBuilder, DartAssistKind.CONVERT_INTO_FOR_INDEX);
- }
-
- Future<void> _addProposal_convertToIsNot_onIs() async {
- // may be child of "is"
- var node = this.node;
- while (node != null && node is! IsExpression) {
- node = node.parent;
- }
- // prepare "is"
- if (node is! IsExpression) {
- return;
- }
- var isExpression = node as IsExpression;
- if (isExpression.notOperator != null) {
- return;
- }
- // prepare enclosing ()
- var parent = isExpression.parent;
- if (parent is! ParenthesizedExpression) {
- return;
- }
- var parExpression = parent as ParenthesizedExpression;
- // prepare enclosing !()
- var parent2 = parent.parent;
- if (parent2 is! PrefixExpression) {
- return;
- }
- var prefExpression = parent2 as PrefixExpression;
- if (prefExpression.operator.type != TokenType.BANG) {
- return;
- }
-
- var changeBuilder = _newDartChangeBuilder();
- await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) {
- if (getExpressionParentPrecedence(prefExpression) >=
- Precedence.relational) {
- builder.addDeletion(range.token(prefExpression.operator));
- } else {
- builder.addDeletion(
- range.startEnd(prefExpression, parExpression.leftParenthesis));
- builder.addDeletion(
- range.startEnd(parExpression.rightParenthesis, prefExpression));
- }
- builder.addSimpleInsertion(isExpression.isOperator.end, '!');
- });
- _addAssistFromBuilder(changeBuilder, DartAssistKind.CONVERT_INTO_IS_NOT);
- }
-
- Future<void> _addProposal_convertToIsNot_onNot() async {
- // may be () in prefix expression
- var node = this.node;
- if (node is ParenthesizedExpression && node.parent is PrefixExpression) {
- node = node.parent;
- }
- // prepare !()
- if (node is! PrefixExpression) {
- return;
- }
- var prefExpression = node as PrefixExpression;
- // should be ! operator
- if (prefExpression.operator.type != TokenType.BANG) {
- return;
- }
- // prepare !()
- var operand = prefExpression.operand;
- if (operand is! ParenthesizedExpression) {
- return;
- }
- var parExpression = operand as ParenthesizedExpression;
- operand = parExpression.expression;
- // prepare "is"
- if (operand is! IsExpression) {
- return;
- }
- var isExpression = operand as IsExpression;
- if (isExpression.notOperator != null) {
- return;
- }
-
- var changeBuilder = _newDartChangeBuilder();
- await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) {
- if (getExpressionParentPrecedence(prefExpression) >=
- Precedence.relational) {
- builder.addDeletion(range.token(prefExpression.operator));
- } else {
- builder.addDeletion(
- range.startEnd(prefExpression, parExpression.leftParenthesis));
- builder.addDeletion(
- range.startEnd(parExpression.rightParenthesis, prefExpression));
- }
- builder.addSimpleInsertion(isExpression.isOperator.end, '!');
- });
- _addAssistFromBuilder(changeBuilder, DartAssistKind.CONVERT_INTO_IS_NOT);
- }
-
- /// Converts "!isEmpty" -> "isNotEmpty" if possible.
- Future<void> _addProposal_convertToIsNotEmpty() async {
- // prepare "expr.isEmpty"
- AstNode isEmptyAccess;
- SimpleIdentifier isEmptyIdentifier;
- if (node is SimpleIdentifier) {
- var identifier = node as SimpleIdentifier;
- var parent = identifier.parent;
- // normal case (but rare)
- if (parent is PropertyAccess) {
- isEmptyIdentifier = parent.propertyName;
- isEmptyAccess = parent;
- }
- // usual case
- if (parent is PrefixedIdentifier) {
- isEmptyIdentifier = parent.identifier;
- isEmptyAccess = parent;
- }
- }
- if (isEmptyIdentifier == null) {
- return;
- }
- // should be "isEmpty"
- var propertyElement = isEmptyIdentifier.staticElement;
- if (propertyElement == null || 'isEmpty' != propertyElement.name) {
- return;
- }
- // should have "isNotEmpty"
- var propertyTarget = propertyElement.enclosingElement;
- if (propertyTarget == null ||
- getChildren(propertyTarget, 'isNotEmpty').isEmpty) {
- return;
- }
- // should be in PrefixExpression
- if (isEmptyAccess.parent is! PrefixExpression) {
- return;
- }
- var prefixExpression = isEmptyAccess.parent as PrefixExpression;
- // should be !
- if (prefixExpression.operator.type != TokenType.BANG) {
- return;
- }
-
- var changeBuilder = _newDartChangeBuilder();
- await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) {
- builder.addDeletion(
- range.startStart(prefixExpression, prefixExpression.operand));
- builder.addSimpleReplacement(range.node(isEmptyIdentifier), 'isNotEmpty');
- });
- _addAssistFromBuilder(
- changeBuilder, DartAssistKind.CONVERT_INTO_IS_NOT_EMPTY);
- }
-
- Future<void> _addProposal_convertToMultilineString() async {
- var node = this.node;
- if (node is InterpolationElement) {
- node = (node as InterpolationElement).parent;
- }
- if (node is SingleStringLiteral) {
- var literal = node;
- if (!literal.isMultiline) {
- var changeBuilder = _newDartChangeBuilder();
- await changeBuilder.addFileEdit(file, (builder) {
- var newQuote = literal.isSingleQuoted ? "'''" : '"""';
- builder.addReplacement(
- SourceRange(literal.offset + (literal.isRaw ? 1 : 0), 1),
- (builder) {
- builder.writeln(newQuote);
- },
- );
- builder.addSimpleReplacement(
- SourceRange(literal.end - 1, 1),
- newQuote,
- );
- });
- _addAssistFromBuilder(
- changeBuilder,
- DartAssistKind.CONVERT_TO_MULTILINE_STRING,
- );
- }
- }
- }
-
- Future<void> _addProposal_convertToNormalParameter() async {
- if (node is SimpleIdentifier &&
- node.parent is FieldFormalParameter &&
- node.parent.parent is FormalParameterList &&
- node.parent.parent.parent is ConstructorDeclaration) {
- ConstructorDeclaration constructor = node.parent.parent.parent;
- FormalParameterList parameterList = node.parent.parent;
- FieldFormalParameter parameter = node.parent;
- var parameterElement = parameter.declaredElement;
- var name = (node as SimpleIdentifier).name;
- // prepare type
- var type = parameterElement.type;
-
- var changeBuilder = _newDartChangeBuilder();
- await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) {
- // replace parameter
- if (type.isDynamic) {
- builder.addSimpleReplacement(range.node(parameter), name);
- } else {
- builder.addReplacement(range.node(parameter),
- (DartEditBuilder builder) {
- builder.writeType(type);
- builder.write(' ');
- builder.write(name);
- });
- }
- // add field initializer
- List<ConstructorInitializer> initializers = constructor.initializers;
- if (initializers.isEmpty) {
- builder.addSimpleInsertion(parameterList.end, ' : $name = $name');
- } else {
- builder.addSimpleInsertion(initializers.last.end, ', $name = $name');
- }
- });
- _addAssistFromBuilder(
- changeBuilder, DartAssistKind.CONVERT_TO_NORMAL_PARAMETER);
- }
- }
-
- Future<void> _addProposal_encapsulateField() async {
- // find FieldDeclaration
- var fieldDeclaration = node.thisOrAncestorOfType<FieldDeclaration>();
- if (fieldDeclaration == null) {
- return;
- }
- // not interesting for static
- if (fieldDeclaration.isStatic) {
- return;
- }
- // has a parse error
- var variableList = fieldDeclaration.fields;
- if (variableList.keyword == null && variableList.type == null) {
- return;
- }
- // not interesting for final
- if (variableList.isFinal) {
- return;
- }
- // should have exactly one field
- List<VariableDeclaration> fields = variableList.variables;
- if (fields.length != 1) {
- return;
- }
- var field = fields.first;
- var nameNode = field.name;
- FieldElement fieldElement = nameNode.staticElement;
- // should have a public name
- var name = nameNode.name;
- if (Identifier.isPrivateName(name)) {
- return;
- }
- // should be on the name
- if (nameNode != node) {
- return;
- }
- var changeBuilder = _newDartChangeBuilder();
- await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) {
- // rename field
- builder.addSimpleReplacement(range.node(nameNode), '_$name');
- // update references in constructors
- ClassOrMixinDeclaration classDeclaration = fieldDeclaration.parent;
- for (var member in classDeclaration.members) {
- if (member is ConstructorDeclaration) {
- for (var parameter in member.parameters.parameters) {
- var parameterElement = parameter.declaredElement;
- if (parameterElement is FieldFormalParameterElement &&
- parameterElement.field == fieldElement) {
- var identifier = parameter.identifier;
- builder.addSimpleReplacement(range.node(identifier), '_$name');
- }
- }
- }
- }
-
- // Write getter and setter.
- builder.addInsertion(fieldDeclaration.end, (builder) {
- String docCode;
- if (fieldDeclaration.documentationComment != null) {
- docCode = utils.getNodeText(fieldDeclaration.documentationComment);
- }
-
- var typeCode = '';
- if (variableList.type != null) {
- typeCode = _getNodeText(variableList.type) + ' ';
- }
-
- // Write getter.
- builder.writeln();
- builder.writeln();
- if (docCode != null) {
- builder.write(' ');
- builder.writeln(docCode);
- }
- builder.write(' ${typeCode}get $name => _$name;');
-
- // Write setter.
- builder.writeln();
- builder.writeln();
- if (docCode != null) {
- builder.write(' ');
- builder.writeln(docCode);
- }
- builder.writeln(' set $name($typeCode$name) {');
- builder.writeln(' _$name = $name;');
- builder.write(' }');
- });
- });
- _addAssistFromBuilder(changeBuilder, DartAssistKind.ENCAPSULATE_FIELD);
- }
-
- Future<void> _addProposal_flutterConvertToChildren() async {
- // Find "child: widget" under selection.
- NamedExpression namedExp;
- {
- var node = this.node;
- var parent = node?.parent;
- var parent2 = parent?.parent;
- if (node is SimpleIdentifier &&
- parent is Label &&
- parent2 is NamedExpression &&
- node.name == 'child' &&
- node.staticElement != null &&
- flutter.isWidgetExpression(parent2.expression)) {
- namedExp = parent2;
- } else {
- return;
- }
- }
-
- var changeBuilder = _newDartChangeBuilder();
- await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) {
- _convertFlutterChildToChildren(namedExp, eol, utils.getNodeText,
- utils.getLinePrefix, utils.getIndent, utils.getText, builder);
- });
- _addAssistFromBuilder(
- changeBuilder, DartAssistKind.FLUTTER_CONVERT_TO_CHILDREN);
- }
-
- Future<void> _addProposal_flutterConvertToStatefulWidget() async {
- var widgetClass = node.thisOrAncestorOfType<ClassDeclaration>();
- var superclass = widgetClass?.extendsClause?.superclass;
- if (widgetClass == null || superclass == null) {
- return;
- }
-
- // Don't spam, activate only from the `class` keyword to the class body.
- if (selectionOffset < widgetClass.classKeyword.offset ||
- selectionOffset > widgetClass.leftBracket.end) {
- return;
- }
-
- // Find the build() method.
- MethodDeclaration buildMethod;
- for (var member in widgetClass.members) {
- if (member is MethodDeclaration &&
- member.name.name == 'build' &&
- member.parameters != null &&
- member.parameters.parameters.length == 1) {
- buildMethod = member;
- break;
- }
- }
- if (buildMethod == null) {
- return;
- }
-
- // Must be a StatelessWidget subclasses.
- var widgetClassElement = widgetClass.declaredElement;
- if (!flutter.isExactlyStatelessWidgetType(widgetClassElement.supertype)) {
- return;
- }
-
- var widgetName = widgetClassElement.displayName;
- var stateName = '_${widgetName}State';
-
- // Find fields assigned in constructors.
- var fieldsAssignedInConstructors = <FieldElement>{};
- for (var member in widgetClass.members) {
- if (member is ConstructorDeclaration) {
- member.accept(_SimpleIdentifierRecursiveAstVisitor((node) {
- if (node.parent is FieldFormalParameter) {
- var element = node.staticElement;
- if (element is FieldFormalParameterElement) {
- fieldsAssignedInConstructors.add(element.field);
- }
- }
- if (node.parent is ConstructorFieldInitializer) {
- var element = node.staticElement;
- if (element is FieldElement) {
- fieldsAssignedInConstructors.add(element);
- }
- }
- if (node.inSetterContext()) {
- var element = node.staticElement;
- if (element is PropertyAccessorElement) {
- var field = element.variable;
- if (field is FieldElement) {
- fieldsAssignedInConstructors.add(field);
- }
- }
- }
- }));
- }
- }
-
- // Prepare nodes to move.
- var nodesToMove = <ClassMember>{};
- var elementsToMove = <Element>{};
- for (var member in widgetClass.members) {
- if (member is FieldDeclaration && !member.isStatic) {
- for (var fieldNode in member.fields.variables) {
- FieldElement fieldElement = fieldNode.declaredElement;
- if (!fieldsAssignedInConstructors.contains(fieldElement)) {
- nodesToMove.add(member);
- elementsToMove.add(fieldElement);
- elementsToMove.add(fieldElement.getter);
- if (fieldElement.setter != null) {
- elementsToMove.add(fieldElement.setter);
- }
- }
- }
- }
- if (member is MethodDeclaration && !member.isStatic) {
- nodesToMove.add(member);
- elementsToMove.add(member.declaredElement);
- }
- }
-
- /// Return the code for the [movedNode] which is suitable to be used
- /// inside the `State` class, so that references to the widget fields and
- /// methods, that are not moved, are qualified with the corresponding
- /// instance `widget.`, or static `MyWidgetClass.` qualifier.
- String rewriteWidgetMemberReferences(AstNode movedNode) {
- var linesRange = utils.getLinesRange(range.node(movedNode));
- var text = utils.getRangeText(linesRange);
-
- // Insert `widget.` before references to the widget instance members.
- var edits = <SourceEdit>[];
- movedNode.accept(_SimpleIdentifierRecursiveAstVisitor((node) {
- if (node.inDeclarationContext()) {
- return;
- }
- var element = node.staticElement;
- if (element is ExecutableElement &&
- element?.enclosingElement == widgetClassElement &&
- !elementsToMove.contains(element)) {
- var offset = node.offset - linesRange.offset;
- var qualifier = element.isStatic ? widgetName : 'widget';
-
- var parent = node.parent;
- if (parent is InterpolationExpression &&
- parent.leftBracket.type ==
- TokenType.STRING_INTERPOLATION_IDENTIFIER) {
- edits.add(SourceEdit(offset, 0, '{$qualifier.'));
- edits.add(SourceEdit(offset + node.length, 0, '}'));
- } else {
- edits.add(SourceEdit(offset, 0, '$qualifier.'));
- }
- }
- }));
- return SourceEdit.applySequence(text, edits.reversed);
- }
-
- var statefulWidgetClass = await sessionHelper.getClass(
- flutter.widgetsUri,
- 'StatefulWidget',
- );
- var stateClass = await sessionHelper.getClass(
- flutter.widgetsUri,
- 'State',
- );
- if (statefulWidgetClass == null || stateClass == null) {
- return;
- }
-
- var changeBuilder = _newDartChangeBuilder();
- await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) {
- builder.addReplacement(range.node(superclass), (builder) {
- builder.writeReference(statefulWidgetClass);
- });
-
- var replaceOffset = 0;
- var hasBuildMethod = false;
-
- var typeParams = '';
- if (widgetClass.typeParameters != null) {
- typeParams = utils.getNodeText(widgetClass.typeParameters);
- }
-
- /// Replace code between [replaceOffset] and [replaceEnd] with
- /// `createState()`, empty line, or nothing.
- void replaceInterval(int replaceEnd,
- {bool replaceWithEmptyLine = false,
- bool hasEmptyLineBeforeCreateState = false,
- bool hasEmptyLineAfterCreateState = true}) {
- var replaceLength = replaceEnd - replaceOffset;
- builder.addReplacement(
- SourceRange(replaceOffset, replaceLength),
- (builder) {
- if (hasBuildMethod) {
- if (hasEmptyLineBeforeCreateState) {
- builder.writeln();
- }
- builder.writeln(' @override');
- builder.writeln(
- ' $stateName$typeParams createState() => $stateName$typeParams();');
- if (hasEmptyLineAfterCreateState) {
- builder.writeln();
- }
- hasBuildMethod = false;
- } else if (replaceWithEmptyLine) {
- builder.writeln();
- }
- },
- );
- replaceOffset = 0;
- }
-
- // Remove continuous ranges of lines of nodes being moved.
- var lastToRemoveIsField = false;
- var endOfLastNodeToKeep = 0;
- for (var node in widgetClass.members) {
- if (nodesToMove.contains(node)) {
- if (replaceOffset == 0) {
- var linesRange = utils.getLinesRange(range.node(node));
- replaceOffset = linesRange.offset;
- }
- if (node == buildMethod) {
- hasBuildMethod = true;
- }
- lastToRemoveIsField = node is FieldDeclaration;
- } else {
- var linesRange = utils.getLinesRange(range.node(node));
- endOfLastNodeToKeep = linesRange.end;
- if (replaceOffset != 0) {
- replaceInterval(linesRange.offset,
- replaceWithEmptyLine:
- lastToRemoveIsField && node is! FieldDeclaration);
- }
- }
- }
-
- // Remove nodes at the end of the widget class.
- if (replaceOffset != 0) {
- // Remove from the last node to keep, so remove empty lines.
- if (endOfLastNodeToKeep != 0) {
- replaceOffset = endOfLastNodeToKeep;
- }
- replaceInterval(widgetClass.rightBracket.offset,
- hasEmptyLineBeforeCreateState: endOfLastNodeToKeep != 0,
- hasEmptyLineAfterCreateState: false);
- }
-
- // Create the State subclass.
- builder.addInsertion(widgetClass.end, (builder) {
- builder.writeln();
- builder.writeln();
-
- builder.write('class $stateName$typeParams extends ');
- builder.writeReference(stateClass);
-
- // Write just param names (and not bounds, metadata and docs).
- builder.write('<${widgetClass.name}');
- if (widgetClass.typeParameters != null) {
- builder.write('<');
- var first = true;
- for (var param in widgetClass.typeParameters.typeParameters) {
- if (!first) {
- builder.write(', ');
- first = false;
- }
- builder.write(param.name.name);
- }
- builder.write('>');
- }
-
- builder.writeln('> {');
-
- var writeEmptyLine = false;
- for (var member in nodesToMove) {
- if (writeEmptyLine) {
- builder.writeln();
- }
- var text = rewriteWidgetMemberReferences(member);
- builder.write(text);
- // Write empty lines between members, but not before the first.
- writeEmptyLine = true;
- }
-
- builder.write('}');
- });
- });
- _addAssistFromBuilder(
- changeBuilder, DartAssistKind.FLUTTER_CONVERT_TO_STATEFUL_WIDGET);
- }
-
- Future<void> _addProposal_flutterMoveWidgetDown() async {
- var widget = flutter.identifyWidgetExpression(node);
- if (widget == null) {
- return;
- }
-
- var parentList = widget.parent;
- if (parentList is ListLiteral) {
- List<CollectionElement> parentElements = parentList.elements;
- var index = parentElements.indexOf(widget);
- if (index != parentElements.length - 1) {
- var changeBuilder = _newDartChangeBuilder();
- await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) {
- var nextWidget = parentElements[index + 1];
- var nextRange = range.node(nextWidget);
- var nextText = utils.getRangeText(nextRange);
-
- var widgetRange = range.node(widget);
- var widgetText = utils.getRangeText(widgetRange);
-
- builder.addSimpleReplacement(nextRange, widgetText);
- builder.addSimpleReplacement(widgetRange, nextText);
-
- var lengthDelta = nextRange.length - widgetRange.length;
- var newWidgetOffset = nextRange.offset + lengthDelta;
- changeBuilder.setSelection(Position(file, newWidgetOffset));
- });
- _addAssistFromBuilder(changeBuilder, DartAssistKind.FLUTTER_MOVE_DOWN);
- }
- }
- }
-
- Future<void> _addProposal_flutterMoveWidgetUp() async {
- var widget = flutter.identifyWidgetExpression(node);
- if (widget == null) {
- return;
- }
-
- var parentList = widget.parent;
- if (parentList is ListLiteral) {
- List<CollectionElement> parentElements = parentList.elements;
- var index = parentElements.indexOf(widget);
- if (index > 0) {
- var changeBuilder = _newDartChangeBuilder();
- await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) {
- var previousWidget = parentElements[index - 1];
- var previousRange = range.node(previousWidget);
- var previousText = utils.getRangeText(previousRange);
-
- var widgetRange = range.node(widget);
- var widgetText = utils.getRangeText(widgetRange);
-
- builder.addSimpleReplacement(previousRange, widgetText);
- builder.addSimpleReplacement(widgetRange, previousText);
-
- var newWidgetOffset = previousRange.offset;
- changeBuilder.setSelection(Position(file, newWidgetOffset));
- });
- _addAssistFromBuilder(changeBuilder, DartAssistKind.FLUTTER_MOVE_UP);
- }
- }
- }
-
- Future<void> _addProposal_flutterRemoveWidget_multipleChildren() async {
- var widgetCreation = flutter.identifyNewExpression(node);
- if (widgetCreation == null) {
- return;
- }
-
- // Prepare the list of our children.
- List<CollectionElement> childrenExpressions;
- {
- var childrenArgument = flutter.findChildrenArgument(widgetCreation);
- var childrenExpression = childrenArgument?.expression;
- if (childrenExpression is ListLiteral &&
- childrenExpression.elements.isNotEmpty) {
- childrenExpressions = childrenExpression.elements;
- } else {
- return;
- }
- }
-
- // We can inline the list of our children only into another list.
- var widgetParentNode = widgetCreation.parent;
- if (childrenExpressions.length > 1 && widgetParentNode is! ListLiteral) {
- return;
- }
-
- var changeBuilder = _newDartChangeBuilder();
- await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) {
- var firstChild = childrenExpressions.first;
- var lastChild = childrenExpressions.last;
- var childText = utils.getRangeText(range.startEnd(firstChild, lastChild));
- var indentOld = utils.getLinePrefix(firstChild.offset);
- var indentNew = utils.getLinePrefix(widgetCreation.offset);
- childText = _replaceSourceIndent(childText, indentOld, indentNew);
- builder.addSimpleReplacement(range.node(widgetCreation), childText);
- });
- _addAssistFromBuilder(changeBuilder, DartAssistKind.FLUTTER_REMOVE_WIDGET);
- }
-
- Future<void> _addProposal_flutterRemoveWidget_singleChild() async {
- var widgetCreation = flutter.identifyNewExpression(node);
- if (widgetCreation == null) {
- return;
- }
-
- var childArgument = flutter.findChildArgument(widgetCreation);
- if (childArgument == null) {
- return;
- }
-
- // child: ThisWidget(child: ourChild)
- // children: [foo, ThisWidget(child: ourChild), bar]
- var changeBuilder = _newDartChangeBuilder();
- await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) {
- var childExpression = childArgument.expression;
- var childText = utils.getNodeText(childExpression);
- var indentOld = utils.getLinePrefix(childExpression.offset);
- var indentNew = utils.getLinePrefix(widgetCreation.offset);
- childText = _replaceSourceIndent(childText, indentOld, indentNew);
- builder.addSimpleReplacement(range.node(widgetCreation), childText);
- });
- _addAssistFromBuilder(changeBuilder, DartAssistKind.FLUTTER_REMOVE_WIDGET);
- }
-
- Future<void> _addProposal_flutterSwapWithChild() async {
- var parent = flutter.identifyNewExpression(node);
- if (!flutter.isWidgetCreation(parent)) {
- return;
- }
-
- var childArgument = flutter.findChildArgument(parent);
- if (childArgument?.expression is! InstanceCreationExpression ||
- !flutter.isWidgetCreation(childArgument.expression)) {
- return;
- }
- InstanceCreationExpression child = childArgument.expression;
-
- await _swapParentAndChild(
- parent, child, DartAssistKind.FLUTTER_SWAP_WITH_CHILD);
- }
-
- Future<void> _addProposal_flutterSwapWithParent() async {
- var child = flutter.identifyNewExpression(node);
- if (!flutter.isWidgetCreation(child)) {
- return;
- }
-
- // NamedExpression (child:), ArgumentList, InstanceCreationExpression
- var expr = child.parent?.parent?.parent;
- if (expr is! InstanceCreationExpression) {
- return;
- }
- InstanceCreationExpression parent = expr;
-
- await _swapParentAndChild(
- parent, child, DartAssistKind.FLUTTER_SWAP_WITH_PARENT);
- }
-
- Future<void> _addProposal_flutterWrapStreamBuilder() async {
- var widgetExpr = flutter.identifyWidgetExpression(node);
- if (widgetExpr == null) {
- return;
- }
- if (flutter.isExactWidgetTypeStreamBuilder(widgetExpr.staticType)) {
- return;
- }
- var widgetSrc = utils.getNodeText(widgetExpr);
-
- var streamBuilderElement = await sessionHelper.getClass(
- flutter.widgetsUri,
- 'StreamBuilder',
- );
- if (streamBuilderElement == null) {
- return;
- }
-
- var changeBuilder = _newDartChangeBuilder();
- await changeBuilder.addFileEdit(file, (builder) {
- builder.addReplacement(range.node(widgetExpr), (builder) {
- builder.writeReference(streamBuilderElement);
-
- builder.write('<');
- builder.addSimpleLinkedEdit('type', 'Object');
- builder.writeln('>(');
-
- var indentOld = utils.getLinePrefix(widgetExpr.offset);
- var indentNew1 = indentOld + utils.getIndent(1);
- var indentNew2 = indentOld + utils.getIndent(2);
-
- builder.write(indentNew1);
- builder.writeln('stream: null,');
-
- builder.write(indentNew1);
- builder.writeln('builder: (context, snapshot) {');
-
- widgetSrc = widgetSrc.replaceAll(
- RegExp('^$indentOld', multiLine: true),
- indentNew2,
- );
- builder.write(indentNew2);
- builder.write('return $widgetSrc');
- builder.writeln(';');
-
- builder.write(indentNew1);
- builder.writeln('}');
-
- builder.write(indentOld);
- builder.write(')');
- });
- });
- _addAssistFromBuilder(
- changeBuilder,
- DartAssistKind.FLUTTER_WRAP_STREAM_BUILDER,
- );
- }
-
- Future<void> _addProposal_flutterWrapWidget() async {
- await _addProposal_flutterWrapWidgetImpl();
- await _addProposal_flutterWrapWidgetImpl(
- kind: DartAssistKind.FLUTTER_WRAP_CENTER,
- parentLibraryUri: flutter.widgetsUri,
- parentClassName: 'Center',
- widgetValidator: (expr) {
- return !flutter.isExactWidgetTypeCenter(expr.staticType);
- });
- await _addProposal_flutterWrapWidgetImpl(
- kind: DartAssistKind.FLUTTER_WRAP_CONTAINER,
- parentLibraryUri: flutter.widgetsUri,
- parentClassName: 'Container',
- widgetValidator: (expr) {
- return !flutter.isExactWidgetTypeContainer(expr.staticType);
- });
- await _addProposal_flutterWrapWidgetImpl(
- kind: DartAssistKind.FLUTTER_WRAP_PADDING,
- parentLibraryUri: flutter.widgetsUri,
- parentClassName: 'Padding',
- leadingLines: ['padding: const EdgeInsets.all(8.0),'],
- widgetValidator: (expr) {
- return !flutter.isExactWidgetTypePadding(expr.staticType);
- });
- }
-
- Future<void> _addProposal_flutterWrapWidgetImpl(
- {AssistKind kind = DartAssistKind.FLUTTER_WRAP_GENERIC,
- bool Function(Expression widgetExpr) widgetValidator,
- String parentLibraryUri,
- String parentClassName,
- List<String> leadingLines = const []}) async {
- var widgetExpr = flutter.identifyWidgetExpression(node);
- if (widgetExpr == null) {
- return;
- }
- if (widgetValidator != null && !widgetValidator(widgetExpr)) {
- return;
- }
- var widgetSrc = utils.getNodeText(widgetExpr);
-
- // If the wrapper class is specified, find its element.
- ClassElement parentClassElement;
- if (parentLibraryUri != null && parentClassName != null) {
- parentClassElement =
- await sessionHelper.getClass(parentLibraryUri, parentClassName);
- if (parentClassElement == null) {
- return;
- }
- }
-
- var changeBuilder = _newDartChangeBuilder();
- await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) {
- builder.addReplacement(range.node(widgetExpr), (DartEditBuilder builder) {
- if (parentClassElement == null) {
- builder.addSimpleLinkedEdit('WIDGET', 'widget');
- } else {
- builder.writeReference(parentClassElement);
- }
- builder.write('(');
- if (widgetSrc.contains(eol) || leadingLines.isNotEmpty) {
- var indentOld = utils.getLinePrefix(widgetExpr.offset);
- var indentNew = '$indentOld${utils.getIndent(1)}';
-
- for (var leadingLine in leadingLines) {
- builder.write(eol);
- builder.write(indentNew);
- builder.write(leadingLine);
- }
-
- builder.write(eol);
- builder.write(indentNew);
- widgetSrc = widgetSrc.replaceAll(
- RegExp('^$indentOld', multiLine: true), indentNew);
- widgetSrc += ',$eol$indentOld';
- }
- if (parentClassElement == null) {
- builder.addSimpleLinkedEdit('CHILD', 'child');
- } else {
- builder.write('child');
- }
- builder.write(': ');
- builder.write(widgetSrc);
- builder.write(')');
- });
- });
- _addAssistFromBuilder(changeBuilder, kind);
- }
-
- Future<void> _addProposal_flutterWrapWidgets() async {
- var selectionRange = SourceRange(selectionOffset, selectionLength);
- var analyzer = SelectionAnalyzer(selectionRange);
- context.resolveResult.unit.accept(analyzer);
-
- var widgetExpressions = <Expression>[];
- if (analyzer.hasSelectedNodes) {
- for (var selectedNode in analyzer.selectedNodes) {
- if (!flutter.isWidgetExpression(selectedNode)) {
- return;
- }
- widgetExpressions.add(selectedNode);
- }
- } else {
- var widget = flutter.identifyWidgetExpression(analyzer.coveringNode);
- if (widget != null) {
- widgetExpressions.add(widget);
- }
- }
- if (widgetExpressions.isEmpty) {
- return;
- }
-
- var firstWidget = widgetExpressions.first;
- var lastWidget = widgetExpressions.last;
- var selectedRange = range.startEnd(firstWidget, lastWidget);
- var src = utils.getRangeText(selectedRange);
-
- Future<void> addAssist(
- {@required AssistKind kind,
- @required String parentLibraryUri,
- @required String parentClassName}) async {
- var parentClassElement =
- await sessionHelper.getClass(parentLibraryUri, parentClassName);
- var widgetClassElement =
- await sessionHelper.getClass(flutter.widgetsUri, 'Widget');
- if (parentClassElement == null || widgetClassElement == null) {
- return;
- }
-
- var changeBuilder = _newDartChangeBuilder();
- await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) {
- builder.addReplacement(selectedRange, (DartEditBuilder builder) {
- builder.writeReference(parentClassElement);
- builder.write('(');
-
- var indentOld = utils.getLinePrefix(firstWidget.offset);
- var indentNew1 = indentOld + utils.getIndent(1);
- var indentNew2 = indentOld + utils.getIndent(2);
-
- builder.write(eol);
- builder.write(indentNew1);
- builder.write('children: [');
- builder.write(eol);
-
- var newSrc = _replaceSourceIndent(src, indentOld, indentNew2);
- builder.write(indentNew2);
- builder.write(newSrc);
-
- builder.write(',');
- builder.write(eol);
-
- builder.write(indentNew1);
- builder.write('],');
- builder.write(eol);
-
- builder.write(indentOld);
- builder.write(')');
- });
- });
- _addAssistFromBuilder(changeBuilder, kind);
- }
-
- await addAssist(
- kind: DartAssistKind.FLUTTER_WRAP_COLUMN,
- parentLibraryUri: flutter.widgetsUri,
- parentClassName: 'Column');
- await addAssist(
- kind: DartAssistKind.FLUTTER_WRAP_ROW,
- parentLibraryUri: flutter.widgetsUri,
- parentClassName: 'Row');
- }
-
- Future<void> _addProposal_importAddShow() async {
- // prepare ImportDirective
- var importDirective = node.thisOrAncestorOfType<ImportDirective>();
- if (importDirective == null) {
- return;
- }
- // there should be no existing combinators
- if (importDirective.combinators.isNotEmpty) {
- return;
- }
- // prepare whole import namespace
- ImportElement importElement = importDirective.element;
- if (importElement == null) {
- return;
- }
- var namespace = getImportNamespace(importElement);
- // prepare names of referenced elements (from this import)
- var referencedNames = SplayTreeSet<String>();
- var visitor = _SimpleIdentifierRecursiveAstVisitor((SimpleIdentifier node) {
- var element = node.staticElement;
- if (element != null &&
- (namespace[node.name] == element ||
- (node.name != element.name &&
- namespace[element.name] == element))) {
- referencedNames.add(element.displayName);
- }
- });
- context.resolveResult.unit.accept(visitor);
- // ignore if unused
- if (referencedNames.isEmpty) {
- return;
- }
- var changeBuilder = _newDartChangeBuilder();
- await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) {
- var showCombinator = ' show ${referencedNames.join(', ')}';
- builder.addSimpleInsertion(importDirective.end - 1, showCombinator);
- });
- _addAssistFromBuilder(changeBuilder, DartAssistKind.IMPORT_ADD_SHOW);
- }
-
- Future<void> _addProposal_introduceLocalTestedType() async {
- var node = this.node;
- if (node is IfStatement) {
- node = (node as IfStatement).condition;
- } else if (node is WhileStatement) {
- node = (node as WhileStatement).condition;
- }
- // prepare IsExpression
- if (node is! IsExpression) {
- return;
- }
- IsExpression isExpression = node;
- var castType = isExpression.type.type;
- var castTypeCode = _getNodeText(isExpression.type);
- // prepare environment
- var indent = utils.getIndent(1);
- String prefix;
- Block targetBlock;
- {
- var statement = node.thisOrAncestorOfType<Statement>();
- if (statement is IfStatement && statement.thenStatement is Block) {
- targetBlock = statement.thenStatement;
- } else if (statement is WhileStatement && statement.body is Block) {
- targetBlock = statement.body;
- } else {
- return;
- }
- prefix = utils.getNodePrefix(statement);
- }
- // prepare location
- int offset;
- String statementPrefix;
- if (isExpression.notOperator == null) {
- offset = targetBlock.leftBracket.end;
- statementPrefix = indent;
- } else {
- offset = targetBlock.rightBracket.end;
- statementPrefix = '';
- }
- // prepare excluded names
- var excluded = <String>{};
- var scopedNameFinder = ScopedNameFinder(offset);
- isExpression.accept(scopedNameFinder);
- excluded.addAll(scopedNameFinder.locals.keys.toSet());
- // name(s)
- var suggestions =
- getVariableNameSuggestionsForExpression(castType, null, excluded);
-
- if (suggestions.isNotEmpty) {
- var changeBuilder = _newDartChangeBuilder();
- await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) {
- builder.addInsertion(offset, (DartEditBuilder builder) {
- builder.write(eol + prefix + statementPrefix);
- builder.write(castTypeCode);
- builder.write(' ');
- builder.addSimpleLinkedEdit('NAME', suggestions[0],
- kind: LinkedEditSuggestionKind.VARIABLE,
- suggestions: suggestions);
- builder.write(' = ');
- builder.write(_getNodeText(isExpression.expression));
- builder.write(';');
- builder.selectHere();
- });
- });
- _addAssistFromBuilder(
- changeBuilder, DartAssistKind.INTRODUCE_LOCAL_CAST_TYPE);
- }
- }
-
- Future<void> _addProposal_invertIf() async {
- if (node is! IfStatement) {
- return;
- }
- var ifStatement = node as IfStatement;
- var condition = ifStatement.condition;
- // should have both "then" and "else"
- var thenStatement = ifStatement.thenStatement;
- var elseStatement = ifStatement.elseStatement;
- if (thenStatement == null || elseStatement == null) {
- return;
- }
- // prepare source
- var invertedCondition = utils.invertCondition(condition);
- var thenSource = _getNodeText(thenStatement);
- var elseSource = _getNodeText(elseStatement);
-
- var changeBuilder = _newDartChangeBuilder();
- await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) {
- builder.addSimpleReplacement(range.node(condition), invertedCondition);
- builder.addSimpleReplacement(range.node(thenStatement), elseSource);
- builder.addSimpleReplacement(range.node(elseStatement), thenSource);
- });
- _addAssistFromBuilder(changeBuilder, DartAssistKind.INVERT_IF_STATEMENT);
- }
-
- Future<void> _addProposal_joinIfStatementInner() async {
- // climb up condition to the (supposedly) "if" statement
- var node = this.node;
- while (node is Expression) {
- node = node.parent;
- }
- // prepare target "if" statement
- if (node is! IfStatement) {
- return;
- }
- var targetIfStatement = node as IfStatement;
- if (targetIfStatement.elseStatement != null) {
- return;
- }
- // prepare inner "if" statement
- var targetThenStatement = targetIfStatement.thenStatement;
- var innerStatement = getSingleStatement(targetThenStatement);
- if (innerStatement is! IfStatement) {
- return;
- }
- var innerIfStatement = innerStatement as IfStatement;
- if (innerIfStatement.elseStatement != null) {
- return;
- }
- // prepare environment
- var prefix = utils.getNodePrefix(targetIfStatement);
- // merge conditions
- String condition;
- {
- var targetCondition = targetIfStatement.condition;
- var innerCondition = innerIfStatement.condition;
- var targetConditionSource = _getNodeText(targetCondition);
- var innerConditionSource = _getNodeText(innerCondition);
- if (_shouldWrapParenthesisBeforeAnd(targetCondition)) {
- targetConditionSource = '($targetConditionSource)';
- }
- if (_shouldWrapParenthesisBeforeAnd(innerCondition)) {
- innerConditionSource = '($innerConditionSource)';
- }
- condition = '$targetConditionSource && $innerConditionSource';
- }
- // replace target "if" statement
- var innerThenStatement = innerIfStatement.thenStatement;
- var innerThenStatements = getStatements(innerThenStatement);
- var lineRanges = utils.getLinesRangeStatements(innerThenStatements);
- var oldSource = utils.getRangeText(lineRanges);
- var newSource = utils.indentSourceLeftRight(oldSource);
-
- var changeBuilder = _newDartChangeBuilder();
- await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) {
- builder.addSimpleReplacement(range.node(targetIfStatement),
- 'if ($condition) {$eol$newSource$prefix}');
- });
- _addAssistFromBuilder(changeBuilder, DartAssistKind.JOIN_IF_WITH_INNER);
- }
-
- Future<void> _addProposal_joinIfStatementOuter() async {
- // climb up condition to the (supposedly) "if" statement
- var node = this.node;
- while (node is Expression) {
- node = node.parent;
- }
- // prepare target "if" statement
- if (node is! IfStatement) {
- return;
- }
- var targetIfStatement = node as IfStatement;
- if (targetIfStatement.elseStatement != null) {
- return;
- }
- // prepare outer "if" statement
- var parent = targetIfStatement.parent;
- if (parent is Block) {
- if ((parent as Block).statements.length != 1) {
- return;
- }
- parent = parent.parent;
- }
- if (parent is! IfStatement) {
- return;
- }
- var outerIfStatement = parent as IfStatement;
- if (outerIfStatement.elseStatement != null) {
- return;
- }
- // prepare environment
- var prefix = utils.getNodePrefix(outerIfStatement);
- // merge conditions
- var targetCondition = targetIfStatement.condition;
- var outerCondition = outerIfStatement.condition;
- var targetConditionSource = _getNodeText(targetCondition);
- var outerConditionSource = _getNodeText(outerCondition);
- if (_shouldWrapParenthesisBeforeAnd(targetCondition)) {
- targetConditionSource = '($targetConditionSource)';
- }
- if (_shouldWrapParenthesisBeforeAnd(outerCondition)) {
- outerConditionSource = '($outerConditionSource)';
- }
- var condition = '$outerConditionSource && $targetConditionSource';
- // replace outer "if" statement
- var targetThenStatement = targetIfStatement.thenStatement;
- var targetThenStatements = getStatements(targetThenStatement);
- var lineRanges = utils.getLinesRangeStatements(targetThenStatements);
- var oldSource = utils.getRangeText(lineRanges);
- var newSource = utils.indentSourceLeftRight(oldSource);
-
- var changeBuilder = _newDartChangeBuilder();
- await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) {
- builder.addSimpleReplacement(range.node(outerIfStatement),
- 'if ($condition) {$eol$newSource$prefix}');
- });
- _addAssistFromBuilder(changeBuilder, DartAssistKind.JOIN_IF_WITH_OUTER);
- }
-
- Future<void> _addProposal_joinVariableDeclaration_onAssignment() async {
- // check that node is LHS in assignment
- if (node is SimpleIdentifier &&
- node.parent is AssignmentExpression &&
- (node.parent as AssignmentExpression).leftHandSide == node &&
- node.parent.parent is ExpressionStatement) {
- } else {
- return;
- }
- var assignExpression = node.parent as AssignmentExpression;
- // check that binary expression is assignment
- if (assignExpression.operator.type != TokenType.EQ) {
- return;
- }
- // prepare "declaration" statement
- var element = (node as SimpleIdentifier).staticElement;
- if (element == null) {
- return;
- }
- var declOffset = element.nameOffset;
- var unit = context.resolveResult.unit;
- var declNode = NodeLocator(declOffset).searchWithin(unit);
- if (declNode != null &&
- declNode.parent is VariableDeclaration &&
- (declNode.parent as VariableDeclaration).name == declNode &&
- declNode.parent.parent is VariableDeclarationList &&
- declNode.parent.parent.parent is VariableDeclarationStatement) {
- } else {
- return;
- }
- var decl = declNode.parent as VariableDeclaration;
- var declStatement = decl.parent.parent as VariableDeclarationStatement;
- // may be has initializer
- if (decl.initializer != null) {
- return;
- }
- // check that "declaration" statement declared only one variable
- if (declStatement.variables.variables.length != 1) {
- return;
- }
- // check that the "declaration" and "assignment" statements are
- // parts of the same Block
- var assignStatement = node.parent.parent as ExpressionStatement;
- if (assignStatement.parent is Block &&
- assignStatement.parent == declStatement.parent) {
- } else {
- return;
- }
- var block = assignStatement.parent as Block;
- // check that "declaration" and "assignment" statements are adjacent
- List<Statement> statements = block.statements;
- if (statements.indexOf(assignStatement) ==
- statements.indexOf(declStatement) + 1) {
- } else {
- return;
- }
-
- var changeBuilder = _newDartChangeBuilder();
- await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) {
- builder.addSimpleReplacement(
- range.endStart(declNode, assignExpression.operator), ' ');
- });
- _addAssistFromBuilder(
- changeBuilder, DartAssistKind.JOIN_VARIABLE_DECLARATION);
- }
-
- Future<void> _addProposal_joinVariableDeclaration_onDeclaration() async {
- // prepare enclosing VariableDeclarationList
- var declList = node.thisOrAncestorOfType<VariableDeclarationList>();
- if (declList != null && declList.variables.length == 1) {
- } else {
- return;
- }
- var decl = declList.variables[0];
- // already initialized
- if (decl.initializer != null) {
- return;
- }
- // prepare VariableDeclarationStatement in Block
- if (declList.parent is VariableDeclarationStatement &&
- declList.parent.parent is Block) {
- } else {
- return;
- }
- var declStatement = declList.parent as VariableDeclarationStatement;
- var block = declStatement.parent as Block;
- List<Statement> statements = block.statements;
- // prepare assignment
- AssignmentExpression assignExpression;
- {
- // declaration should not be last Statement
- var declIndex = statements.indexOf(declStatement);
- if (declIndex < statements.length - 1) {
- } else {
- return;
- }
- // next Statement should be assignment
- var assignStatement = statements[declIndex + 1];
- if (assignStatement is ExpressionStatement) {
- } else {
- return;
- }
- var expressionStatement = assignStatement as ExpressionStatement;
- // expression should be assignment
- if (expressionStatement.expression is AssignmentExpression) {
- } else {
- return;
- }
- assignExpression = expressionStatement.expression as AssignmentExpression;
- }
- // check that pure assignment
- if (assignExpression.operator.type != TokenType.EQ) {
- return;
- }
-
- var changeBuilder = _newDartChangeBuilder();
- await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) {
- builder.addSimpleReplacement(
- range.endStart(decl.name, assignExpression.operator), ' ');
- });
- _addAssistFromBuilder(
- changeBuilder, DartAssistKind.JOIN_VARIABLE_DECLARATION);
- }
-
- Future<void> _addProposal_reparentFlutterList() async {
- if (node is! ListLiteral) {
- return;
- }
- if ((node as ListLiteral).elements.any((CollectionElement exp) =>
- !(exp is InstanceCreationExpression &&
- flutter.isWidgetCreation(exp)))) {
- return;
- }
- var literalSrc = utils.getNodeText(node);
- var newlineIdx = literalSrc.lastIndexOf(eol);
- if (newlineIdx < 0 || newlineIdx == literalSrc.length - 1) {
- return; // Lists need to be in multi-line format already.
- }
- var indentOld = utils.getLinePrefix(node.offset + 1 + newlineIdx);
- var indentArg = '$indentOld${utils.getIndent(1)}';
- var indentList = '$indentOld${utils.getIndent(2)}';
-
- var changeBuilder = _newDartChangeBuilder();
- await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) {
- builder.addReplacement(range.node(node), (DartEditBuilder builder) {
- builder.write('[');
- builder.write(eol);
- builder.write(indentArg);
- builder.addSimpleLinkedEdit('WIDGET', 'widget');
- builder.write('(');
- builder.write(eol);
- builder.write(indentList);
- // Linked editing not needed since arg is always a list.
- builder.write('children: ');
- builder.write(literalSrc.replaceAll(
- RegExp('^$indentOld', multiLine: true), '$indentList'));
- builder.write(',');
- builder.write(eol);
- builder.write(indentArg);
- builder.write('),');
- builder.write(eol);
- builder.write(indentOld);
- builder.write(']');
- });
- });
- _addAssistFromBuilder(changeBuilder, DartAssistKind.FLUTTER_WRAP_GENERIC);
- }
-
- Future<void> _addProposal_replaceConditionalWithIfElse() async {
- ConditionalExpression conditional;
- // may be on Statement with Conditional
- var statement = node.thisOrAncestorOfType<Statement>();
- if (statement == null) {
- return;
- }
- // variable declaration
- var inVariable = false;
- if (statement is VariableDeclarationStatement) {
- var variableStatement = statement;
- for (var variable in variableStatement.variables.variables) {
- if (variable.initializer is ConditionalExpression) {
- conditional = variable.initializer as ConditionalExpression;
- inVariable = true;
- break;
- }
- }
- }
- // assignment
- var inAssignment = false;
- if (statement is ExpressionStatement) {
- var exprStmt = statement;
- if (exprStmt.expression is AssignmentExpression) {
- var assignment = exprStmt.expression as AssignmentExpression;
- if (assignment.operator.type == TokenType.EQ &&
- assignment.rightHandSide is ConditionalExpression) {
- conditional = assignment.rightHandSide as ConditionalExpression;
- inAssignment = true;
- }
- }
- }
- // return
- var inReturn = false;
- if (statement is ReturnStatement) {
- var returnStatement = statement;
- if (returnStatement.expression is ConditionalExpression) {
- conditional = returnStatement.expression as ConditionalExpression;
- inReturn = true;
- }
- }
- // prepare environment
- var indent = utils.getIndent(1);
- var prefix = utils.getNodePrefix(statement);
-
- if (inVariable || inAssignment || inReturn) {
- var changeBuilder = _newDartChangeBuilder();
- await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) {
- // Type v = Conditional;
- if (inVariable) {
- var variable = conditional.parent as VariableDeclaration;
- builder.addDeletion(range.endEnd(variable.name, conditional));
- var conditionSrc = _getNodeText(conditional.condition);
- var thenSrc = _getNodeText(conditional.thenExpression);
- var elseSrc = _getNodeText(conditional.elseExpression);
- var name = variable.name.name;
- var src = eol;
- src += prefix + 'if ($conditionSrc) {' + eol;
- src += prefix + indent + '$name = $thenSrc;' + eol;
- src += prefix + '} else {' + eol;
- src += prefix + indent + '$name = $elseSrc;' + eol;
- src += prefix + '}';
- builder.addSimpleReplacement(range.endLength(statement, 0), src);
- }
- // v = Conditional;
- if (inAssignment) {
- var assignment = conditional.parent as AssignmentExpression;
- var leftSide = assignment.leftHandSide;
- var conditionSrc = _getNodeText(conditional.condition);
- var thenSrc = _getNodeText(conditional.thenExpression);
- var elseSrc = _getNodeText(conditional.elseExpression);
- var name = _getNodeText(leftSide);
- var src = '';
- src += 'if ($conditionSrc) {' + eol;
- src += prefix + indent + '$name = $thenSrc;' + eol;
- src += prefix + '} else {' + eol;
- src += prefix + indent + '$name = $elseSrc;' + eol;
- src += prefix + '}';
- builder.addSimpleReplacement(range.node(statement), src);
- }
- // return Conditional;
- if (inReturn) {
- var conditionSrc = _getNodeText(conditional.condition);
- var thenSrc = _getNodeText(conditional.thenExpression);
- var elseSrc = _getNodeText(conditional.elseExpression);
- var src = '';
- src += 'if ($conditionSrc) {' + eol;
- src += prefix + indent + 'return $thenSrc;' + eol;
- src += prefix + '} else {' + eol;
- src += prefix + indent + 'return $elseSrc;' + eol;
- src += prefix + '}';
- builder.addSimpleReplacement(range.node(statement), src);
- }
- });
- _addAssistFromBuilder(
- changeBuilder, DartAssistKind.REPLACE_CONDITIONAL_WITH_IF_ELSE);
- }
- }
-
- Future<void> _addProposal_replaceIfElseWithConditional() async {
- // should be "if"
- if (node is! IfStatement) {
- return;
- }
- var ifStatement = node as IfStatement;
- // single then/else statements
- var thenStatement = getSingleStatement(ifStatement.thenStatement);
- var elseStatement = getSingleStatement(ifStatement.elseStatement);
- if (thenStatement == null || elseStatement == null) {
- return;
- }
- Expression thenExpression;
- Expression elseExpression;
- var hasReturnStatements = false;
- if (thenStatement is ReturnStatement && elseStatement is ReturnStatement) {
- hasReturnStatements = true;
- thenExpression = thenStatement.expression;
- elseExpression = elseStatement.expression;
- }
- var hasExpressionStatements = false;
- if (thenStatement is ExpressionStatement &&
- elseStatement is ExpressionStatement) {
- if (thenStatement.expression is AssignmentExpression &&
- elseStatement.expression is AssignmentExpression) {
- hasExpressionStatements = true;
- thenExpression = thenStatement.expression;
- elseExpression = elseStatement.expression;
- }
- }
-
- if (hasReturnStatements || hasExpressionStatements) {
- var changeBuilder = _newDartChangeBuilder();
- await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) {
- // returns
- if (hasReturnStatements) {
- var conditionSrc = _getNodeText(ifStatement.condition);
- var theSrc = _getNodeText(thenExpression);
- var elseSrc = _getNodeText(elseExpression);
- builder.addSimpleReplacement(range.node(ifStatement),
- 'return $conditionSrc ? $theSrc : $elseSrc;');
- }
- // assignments -> v = Conditional;
- if (hasExpressionStatements) {
- AssignmentExpression thenAssignment = thenExpression;
- AssignmentExpression elseAssignment = elseExpression;
- var thenTarget = _getNodeText(thenAssignment.leftHandSide);
- var elseTarget = _getNodeText(elseAssignment.leftHandSide);
- if (thenAssignment.operator.type == TokenType.EQ &&
- elseAssignment.operator.type == TokenType.EQ &&
- thenTarget == elseTarget) {
- var conditionSrc = _getNodeText(ifStatement.condition);
- var theSrc = _getNodeText(thenAssignment.rightHandSide);
- var elseSrc = _getNodeText(elseAssignment.rightHandSide);
- builder.addSimpleReplacement(range.node(ifStatement),
- '$thenTarget = $conditionSrc ? $theSrc : $elseSrc;');
- }
- }
- });
- _addAssistFromBuilder(
- changeBuilder, DartAssistKind.REPLACE_IF_ELSE_WITH_CONDITIONAL);
- }
- }
-
- Future<void> _addProposal_splitVariableDeclaration() async {
- var variableList = node?.thisOrAncestorOfType<VariableDeclarationList>();
-
- // Must be a local variable declaration.
- if (variableList?.parent is! VariableDeclarationStatement) {
- return;
- }
- VariableDeclarationStatement statement = variableList.parent;
-
- // Cannot be `const` or `final`.
- var keywordKind = variableList.keyword?.keyword;
- if (keywordKind == Keyword.CONST || keywordKind == Keyword.FINAL) {
- return;
- }
-
- var variables = variableList.variables;
- if (variables.length != 1) {
- return;
- }
-
- // The caret must be between the type and the variable name.
- var variable = variables[0];
- if (!range.startEnd(statement, variable.name).contains(selectionOffset)) {
- return;
- }
-
- // The variable must have an initializer.
- if (variable.initializer == null) {
- return;
- }
-
- var changeBuilder = _newDartChangeBuilder();
- await changeBuilder.addFileEdit(file, (builder) {
- if (variableList.type == null) {
- final type = variable.declaredElement.type;
- if (!type.isDynamic) {
- builder.addReplacement(range.token(variableList.keyword), (builder) {
- builder.writeType(type);
- });
- }
- }
-
- var indent = utils.getNodePrefix(statement);
- var name = variable.name.name;
- builder.addSimpleInsertion(variable.name.end, ';' + eol + indent + name);
- });
- _addAssistFromBuilder(
- changeBuilder, DartAssistKind.SPLIT_VARIABLE_DECLARATION);
- }
-
- Future<void> _addProposal_surroundWith() async {
- // prepare selected statements
- List<Statement> selectedStatements;
- {
- var selectionAnalyzer = StatementAnalyzer(
- context.resolveResult, SourceRange(selectionOffset, selectionLength));
- selectionAnalyzer.analyze();
- var selectedNodes = selectionAnalyzer.selectedNodes;
- // convert nodes to statements
- selectedStatements = [];
- for (var selectedNode in selectedNodes) {
- if (selectedNode is Statement) {
- selectedStatements.add(selectedNode);
- }
- }
- // we want only statements in blocks
- for (var statement in selectedStatements) {
- if (statement.parent is! Block) {
- return;
- }
- }
- // we want only statements
- if (selectedStatements.isEmpty ||
- selectedStatements.length != selectedNodes.length) {
- return;
- }
- }
- // prepare statement information
- var firstStatement = selectedStatements[0];
- var statementsRange = utils.getLinesRangeStatements(selectedStatements);
- // prepare environment
- var indentOld = utils.getNodePrefix(firstStatement);
- var indentNew = '$indentOld${utils.getIndent(1)}';
- var indentedCode =
- utils.replaceSourceRangeIndent(statementsRange, indentOld, indentNew);
- // "block"
- {
- var changeBuilder = _newDartChangeBuilder();
- await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) {
- builder.addSimpleInsertion(statementsRange.offset, '$indentOld{$eol');
- builder.addSimpleReplacement(
- statementsRange,
- utils.replaceSourceRangeIndent(
- statementsRange, indentOld, indentNew));
- builder.addSimpleInsertion(statementsRange.end, '$indentOld}$eol');
- });
- _addAssistFromBuilder(changeBuilder, DartAssistKind.SURROUND_WITH_BLOCK);
- }
- // "if"
- {
- var changeBuilder = _newDartChangeBuilder();
- await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) {
- builder.addReplacement(statementsRange, (DartEditBuilder builder) {
- builder.write(indentOld);
- builder.write('if (');
- builder.addSimpleLinkedEdit('CONDITION', 'condition');
- builder.write(') {');
- builder.write(eol);
- builder.write(indentedCode);
- builder.write(indentOld);
- builder.write('}');
- builder.selectHere();
- builder.write(eol);
- });
- });
- _addAssistFromBuilder(changeBuilder, DartAssistKind.SURROUND_WITH_IF);
- }
- // "while"
- {
- var changeBuilder = _newDartChangeBuilder();
- await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) {
- builder.addReplacement(statementsRange, (DartEditBuilder builder) {
- builder.write(indentOld);
- builder.write('while (');
- builder.addSimpleLinkedEdit('CONDITION', 'condition');
- builder.write(') {');
- builder.write(eol);
- builder.write(indentedCode);
- builder.write(indentOld);
- builder.write('}');
- builder.selectHere();
- builder.write(eol);
- });
- });
- _addAssistFromBuilder(changeBuilder, DartAssistKind.SURROUND_WITH_WHILE);
- }
- // "for-in"
- {
- var changeBuilder = _newDartChangeBuilder();
- await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) {
- builder.addReplacement(statementsRange, (DartEditBuilder builder) {
- builder.write(indentOld);
- builder.write('for (var ');
- builder.addSimpleLinkedEdit('NAME', 'item');
- builder.write(' in ');
- builder.addSimpleLinkedEdit('ITERABLE', 'iterable');
- builder.write(') {');
- builder.write(eol);
- builder.write(indentedCode);
- builder.write(indentOld);
- builder.write('}');
- builder.selectHere();
- builder.write(eol);
- });
- });
- _addAssistFromBuilder(changeBuilder, DartAssistKind.SURROUND_WITH_FOR_IN);
- }
- // "for"
- {
- var changeBuilder = _newDartChangeBuilder();
- await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) {
- builder.addReplacement(statementsRange, (DartEditBuilder builder) {
- builder.write(indentOld);
- builder.write('for (var ');
- builder.addSimpleLinkedEdit('VAR', 'v');
- builder.write(' = ');
- builder.addSimpleLinkedEdit('INIT', 'init');
- builder.write('; ');
- builder.addSimpleLinkedEdit('CONDITION', 'condition');
- builder.write('; ');
- builder.addSimpleLinkedEdit('INCREMENT', 'increment');
- builder.write(') {');
- builder.write(eol);
- builder.write(indentedCode);
- builder.write(indentOld);
- builder.write('}');
- builder.selectHere();
- builder.write(eol);
- });
- });
- _addAssistFromBuilder(changeBuilder, DartAssistKind.SURROUND_WITH_FOR);
- }
- // "do-while"
- {
- var changeBuilder = _newDartChangeBuilder();
- await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) {
- builder.addReplacement(statementsRange, (DartEditBuilder builder) {
- builder.write(indentOld);
- builder.write('do {');
- builder.write(eol);
- builder.write(indentedCode);
- builder.write(indentOld);
- builder.write('} while (');
- builder.addSimpleLinkedEdit('CONDITION', 'condition');
- builder.write(');');
- builder.selectHere();
- builder.write(eol);
- });
- });
- _addAssistFromBuilder(
- changeBuilder, DartAssistKind.SURROUND_WITH_DO_WHILE);
- }
- // "try-catch"
- {
- var changeBuilder = _newDartChangeBuilder();
- await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) {
- builder.addReplacement(statementsRange, (DartEditBuilder builder) {
- builder.write(indentOld);
- builder.write('try {');
- builder.write(eol);
- builder.write(indentedCode);
- builder.write(indentOld);
- builder.write('} on ');
- builder.addSimpleLinkedEdit('EXCEPTION_TYPE', 'Exception');
- builder.write(' catch (');
- builder.addSimpleLinkedEdit('EXCEPTION_VAR', 'e');
- builder.write(') {');
- builder.write(eol);
- //
- builder.write(indentNew);
- builder.addSimpleLinkedEdit('CATCH', '// TODO');
- builder.selectHere();
- builder.write(eol);
- //
- builder.write(indentOld);
- builder.write('}');
- builder.write(eol);
- });
- });
- _addAssistFromBuilder(
- changeBuilder, DartAssistKind.SURROUND_WITH_TRY_CATCH);
- }
- // "try-finally"
- {
- var changeBuilder = _newDartChangeBuilder();
- await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) {
- builder.addReplacement(statementsRange, (DartEditBuilder builder) {
- builder.write(indentOld);
- builder.write('try {');
- builder.write(eol);
- //
- builder.write(indentedCode);
- //
- builder.write(indentOld);
- builder.write('} finally {');
- builder.write(eol);
- //
- builder.write(indentNew);
- builder.addSimpleLinkedEdit('FINALLY', '// TODO');
- builder.selectHere();
- builder.write(eol);
- //
- builder.write(indentOld);
- builder.write('}');
- builder.write(eol);
- });
- });
- _addAssistFromBuilder(
- changeBuilder, DartAssistKind.SURROUND_WITH_TRY_FINALLY);
- }
-
- // flutter "setState((){ .. });"
- {
- var classDeclaration =
- node.parent.thisOrAncestorOfType<ClassDeclaration>();
- if (classDeclaration != null &&
- flutter.isState(classDeclaration.declaredElement)) {
- final changeBuilder = _newDartChangeBuilder();
- await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) {
- builder.addReplacement(statementsRange, (DartEditBuilder builder) {
- builder.write(indentOld);
- builder.writeln('setState(() {');
- builder.write(indentedCode);
- builder.write(indentOld);
- builder.selectHere();
- builder.writeln('});');
- });
- });
- _addAssistFromBuilder(
- changeBuilder, DartAssistKind.SURROUND_WITH_SET_STATE);
+ for (var multiGenerator in multiGenerators) {
+ var multiProducer = multiGenerator();
+ multiProducer.configure(context);
+ for (var producer in multiProducer.producers) {
+ var builder = DartChangeBuilderImpl.forWorkspace(context.workspace);
+ producer.configure(context);
+ await producer.compute(builder);
+ _addAssistFromBuilder(builder, producer.assistKind,
+ args: producer.assistArguments);
}
}
}
@@ -2334,141 +284,6 @@
return false;
}
- void _convertFlutterChildToChildren(
- NamedExpression namedExp,
- String eol,
- Function getNodeText,
- Function getLinePrefix,
- Function getIndent,
- Function getText,
- DartFileEditBuilder builder) {
- var childArg = namedExp.expression;
- var childLoc = namedExp.offset + 'child'.length;
- builder.addSimpleInsertion(childLoc, 'ren');
- var listLoc = childArg.offset;
- String childArgSrc = getNodeText(childArg);
- if (!childArgSrc.contains(eol)) {
- builder.addSimpleInsertion(listLoc, '[');
- builder.addSimpleInsertion(listLoc + childArg.length, ']');
- } else {
- var newlineLoc = childArgSrc.lastIndexOf(eol);
- if (newlineLoc == childArgSrc.length) {
- newlineLoc -= 1;
- }
- String indentOld = getLinePrefix(childArg.offset + 1 + newlineLoc);
- var indentNew = '$indentOld${getIndent(1)}';
- // The separator includes 'child:' but that has no newlines.
- String separator =
- getText(namedExp.offset, childArg.offset - namedExp.offset);
- var prefix = separator.contains(eol) ? '' : '$eol$indentNew';
- if (prefix.isEmpty) {
- builder.addSimpleInsertion(namedExp.offset + 'child:'.length, ' [');
- var argOffset = childArg.offset;
- builder
- .addDeletion(range.startOffsetEndOffset(argOffset - 2, argOffset));
- } else {
- builder.addSimpleInsertion(listLoc, '[');
- }
- var newChildArgSrc =
- _replaceSourceIndent(childArgSrc, indentOld, indentNew);
- newChildArgSrc = '$prefix$newChildArgSrc,$eol$indentOld]';
- builder.addSimpleReplacement(range.node(childArg), newChildArgSrc);
- }
- }
-
- /// Returns the text of the given node in the unit.
- String _getNodeText(AstNode node) {
- return utils.getNodeText(node);
- }
-
- /// Returns the text of the given range in the unit.
- String _getRangeText(SourceRange range) {
- return utils.getRangeText(range);
- }
-
- DartChangeBuilder _newDartChangeBuilder() {
- return DartChangeBuilderImpl.forWorkspace(context.workspace);
- }
-
- Future<void> _swapParentAndChild(InstanceCreationExpression parent,
- InstanceCreationExpression child, AssistKind kind) async {
- // The child must have its own child.
- if (flutter.findChildArgument(child) == null) {
- return;
- }
-
- var changeBuilder = _newDartChangeBuilder();
- await changeBuilder.addFileEdit(file, (builder) {
- builder.addReplacement(range.node(parent), (builder) {
- var childArgs = child.argumentList;
- var parentArgs = parent.argumentList;
- var childText = _getRangeText(range.startStart(child, childArgs));
- var parentText = _getRangeText(range.startStart(parent, parentArgs));
-
- var parentIndent = utils.getLinePrefix(parent.offset);
- var childIndent = parentIndent + ' ';
-
- // Write the beginning of the child.
- builder.write(childText);
- builder.writeln('(');
-
- // Write all the arguments of the parent.
- // Don't write the "child".
- Expression stableChild;
- for (var argument in childArgs.arguments) {
- if (flutter.isChildArgument(argument)) {
- stableChild = argument;
- } else {
- var text = _getNodeText(argument);
- text = _replaceSourceIndent(text, childIndent, parentIndent);
- builder.write(parentIndent);
- builder.write(' ');
- builder.write(text);
- builder.writeln(',');
- }
- }
-
- // Write the parent as a new child.
- builder.write(parentIndent);
- builder.write(' ');
- builder.write('child: ');
- builder.write(parentText);
- builder.writeln('(');
-
- // Write all arguments of the parent.
- // Don't write its child.
- for (var argument in parentArgs.arguments) {
- if (!flutter.isChildArgument(argument)) {
- var text = _getNodeText(argument);
- text = _replaceSourceIndent(text, parentIndent, childIndent);
- builder.write(childIndent);
- builder.write(' ');
- builder.write(text);
- builder.writeln(',');
- }
- }
-
- // Write the child of the "child" now, as the child of the "parent".
- {
- var text = _getNodeText(stableChild);
- builder.write(childIndent);
- builder.write(' ');
- builder.write(text);
- builder.writeln(',');
- }
-
- // Close the parent expression.
- builder.write(childIndent);
- builder.writeln('),');
-
- // Close the child expression.
- builder.write(parentIndent);
- builder.write(')');
- });
- });
- _addAssistFromBuilder(changeBuilder, kind);
- }
-
/// Create and return a map that can be used to look up the names of the lints
/// for which a given `CorrectionProducer` will be used. This allows us to
/// ensure that we do not also offer the change as an assist when it's already
@@ -2483,31 +298,4 @@
}
return map;
}
-
- static String _replaceSourceIndent(
- String source, String indentOld, String indentNew) {
- return source.replaceAll(RegExp('^$indentOld', multiLine: true), indentNew);
- }
-
- /// Checks if the given [Expression] should be wrapped with parenthesis when
- /// we want to use it as operand of a logical `and` expression.
- static bool _shouldWrapParenthesisBeforeAnd(Expression expr) {
- if (expr is BinaryExpression) {
- var binary = expr;
- var precedence = binary.operator.type.precedence;
- return precedence < TokenClass.LOGICAL_AND_OPERATOR.precedence;
- }
- return false;
- }
-}
-
-class _SimpleIdentifierRecursiveAstVisitor extends RecursiveAstVisitor<void> {
- final _SimpleIdentifierVisitor visitor;
-
- _SimpleIdentifierRecursiveAstVisitor(this.visitor);
-
- @override
- void visitSimpleIdentifier(SimpleIdentifier node) {
- visitor(node);
- }
}
diff --git a/pkg/analysis_server/lib/src/services/correction/base_processor.dart b/pkg/analysis_server/lib/src/services/correction/base_processor.dart
index 2e797a5..0bb0cff 100644
--- a/pkg/analysis_server/lib/src/services/correction/base_processor.dart
+++ b/pkg/analysis_server/lib/src/services/correction/base_processor.dart
@@ -8,10 +8,8 @@
import 'package:analyzer/dart/analysis/session.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/element/type_provider.dart';
-import 'package:analyzer/src/dart/analysis/experiments.dart';
import 'package:analyzer/src/dart/analysis/session_helper.dart';
import 'package:analyzer/src/dart/ast/utilities.dart';
-import 'package:analyzer/src/generated/engine.dart' show AnalysisOptionsImpl;
import 'package:analyzer_plugin/utilities/change_builder/change_workspace.dart';
import 'package:meta/meta.dart';
@@ -47,14 +45,6 @@
selectionEnd = (selectionOffset ?? 0) + (selectionLength ?? 0),
utils = CorrectionUtils(resolvedResult);
- /// Returns the EOL to use for this [CompilationUnit].
- String get eol => utils.endOfLine;
-
- /// Return the status of known experiments.
- ExperimentStatus get experimentStatus =>
- (session.analysisContext.analysisOptions as AnalysisOptionsImpl)
- .experimentStatus;
-
@protected
bool setupCompute() {
final locator = NodeLocator(selectionOffset, selectionEnd);
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/abstract_producer.dart b/pkg/analysis_server/lib/src/services/correction/dart/abstract_producer.dart
index b470bf4..00a5cc6 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/abstract_producer.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/abstract_producer.dart
@@ -4,6 +4,7 @@
import 'dart:math' as math;
+import 'package:_fe_analyzer_shared/src/scanner/token.dart';
import 'package:analysis_server/plugin/edit/fix/fix_dart.dart';
import 'package:analysis_server/src/services/correction/fix/dart/top_level_declarations.dart';
import 'package:analysis_server/src/services/correction/util.dart';
@@ -462,6 +463,24 @@
return false;
}
+ /// Replace all occurrences of the [oldIndent] with the [newIndent] within the
+ /// [source].
+ String replaceSourceIndent(
+ String source, String oldIndent, String newIndent) {
+ return source.replaceAll(RegExp('^$oldIndent', multiLine: true), newIndent);
+ }
+
+ /// Return `true` if the given [expression] should be wrapped with parenthesis
+ /// when we want to use it as operand of a logical `and` expression.
+ bool shouldWrapParenthesisBeforeAnd(Expression expression) {
+ if (expression is BinaryExpression) {
+ var binary = expression;
+ var precedence = binary.operator.type.precedence;
+ return precedence < TokenClass.LOGICAL_AND_OPERATOR.precedence;
+ }
+ return false;
+ }
+
/// Return `true` if the [name] is capitalized.
bool _isNameOfType(String name) {
if (name.isEmpty) {
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/add_late.dart b/pkg/analysis_server/lib/src/services/correction/dart/add_late.dart
new file mode 100644
index 0000000..c1de45b
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/correction/dart/add_late.dart
@@ -0,0 +1,55 @@
+// 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:analysis_server/src/services/correction/dart/abstract_producer.dart';
+import 'package:analysis_server/src/services/correction/fix.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/dart/ast/token.dart';
+import 'package:analyzer_plugin/utilities/change_builder/change_builder_dart.dart';
+import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
+
+class AddLate extends CorrectionProducer {
+ @override
+ FixKind get fixKind => DartFixKind.ADD_LATE;
+
+ @override
+ Future<void> compute(DartChangeBuilder builder) async {
+ if (!libraryElement.isNonNullableByDefault) {
+ return;
+ }
+ if (node is SimpleIdentifier &&
+ node.parent is VariableDeclaration &&
+ node.parent.parent is VariableDeclarationList) {
+ var list = node.parent.parent as VariableDeclarationList;
+ if (!list.isLate) {
+ if (list.type == null) {
+ var keyword = list.keyword;
+ if (keyword == null) {
+ await _insertAt(builder, list.variables[0].offset);
+ // TODO(brianwilkerson) Consider converting this into an assist and
+ // expand it to support converting `var` to `late` as well as
+ // working anywhere a non-late local variable or field is selected.
+// } else if (keyword.type == Keyword.VAR) {
+// builder.addFileEdit(file, (builder) {
+// builder.addSimpleReplacement(range.token(keyword), 'late');
+// });
+ } else if (keyword.type != Keyword.CONST) {
+ await _insertAt(builder, list.variables[0].offset);
+ }
+ } else {
+ await _insertAt(builder, list.type.offset);
+ }
+ }
+ }
+ }
+
+ void _insertAt(DartChangeBuilder builder, int offset) async {
+ await builder.addFileEdit(file, (builder) {
+ builder.addSimpleInsertion(offset, 'late ');
+ });
+ }
+
+ /// Return an instance of this class. Used as a tear-off in `FixProcessor`.
+ static AddLate newInstance() => AddLate();
+}
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/add_not_null_assert.dart b/pkg/analysis_server/lib/src/services/correction/dart/add_not_null_assert.dart
new file mode 100644
index 0000000..05c17eb
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/correction/dart/add_not_null_assert.dart
@@ -0,0 +1,73 @@
+// 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:math';
+
+import 'package:_fe_analyzer_shared/src/scanner/token.dart';
+import 'package:analysis_server/src/services/correction/assist.dart';
+import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer_plugin/utilities/assist/assist.dart';
+import 'package:analyzer_plugin/utilities/change_builder/change_builder_dart.dart';
+
+class AddNotNullAssert extends CorrectionProducer {
+ @override
+ AssistKind get assistKind => DartAssistKind.ADD_NOT_NULL_ASSERT;
+
+ @override
+ Future<void> compute(DartChangeBuilder builder) async {
+ final identifier = node;
+ if (identifier is SimpleIdentifier) {
+ if (identifier.parent is FormalParameter) {
+ final exp = identifier.parent.thisOrAncestorMatching(
+ (node) => node is FunctionExpression || node is MethodDeclaration);
+ var body;
+ if (exp is FunctionExpression) {
+ body = exp.body;
+ } else if (exp is MethodDeclaration) {
+ body = exp.body;
+ }
+ if (body is BlockFunctionBody) {
+ var blockBody = body;
+ // Check for an obvious pre-existing assertion.
+ for (var statement in blockBody.block.statements) {
+ if (statement is AssertStatement) {
+ final condition = statement.condition;
+ if (condition is BinaryExpression) {
+ final leftOperand = condition.leftOperand;
+ if (leftOperand is SimpleIdentifier) {
+ if (leftOperand.staticElement == identifier.staticElement &&
+ condition.operator.type == TokenType.BANG_EQ &&
+ condition.rightOperand is NullLiteral) {
+ return;
+ }
+ }
+ }
+ }
+ }
+
+ await builder.addFileEdit(file, (DartFileEditBuilder builder) {
+ final id = identifier.name;
+ final prefix = utils.getNodePrefix(exp);
+ final indent = utils.getIndent(1);
+ // todo (pq): follow-ups:
+ // 1. if the end token is on the same line as the body
+ // we should add an `eol` before the assert as well.
+ // 2. also, consider asking the block for the list of statements and
+ // adding the statement to the beginning of the list, special casing
+ // when there are no statements (or when there's a single statement
+ // and the whole block is on the same line).
+ var offset = min(utils.getLineNext(blockBody.beginToken.offset),
+ blockBody.endToken.offset);
+ builder.addSimpleInsertion(
+ offset, '$prefix${indent}assert($id != null);$eol');
+ });
+ }
+ }
+ }
+ }
+
+ /// Return an instance of this class. Used as a tear-off in `AssistProcessor`.
+ static AddNotNullAssert newInstance() => AddNotNullAssert();
+}
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/convert_into_for_index.dart b/pkg/analysis_server/lib/src/services/correction/dart/convert_into_for_index.dart
new file mode 100644
index 0000000..8859bcb
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/correction/dart/convert_into_for_index.dart
@@ -0,0 +1,93 @@
+// 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:analysis_server/src/services/correction/assist.dart';
+import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/dart/element/element.dart';
+import 'package:analyzer/dart/element/type.dart';
+import 'package:analyzer_plugin/utilities/assist/assist.dart';
+import 'package:analyzer_plugin/utilities/change_builder/change_builder_dart.dart';
+import 'package:analyzer_plugin/utilities/range_factory.dart';
+
+class ConvertIntoForIndex extends CorrectionProducer {
+ @override
+ AssistKind get assistKind => DartAssistKind.CONVERT_INTO_FOR_INDEX;
+
+ @override
+ Future<void> compute(DartChangeBuilder builder) async {
+ // find enclosing ForEachStatement
+ var forEachStatement = node.thisOrAncestorMatching(
+ (node) => node is ForStatement && node.forLoopParts is ForEachParts)
+ as ForStatement;
+ if (forEachStatement == null) {
+ return;
+ }
+ ForEachParts forEachParts = forEachStatement.forLoopParts;
+ if (selectionOffset < forEachStatement.offset ||
+ forEachStatement.rightParenthesis.end < selectionOffset) {
+ return;
+ }
+ // loop should declare variable
+ var loopVariable = forEachParts is ForEachPartsWithDeclaration
+ ? forEachParts.loopVariable
+ : null;
+ if (loopVariable == null) {
+ return;
+ }
+ // iterable should be VariableElement
+ String listName;
+ var iterable = forEachParts.iterable;
+ if (iterable is SimpleIdentifier &&
+ iterable.staticElement is VariableElement) {
+ listName = iterable.name;
+ } else {
+ return;
+ }
+ // iterable should be List
+ {
+ var iterableType = iterable.staticType;
+ if (iterableType is! InterfaceType ||
+ iterableType.element != typeProvider.listElement) {
+ return;
+ }
+ }
+ // body should be Block
+ if (forEachStatement.body is! Block) {
+ return;
+ }
+ Block body = forEachStatement.body;
+ // prepare a name for the index variable
+ String indexName;
+ {
+ var conflicts =
+ utils.findPossibleLocalVariableConflicts(forEachStatement.offset);
+ if (!conflicts.contains('i')) {
+ indexName = 'i';
+ } else if (!conflicts.contains('j')) {
+ indexName = 'j';
+ } else if (!conflicts.contains('k')) {
+ indexName = 'k';
+ } else {
+ return;
+ }
+ }
+ // prepare environment
+ var prefix = utils.getNodePrefix(forEachStatement);
+ var indent = utils.getIndent(1);
+ var firstBlockLine = utils.getLineContentEnd(body.leftBracket.end);
+ // add change
+ await builder.addFileEdit(file, (DartFileEditBuilder builder) {
+ // TODO(brianwilkerson) Create linked positions for the loop variable.
+ builder.addSimpleReplacement(
+ range.startEnd(forEachStatement, forEachStatement.rightParenthesis),
+ 'for (int $indexName = 0; $indexName < $listName.length; $indexName++)');
+ builder.addSimpleInsertion(firstBlockLine,
+ '$prefix$indent$loopVariable = $listName[$indexName];$eol');
+ });
+ }
+
+ /// Return an instance of this class. Used as a tear-off in `AssistProcessor`.
+ static ConvertIntoForIndex newInstance() => ConvertIntoForIndex();
+}
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/convert_into_is_not.dart b/pkg/analysis_server/lib/src/services/correction/dart/convert_into_is_not.dart
new file mode 100644
index 0000000..8a2fd09
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/correction/dart/convert_into_is_not.dart
@@ -0,0 +1,74 @@
+// 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:analysis_server/src/services/correction/assist.dart';
+import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart';
+import 'package:analysis_server/src/services/correction/util.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/dart/ast/precedence.dart';
+import 'package:analyzer/dart/ast/token.dart';
+import 'package:analyzer_plugin/utilities/assist/assist.dart';
+import 'package:analyzer_plugin/utilities/change_builder/change_builder_dart.dart';
+import 'package:analyzer_plugin/utilities/range_factory.dart';
+
+class ConvertIntoIsNot extends CorrectionProducer {
+ @override
+ AssistKind get assistKind => DartAssistKind.CONVERT_INTO_IS_NOT;
+
+ @override
+ Future<void> compute(DartChangeBuilder builder) async {
+ // Find the is expression
+ var isExpression = node.thisOrAncestorOfType<IsExpression>();
+ if (isExpression == null) {
+ var node = this.node;
+ if (node is PrefixExpression) {
+ var operand = node.operand;
+ if (operand is ParenthesizedExpression &&
+ operand.expression is IsExpression) {
+ isExpression = operand.expression as IsExpression;
+ }
+ } else if (node is ParenthesizedExpression &&
+ node.expression is IsExpression) {
+ isExpression = node.expression as IsExpression;
+ }
+ }
+ if (isExpression == null) {
+ return;
+ }
+ if (isExpression.notOperator != null) {
+ return;
+ }
+ // prepare enclosing ()
+ var parent = isExpression.parent;
+ if (parent is! ParenthesizedExpression) {
+ return;
+ }
+ var parExpression = parent as ParenthesizedExpression;
+ // prepare enclosing !()
+ var parent2 = parent.parent;
+ if (parent2 is! PrefixExpression) {
+ return;
+ }
+ var prefExpression = parent2 as PrefixExpression;
+ if (prefExpression.operator.type != TokenType.BANG) {
+ return;
+ }
+
+ await builder.addFileEdit(file, (DartFileEditBuilder builder) {
+ if (getExpressionParentPrecedence(prefExpression) >=
+ Precedence.relational) {
+ builder.addDeletion(range.token(prefExpression.operator));
+ } else {
+ builder.addDeletion(
+ range.startEnd(prefExpression, parExpression.leftParenthesis));
+ builder.addDeletion(
+ range.startEnd(parExpression.rightParenthesis, prefExpression));
+ }
+ builder.addSimpleInsertion(isExpression.isOperator.end, '!');
+ });
+ }
+
+ /// Return an instance of this class. Used as a tear-off in `AssistProcessor`.
+ static ConvertIntoIsNot newInstance() => ConvertIntoIsNot();
+}
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/convert_into_is_not_empty.dart b/pkg/analysis_server/lib/src/services/correction/dart/convert_into_is_not_empty.dart
new file mode 100644
index 0000000..2b82a89
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/correction/dart/convert_into_is_not_empty.dart
@@ -0,0 +1,70 @@
+// 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:analysis_server/src/services/correction/assist.dart';
+import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart';
+import 'package:analysis_server/src/services/search/hierarchy.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer_plugin/utilities/assist/assist.dart';
+import 'package:analyzer_plugin/utilities/change_builder/change_builder_dart.dart';
+import 'package:analyzer_plugin/utilities/range_factory.dart';
+
+class ConvertIntoIsNotEmpty extends CorrectionProducer {
+ @override
+ AssistKind get assistKind => DartAssistKind.CONVERT_INTO_IS_NOT_EMPTY;
+
+ @override
+ Future<void> compute(DartChangeBuilder builder) async {
+ // prepare "expr.isEmpty"
+ AstNode isEmptyAccess;
+ SimpleIdentifier isEmptyIdentifier;
+ if (node is SimpleIdentifier) {
+ var identifier = node as SimpleIdentifier;
+ var parent = identifier.parent;
+ // normal case (but rare)
+ if (parent is PropertyAccess) {
+ isEmptyIdentifier = parent.propertyName;
+ isEmptyAccess = parent;
+ }
+ // usual case
+ if (parent is PrefixedIdentifier) {
+ isEmptyIdentifier = parent.identifier;
+ isEmptyAccess = parent;
+ }
+ }
+ if (isEmptyIdentifier == null) {
+ return;
+ }
+ // should be "isEmpty"
+ var propertyElement = isEmptyIdentifier.staticElement;
+ if (propertyElement == null || 'isEmpty' != propertyElement.name) {
+ return;
+ }
+ // should have "isNotEmpty"
+ var propertyTarget = propertyElement.enclosingElement;
+ if (propertyTarget == null ||
+ getChildren(propertyTarget, 'isNotEmpty').isEmpty) {
+ return;
+ }
+ // should be in PrefixExpression
+ if (isEmptyAccess.parent is! PrefixExpression) {
+ return;
+ }
+ var prefixExpression = isEmptyAccess.parent as PrefixExpression;
+ // should be !
+ if (prefixExpression.operator.type != TokenType.BANG) {
+ return;
+ }
+
+ await builder.addFileEdit(file, (DartFileEditBuilder builder) {
+ builder.addDeletion(
+ range.startStart(prefixExpression, prefixExpression.operand));
+ builder.addSimpleReplacement(range.node(isEmptyIdentifier), 'isNotEmpty');
+ });
+ }
+
+ /// Return an instance of this class. Used as a tear-off in `AssistProcessor`.
+ static ConvertIntoIsNotEmpty newInstance() => ConvertIntoIsNotEmpty();
+}
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/convert_to_field_parameter.dart b/pkg/analysis_server/lib/src/services/correction/dart/convert_to_field_parameter.dart
new file mode 100644
index 0000000..0223626
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/correction/dart/convert_to_field_parameter.dart
@@ -0,0 +1,116 @@
+// 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:analysis_server/src/services/correction/assist.dart';
+import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/dart/ast/visitor.dart';
+import 'package:analyzer/dart/element/element.dart';
+import 'package:analyzer_plugin/utilities/assist/assist.dart';
+import 'package:analyzer_plugin/utilities/change_builder/change_builder_dart.dart';
+import 'package:analyzer_plugin/utilities/range_factory.dart';
+
+class ConvertToFieldParameter extends CorrectionProducer {
+ @override
+ AssistKind get assistKind => DartAssistKind.CONVERT_TO_FIELD_PARAMETER;
+
+ @override
+ Future<void> compute(DartChangeBuilder builder) async {
+ if (node == null) {
+ return;
+ }
+ // prepare ConstructorDeclaration
+ var constructor = node.thisOrAncestorOfType<ConstructorDeclaration>();
+ if (constructor == null) {
+ return;
+ }
+ var parameterList = constructor.parameters;
+ List<ConstructorInitializer> initializers = constructor.initializers;
+ // prepare parameter
+ SimpleFormalParameter parameter;
+ if (node.parent is SimpleFormalParameter &&
+ node.parent.parent is FormalParameterList &&
+ node.parent.parent.parent is ConstructorDeclaration) {
+ parameter = node.parent;
+ }
+ if (node is SimpleIdentifier &&
+ node.parent is ConstructorFieldInitializer) {
+ var name = (node as SimpleIdentifier).name;
+ ConstructorFieldInitializer initializer = node.parent;
+ if (initializer.expression == node) {
+ for (var formalParameter in parameterList.parameters) {
+ if (formalParameter is SimpleFormalParameter &&
+ formalParameter.identifier.name == name) {
+ parameter = formalParameter;
+ }
+ }
+ }
+ }
+ // analyze parameter
+ if (parameter != null) {
+ var parameterName = parameter.identifier.name;
+ var parameterElement = parameter.declaredElement;
+ // check number of references
+ var visitor = _ReferenceCounter(parameterElement);
+ for (var initializer in initializers) {
+ initializer.accept(visitor);
+ }
+ if (visitor.count != 1) {
+ return;
+ }
+ // find the field initializer
+ ConstructorFieldInitializer parameterInitializer;
+ for (var initializer in initializers) {
+ if (initializer is ConstructorFieldInitializer) {
+ var expression = initializer.expression;
+ if (expression is SimpleIdentifier &&
+ expression.name == parameterName) {
+ parameterInitializer = initializer;
+ }
+ }
+ }
+ if (parameterInitializer == null) {
+ return;
+ }
+ var fieldName = parameterInitializer.fieldName.name;
+
+ await builder.addFileEdit(file, (DartFileEditBuilder builder) {
+ // replace parameter
+ builder.addSimpleReplacement(range.node(parameter), 'this.$fieldName');
+ // remove initializer
+ var initializerIndex = initializers.indexOf(parameterInitializer);
+ if (initializers.length == 1) {
+ builder
+ .addDeletion(range.endEnd(parameterList, parameterInitializer));
+ } else {
+ if (initializerIndex == 0) {
+ var next = initializers[initializerIndex + 1];
+ builder.addDeletion(range.startStart(parameterInitializer, next));
+ } else {
+ var prev = initializers[initializerIndex - 1];
+ builder.addDeletion(range.endEnd(prev, parameterInitializer));
+ }
+ }
+ });
+ }
+ }
+
+ /// Return an instance of this class. Used as a tear-off in `AssistProcessor`.
+ static ConvertToFieldParameter newInstance() => ConvertToFieldParameter();
+}
+
+class _ReferenceCounter extends RecursiveAstVisitor<void> {
+ final ParameterElement parameterElement;
+
+ int count = 0;
+
+ _ReferenceCounter(this.parameterElement);
+
+ @override
+ void visitSimpleIdentifier(SimpleIdentifier node) {
+ if (node.staticElement == parameterElement) {
+ count++;
+ }
+ }
+}
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/convert_to_multiline_string.dart b/pkg/analysis_server/lib/src/services/correction/dart/convert_to_multiline_string.dart
new file mode 100644
index 0000000..bf43b16
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/correction/dart/convert_to_multiline_string.dart
@@ -0,0 +1,44 @@
+// 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:analysis_server/src/services/correction/assist.dart';
+import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/source/source_range.dart';
+import 'package:analyzer_plugin/utilities/assist/assist.dart';
+import 'package:analyzer_plugin/utilities/change_builder/change_builder_dart.dart';
+
+class ConvertToMutilineString extends CorrectionProducer {
+ @override
+ AssistKind get assistKind => DartAssistKind.CONVERT_TO_MULTILINE_STRING;
+
+ @override
+ Future<void> compute(DartChangeBuilder builder) async {
+ var node = this.node;
+ if (node is InterpolationElement) {
+ node = (node as InterpolationElement).parent;
+ }
+ if (node is SingleStringLiteral) {
+ var literal = node;
+ if (!literal.isMultiline) {
+ await builder.addFileEdit(file, (builder) {
+ var newQuote = literal.isSingleQuoted ? "'''" : '"""';
+ builder.addReplacement(
+ SourceRange(literal.offset + (literal.isRaw ? 1 : 0), 1),
+ (builder) {
+ builder.writeln(newQuote);
+ },
+ );
+ builder.addSimpleReplacement(
+ SourceRange(literal.end - 1, 1),
+ newQuote,
+ );
+ });
+ }
+ }
+ }
+
+ /// Return an instance of this class. Used as a tear-off in `AssistProcessor`.
+ static ConvertToMutilineString newInstance() => ConvertToMutilineString();
+}
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/convert_to_normal_parameter.dart b/pkg/analysis_server/lib/src/services/correction/dart/convert_to_normal_parameter.dart
new file mode 100644
index 0000000..d3c13e5
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/correction/dart/convert_to_normal_parameter.dart
@@ -0,0 +1,55 @@
+// 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:analysis_server/src/services/correction/assist.dart';
+import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer_plugin/utilities/assist/assist.dart';
+import 'package:analyzer_plugin/utilities/change_builder/change_builder_dart.dart';
+import 'package:analyzer_plugin/utilities/range_factory.dart';
+
+class ConvertToNormalParameter extends CorrectionProducer {
+ @override
+ AssistKind get assistKind => DartAssistKind.CONVERT_TO_NORMAL_PARAMETER;
+
+ @override
+ Future<void> compute(DartChangeBuilder builder) async {
+ if (node is SimpleIdentifier &&
+ node.parent is FieldFormalParameter &&
+ node.parent.parent is FormalParameterList &&
+ node.parent.parent.parent is ConstructorDeclaration) {
+ ConstructorDeclaration constructor = node.parent.parent.parent;
+ FormalParameterList parameterList = node.parent.parent;
+ FieldFormalParameter parameter = node.parent;
+ var parameterElement = parameter.declaredElement;
+ var name = (node as SimpleIdentifier).name;
+ // prepare type
+ var type = parameterElement.type;
+
+ await builder.addFileEdit(file, (DartFileEditBuilder builder) {
+ // replace parameter
+ if (type.isDynamic) {
+ builder.addSimpleReplacement(range.node(parameter), name);
+ } else {
+ builder.addReplacement(range.node(parameter),
+ (DartEditBuilder builder) {
+ builder.writeType(type);
+ builder.write(' ');
+ builder.write(name);
+ });
+ }
+ // add field initializer
+ List<ConstructorInitializer> initializers = constructor.initializers;
+ if (initializers.isEmpty) {
+ builder.addSimpleInsertion(parameterList.end, ' : $name = $name');
+ } else {
+ builder.addSimpleInsertion(initializers.last.end, ', $name = $name');
+ }
+ });
+ }
+ }
+
+ /// Return an instance of this class. Used as a tear-off in `AssistProcessor`.
+ static ConvertToNormalParameter newInstance() => ConvertToNormalParameter();
+}
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/encapsulate_field.dart b/pkg/analysis_server/lib/src/services/correction/dart/encapsulate_field.dart
new file mode 100644
index 0000000..3c7106d
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/correction/dart/encapsulate_field.dart
@@ -0,0 +1,109 @@
+// 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:analysis_server/src/services/correction/assist.dart';
+import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/dart/element/element.dart';
+import 'package:analyzer_plugin/utilities/assist/assist.dart';
+import 'package:analyzer_plugin/utilities/change_builder/change_builder_dart.dart';
+import 'package:analyzer_plugin/utilities/range_factory.dart';
+
+class EncapsulateField extends CorrectionProducer {
+ @override
+ AssistKind get assistKind => DartAssistKind.ENCAPSULATE_FIELD;
+
+ @override
+ Future<void> compute(DartChangeBuilder builder) async {
+ // find FieldDeclaration
+ var fieldDeclaration = node.thisOrAncestorOfType<FieldDeclaration>();
+ if (fieldDeclaration == null) {
+ return;
+ }
+ // not interesting for static
+ if (fieldDeclaration.isStatic) {
+ return;
+ }
+ // has a parse error
+ var variableList = fieldDeclaration.fields;
+ if (variableList.keyword == null && variableList.type == null) {
+ return;
+ }
+ // not interesting for final
+ if (variableList.isFinal) {
+ return;
+ }
+ // should have exactly one field
+ List<VariableDeclaration> fields = variableList.variables;
+ if (fields.length != 1) {
+ return;
+ }
+ var field = fields.first;
+ var nameNode = field.name;
+ FieldElement fieldElement = nameNode.staticElement;
+ // should have a public name
+ var name = nameNode.name;
+ if (Identifier.isPrivateName(name)) {
+ return;
+ }
+ // should be on the name
+ if (nameNode != node) {
+ return;
+ }
+ await builder.addFileEdit(file, (DartFileEditBuilder builder) {
+ // rename field
+ builder.addSimpleReplacement(range.node(nameNode), '_$name');
+ // update references in constructors
+ ClassOrMixinDeclaration classDeclaration = fieldDeclaration.parent;
+ for (var member in classDeclaration.members) {
+ if (member is ConstructorDeclaration) {
+ for (var parameter in member.parameters.parameters) {
+ var parameterElement = parameter.declaredElement;
+ if (parameterElement is FieldFormalParameterElement &&
+ parameterElement.field == fieldElement) {
+ var identifier = parameter.identifier;
+ builder.addSimpleReplacement(range.node(identifier), '_$name');
+ }
+ }
+ }
+ }
+
+ // Write getter and setter.
+ builder.addInsertion(fieldDeclaration.end, (builder) {
+ String docCode;
+ if (fieldDeclaration.documentationComment != null) {
+ docCode = utils.getNodeText(fieldDeclaration.documentationComment);
+ }
+
+ var typeCode = '';
+ if (variableList.type != null) {
+ typeCode = utils.getNodeText(variableList.type) + ' ';
+ }
+
+ // Write getter.
+ builder.writeln();
+ builder.writeln();
+ if (docCode != null) {
+ builder.write(' ');
+ builder.writeln(docCode);
+ }
+ builder.write(' ${typeCode}get $name => _$name;');
+
+ // Write setter.
+ builder.writeln();
+ builder.writeln();
+ if (docCode != null) {
+ builder.write(' ');
+ builder.writeln(docCode);
+ }
+ builder.writeln(' set $name($typeCode$name) {');
+ builder.writeln(' _$name = $name;');
+ builder.write(' }');
+ });
+ });
+ }
+
+ /// Return an instance of this class. Used as a tear-off in `AssistProcessor`.
+ static EncapsulateField newInstance() => EncapsulateField();
+}
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/flutter_convert_to_children.dart b/pkg/analysis_server/lib/src/services/correction/dart/flutter_convert_to_children.dart
new file mode 100644
index 0000000..08e28d1
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/correction/dart/flutter_convert_to_children.dart
@@ -0,0 +1,86 @@
+// 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:analysis_server/src/services/correction/assist.dart';
+import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer_plugin/utilities/assist/assist.dart';
+import 'package:analyzer_plugin/utilities/change_builder/change_builder_dart.dart';
+import 'package:analyzer_plugin/utilities/range_factory.dart';
+
+class FlutterConvertToChildren extends CorrectionProducer {
+ @override
+ AssistKind get assistKind => DartAssistKind.FLUTTER_CONVERT_TO_CHILDREN;
+
+ @override
+ Future<void> compute(DartChangeBuilder builder) async {
+ // Find "child: widget" under selection.
+ NamedExpression namedExp;
+ {
+ var node = this.node;
+ var parent = node?.parent;
+ var parent2 = parent?.parent;
+ if (node is SimpleIdentifier &&
+ parent is Label &&
+ parent2 is NamedExpression &&
+ node.name == 'child' &&
+ node.staticElement != null &&
+ flutter.isWidgetExpression(parent2.expression)) {
+ namedExp = parent2;
+ } else {
+ return;
+ }
+ }
+
+ await builder.addFileEdit(file, (DartFileEditBuilder builder) {
+ _convertFlutterChildToChildren(namedExp, eol, utils.getNodeText,
+ utils.getLinePrefix, utils.getIndent, utils.getText, builder);
+ });
+ }
+
+ void _convertFlutterChildToChildren(
+ NamedExpression namedExp,
+ String eol,
+ Function getNodeText,
+ Function getLinePrefix,
+ Function getIndent,
+ Function getText,
+ DartFileEditBuilder builder) {
+ var childArg = namedExp.expression;
+ var childLoc = namedExp.offset + 'child'.length;
+ builder.addSimpleInsertion(childLoc, 'ren');
+ var listLoc = childArg.offset;
+ String childArgSrc = getNodeText(childArg);
+ if (!childArgSrc.contains(eol)) {
+ builder.addSimpleInsertion(listLoc, '[');
+ builder.addSimpleInsertion(listLoc + childArg.length, ']');
+ } else {
+ var newlineLoc = childArgSrc.lastIndexOf(eol);
+ if (newlineLoc == childArgSrc.length) {
+ newlineLoc -= 1;
+ }
+ String indentOld = getLinePrefix(childArg.offset + 1 + newlineLoc);
+ var indentNew = '$indentOld${getIndent(1)}';
+ // The separator includes 'child:' but that has no newlines.
+ String separator =
+ getText(namedExp.offset, childArg.offset - namedExp.offset);
+ var prefix = separator.contains(eol) ? '' : '$eol$indentNew';
+ if (prefix.isEmpty) {
+ builder.addSimpleInsertion(namedExp.offset + 'child:'.length, ' [');
+ var argOffset = childArg.offset;
+ builder
+ .addDeletion(range.startOffsetEndOffset(argOffset - 2, argOffset));
+ } else {
+ builder.addSimpleInsertion(listLoc, '[');
+ }
+ var newChildArgSrc =
+ replaceSourceIndent(childArgSrc, indentOld, indentNew);
+ newChildArgSrc = '$prefix$newChildArgSrc,$eol$indentOld]';
+ builder.addSimpleReplacement(range.node(childArg), newChildArgSrc);
+ }
+ }
+
+ /// Return an instance of this class. Used as a tear-off in `AssistProcessor`.
+ static FlutterConvertToChildren newInstance() => FlutterConvertToChildren();
+}
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/flutter_convert_to_stateful_widget.dart b/pkg/analysis_server/lib/src/services/correction/dart/flutter_convert_to_stateful_widget.dart
new file mode 100644
index 0000000..b535b69
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/correction/dart/flutter_convert_to_stateful_widget.dart
@@ -0,0 +1,307 @@
+// 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:analysis_server/src/services/correction/assist.dart';
+import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/dart/ast/visitor.dart';
+import 'package:analyzer/dart/element/element.dart';
+import 'package:analyzer/source/source_range.dart';
+import 'package:analyzer_plugin/protocol/protocol_common.dart' hide Element;
+import 'package:analyzer_plugin/utilities/assist/assist.dart';
+import 'package:analyzer_plugin/utilities/change_builder/change_builder_dart.dart';
+import 'package:analyzer_plugin/utilities/range_factory.dart';
+
+class FlutterConvertToStatefulWidget extends CorrectionProducer {
+ @override
+ AssistKind get assistKind =>
+ DartAssistKind.FLUTTER_CONVERT_TO_STATEFUL_WIDGET;
+
+ @override
+ Future<void> compute(DartChangeBuilder builder) async {
+ var widgetClass = node.thisOrAncestorOfType<ClassDeclaration>();
+ var superclass = widgetClass?.extendsClause?.superclass;
+ if (widgetClass == null || superclass == null) {
+ return;
+ }
+
+ // Don't spam, activate only from the `class` keyword to the class body.
+ if (selectionOffset < widgetClass.classKeyword.offset ||
+ selectionOffset > widgetClass.leftBracket.end) {
+ return;
+ }
+
+ // Find the build() method.
+ MethodDeclaration buildMethod;
+ for (var member in widgetClass.members) {
+ if (member is MethodDeclaration &&
+ member.name.name == 'build' &&
+ member.parameters != null &&
+ member.parameters.parameters.length == 1) {
+ buildMethod = member;
+ break;
+ }
+ }
+ if (buildMethod == null) {
+ return;
+ }
+
+ // Must be a StatelessWidget subclasses.
+ var widgetClassElement = widgetClass.declaredElement;
+ if (!flutter.isExactlyStatelessWidgetType(widgetClassElement.supertype)) {
+ return;
+ }
+
+ var widgetName = widgetClassElement.displayName;
+ var stateName = '_${widgetName}State';
+
+ // Find fields assigned in constructors.
+ var visitor = _FieldFinder();
+ for (var member in widgetClass.members) {
+ if (member is ConstructorDeclaration) {
+ member.accept(visitor);
+ }
+ }
+ var fieldsAssignedInConstructors = visitor.fieldsAssignedInConstructors;
+
+ // Prepare nodes to move.
+ var nodesToMove = <ClassMember>{};
+ var elementsToMove = <Element>{};
+ for (var member in widgetClass.members) {
+ if (member is FieldDeclaration && !member.isStatic) {
+ for (var fieldNode in member.fields.variables) {
+ FieldElement fieldElement = fieldNode.declaredElement;
+ if (!fieldsAssignedInConstructors.contains(fieldElement)) {
+ nodesToMove.add(member);
+ elementsToMove.add(fieldElement);
+ elementsToMove.add(fieldElement.getter);
+ if (fieldElement.setter != null) {
+ elementsToMove.add(fieldElement.setter);
+ }
+ }
+ }
+ }
+ if (member is MethodDeclaration && !member.isStatic) {
+ nodesToMove.add(member);
+ elementsToMove.add(member.declaredElement);
+ }
+ }
+
+ /// Return the code for the [movedNode] which is suitable to be used
+ /// inside the `State` class, so that references to the widget fields and
+ /// methods, that are not moved, are qualified with the corresponding
+ /// instance `widget.`, or static `MyWidgetClass.` qualifier.
+ String rewriteWidgetMemberReferences(AstNode movedNode) {
+ var linesRange = utils.getLinesRange(range.node(movedNode));
+ var text = utils.getRangeText(linesRange);
+
+ // Insert `widget.` before references to the widget instance members.
+ var visitor = _ReplacementEditBuilder(
+ widgetClassElement, elementsToMove, linesRange);
+ movedNode.accept(visitor);
+ return SourceEdit.applySequence(text, visitor.edits.reversed);
+ }
+
+ var statefulWidgetClass = await sessionHelper.getClass(
+ flutter.widgetsUri,
+ 'StatefulWidget',
+ );
+ var stateClass = await sessionHelper.getClass(
+ flutter.widgetsUri,
+ 'State',
+ );
+ if (statefulWidgetClass == null || stateClass == null) {
+ return;
+ }
+
+ await builder.addFileEdit(file, (DartFileEditBuilder builder) {
+ builder.addReplacement(range.node(superclass), (builder) {
+ builder.writeReference(statefulWidgetClass);
+ });
+
+ var replaceOffset = 0;
+ var hasBuildMethod = false;
+
+ var typeParams = '';
+ if (widgetClass.typeParameters != null) {
+ typeParams = utils.getNodeText(widgetClass.typeParameters);
+ }
+
+ /// Replace code between [replaceOffset] and [replaceEnd] with
+ /// `createState()`, empty line, or nothing.
+ void replaceInterval(int replaceEnd,
+ {bool replaceWithEmptyLine = false,
+ bool hasEmptyLineBeforeCreateState = false,
+ bool hasEmptyLineAfterCreateState = true}) {
+ var replaceLength = replaceEnd - replaceOffset;
+ builder.addReplacement(
+ SourceRange(replaceOffset, replaceLength),
+ (builder) {
+ if (hasBuildMethod) {
+ if (hasEmptyLineBeforeCreateState) {
+ builder.writeln();
+ }
+ builder.writeln(' @override');
+ builder.writeln(
+ ' $stateName$typeParams createState() => $stateName$typeParams();');
+ if (hasEmptyLineAfterCreateState) {
+ builder.writeln();
+ }
+ hasBuildMethod = false;
+ } else if (replaceWithEmptyLine) {
+ builder.writeln();
+ }
+ },
+ );
+ replaceOffset = 0;
+ }
+
+ // Remove continuous ranges of lines of nodes being moved.
+ var lastToRemoveIsField = false;
+ var endOfLastNodeToKeep = 0;
+ for (var node in widgetClass.members) {
+ if (nodesToMove.contains(node)) {
+ if (replaceOffset == 0) {
+ var linesRange = utils.getLinesRange(range.node(node));
+ replaceOffset = linesRange.offset;
+ }
+ if (node == buildMethod) {
+ hasBuildMethod = true;
+ }
+ lastToRemoveIsField = node is FieldDeclaration;
+ } else {
+ var linesRange = utils.getLinesRange(range.node(node));
+ endOfLastNodeToKeep = linesRange.end;
+ if (replaceOffset != 0) {
+ replaceInterval(linesRange.offset,
+ replaceWithEmptyLine:
+ lastToRemoveIsField && node is! FieldDeclaration);
+ }
+ }
+ }
+
+ // Remove nodes at the end of the widget class.
+ if (replaceOffset != 0) {
+ // Remove from the last node to keep, so remove empty lines.
+ if (endOfLastNodeToKeep != 0) {
+ replaceOffset = endOfLastNodeToKeep;
+ }
+ replaceInterval(widgetClass.rightBracket.offset,
+ hasEmptyLineBeforeCreateState: endOfLastNodeToKeep != 0,
+ hasEmptyLineAfterCreateState: false);
+ }
+
+ // Create the State subclass.
+ builder.addInsertion(widgetClass.end, (builder) {
+ builder.writeln();
+ builder.writeln();
+
+ builder.write('class $stateName$typeParams extends ');
+ builder.writeReference(stateClass);
+
+ // Write just param names (and not bounds, metadata and docs).
+ builder.write('<${widgetClass.name}');
+ if (widgetClass.typeParameters != null) {
+ builder.write('<');
+ var first = true;
+ for (var param in widgetClass.typeParameters.typeParameters) {
+ if (!first) {
+ builder.write(', ');
+ first = false;
+ }
+ builder.write(param.name.name);
+ }
+ builder.write('>');
+ }
+
+ builder.writeln('> {');
+
+ var writeEmptyLine = false;
+ for (var member in nodesToMove) {
+ if (writeEmptyLine) {
+ builder.writeln();
+ }
+ var text = rewriteWidgetMemberReferences(member);
+ builder.write(text);
+ // Write empty lines between members, but not before the first.
+ writeEmptyLine = true;
+ }
+
+ builder.write('}');
+ });
+ });
+ }
+
+ /// Return an instance of this class. Used as a tear-off in `AssistProcessor`.
+ static FlutterConvertToStatefulWidget newInstance() =>
+ FlutterConvertToStatefulWidget();
+}
+
+class _FieldFinder extends RecursiveAstVisitor<void> {
+ Set<FieldElement> fieldsAssignedInConstructors = {};
+
+ @override
+ void visitSimpleIdentifier(SimpleIdentifier node) {
+ if (node.parent is FieldFormalParameter) {
+ var element = node.staticElement;
+ if (element is FieldFormalParameterElement) {
+ fieldsAssignedInConstructors.add(element.field);
+ }
+ }
+ if (node.parent is ConstructorFieldInitializer) {
+ var element = node.staticElement;
+ if (element is FieldElement) {
+ fieldsAssignedInConstructors.add(element);
+ }
+ }
+ if (node.inSetterContext()) {
+ var element = node.staticElement;
+ if (element is PropertyAccessorElement) {
+ var field = element.variable;
+ if (field is FieldElement) {
+ fieldsAssignedInConstructors.add(field);
+ }
+ }
+ }
+ }
+}
+
+class _ReplacementEditBuilder extends RecursiveAstVisitor<void> {
+ final ClassElement widgetClassElement;
+
+ final Set<Element> elementsToMove;
+
+ final SourceRange linesRange;
+
+ List<SourceEdit> edits = [];
+
+ _ReplacementEditBuilder(
+ this.widgetClassElement, this.elementsToMove, this.linesRange);
+
+ @override
+ void visitSimpleIdentifier(SimpleIdentifier node) {
+ if (node.inDeclarationContext()) {
+ return;
+ }
+ var element = node.staticElement;
+ if (element is ExecutableElement &&
+ element?.enclosingElement == widgetClassElement &&
+ !elementsToMove.contains(element)) {
+ var offset = node.offset - linesRange.offset;
+ var qualifier =
+ element.isStatic ? widgetClassElement.displayName : 'widget';
+
+ var parent = node.parent;
+ if (parent is InterpolationExpression &&
+ parent.leftBracket.type ==
+ TokenType.STRING_INTERPOLATION_IDENTIFIER) {
+ edits.add(SourceEdit(offset, 0, '{$qualifier.'));
+ edits.add(SourceEdit(offset + node.length, 0, '}'));
+ } else {
+ edits.add(SourceEdit(offset, 0, '$qualifier.'));
+ }
+ }
+ }
+}
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/flutter_move_down.dart b/pkg/analysis_server/lib/src/services/correction/dart/flutter_move_down.dart
new file mode 100644
index 0000000..dd16b00
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/correction/dart/flutter_move_down.dart
@@ -0,0 +1,50 @@
+// 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:analysis_server/src/services/correction/assist.dart';
+import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer_plugin/protocol/protocol_common.dart';
+import 'package:analyzer_plugin/utilities/assist/assist.dart';
+import 'package:analyzer_plugin/utilities/change_builder/change_builder_dart.dart';
+import 'package:analyzer_plugin/utilities/range_factory.dart';
+
+class FlutterMoveDown extends CorrectionProducer {
+ @override
+ AssistKind get assistKind => DartAssistKind.FLUTTER_MOVE_DOWN;
+
+ @override
+ Future<void> compute(DartChangeBuilder builder) async {
+ var widget = flutter.identifyWidgetExpression(node);
+ if (widget == null) {
+ return;
+ }
+
+ var parentList = widget.parent;
+ if (parentList is ListLiteral) {
+ List<CollectionElement> parentElements = parentList.elements;
+ var index = parentElements.indexOf(widget);
+ if (index != parentElements.length - 1) {
+ await builder.addFileEdit(file, (fileBuilder) {
+ var nextWidget = parentElements[index + 1];
+ var nextRange = range.node(nextWidget);
+ var nextText = utils.getRangeText(nextRange);
+
+ var widgetRange = range.node(widget);
+ var widgetText = utils.getRangeText(widgetRange);
+
+ fileBuilder.addSimpleReplacement(nextRange, widgetText);
+ fileBuilder.addSimpleReplacement(widgetRange, nextText);
+
+ var lengthDelta = nextRange.length - widgetRange.length;
+ var newWidgetOffset = nextRange.offset + lengthDelta;
+ builder.setSelection(Position(file, newWidgetOffset));
+ });
+ }
+ }
+ }
+
+ /// Return an instance of this class. Used as a tear-off in `AssistProcessor`.
+ static FlutterMoveDown newInstance() => FlutterMoveDown();
+}
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/flutter_move_up.dart b/pkg/analysis_server/lib/src/services/correction/dart/flutter_move_up.dart
new file mode 100644
index 0000000..eabb367
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/correction/dart/flutter_move_up.dart
@@ -0,0 +1,49 @@
+// 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:analysis_server/src/services/correction/assist.dart';
+import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer_plugin/protocol/protocol_common.dart';
+import 'package:analyzer_plugin/utilities/assist/assist.dart';
+import 'package:analyzer_plugin/utilities/change_builder/change_builder_dart.dart';
+import 'package:analyzer_plugin/utilities/range_factory.dart';
+
+class FlutterMoveUp extends CorrectionProducer {
+ @override
+ AssistKind get assistKind => DartAssistKind.FLUTTER_MOVE_UP;
+
+ @override
+ Future<void> compute(DartChangeBuilder builder) async {
+ var widget = flutter.identifyWidgetExpression(node);
+ if (widget == null) {
+ return;
+ }
+
+ var parentList = widget.parent;
+ if (parentList is ListLiteral) {
+ List<CollectionElement> parentElements = parentList.elements;
+ var index = parentElements.indexOf(widget);
+ if (index > 0) {
+ await builder.addFileEdit(file, (fileBuilder) {
+ var previousWidget = parentElements[index - 1];
+ var previousRange = range.node(previousWidget);
+ var previousText = utils.getRangeText(previousRange);
+
+ var widgetRange = range.node(widget);
+ var widgetText = utils.getRangeText(widgetRange);
+
+ fileBuilder.addSimpleReplacement(previousRange, widgetText);
+ fileBuilder.addSimpleReplacement(widgetRange, previousText);
+
+ var newWidgetOffset = previousRange.offset;
+ builder.setSelection(Position(file, newWidgetOffset));
+ });
+ }
+ }
+ }
+
+ /// Return an instance of this class. Used as a tear-off in `AssistProcessor`.
+ static FlutterMoveUp newInstance() => FlutterMoveUp();
+}
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/flutter_remove_widget.dart b/pkg/analysis_server/lib/src/services/correction/dart/flutter_remove_widget.dart
new file mode 100644
index 0000000..4e9ea67
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/correction/dart/flutter_remove_widget.dart
@@ -0,0 +1,79 @@
+// 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:analysis_server/src/services/correction/assist.dart';
+import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer_plugin/utilities/assist/assist.dart';
+import 'package:analyzer_plugin/utilities/change_builder/change_builder_dart.dart';
+import 'package:analyzer_plugin/utilities/range_factory.dart';
+
+class FlutterRemoveWidget extends CorrectionProducer {
+ @override
+ AssistKind get assistKind => DartAssistKind.FLUTTER_REMOVE_WIDGET;
+
+ @override
+ Future<void> compute(DartChangeBuilder builder) async {
+ var widgetCreation = flutter.identifyNewExpression(node);
+ if (widgetCreation == null) {
+ return;
+ }
+
+ // Prepare the list of our children.
+ var childrenArgument = flutter.findChildrenArgument(widgetCreation);
+ if (childrenArgument != null) {
+ var childrenExpression = childrenArgument?.expression;
+ if (childrenExpression is ListLiteral &&
+ childrenExpression.elements.isNotEmpty) {
+ await _removeChildren(
+ builder, widgetCreation, childrenExpression.elements);
+ }
+ } else {
+ var childArgument = flutter.findChildArgument(widgetCreation);
+ if (childArgument != null) {
+ await _removeChild(builder, widgetCreation, childArgument);
+ }
+ }
+ }
+
+ Future<void> _removeChild(
+ DartChangeBuilder builder,
+ InstanceCreationExpression widgetCreation,
+ NamedExpression childArgument) async {
+ // child: ThisWidget(child: ourChild)
+ // children: [foo, ThisWidget(child: ourChild), bar]
+ await builder.addFileEdit(file, (DartFileEditBuilder builder) {
+ var childExpression = childArgument.expression;
+ var childText = utils.getNodeText(childExpression);
+ var indentOld = utils.getLinePrefix(childExpression.offset);
+ var indentNew = utils.getLinePrefix(widgetCreation.offset);
+ childText = replaceSourceIndent(childText, indentOld, indentNew);
+ builder.addSimpleReplacement(range.node(widgetCreation), childText);
+ });
+ }
+
+ Future<void> _removeChildren(
+ DartChangeBuilder builder,
+ InstanceCreationExpression widgetCreation,
+ List<CollectionElement> childrenExpressions) async {
+ // We can inline the list of our children only into another list.
+ var widgetParentNode = widgetCreation.parent;
+ if (childrenExpressions.length > 1 && widgetParentNode is! ListLiteral) {
+ return;
+ }
+
+ await builder.addFileEdit(file, (DartFileEditBuilder builder) {
+ var firstChild = childrenExpressions.first;
+ var lastChild = childrenExpressions.last;
+ var childText = utils.getRangeText(range.startEnd(firstChild, lastChild));
+ var indentOld = utils.getLinePrefix(firstChild.offset);
+ var indentNew = utils.getLinePrefix(widgetCreation.offset);
+ childText = replaceSourceIndent(childText, indentOld, indentNew);
+ builder.addSimpleReplacement(range.node(widgetCreation), childText);
+ });
+ }
+
+ /// Return an instance of this class. Used as a tear-off in `AssistProcessor`.
+ static FlutterRemoveWidget newInstance() => FlutterRemoveWidget();
+}
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/flutter_swap_with_child.dart b/pkg/analysis_server/lib/src/services/correction/dart/flutter_swap_with_child.dart
new file mode 100644
index 0000000..af9c47b
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/correction/dart/flutter_swap_with_child.dart
@@ -0,0 +1,117 @@
+// 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:analysis_server/src/services/correction/assist.dart';
+import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer_plugin/utilities/assist/assist.dart';
+import 'package:analyzer_plugin/utilities/change_builder/change_builder_dart.dart';
+import 'package:analyzer_plugin/utilities/range_factory.dart';
+
+abstract class FlutterParentAndChild extends CorrectionProducer {
+ Future<void> swapParentAndChild(
+ DartChangeBuilder builder,
+ InstanceCreationExpression parent,
+ InstanceCreationExpression child) async {
+ // The child must have its own child.
+ if (flutter.findChildArgument(child) == null) {
+ return;
+ }
+
+ await builder.addFileEdit(file, (builder) {
+ builder.addReplacement(range.node(parent), (builder) {
+ var childArgs = child.argumentList;
+ var parentArgs = parent.argumentList;
+ var childText = utils.getRangeText(range.startStart(child, childArgs));
+ var parentText =
+ utils.getRangeText(range.startStart(parent, parentArgs));
+
+ var parentIndent = utils.getLinePrefix(parent.offset);
+ var childIndent = parentIndent + ' ';
+
+ // Write the beginning of the child.
+ builder.write(childText);
+ builder.writeln('(');
+
+ // Write all the arguments of the parent.
+ // Don't write the "child".
+ Expression stableChild;
+ for (var argument in childArgs.arguments) {
+ if (flutter.isChildArgument(argument)) {
+ stableChild = argument;
+ } else {
+ var text = utils.getNodeText(argument);
+ text = replaceSourceIndent(text, childIndent, parentIndent);
+ builder.write(parentIndent);
+ builder.write(' ');
+ builder.write(text);
+ builder.writeln(',');
+ }
+ }
+
+ // Write the parent as a new child.
+ builder.write(parentIndent);
+ builder.write(' ');
+ builder.write('child: ');
+ builder.write(parentText);
+ builder.writeln('(');
+
+ // Write all arguments of the parent.
+ // Don't write its child.
+ for (var argument in parentArgs.arguments) {
+ if (!flutter.isChildArgument(argument)) {
+ var text = utils.getNodeText(argument);
+ text = replaceSourceIndent(text, parentIndent, childIndent);
+ builder.write(childIndent);
+ builder.write(' ');
+ builder.write(text);
+ builder.writeln(',');
+ }
+ }
+
+ // Write the child of the "child" now, as the child of the "parent".
+ {
+ var text = utils.getNodeText(stableChild);
+ builder.write(childIndent);
+ builder.write(' ');
+ builder.write(text);
+ builder.writeln(',');
+ }
+
+ // Close the parent expression.
+ builder.write(childIndent);
+ builder.writeln('),');
+
+ // Close the child expression.
+ builder.write(parentIndent);
+ builder.write(')');
+ });
+ });
+ }
+}
+
+class FlutterSwapWithChild extends FlutterParentAndChild {
+ @override
+ AssistKind get assistKind => DartAssistKind.FLUTTER_SWAP_WITH_CHILD;
+
+ @override
+ Future<void> compute(DartChangeBuilder builder) async {
+ var parent = flutter.identifyNewExpression(node);
+ if (!flutter.isWidgetCreation(parent)) {
+ return;
+ }
+
+ var childArgument = flutter.findChildArgument(parent);
+ if (childArgument?.expression is! InstanceCreationExpression ||
+ !flutter.isWidgetCreation(childArgument.expression)) {
+ return;
+ }
+ InstanceCreationExpression child = childArgument.expression;
+
+ await swapParentAndChild(builder, parent, child);
+ }
+
+ /// Return an instance of this class. Used as a tear-off in `AssistProcessor`.
+ static FlutterSwapWithChild newInstance() => FlutterSwapWithChild();
+}
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/flutter_swap_with_parent.dart b/pkg/analysis_server/lib/src/services/correction/dart/flutter_swap_with_parent.dart
new file mode 100644
index 0000000..e385a15
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/correction/dart/flutter_swap_with_parent.dart
@@ -0,0 +1,33 @@
+// 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:analysis_server/src/services/correction/assist.dart';
+import 'package:analysis_server/src/services/correction/dart/flutter_swap_with_child.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer_plugin/utilities/assist/assist.dart';
+import 'package:analyzer_plugin/utilities/change_builder/change_builder_dart.dart';
+
+class FlutterSwapWithParent extends FlutterParentAndChild {
+ @override
+ AssistKind get assistKind => DartAssistKind.FLUTTER_SWAP_WITH_PARENT;
+
+ @override
+ Future<void> compute(DartChangeBuilder builder) async {
+ var child = flutter.identifyNewExpression(node);
+ if (!flutter.isWidgetCreation(child)) {
+ return;
+ }
+
+ // NamedExpression (child:), ArgumentList, InstanceCreationExpression
+ var expr = child.parent?.parent?.parent;
+ if (expr is! InstanceCreationExpression) {
+ return;
+ }
+
+ await swapParentAndChild(builder, expr, child);
+ }
+
+ /// Return an instance of this class. Used as a tear-off in `AssistProcessor`.
+ static FlutterSwapWithParent newInstance() => FlutterSwapWithParent();
+}
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/flutter_wrap.dart b/pkg/analysis_server/lib/src/services/correction/dart/flutter_wrap.dart
new file mode 100644
index 0000000..0c077db
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/correction/dart/flutter_wrap.dart
@@ -0,0 +1,273 @@
+// 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:analysis_server/src/services/correction/assist.dart';
+import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart';
+import 'package:analysis_server/src/services/correction/selection_analyzer.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/dart/element/element.dart';
+import 'package:analyzer/source/source_range.dart';
+import 'package:analyzer_plugin/utilities/assist/assist.dart';
+import 'package:analyzer_plugin/utilities/change_builder/change_builder_dart.dart';
+import 'package:analyzer_plugin/utilities/range_factory.dart';
+
+class FlutterWrap extends MultiCorrectionProducer {
+ @override
+ Iterable<CorrectionProducer> get producers sync* {
+ var widgetExpr = flutter.identifyWidgetExpression(node);
+ if (widgetExpr != null) {
+ var widgetType = widgetExpr.staticType;
+ yield _FlutterWrapGeneric(widgetExpr);
+ if (!flutter.isExactWidgetTypeCenter(widgetType)) {
+ yield _FlutterWrapCenter(widgetExpr);
+ }
+ if (!flutter.isExactWidgetTypeContainer(widgetType)) {
+ yield _FlutterWrapContainer(widgetExpr);
+ }
+ if (!flutter.isExactWidgetTypePadding(widgetType)) {
+ yield _FlutterWrapPadding(widgetExpr);
+ }
+ }
+ yield* _wrapMultipleWidgets();
+ }
+
+ Iterable<CorrectionProducer> _wrapMultipleWidgets() sync* {
+ var selectionRange = SourceRange(selectionOffset, selectionLength);
+ var analyzer = SelectionAnalyzer(selectionRange);
+ resolvedResult.unit.accept(analyzer);
+
+ var widgetExpressions = <Expression>[];
+ if (analyzer.hasSelectedNodes) {
+ for (var selectedNode in analyzer.selectedNodes) {
+ if (!flutter.isWidgetExpression(selectedNode)) {
+ return;
+ }
+ widgetExpressions.add(selectedNode);
+ }
+ } else {
+ var widget = flutter.identifyWidgetExpression(analyzer.coveringNode);
+ if (widget != null) {
+ widgetExpressions.add(widget);
+ }
+ }
+ if (widgetExpressions.isEmpty) {
+ return;
+ }
+
+ var firstWidget = widgetExpressions.first;
+ var lastWidget = widgetExpressions.last;
+ yield _FlutterWrapColumn(firstWidget, lastWidget);
+ yield _FlutterWrapRow(firstWidget, lastWidget);
+ }
+
+ /// Return an instance of this class. Used as a tear-off in `AssistProcessor`.
+ static FlutterWrap newInstance() => FlutterWrap();
+}
+
+/// A correction processor that can make one of the possible change computed by
+/// the [FlutterWrap] producer.
+class _FlutterWrapCenter extends _WrapSingleWidget {
+ _FlutterWrapCenter(Expression widgetExpr) : super(widgetExpr);
+
+ @override
+ AssistKind get assistKind => DartAssistKind.FLUTTER_WRAP_CENTER;
+
+ @override
+ String get _parentClassName => 'Center';
+
+ @override
+ String get _parentLibraryUri => flutter.widgetsUri;
+}
+
+/// A correction processor that can make one of the possible change computed by
+/// the [FlutterWrap] producer.
+class _FlutterWrapColumn extends _WrapMultipleWidgets {
+ _FlutterWrapColumn(Expression firstWidget, Expression lastWidget)
+ : super(firstWidget, lastWidget);
+
+ @override
+ AssistKind get assistKind => DartAssistKind.FLUTTER_WRAP_COLUMN;
+
+ @override
+ String get _parentClassName => 'Column';
+}
+
+/// A correction processor that can make one of the possible change computed by
+/// the [FlutterWrap] producer.
+class _FlutterWrapContainer extends _WrapSingleWidget {
+ _FlutterWrapContainer(Expression widgetExpr) : super(widgetExpr);
+
+ @override
+ AssistKind get assistKind => DartAssistKind.FLUTTER_WRAP_CONTAINER;
+
+ @override
+ String get _parentClassName => 'Container';
+
+ @override
+ String get _parentLibraryUri => flutter.widgetsUri;
+}
+
+/// A correction processor that can make one of the possible change computed by
+/// the [FlutterWrap] producer.
+class _FlutterWrapGeneric extends _WrapSingleWidget {
+ _FlutterWrapGeneric(Expression widgetExpr) : super(widgetExpr);
+
+ @override
+ AssistKind get assistKind => DartAssistKind.FLUTTER_WRAP_GENERIC;
+}
+
+/// A correction processor that can make one of the possible change computed by
+/// the [FlutterWrap] producer.
+class _FlutterWrapPadding extends _WrapSingleWidget {
+ _FlutterWrapPadding(Expression widgetExpr) : super(widgetExpr);
+
+ @override
+ AssistKind get assistKind => DartAssistKind.FLUTTER_WRAP_PADDING;
+
+ @override
+ List<String> get _leadingLines => ['padding: const EdgeInsets.all(8.0),'];
+
+ @override
+ String get _parentClassName => 'Padding';
+
+ @override
+ String get _parentLibraryUri => flutter.widgetsUri;
+}
+
+/// A correction processor that can make one of the possible change computed by
+/// the [FlutterWrap] producer.
+class _FlutterWrapRow extends _WrapMultipleWidgets {
+ _FlutterWrapRow(Expression firstWidget, Expression lastWidget)
+ : super(firstWidget, lastWidget);
+
+ @override
+ AssistKind get assistKind => DartAssistKind.FLUTTER_WRAP_ROW;
+
+ @override
+ String get _parentClassName => 'Row';
+}
+
+/// A correction processor that can make one of the possible change computed by
+/// the [FlutterWrap] producer.
+abstract class _WrapMultipleWidgets extends CorrectionProducer {
+ final Expression firstWidget;
+
+ final Expression lastWidget;
+
+ _WrapMultipleWidgets(this.firstWidget, this.lastWidget);
+
+ String get _parentClassName;
+
+ String get _parentLibraryUri => flutter.widgetsUri;
+
+ @override
+ Future<void> compute(DartChangeBuilder builder) async {
+ var selectedRange = range.startEnd(firstWidget, lastWidget);
+ var src = utils.getRangeText(selectedRange);
+ var parentClassElement =
+ await sessionHelper.getClass(_parentLibraryUri, _parentClassName);
+ var widgetClassElement =
+ await sessionHelper.getClass(flutter.widgetsUri, 'Widget');
+ if (parentClassElement == null || widgetClassElement == null) {
+ return;
+ }
+
+ await builder.addFileEdit(file, (DartFileEditBuilder builder) {
+ builder.addReplacement(selectedRange, (DartEditBuilder builder) {
+ builder.writeReference(parentClassElement);
+ builder.write('(');
+
+ var indentOld = utils.getLinePrefix(firstWidget.offset);
+ var indentNew1 = indentOld + utils.getIndent(1);
+ var indentNew2 = indentOld + utils.getIndent(2);
+
+ builder.write(eol);
+ builder.write(indentNew1);
+ builder.write('children: [');
+ builder.write(eol);
+
+ var newSrc = replaceSourceIndent(src, indentOld, indentNew2);
+ builder.write(indentNew2);
+ builder.write(newSrc);
+
+ builder.write(',');
+ builder.write(eol);
+
+ builder.write(indentNew1);
+ builder.write('],');
+ builder.write(eol);
+
+ builder.write(indentOld);
+ builder.write(')');
+ });
+ });
+ }
+}
+
+/// A correction processor that can make one of the possible change computed by
+/// the [FlutterWrap] producer.
+abstract class _WrapSingleWidget extends CorrectionProducer {
+ final Expression widgetExpr;
+
+ _WrapSingleWidget(this.widgetExpr);
+
+ List<String> get _leadingLines => const [];
+
+ String get _parentClassName => null;
+
+ String get _parentLibraryUri => null;
+
+ @override
+ Future<void> compute(DartChangeBuilder builder) async {
+ var widgetSrc = utils.getNodeText(widgetExpr);
+
+ // If the wrapper class is specified, find its element.
+ var parentLibraryUri = _parentLibraryUri;
+ var parentClassName = _parentClassName;
+ ClassElement parentClassElement;
+ if (parentLibraryUri != null && parentClassName != null) {
+ parentClassElement =
+ await sessionHelper.getClass(parentLibraryUri, parentClassName);
+ if (parentClassElement == null) {
+ return;
+ }
+ }
+
+ await builder.addFileEdit(file, (DartFileEditBuilder builder) {
+ builder.addReplacement(range.node(widgetExpr), (DartEditBuilder builder) {
+ if (parentClassElement == null) {
+ builder.addSimpleLinkedEdit('WIDGET', 'widget');
+ } else {
+ builder.writeReference(parentClassElement);
+ }
+ builder.write('(');
+ var leadingLines = _leadingLines;
+ if (widgetSrc.contains(eol) || leadingLines.isNotEmpty) {
+ var indentOld = utils.getLinePrefix(widgetExpr.offset);
+ var indentNew = '$indentOld${utils.getIndent(1)}';
+
+ for (var leadingLine in leadingLines) {
+ builder.write(eol);
+ builder.write(indentNew);
+ builder.write(leadingLine);
+ }
+
+ builder.write(eol);
+ builder.write(indentNew);
+ widgetSrc = widgetSrc.replaceAll(
+ RegExp('^$indentOld', multiLine: true), indentNew);
+ widgetSrc += ',$eol$indentOld';
+ }
+ if (parentClassElement == null) {
+ builder.addSimpleLinkedEdit('CHILD', 'child');
+ } else {
+ builder.write('child');
+ }
+ builder.write(': ');
+ builder.write(widgetSrc);
+ builder.write(')');
+ });
+ });
+ }
+}
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/flutter_wrap_generic.dart b/pkg/analysis_server/lib/src/services/correction/dart/flutter_wrap_generic.dart
new file mode 100644
index 0000000..3eaec41
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/correction/dart/flutter_wrap_generic.dart
@@ -0,0 +1,61 @@
+// 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:analysis_server/src/services/correction/assist.dart';
+import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer_plugin/utilities/assist/assist.dart';
+import 'package:analyzer_plugin/utilities/change_builder/change_builder_dart.dart';
+import 'package:analyzer_plugin/utilities/range_factory.dart';
+
+class FlutterWrapGeneric extends CorrectionProducer {
+ @override
+ AssistKind get assistKind => DartAssistKind.FLUTTER_WRAP_GENERIC;
+
+ @override
+ Future<void> compute(DartChangeBuilder builder) async {
+ if (node is! ListLiteral) {
+ return;
+ }
+ if ((node as ListLiteral).elements.any((CollectionElement exp) =>
+ !(exp is InstanceCreationExpression &&
+ flutter.isWidgetCreation(exp)))) {
+ return;
+ }
+ var literalSrc = utils.getNodeText(node);
+ var newlineIdx = literalSrc.lastIndexOf(eol);
+ if (newlineIdx < 0 || newlineIdx == literalSrc.length - 1) {
+ return; // Lists need to be in multi-line format already.
+ }
+ var indentOld = utils.getLinePrefix(node.offset + 1 + newlineIdx);
+ var indentArg = '$indentOld${utils.getIndent(1)}';
+ var indentList = '$indentOld${utils.getIndent(2)}';
+
+ await builder.addFileEdit(file, (DartFileEditBuilder builder) {
+ builder.addReplacement(range.node(node), (DartEditBuilder builder) {
+ builder.write('[');
+ builder.write(eol);
+ builder.write(indentArg);
+ builder.addSimpleLinkedEdit('WIDGET', 'widget');
+ builder.write('(');
+ builder.write(eol);
+ builder.write(indentList);
+ // Linked editing not needed since arg is always a list.
+ builder.write('children: ');
+ builder.write(literalSrc.replaceAll(
+ RegExp('^$indentOld', multiLine: true), '$indentList'));
+ builder.write(',');
+ builder.write(eol);
+ builder.write(indentArg);
+ builder.write('),');
+ builder.write(eol);
+ builder.write(indentOld);
+ builder.write(']');
+ });
+ });
+ }
+
+ /// Return an instance of this class. Used as a tear-off in `AssistProcessor`.
+ static FlutterWrapGeneric newInstance() => FlutterWrapGeneric();
+}
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/flutter_wrap_stream_builder.dart b/pkg/analysis_server/lib/src/services/correction/dart/flutter_wrap_stream_builder.dart
new file mode 100644
index 0000000..aad39af
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/correction/dart/flutter_wrap_stream_builder.dart
@@ -0,0 +1,71 @@
+// 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:analysis_server/src/services/correction/assist.dart';
+import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart';
+import 'package:analyzer_plugin/utilities/assist/assist.dart';
+import 'package:analyzer_plugin/utilities/change_builder/change_builder_dart.dart';
+import 'package:analyzer_plugin/utilities/range_factory.dart';
+
+class FlutterWrapStreamBuilder extends CorrectionProducer {
+ @override
+ AssistKind get assistKind => DartAssistKind.FLUTTER_WRAP_STREAM_BUILDER;
+
+ @override
+ Future<void> compute(DartChangeBuilder builder) async {
+ var widgetExpr = flutter.identifyWidgetExpression(node);
+ if (widgetExpr == null) {
+ return;
+ }
+ if (flutter.isExactWidgetTypeStreamBuilder(widgetExpr.staticType)) {
+ return;
+ }
+ var widgetSrc = utils.getNodeText(widgetExpr);
+
+ var streamBuilderElement = await sessionHelper.getClass(
+ flutter.widgetsUri,
+ 'StreamBuilder',
+ );
+ if (streamBuilderElement == null) {
+ return;
+ }
+
+ await builder.addFileEdit(file, (builder) {
+ builder.addReplacement(range.node(widgetExpr), (builder) {
+ builder.writeReference(streamBuilderElement);
+
+ builder.write('<');
+ builder.addSimpleLinkedEdit('type', 'Object');
+ builder.writeln('>(');
+
+ var indentOld = utils.getLinePrefix(widgetExpr.offset);
+ var indentNew1 = indentOld + utils.getIndent(1);
+ var indentNew2 = indentOld + utils.getIndent(2);
+
+ builder.write(indentNew1);
+ builder.writeln('stream: null,');
+
+ builder.write(indentNew1);
+ builder.writeln('builder: (context, snapshot) {');
+
+ widgetSrc = widgetSrc.replaceAll(
+ RegExp('^$indentOld', multiLine: true),
+ indentNew2,
+ );
+ builder.write(indentNew2);
+ builder.write('return $widgetSrc');
+ builder.writeln(';');
+
+ builder.write(indentNew1);
+ builder.writeln('}');
+
+ builder.write(indentOld);
+ builder.write(')');
+ });
+ });
+ }
+
+ /// Return an instance of this class. Used as a tear-off in `AssistProcessor`.
+ static FlutterWrapStreamBuilder newInstance() => FlutterWrapStreamBuilder();
+}
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/import_add_show.dart b/pkg/analysis_server/lib/src/services/correction/dart/import_add_show.dart
new file mode 100644
index 0000000..c820844
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/correction/dart/import_add_show.dart
@@ -0,0 +1,72 @@
+// 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:collection';
+
+import 'package:analysis_server/src/services/correction/assist.dart';
+import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart';
+import 'package:analysis_server/src/services/correction/util.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/dart/ast/visitor.dart';
+import 'package:analyzer/dart/element/element.dart';
+import 'package:analyzer_plugin/utilities/assist/assist.dart';
+import 'package:analyzer_plugin/utilities/change_builder/change_builder_dart.dart';
+
+class ImportAddShow extends CorrectionProducer {
+ @override
+ AssistKind get assistKind => DartAssistKind.IMPORT_ADD_SHOW;
+
+ @override
+ Future<void> compute(DartChangeBuilder builder) async {
+ // prepare ImportDirective
+ var importDirective = node.thisOrAncestorOfType<ImportDirective>();
+ if (importDirective == null) {
+ return;
+ }
+ // there should be no existing combinators
+ if (importDirective.combinators.isNotEmpty) {
+ return;
+ }
+ // prepare whole import namespace
+ ImportElement importElement = importDirective.element;
+ if (importElement == null) {
+ return;
+ }
+ var namespace = getImportNamespace(importElement);
+ // prepare names of referenced elements (from this import)
+ var visitor = _ReferenceFinder(namespace);
+ resolvedResult.unit.accept(visitor);
+ var referencedNames = visitor.referencedNames;
+ // ignore if unused
+ if (referencedNames.isEmpty) {
+ return;
+ }
+ await builder.addFileEdit(file, (DartFileEditBuilder builder) {
+ var showCombinator = ' show ${referencedNames.join(', ')}';
+ builder.addSimpleInsertion(importDirective.end - 1, showCombinator);
+ });
+ }
+
+ /// Return an instance of this class. Used as a tear-off in `AssistProcessor`.
+ static ImportAddShow newInstance() => ImportAddShow();
+}
+
+class _ReferenceFinder extends RecursiveAstVisitor<void> {
+ final Map<String, Element> namespace;
+
+ Set<String> referencedNames = SplayTreeSet<String>();
+
+ _ReferenceFinder(this.namespace);
+
+ @override
+ void visitSimpleIdentifier(SimpleIdentifier node) {
+ var element = node.staticElement;
+ if (element != null &&
+ (namespace[node.name] == element ||
+ (node.name != element.name &&
+ namespace[element.name] == element))) {
+ referencedNames.add(element.displayName);
+ }
+ }
+}
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/introduce_local_cast_type.dart b/pkg/analysis_server/lib/src/services/correction/dart/introduce_local_cast_type.dart
new file mode 100644
index 0000000..30bf94a
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/correction/dart/introduce_local_cast_type.dart
@@ -0,0 +1,87 @@
+// 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:analysis_server/src/services/correction/assist.dart';
+import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart';
+import 'package:analysis_server/src/services/correction/name_suggestion.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/src/dart/ast/utilities.dart';
+import 'package:analyzer_plugin/protocol/protocol_common.dart';
+import 'package:analyzer_plugin/utilities/assist/assist.dart';
+import 'package:analyzer_plugin/utilities/change_builder/change_builder_dart.dart';
+
+class IntroduceLocalCastType extends CorrectionProducer {
+ @override
+ AssistKind get assistKind => DartAssistKind.INTRODUCE_LOCAL_CAST_TYPE;
+
+ @override
+ Future<void> compute(DartChangeBuilder builder) async {
+ var node = this.node;
+ if (node is IfStatement) {
+ node = (node as IfStatement).condition;
+ } else if (node is WhileStatement) {
+ node = (node as WhileStatement).condition;
+ }
+ // prepare IsExpression
+ if (node is! IsExpression) {
+ return;
+ }
+ IsExpression isExpression = node;
+ var castType = isExpression.type.type;
+ var castTypeCode = utils.getNodeText(isExpression.type);
+ // prepare environment
+ var indent = utils.getIndent(1);
+ String prefix;
+ Block targetBlock;
+ {
+ var statement = node.thisOrAncestorOfType<Statement>();
+ if (statement is IfStatement && statement.thenStatement is Block) {
+ targetBlock = statement.thenStatement;
+ } else if (statement is WhileStatement && statement.body is Block) {
+ targetBlock = statement.body;
+ } else {
+ return;
+ }
+ prefix = utils.getNodePrefix(statement);
+ }
+ // prepare location
+ int offset;
+ String statementPrefix;
+ if (isExpression.notOperator == null) {
+ offset = targetBlock.leftBracket.end;
+ statementPrefix = indent;
+ } else {
+ offset = targetBlock.rightBracket.end;
+ statementPrefix = '';
+ }
+ // prepare excluded names
+ var excluded = <String>{};
+ var scopedNameFinder = ScopedNameFinder(offset);
+ isExpression.accept(scopedNameFinder);
+ excluded.addAll(scopedNameFinder.locals.keys.toSet());
+ // name(s)
+ var suggestions =
+ getVariableNameSuggestionsForExpression(castType, null, excluded);
+
+ if (suggestions.isNotEmpty) {
+ await builder.addFileEdit(file, (DartFileEditBuilder builder) {
+ builder.addInsertion(offset, (DartEditBuilder builder) {
+ builder.write(eol + prefix + statementPrefix);
+ builder.write(castTypeCode);
+ builder.write(' ');
+ builder.addSimpleLinkedEdit('NAME', suggestions[0],
+ kind: LinkedEditSuggestionKind.VARIABLE,
+ suggestions: suggestions);
+ builder.write(' = ');
+ builder.write(utils.getNodeText(isExpression.expression));
+ builder.write(';');
+ builder.selectHere();
+ });
+ });
+ }
+ }
+
+ /// Return an instance of this class. Used as a tear-off in `AssistProcessor`.
+ static IntroduceLocalCastType newInstance() => IntroduceLocalCastType();
+}
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/invert_if_statement.dart b/pkg/analysis_server/lib/src/services/correction/dart/invert_if_statement.dart
new file mode 100644
index 0000000..b632c3d
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/correction/dart/invert_if_statement.dart
@@ -0,0 +1,43 @@
+// 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:analysis_server/src/services/correction/assist.dart';
+import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer_plugin/utilities/assist/assist.dart';
+import 'package:analyzer_plugin/utilities/change_builder/change_builder_dart.dart';
+import 'package:analyzer_plugin/utilities/range_factory.dart';
+
+class InvertIfStatement extends CorrectionProducer {
+ @override
+ AssistKind get assistKind => DartAssistKind.INVERT_IF_STATEMENT;
+
+ @override
+ Future<void> compute(DartChangeBuilder builder) async {
+ if (node is! IfStatement) {
+ return;
+ }
+ var ifStatement = node as IfStatement;
+ var condition = ifStatement.condition;
+ // should have both "then" and "else"
+ var thenStatement = ifStatement.thenStatement;
+ var elseStatement = ifStatement.elseStatement;
+ if (thenStatement == null || elseStatement == null) {
+ return;
+ }
+ // prepare source
+ var invertedCondition = utils.invertCondition(condition);
+ var thenSource = utils.getNodeText(thenStatement);
+ var elseSource = utils.getNodeText(elseStatement);
+
+ await builder.addFileEdit(file, (DartFileEditBuilder builder) {
+ builder.addSimpleReplacement(range.node(condition), invertedCondition);
+ builder.addSimpleReplacement(range.node(thenStatement), elseSource);
+ builder.addSimpleReplacement(range.node(elseStatement), thenSource);
+ });
+ }
+
+ /// Return an instance of this class. Used as a tear-off in `AssistProcessor`.
+ static InvertIfStatement newInstance() => InvertIfStatement();
+}
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/join_if_with_inner.dart b/pkg/analysis_server/lib/src/services/correction/dart/join_if_with_inner.dart
new file mode 100644
index 0000000..82f33e7
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/correction/dart/join_if_with_inner.dart
@@ -0,0 +1,71 @@
+// 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:analysis_server/src/services/correction/assist.dart';
+import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart';
+import 'package:analysis_server/src/services/correction/util.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer_plugin/utilities/assist/assist.dart';
+import 'package:analyzer_plugin/utilities/change_builder/change_builder_dart.dart';
+import 'package:analyzer_plugin/utilities/range_factory.dart';
+
+class JoinIfWithInner extends CorrectionProducer {
+ @override
+ AssistKind get assistKind => DartAssistKind.JOIN_IF_WITH_INNER;
+
+ @override
+ Future<void> compute(DartChangeBuilder builder) async {
+ // climb up condition to the (supposedly) "if" statement
+ var node = this.node;
+ while (node is Expression) {
+ node = node.parent;
+ }
+ // prepare target "if" statement
+ if (node is! IfStatement) {
+ return;
+ }
+ var targetIfStatement = node as IfStatement;
+ if (targetIfStatement.elseStatement != null) {
+ return;
+ }
+ // prepare inner "if" statement
+ var targetThenStatement = targetIfStatement.thenStatement;
+ var innerStatement = getSingleStatement(targetThenStatement);
+ if (innerStatement is! IfStatement) {
+ return;
+ }
+ var innerIfStatement = innerStatement as IfStatement;
+ if (innerIfStatement.elseStatement != null) {
+ return;
+ }
+ // prepare environment
+ var prefix = utils.getNodePrefix(targetIfStatement);
+ // merge conditions
+ var targetCondition = targetIfStatement.condition;
+ var innerCondition = innerIfStatement.condition;
+ var targetConditionSource = utils.getNodeText(targetCondition);
+ var innerConditionSource = utils.getNodeText(innerCondition);
+ if (shouldWrapParenthesisBeforeAnd(targetCondition)) {
+ targetConditionSource = '($targetConditionSource)';
+ }
+ if (shouldWrapParenthesisBeforeAnd(innerCondition)) {
+ innerConditionSource = '($innerConditionSource)';
+ }
+ var condition = '$targetConditionSource && $innerConditionSource';
+ // replace target "if" statement
+ var innerThenStatement = innerIfStatement.thenStatement;
+ var innerThenStatements = getStatements(innerThenStatement);
+ var lineRanges = utils.getLinesRangeStatements(innerThenStatements);
+ var oldSource = utils.getRangeText(lineRanges);
+ var newSource = utils.indentSourceLeftRight(oldSource);
+
+ await builder.addFileEdit(file, (DartFileEditBuilder builder) {
+ builder.addSimpleReplacement(range.node(targetIfStatement),
+ 'if ($condition) {$eol$newSource$prefix}');
+ });
+ }
+
+ /// Return an instance of this class. Used as a tear-off in `AssistProcessor`.
+ static JoinIfWithInner newInstance() => JoinIfWithInner();
+}
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/join_if_with_outer.dart b/pkg/analysis_server/lib/src/services/correction/dart/join_if_with_outer.dart
new file mode 100644
index 0000000..5f61bae
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/correction/dart/join_if_with_outer.dart
@@ -0,0 +1,76 @@
+// 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:analysis_server/src/services/correction/assist.dart';
+import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart';
+import 'package:analysis_server/src/services/correction/util.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer_plugin/utilities/assist/assist.dart';
+import 'package:analyzer_plugin/utilities/change_builder/change_builder_dart.dart';
+import 'package:analyzer_plugin/utilities/range_factory.dart';
+
+class JoinIfWithOuter extends CorrectionProducer {
+ @override
+ AssistKind get assistKind => DartAssistKind.JOIN_IF_WITH_OUTER;
+
+ @override
+ Future<void> compute(DartChangeBuilder builder) async {
+ // climb up condition to the (supposedly) "if" statement
+ var node = this.node;
+ while (node is Expression) {
+ node = node.parent;
+ }
+ // prepare target "if" statement
+ if (node is! IfStatement) {
+ return;
+ }
+ var targetIfStatement = node as IfStatement;
+ if (targetIfStatement.elseStatement != null) {
+ return;
+ }
+ // prepare outer "if" statement
+ var parent = targetIfStatement.parent;
+ if (parent is Block) {
+ if ((parent as Block).statements.length != 1) {
+ return;
+ }
+ parent = parent.parent;
+ }
+ if (parent is! IfStatement) {
+ return;
+ }
+ var outerIfStatement = parent as IfStatement;
+ if (outerIfStatement.elseStatement != null) {
+ return;
+ }
+ // prepare environment
+ var prefix = utils.getNodePrefix(outerIfStatement);
+ // merge conditions
+ var targetCondition = targetIfStatement.condition;
+ var outerCondition = outerIfStatement.condition;
+ var targetConditionSource = utils.getNodeText(targetCondition);
+ var outerConditionSource = utils.getNodeText(outerCondition);
+ if (shouldWrapParenthesisBeforeAnd(targetCondition)) {
+ targetConditionSource = '($targetConditionSource)';
+ }
+ if (shouldWrapParenthesisBeforeAnd(outerCondition)) {
+ outerConditionSource = '($outerConditionSource)';
+ }
+ var condition = '$outerConditionSource && $targetConditionSource';
+ // replace outer "if" statement
+ var targetThenStatement = targetIfStatement.thenStatement;
+ var targetThenStatements = getStatements(targetThenStatement);
+ var lineRanges = utils.getLinesRangeStatements(targetThenStatements);
+ var oldSource = utils.getRangeText(lineRanges);
+ var newSource = utils.indentSourceLeftRight(oldSource);
+
+ await builder.addFileEdit(file, (DartFileEditBuilder builder) {
+ builder.addSimpleReplacement(range.node(outerIfStatement),
+ 'if ($condition) {$eol$newSource$prefix}');
+ });
+ }
+
+ /// Return an instance of this class. Used as a tear-off in `AssistProcessor`.
+ static JoinIfWithOuter newInstance() => JoinIfWithOuter();
+}
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/join_variable_declaration.dart b/pkg/analysis_server/lib/src/services/correction/dart/join_variable_declaration.dart
new file mode 100644
index 0000000..bba3f14
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/correction/dart/join_variable_declaration.dart
@@ -0,0 +1,145 @@
+// 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:analysis_server/src/services/correction/assist.dart';
+import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/src/dart/ast/utilities.dart';
+import 'package:analyzer_plugin/utilities/assist/assist.dart';
+import 'package:analyzer_plugin/utilities/change_builder/change_builder_dart.dart';
+import 'package:analyzer_plugin/utilities/range_factory.dart';
+
+class JoinVariableDeclaration extends CorrectionProducer {
+ @override
+ AssistKind get assistKind => DartAssistKind.JOIN_VARIABLE_DECLARATION;
+
+ @override
+ Future<void> compute(DartChangeBuilder builder) async {
+ if (node is SimpleIdentifier) {
+ var parent = node.parent;
+ if (parent is AssignmentExpression &&
+ parent.leftHandSide == node &&
+ parent.parent is ExpressionStatement) {
+ await _joinOnAssignment(builder, parent);
+ return;
+ }
+ }
+ var declList = node.thisOrAncestorOfType<VariableDeclarationList>();
+ if (declList != null && declList.variables.length == 1) {
+ await _joinOnDeclaration(builder, declList);
+ }
+ }
+
+ /// Join the declaration when the variable is on the left-hand side of an
+ /// assignment.
+ Future<void> _joinOnAssignment(
+ DartChangeBuilder builder, AssignmentExpression assignExpression) async {
+ // Check that assignment is not a compound assignment.
+ if (assignExpression.operator.type != TokenType.EQ) {
+ return;
+ }
+ // prepare "declaration" statement
+ var element = (node as SimpleIdentifier).staticElement;
+ if (element == null) {
+ return;
+ }
+ var declOffset = element.nameOffset;
+ var unit = resolvedResult.unit;
+ var declNode = NodeLocator(declOffset).searchWithin(unit);
+ if (declNode != null &&
+ declNode.parent is VariableDeclaration &&
+ (declNode.parent as VariableDeclaration).name == declNode &&
+ declNode.parent.parent is VariableDeclarationList &&
+ declNode.parent.parent.parent is VariableDeclarationStatement) {
+ } else {
+ return;
+ }
+ var decl = declNode.parent as VariableDeclaration;
+ var declStatement = decl.parent.parent as VariableDeclarationStatement;
+ // may be has initializer
+ if (decl.initializer != null) {
+ return;
+ }
+ // check that "declaration" statement declared only one variable
+ if (declStatement.variables.variables.length != 1) {
+ return;
+ }
+ // check that the "declaration" and "assignment" statements are
+ // parts of the same Block
+ var assignStatement = node.parent.parent as ExpressionStatement;
+ if (assignStatement.parent is Block &&
+ assignStatement.parent == declStatement.parent) {
+ } else {
+ return;
+ }
+ var block = assignStatement.parent as Block;
+ // check that "declaration" and "assignment" statements are adjacent
+ List<Statement> statements = block.statements;
+ if (statements.indexOf(assignStatement) ==
+ statements.indexOf(declStatement) + 1) {
+ } else {
+ return;
+ }
+
+ await builder.addFileEdit(file, (DartFileEditBuilder builder) {
+ builder.addSimpleReplacement(
+ range.endStart(declNode, assignExpression.operator), ' ');
+ });
+ }
+
+ /// Join the declaration when the variable is on the left-hand side of an
+ /// assignment.
+ Future<void> _joinOnDeclaration(
+ DartChangeBuilder builder, VariableDeclarationList declList) async {
+ // prepare enclosing VariableDeclarationList
+ var decl = declList.variables[0];
+ // already initialized
+ if (decl.initializer != null) {
+ return;
+ }
+ // prepare VariableDeclarationStatement in Block
+ if (declList.parent is VariableDeclarationStatement &&
+ declList.parent.parent is Block) {
+ } else {
+ return;
+ }
+ var declStatement = declList.parent as VariableDeclarationStatement;
+ var block = declStatement.parent as Block;
+ List<Statement> statements = block.statements;
+ // prepare assignment
+ // declaration should not be last Statement
+ var declIndex = statements.indexOf(declStatement);
+ if (declIndex < statements.length - 1) {
+ } else {
+ return;
+ }
+ // next Statement should be assignment
+ var assignStatement = statements[declIndex + 1];
+ if (assignStatement is ExpressionStatement) {
+ } else {
+ return;
+ }
+ var expressionStatement = assignStatement as ExpressionStatement;
+ // expression should be assignment
+ if (expressionStatement.expression is AssignmentExpression) {
+ } else {
+ return;
+ }
+ var assignExpression =
+ expressionStatement.expression as AssignmentExpression;
+ // check that pure assignment
+ if (assignExpression.operator.type != TokenType.EQ) {
+ return;
+ }
+
+ await builder.addFileEdit(file, (DartFileEditBuilder builder) {
+ builder.addSimpleReplacement(
+ range.endStart(decl.name, assignExpression.operator), ' ');
+ });
+ }
+
+ /// Return an instance of this class. Used as a tear-off in `AssistProcessor`.
+ static JoinVariableDeclaration newInstance() => JoinVariableDeclaration();
+}
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/replace_conditional_with_if_else.dart b/pkg/analysis_server/lib/src/services/correction/dart/replace_conditional_with_if_else.dart
new file mode 100644
index 0000000..1ee5fb7
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/correction/dart/replace_conditional_with_if_else.dart
@@ -0,0 +1,117 @@
+// 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:analysis_server/src/services/correction/assist.dart';
+import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer_plugin/utilities/assist/assist.dart';
+import 'package:analyzer_plugin/utilities/change_builder/change_builder_dart.dart';
+import 'package:analyzer_plugin/utilities/range_factory.dart';
+
+class ReplaceConditionalWithIfElse extends CorrectionProducer {
+ @override
+ AssistKind get assistKind => DartAssistKind.REPLACE_CONDITIONAL_WITH_IF_ELSE;
+
+ @override
+ Future<void> compute(DartChangeBuilder builder) async {
+ ConditionalExpression conditional;
+ // may be on Statement with Conditional
+ var statement = node.thisOrAncestorOfType<Statement>();
+ if (statement == null) {
+ return;
+ }
+ // variable declaration
+ var inVariable = false;
+ if (statement is VariableDeclarationStatement) {
+ var variableStatement = statement;
+ for (var variable in variableStatement.variables.variables) {
+ if (variable.initializer is ConditionalExpression) {
+ conditional = variable.initializer as ConditionalExpression;
+ inVariable = true;
+ break;
+ }
+ }
+ }
+ // assignment
+ var inAssignment = false;
+ if (statement is ExpressionStatement) {
+ var exprStmt = statement;
+ if (exprStmt.expression is AssignmentExpression) {
+ var assignment = exprStmt.expression as AssignmentExpression;
+ if (assignment.operator.type == TokenType.EQ &&
+ assignment.rightHandSide is ConditionalExpression) {
+ conditional = assignment.rightHandSide as ConditionalExpression;
+ inAssignment = true;
+ }
+ }
+ }
+ // return
+ var inReturn = false;
+ if (statement is ReturnStatement) {
+ var returnStatement = statement;
+ if (returnStatement.expression is ConditionalExpression) {
+ conditional = returnStatement.expression as ConditionalExpression;
+ inReturn = true;
+ }
+ }
+ // prepare environment
+ var indent = utils.getIndent(1);
+ var prefix = utils.getNodePrefix(statement);
+
+ if (inVariable || inAssignment || inReturn) {
+ await builder.addFileEdit(file, (DartFileEditBuilder builder) {
+ // Type v = Conditional;
+ if (inVariable) {
+ var variable = conditional.parent as VariableDeclaration;
+ builder.addDeletion(range.endEnd(variable.name, conditional));
+ var conditionSrc = utils.getNodeText(conditional.condition);
+ var thenSrc = utils.getNodeText(conditional.thenExpression);
+ var elseSrc = utils.getNodeText(conditional.elseExpression);
+ var name = variable.name.name;
+ var src = eol;
+ src += prefix + 'if ($conditionSrc) {' + eol;
+ src += prefix + indent + '$name = $thenSrc;' + eol;
+ src += prefix + '} else {' + eol;
+ src += prefix + indent + '$name = $elseSrc;' + eol;
+ src += prefix + '}';
+ builder.addSimpleReplacement(range.endLength(statement, 0), src);
+ }
+ // v = Conditional;
+ if (inAssignment) {
+ var assignment = conditional.parent as AssignmentExpression;
+ var leftSide = assignment.leftHandSide;
+ var conditionSrc = utils.getNodeText(conditional.condition);
+ var thenSrc = utils.getNodeText(conditional.thenExpression);
+ var elseSrc = utils.getNodeText(conditional.elseExpression);
+ var name = utils.getNodeText(leftSide);
+ var src = '';
+ src += 'if ($conditionSrc) {' + eol;
+ src += prefix + indent + '$name = $thenSrc;' + eol;
+ src += prefix + '} else {' + eol;
+ src += prefix + indent + '$name = $elseSrc;' + eol;
+ src += prefix + '}';
+ builder.addSimpleReplacement(range.node(statement), src);
+ }
+ // return Conditional;
+ if (inReturn) {
+ var conditionSrc = utils.getNodeText(conditional.condition);
+ var thenSrc = utils.getNodeText(conditional.thenExpression);
+ var elseSrc = utils.getNodeText(conditional.elseExpression);
+ var src = '';
+ src += 'if ($conditionSrc) {' + eol;
+ src += prefix + indent + 'return $thenSrc;' + eol;
+ src += prefix + '} else {' + eol;
+ src += prefix + indent + 'return $elseSrc;' + eol;
+ src += prefix + '}';
+ builder.addSimpleReplacement(range.node(statement), src);
+ }
+ });
+ }
+ }
+
+ /// Return an instance of this class. Used as a tear-off in `AssistProcessor`.
+ static ReplaceConditionalWithIfElse newInstance() =>
+ ReplaceConditionalWithIfElse();
+}
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/replace_if_else_with_conditional.dart b/pkg/analysis_server/lib/src/services/correction/dart/replace_if_else_with_conditional.dart
new file mode 100644
index 0000000..afeaf2d
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/correction/dart/replace_if_else_with_conditional.dart
@@ -0,0 +1,83 @@
+// 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:analysis_server/src/services/correction/assist.dart';
+import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart';
+import 'package:analysis_server/src/services/correction/util.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer_plugin/utilities/assist/assist.dart';
+import 'package:analyzer_plugin/utilities/change_builder/change_builder_dart.dart';
+import 'package:analyzer_plugin/utilities/range_factory.dart';
+
+class ReplaceIfElseWithConditional extends CorrectionProducer {
+ @override
+ AssistKind get assistKind => DartAssistKind.REPLACE_IF_ELSE_WITH_CONDITIONAL;
+
+ @override
+ Future<void> compute(DartChangeBuilder builder) async {
+ // should be "if"
+ if (node is! IfStatement) {
+ return;
+ }
+ var ifStatement = node as IfStatement;
+ // single then/else statements
+ var thenStatement = getSingleStatement(ifStatement.thenStatement);
+ var elseStatement = getSingleStatement(ifStatement.elseStatement);
+ if (thenStatement == null || elseStatement == null) {
+ return;
+ }
+ Expression thenExpression;
+ Expression elseExpression;
+ var hasReturnStatements = false;
+ if (thenStatement is ReturnStatement && elseStatement is ReturnStatement) {
+ hasReturnStatements = true;
+ thenExpression = thenStatement.expression;
+ elseExpression = elseStatement.expression;
+ }
+ var hasExpressionStatements = false;
+ if (thenStatement is ExpressionStatement &&
+ elseStatement is ExpressionStatement) {
+ if (thenStatement.expression is AssignmentExpression &&
+ elseStatement.expression is AssignmentExpression) {
+ hasExpressionStatements = true;
+ thenExpression = thenStatement.expression;
+ elseExpression = elseStatement.expression;
+ }
+ }
+
+ if (hasReturnStatements || hasExpressionStatements) {
+ await builder.addFileEdit(file, (DartFileEditBuilder builder) {
+ // returns
+ if (hasReturnStatements) {
+ var conditionSrc = utils.getNodeText(ifStatement.condition);
+ var theSrc = utils.getNodeText(thenExpression);
+ var elseSrc = utils.getNodeText(elseExpression);
+ builder.addSimpleReplacement(range.node(ifStatement),
+ 'return $conditionSrc ? $theSrc : $elseSrc;');
+ }
+ // assignments -> v = Conditional;
+ if (hasExpressionStatements) {
+ AssignmentExpression thenAssignment = thenExpression;
+ AssignmentExpression elseAssignment = elseExpression;
+ var thenTarget = utils.getNodeText(thenAssignment.leftHandSide);
+ var elseTarget = utils.getNodeText(elseAssignment.leftHandSide);
+ if (thenAssignment.operator.type == TokenType.EQ &&
+ elseAssignment.operator.type == TokenType.EQ &&
+ thenTarget == elseTarget) {
+ var conditionSrc = utils.getNodeText(ifStatement.condition);
+ var theSrc = utils.getNodeText(thenAssignment.rightHandSide);
+ var elseSrc = utils.getNodeText(elseAssignment.rightHandSide);
+ builder.addSimpleReplacement(range.node(ifStatement),
+ '$thenTarget = $conditionSrc ? $theSrc : $elseSrc;');
+ }
+ }
+ });
+ }
+ }
+
+ /// Return an instance of this class. Used as a tear-off in `AssistProcessor`.
+ static ReplaceIfElseWithConditional newInstance() =>
+ ReplaceIfElseWithConditional();
+}
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/split_variable_declaration.dart b/pkg/analysis_server/lib/src/services/correction/dart/split_variable_declaration.dart
new file mode 100644
index 0000000..088e644
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/correction/dart/split_variable_declaration.dart
@@ -0,0 +1,67 @@
+// 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:analysis_server/src/services/correction/assist.dart';
+import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer_plugin/utilities/assist/assist.dart';
+import 'package:analyzer_plugin/utilities/change_builder/change_builder_dart.dart';
+import 'package:analyzer_plugin/utilities/range_factory.dart';
+
+class SplitVariableDeclaration extends CorrectionProducer {
+ @override
+ AssistKind get assistKind => DartAssistKind.SPLIT_VARIABLE_DECLARATION;
+
+ @override
+ Future<void> compute(DartChangeBuilder builder) async {
+ var variableList = node?.thisOrAncestorOfType<VariableDeclarationList>();
+
+ // Must be a local variable declaration.
+ if (variableList?.parent is! VariableDeclarationStatement) {
+ return;
+ }
+ VariableDeclarationStatement statement = variableList.parent;
+
+ // Cannot be `const` or `final`.
+ var keywordKind = variableList.keyword?.keyword;
+ if (keywordKind == Keyword.CONST || keywordKind == Keyword.FINAL) {
+ return;
+ }
+
+ var variables = variableList.variables;
+ if (variables.length != 1) {
+ return;
+ }
+
+ // The caret must be between the type and the variable name.
+ var variable = variables[0];
+ if (!range.startEnd(statement, variable.name).contains(selectionOffset)) {
+ return;
+ }
+
+ // The variable must have an initializer.
+ if (variable.initializer == null) {
+ return;
+ }
+
+ await builder.addFileEdit(file, (builder) {
+ if (variableList.type == null) {
+ final type = variable.declaredElement.type;
+ if (!type.isDynamic) {
+ builder.addReplacement(range.token(variableList.keyword), (builder) {
+ builder.writeType(type);
+ });
+ }
+ }
+
+ var indent = utils.getNodePrefix(statement);
+ var name = variable.name.name;
+ builder.addSimpleInsertion(variable.name.end, ';' + eol + indent + name);
+ });
+ }
+
+ /// Return an instance of this class. Used as a tear-off in `AssistProcessor`.
+ static SplitVariableDeclaration newInstance() => SplitVariableDeclaration();
+}
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/surround_with.dart b/pkg/analysis_server/lib/src/services/correction/dart/surround_with.dart
new file mode 100644
index 0000000..a66877f
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/correction/dart/surround_with.dart
@@ -0,0 +1,364 @@
+// 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:analysis_server/src/services/correction/assist.dart';
+import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart';
+import 'package:analysis_server/src/services/correction/statement_analyzer.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/source/source_range.dart';
+import 'package:analyzer_plugin/utilities/assist/assist.dart';
+import 'package:analyzer_plugin/utilities/change_builder/change_builder_dart.dart';
+
+class SurroundWith extends MultiCorrectionProducer {
+ @override
+ Iterable<CorrectionProducer> get producers sync* {
+ // prepare selected statements
+ var selectionAnalyzer = StatementAnalyzer(
+ resolvedResult, SourceRange(selectionOffset, selectionLength));
+ selectionAnalyzer.analyze();
+ var selectedNodes = selectionAnalyzer.selectedNodes;
+ // convert nodes to statements
+ var selectedStatements = <Statement>[];
+ for (var selectedNode in selectedNodes) {
+ if (selectedNode is Statement) {
+ selectedStatements.add(selectedNode);
+ }
+ }
+ // we want only statements in blocks
+ for (var statement in selectedStatements) {
+ if (statement.parent is! Block) {
+ return;
+ }
+ }
+ // we want only statements
+ if (selectedStatements.isEmpty ||
+ selectedStatements.length != selectedNodes.length) {
+ return;
+ }
+ // prepare statement information
+ var firstStatement = selectedStatements[0];
+ var statementsRange = utils.getLinesRangeStatements(selectedStatements);
+ // prepare environment
+ var indentOld = utils.getNodePrefix(firstStatement);
+ var indentNew = '$indentOld${utils.getIndent(1)}';
+ var indentedCode =
+ utils.replaceSourceRangeIndent(statementsRange, indentOld, indentNew);
+
+ yield _SurroundWithBlock(
+ statementsRange, indentOld, indentNew, indentedCode);
+ yield _SurroundWithDoWhile(
+ statementsRange, indentOld, indentNew, indentedCode);
+ yield _SurroundWithFor(statementsRange, indentOld, indentNew, indentedCode);
+ yield _SurroundWithForIn(
+ statementsRange, indentOld, indentNew, indentedCode);
+ yield _SurroundWithIf(statementsRange, indentOld, indentNew, indentedCode);
+ yield _SurroundWithSetState(
+ statementsRange, indentOld, indentNew, indentedCode);
+ yield _SurroundWithTryCatch(
+ statementsRange, indentOld, indentNew, indentedCode);
+ yield _SurroundWithTryFinally(
+ statementsRange, indentOld, indentNew, indentedCode);
+ yield _SurroundWithWhile(
+ statementsRange, indentOld, indentNew, indentedCode);
+ }
+
+ /// Return an instance of this class. Used as a tear-off in `AssistProcessor`.
+ static SurroundWith newInstance() => SurroundWith();
+}
+
+/// A correction processor that can make one of the possible change computed by
+/// the [SurroundWith] producer.
+abstract class _SurroundWith extends CorrectionProducer {
+ final SourceRange statementsRange;
+
+ final String indentOld;
+
+ final String indentNew;
+
+ final String indentedCode;
+
+ _SurroundWith(
+ this.statementsRange, this.indentOld, this.indentNew, this.indentedCode);
+}
+
+/// A correction processor that can make one of the possible change computed by
+/// the [SurroundWith] producer.
+class _SurroundWithBlock extends _SurroundWith {
+ _SurroundWithBlock(SourceRange statementsRange, String indentOld,
+ String indentNew, String indentedCode)
+ : super(statementsRange, indentOld, indentNew, indentedCode);
+
+ @override
+ AssistKind get assistKind => DartAssistKind.SURROUND_WITH_BLOCK;
+
+ @override
+ Future<void> compute(DartChangeBuilder builder) async {
+ await builder.addFileEdit(file, (DartFileEditBuilder builder) {
+ builder.addSimpleInsertion(statementsRange.offset, '$indentOld{$eol');
+ builder.addSimpleReplacement(
+ statementsRange,
+ utils.replaceSourceRangeIndent(
+ statementsRange, indentOld, indentNew));
+ builder.addSimpleInsertion(statementsRange.end, '$indentOld}$eol');
+ });
+ }
+}
+
+/// A correction processor that can make one of the possible change computed by
+/// the [SurroundWith] producer.
+class _SurroundWithDoWhile extends _SurroundWith {
+ _SurroundWithDoWhile(SourceRange statementsRange, String indentOld,
+ String indentNew, String indentedCode)
+ : super(statementsRange, indentOld, indentNew, indentedCode);
+
+ @override
+ AssistKind get assistKind => DartAssistKind.SURROUND_WITH_DO_WHILE;
+
+ @override
+ Future<void> compute(DartChangeBuilder builder) async {
+ await builder.addFileEdit(file, (DartFileEditBuilder builder) {
+ builder.addReplacement(statementsRange, (DartEditBuilder builder) {
+ builder.write(indentOld);
+ builder.write('do {');
+ builder.write(eol);
+ builder.write(indentedCode);
+ builder.write(indentOld);
+ builder.write('} while (');
+ builder.addSimpleLinkedEdit('CONDITION', 'condition');
+ builder.write(');');
+ builder.selectHere();
+ builder.write(eol);
+ });
+ });
+ }
+}
+
+/// A correction processor that can make one of the possible change computed by
+/// the [SurroundWith] producer.
+class _SurroundWithFor extends _SurroundWith {
+ _SurroundWithFor(SourceRange statementsRange, String indentOld,
+ String indentNew, String indentedCode)
+ : super(statementsRange, indentOld, indentNew, indentedCode);
+
+ @override
+ AssistKind get assistKind => DartAssistKind.SURROUND_WITH_FOR;
+
+ @override
+ Future<void> compute(DartChangeBuilder builder) async {
+ await builder.addFileEdit(file, (DartFileEditBuilder builder) {
+ builder.addReplacement(statementsRange, (DartEditBuilder builder) {
+ builder.write(indentOld);
+ builder.write('for (var ');
+ builder.addSimpleLinkedEdit('VAR', 'v');
+ builder.write(' = ');
+ builder.addSimpleLinkedEdit('INIT', 'init');
+ builder.write('; ');
+ builder.addSimpleLinkedEdit('CONDITION', 'condition');
+ builder.write('; ');
+ builder.addSimpleLinkedEdit('INCREMENT', 'increment');
+ builder.write(') {');
+ builder.write(eol);
+ builder.write(indentedCode);
+ builder.write(indentOld);
+ builder.write('}');
+ builder.selectHere();
+ builder.write(eol);
+ });
+ });
+ }
+}
+
+/// A correction processor that can make one of the possible change computed by
+/// the [SurroundWith] producer.
+class _SurroundWithForIn extends _SurroundWith {
+ _SurroundWithForIn(SourceRange statementsRange, String indentOld,
+ String indentNew, String indentedCode)
+ : super(statementsRange, indentOld, indentNew, indentedCode);
+
+ @override
+ AssistKind get assistKind => DartAssistKind.SURROUND_WITH_FOR_IN;
+
+ @override
+ Future<void> compute(DartChangeBuilder builder) async {
+ await builder.addFileEdit(file, (DartFileEditBuilder builder) {
+ builder.addReplacement(statementsRange, (DartEditBuilder builder) {
+ builder.write(indentOld);
+ builder.write('for (var ');
+ builder.addSimpleLinkedEdit('NAME', 'item');
+ builder.write(' in ');
+ builder.addSimpleLinkedEdit('ITERABLE', 'iterable');
+ builder.write(') {');
+ builder.write(eol);
+ builder.write(indentedCode);
+ builder.write(indentOld);
+ builder.write('}');
+ builder.selectHere();
+ builder.write(eol);
+ });
+ });
+ }
+}
+
+/// A correction processor that can make one of the possible change computed by
+/// the [SurroundWith] producer.
+class _SurroundWithIf extends _SurroundWith {
+ _SurroundWithIf(SourceRange statementsRange, String indentOld,
+ String indentNew, String indentedCode)
+ : super(statementsRange, indentOld, indentNew, indentedCode);
+
+ @override
+ AssistKind get assistKind => DartAssistKind.SURROUND_WITH_IF;
+
+ @override
+ Future<void> compute(DartChangeBuilder builder) async {
+ await builder.addFileEdit(file, (DartFileEditBuilder builder) {
+ builder.addReplacement(statementsRange, (DartEditBuilder builder) {
+ builder.write(indentOld);
+ builder.write('if (');
+ builder.addSimpleLinkedEdit('CONDITION', 'condition');
+ builder.write(') {');
+ builder.write(eol);
+ builder.write(indentedCode);
+ builder.write(indentOld);
+ builder.write('}');
+ builder.selectHere();
+ builder.write(eol);
+ });
+ });
+ }
+}
+
+/// A correction processor that can make one of the possible change computed by
+/// the [SurroundWith] producer.
+class _SurroundWithSetState extends _SurroundWith {
+ _SurroundWithSetState(SourceRange statementsRange, String indentOld,
+ String indentNew, String indentedCode)
+ : super(statementsRange, indentOld, indentNew, indentedCode);
+
+ @override
+ AssistKind get assistKind => DartAssistKind.SURROUND_WITH_SET_STATE;
+
+ @override
+ Future<void> compute(DartChangeBuilder builder) async {
+ var classDeclaration = node.parent.thisOrAncestorOfType<ClassDeclaration>();
+ if (classDeclaration != null &&
+ flutter.isState(classDeclaration.declaredElement)) {
+ await builder.addFileEdit(file, (DartFileEditBuilder builder) {
+ builder.addReplacement(statementsRange, (DartEditBuilder builder) {
+ builder.write(indentOld);
+ builder.writeln('setState(() {');
+ builder.write(indentedCode);
+ builder.write(indentOld);
+ builder.selectHere();
+ builder.writeln('});');
+ });
+ });
+ }
+ }
+}
+
+/// A correction processor that can make one of the possible change computed by
+/// the [SurroundWith] producer.
+class _SurroundWithTryCatch extends _SurroundWith {
+ _SurroundWithTryCatch(SourceRange statementsRange, String indentOld,
+ String indentNew, String indentedCode)
+ : super(statementsRange, indentOld, indentNew, indentedCode);
+
+ @override
+ AssistKind get assistKind => DartAssistKind.SURROUND_WITH_TRY_CATCH;
+
+ @override
+ Future<void> compute(DartChangeBuilder builder) async {
+ await builder.addFileEdit(file, (DartFileEditBuilder builder) {
+ builder.addReplacement(statementsRange, (DartEditBuilder builder) {
+ builder.write(indentOld);
+ builder.write('try {');
+ builder.write(eol);
+ builder.write(indentedCode);
+ builder.write(indentOld);
+ builder.write('} on ');
+ builder.addSimpleLinkedEdit('EXCEPTION_TYPE', 'Exception');
+ builder.write(' catch (');
+ builder.addSimpleLinkedEdit('EXCEPTION_VAR', 'e');
+ builder.write(') {');
+ builder.write(eol);
+ //
+ builder.write(indentNew);
+ builder.addSimpleLinkedEdit('CATCH', '// TODO');
+ builder.selectHere();
+ builder.write(eol);
+ //
+ builder.write(indentOld);
+ builder.write('}');
+ builder.write(eol);
+ });
+ });
+ }
+}
+
+/// A correction processor that can make one of the possible change computed by
+/// the [SurroundWith] producer.
+class _SurroundWithTryFinally extends _SurroundWith {
+ _SurroundWithTryFinally(SourceRange statementsRange, String indentOld,
+ String indentNew, String indentedCode)
+ : super(statementsRange, indentOld, indentNew, indentedCode);
+
+ @override
+ AssistKind get assistKind => DartAssistKind.SURROUND_WITH_TRY_FINALLY;
+
+ @override
+ Future<void> compute(DartChangeBuilder builder) async {
+ await builder.addFileEdit(file, (DartFileEditBuilder builder) {
+ builder.addReplacement(statementsRange, (DartEditBuilder builder) {
+ builder.write(indentOld);
+ builder.write('try {');
+ builder.write(eol);
+ //
+ builder.write(indentedCode);
+ //
+ builder.write(indentOld);
+ builder.write('} finally {');
+ builder.write(eol);
+ //
+ builder.write(indentNew);
+ builder.addSimpleLinkedEdit('FINALLY', '// TODO');
+ builder.selectHere();
+ builder.write(eol);
+ //
+ builder.write(indentOld);
+ builder.write('}');
+ builder.write(eol);
+ });
+ });
+ }
+}
+
+/// A correction processor that can make one of the possible change computed by
+/// the [SurroundWith] producer.
+class _SurroundWithWhile extends _SurroundWith {
+ _SurroundWithWhile(SourceRange statementsRange, String indentOld,
+ String indentNew, String indentedCode)
+ : super(statementsRange, indentOld, indentNew, indentedCode);
+
+ @override
+ AssistKind get assistKind => DartAssistKind.SURROUND_WITH_WHILE;
+
+ @override
+ Future<void> compute(DartChangeBuilder builder) async {
+ await builder.addFileEdit(file, (DartFileEditBuilder builder) {
+ builder.addReplacement(statementsRange, (DartEditBuilder builder) {
+ builder.write(indentOld);
+ builder.write('while (');
+ builder.addSimpleLinkedEdit('CONDITION', 'condition');
+ builder.write(') {');
+ builder.write(eol);
+ builder.write(indentedCode);
+ builder.write(indentOld);
+ builder.write('}');
+ builder.selectHere();
+ builder.write(eol);
+ });
+ });
+ }
+}
diff --git a/pkg/analysis_server/lib/src/services/correction/fix.dart b/pkg/analysis_server/lib/src/services/correction/fix.dart
index 3983092..a24e389 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix.dart
@@ -154,6 +154,8 @@
'dart.fix.add.fieldFormalParameters',
70,
'Add final field formal parameters');
+ static const ADD_LATE =
+ FixKind('dart.fix.add.late', 50, "Add 'late' modifier");
static const ADD_MISSING_ENUM_CASE_CLAUSES = FixKind(
'dart.fix.add.missingEnumCaseClauses', 50, 'Add missing case clauses');
static const ADD_MISSING_PARAMETER_NAMED = FixKind(
diff --git a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
index 82bcb0d..3b987e9 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
@@ -15,6 +15,7 @@
import 'package:analysis_server/src/services/correction/dart/add_diagnostic_property_reference.dart';
import 'package:analysis_server/src/services/correction/dart/add_explicit_cast.dart';
import 'package:analysis_server/src/services/correction/dart/add_field_formal_parameters.dart';
+import 'package:analysis_server/src/services/correction/dart/add_late.dart';
import 'package:analysis_server/src/services/correction/dart/add_missing_enum_case_clauses.dart';
import 'package:analysis_server/src/services/correction/dart/add_missing_parameter.dart';
import 'package:analysis_server/src/services/correction/dart/add_missing_parameter_named.dart';
@@ -627,6 +628,9 @@
ChangeTo.classOrMixin,
CreateClass.newInstance,
],
+ CompileTimeErrorCode.NOT_INITIALIZED_NON_NULLABLE_INSTANCE_FIELD: [
+ AddLate.newInstance,
+ ],
CompileTimeErrorCode.NULLABLE_TYPE_IN_EXTENDS_CLAUSE: [
RemoveQuestionMark.newInstance,
],
@@ -912,6 +916,7 @@
RemoveDeadIfNull.newInstance,
],
StaticWarningCode.FINAL_NOT_INITIALIZED: [
+ AddLate.newInstance,
CreateConstructorForFinalFields.newInstance,
],
StaticWarningCode.FINAL_NOT_INITIALIZED_CONSTRUCTOR_1: [
diff --git a/pkg/analysis_server/pubspec.yaml b/pkg/analysis_server/pubspec.yaml
index 03a88e3..d82ba92 100644
--- a/pkg/analysis_server/pubspec.yaml
+++ b/pkg/analysis_server/pubspec.yaml
@@ -1,4 +1,5 @@
name: analysis_server
+# This package is not intended for consumption on pub.dev. DO NOT publish.
publish_to: none
environment:
diff --git a/pkg/analysis_server/test/client/completion_driver_test.dart b/pkg/analysis_server/test/client/completion_driver_test.dart
index e318791..2311453 100644
--- a/pkg/analysis_server/test/client/completion_driver_test.dart
+++ b/pkg/analysis_server/test/client/completion_driver_test.dart
@@ -695,7 +695,7 @@
// + Top level variables.
assertSuggestion(
- completion: 'PI',
+ completion: 'pi',
file: '/sdk/lib/math/math.dart',
element: ElementKind.TOP_LEVEL_VARIABLE,
kind: CompletionSuggestionKind.INVOCATION);
diff --git a/pkg/analysis_server/test/completion_test.dart b/pkg/analysis_server/test/completion_test.dart
index d98a7c5..f5f37f4 100644
--- a/pkg/analysis_server/test/completion_test.dart
+++ b/pkg/analysis_server/test/completion_test.dart
@@ -811,23 +811,23 @@
buildTests('testCompletion_combinator_afterComma', '''
import 'dart:math' show cos, !1;''',
- <String>['1+PI', '1+sin', '1+Random', '1-String']);
+ <String>['1+pi', '1+sin', '1+Random', '1-String']);
buildTests('testCompletion_combinator_ended', '''
import 'dart:math' show !1;"''',
- <String>['1+PI', '1+sin', '1+Random', '1-String']);
+ <String>['1+pi', '1+sin', '1+Random', '1-String']);
buildTests('testCompletion_combinator_export', '''
export 'dart:math' show !1;"''',
- <String>['1+PI', '1+sin', '1+Random', '1-String']);
+ <String>['1+pi', '1+sin', '1+Random', '1-String']);
buildTests('testCompletion_combinator_hide', '''
import 'dart:math' hide !1;"''',
- <String>['1+PI', '1+sin', '1+Random', '1-String']);
+ <String>['1+pi', '1+sin', '1+Random', '1-String']);
buildTests('testCompletion_combinator_notEnded', '''
import 'dart:math' show !1"''',
- <String>['1+PI', '1+sin', '1+Random', '1-String']);
+ <String>['1+pi', '1+sin', '1+Random', '1-String']);
buildTests('testCompletion_combinator_usePrefix', '''
import 'dart:math' show s!1"''',
@@ -1615,16 +1615,16 @@
failingTests: '1');
buildTests('test_importPrefix_hideCombinator', '''
-import 'dart:math' as math hide PI;
+import 'dart:math' as math hide pi;
main() {
math.!1
-}''', <String>['1-PI', '1+LN10']);
+}''', <String>['1-pi', '1+ln10']);
buildTests('test_importPrefix_showCombinator', '''
-import 'dart:math' as math show PI;
+import 'dart:math' as math show pi;
main() {
math.!1
-}''', <String>['1+PI', '1-LN10']);
+}''', <String>['1+pi', '1-ln10']);
sources.clear();
sources['/lib.dart'] = '''
diff --git a/pkg/analysis_server/test/edit/organize_directives_test.dart b/pkg/analysis_server/test/edit/organize_directives_test.dart
index dff0f63..618c6d6 100644
--- a/pkg/analysis_server/test/edit/organize_directives_test.dart
+++ b/pkg/analysis_server/test/edit/organize_directives_test.dart
@@ -130,7 +130,7 @@
import 'dart:collection';
main() {
- print(PI);
+ print(pi);
new HashMap();
}
''');
@@ -141,7 +141,7 @@
import 'dart:math';
main() {
- print(PI);
+ print(pi);
new HashMap();
}
''');
diff --git a/pkg/analysis_server/test/edit/refactoring_test.dart b/pkg/analysis_server/test/edit/refactoring_test.dart
index 4ae3ed16..acf5f34 100644
--- a/pkg/analysis_server/test/edit/refactoring_test.dart
+++ b/pkg/analysis_server/test/edit/refactoring_test.dart
@@ -992,9 +992,9 @@
import 'dart:async' as test;
import 'dart:math' as test;
main() {
- test.PI;
+ test.pi;
}
-''', 'test.PI;');
+''', 'test.pi;');
}
Future test_rename_hasElement_instanceGetter() {
diff --git a/pkg/analysis_server/test/lsp/completion_test.dart b/pkg/analysis_server/test/lsp/completion_test.dart
index 59b9095..52cad68 100644
--- a/pkg/analysis_server/test/lsp/completion_test.dart
+++ b/pkg/analysis_server/test/lsp/completion_test.dart
@@ -417,7 +417,7 @@
expect(updated, contains('a.abcdefghij'));
}
- Future<void> test_prefixFilter() async {
+ Future<void> test_prefixFilter_endOfSymbol() async {
final content = '''
class UniqueNamedClassForLspOne {}
class UniqueNamedClassForLspTwo {}
@@ -425,7 +425,7 @@
main() {
// Should match only Two and Three
- class UniqueNamedClassForLspT^
+ UniqueNamedClassForLspT^
}
''';
@@ -437,6 +437,46 @@
expect(res.any((c) => c.label == 'UniqueNamedClassForLspThree'), isTrue);
}
+ Future<void> test_prefixFilter_midSymbol() async {
+ final content = '''
+ class UniqueNamedClassForLspOne {}
+ class UniqueNamedClassForLspTwo {}
+ class UniqueNamedClassForLspThree {}
+
+ main() {
+ // Should match only Two and Three
+ UniqueNamedClassForLspT^hree
+ }
+ ''';
+
+ await initialize();
+ await openFile(mainFileUri, withoutMarkers(content));
+ final res = await getCompletion(mainFileUri, positionFromMarker(content));
+ expect(res.any((c) => c.label == 'UniqueNamedClassForLspOne'), isFalse);
+ expect(res.any((c) => c.label == 'UniqueNamedClassForLspTwo'), isTrue);
+ expect(res.any((c) => c.label == 'UniqueNamedClassForLspThree'), isTrue);
+ }
+
+ Future<void> test_prefixFilter_startOfSymbol() async {
+ final content = '''
+ class UniqueNamedClassForLspOne {}
+ class UniqueNamedClassForLspTwo {}
+ class UniqueNamedClassForLspThree {}
+
+ main() {
+ // Should match all three
+ ^UniqueNamedClassForLspT
+ }
+ ''';
+
+ await initialize();
+ await openFile(mainFileUri, withoutMarkers(content));
+ final res = await getCompletion(mainFileUri, positionFromMarker(content));
+ expect(res.any((c) => c.label == 'UniqueNamedClassForLspOne'), isTrue);
+ expect(res.any((c) => c.label == 'UniqueNamedClassForLspTwo'), isTrue);
+ expect(res.any((c) => c.label == 'UniqueNamedClassForLspThree'), isTrue);
+ }
+
Future<void> test_suggestionSets() async {
newFile(
join(projectFolderPath, 'other_file.dart'),
diff --git a/pkg/analysis_server/test/services/completion/dart/combinator_contributor_test.dart b/pkg/analysis_server/test/services/completion/dart/combinator_contributor_test.dart
index ec3822e..f984fb0 100644
--- a/pkg/analysis_server/test/services/completion/dart/combinator_contributor_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/combinator_contributor_test.dart
@@ -153,10 +153,10 @@
assertNotSuggested('B');
}
- Future<void> test_Combinator_show_PI() async {
+ Future<void> test_Combinator_show_pi() async {
addTestSource('import "dart:math" show ^;');
await computeSuggestions();
- assertSuggestTopLevelVar('PI', 'double',
+ assertSuggestTopLevelVar('pi', 'double',
kind: CompletionSuggestionKind.IDENTIFIER);
}
diff --git a/pkg/analysis_server/test/services/completion/dart/relevance/bool_assignment_relevance_test.dart b/pkg/analysis_server/test/services/completion/dart/relevance/bool_assignment_relevance_test.dart
index 3e64f9e..5c01e77 100644
--- a/pkg/analysis_server/test/services/completion/dart/relevance/bool_assignment_relevance_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/relevance/bool_assignment_relevance_test.dart
@@ -56,6 +56,6 @@
element: ElementKind.LOCAL_VARIABLE,
kind: CompletionSuggestionKind.INVOCATION);
- assertOrder([trueSuggestion, falseSuggestion, bLocalVar]);
+ assertOrder([bLocalVar, trueSuggestion, falseSuggestion]);
}
}
diff --git a/pkg/analysis_server/test/services/correction/organize_directives_test.dart b/pkg/analysis_server/test/services/correction/organize_directives_test.dart
index c1dd563..78175d0 100644
--- a/pkg/analysis_server/test/services/correction/organize_directives_test.dart
+++ b/pkg/analysis_server/test/services/correction/organize_directives_test.dart
@@ -140,7 +140,7 @@
import 'dart:collection';
main() {
- print(PI);
+ print(pi);
new HashMap();
}
''');
@@ -152,7 +152,7 @@
import 'dart:math';
main() {
- print(PI);
+ print(pi);
new HashMap();
}
''', removeUnused: true);
diff --git a/pkg/analysis_server/test/services/refactoring/rename_import_test.dart b/pkg/analysis_server/test/services/refactoring/rename_import_test.dart
index 7c7bc63..da9fa1c 100644
--- a/pkg/analysis_server/test/services/refactoring/rename_import_test.dart
+++ b/pkg/analysis_server/test/services/refactoring/rename_import_test.dart
@@ -166,12 +166,12 @@
import 'dart:math' as test;
main() {
test.Future f;
- test.PI;
- test.E;
+ test.pi;
+ test.e;
}
''');
// configure refactoring
- createRenameRefactoringAtString('test.PI');
+ createRenameRefactoringAtString('test.pi');
expect(refactoring.refactoringName, 'Rename Import Prefix');
expect(refactoring.oldName, 'test');
refactoring.newName = 'newName';
@@ -181,8 +181,8 @@
import 'dart:math' as newName;
main() {
test.Future f;
- newName.PI;
- newName.E;
+ newName.pi;
+ newName.e;
}
''');
}
diff --git a/pkg/analysis_server/test/src/cider/completion_test.dart b/pkg/analysis_server/test/src/cider/completion_test.dart
index ed599c0..e0e3371 100644
--- a/pkg/analysis_server/test/src/cider/completion_test.dart
+++ b/pkg/analysis_server/test/src/cider/completion_test.dart
@@ -52,6 +52,40 @@
_assertNoClass(text: 'Random');
}
+ Future<void> test_compute_performance_operations() async {
+ await _compute(r'''
+main() {
+ ^
+}
+''');
+
+ _assertHasOperationPerformance(['resolution']);
+ _assertHasOperationPerformance(['suggestions']);
+ _assertHasOperationPerformance([
+ 'suggestions',
+ 'DartCompletionManager - KeywordContributor',
+ ]);
+ }
+
+ Future<void> test_compute_performance_timers() async {
+ await _compute(r'''
+main() {
+ ^
+}
+''');
+
+ void assertTimerNotEmpty(Duration duration) {
+ expect(duration, isNotNull);
+ expect(duration, isNot(Duration.zero));
+ }
+
+ var performance = _completionResult.performance;
+ assertTimerNotEmpty(performance.file);
+ assertTimerNotEmpty(performance.imports);
+ assertTimerNotEmpty(performance.resolution);
+ assertTimerNotEmpty(performance.suggestions);
+ }
+
Future<void> test_compute_prefixStart_hasPrefix() async {
await _compute('''
class A {
@@ -278,6 +312,30 @@
]);
}
+ Future<void> test_warmUp_cachesImportedLibraries() async {
+ var aPath = convertPath('/workspace/dart/test/lib/a.dart');
+ newFile(aPath, content: r'''
+class A {}
+''');
+
+ var bPath = convertPath('/workspace/dart/test/lib/b.dart');
+ newFile(bPath, content: r'''
+import 'a.dart';
+''');
+
+ // Pre-cache `a.dart` using import in `b.dart`
+ await _newComputer().warmUp([bPath]);
+ _assertComputedImportedLibraries([aPath], hasCore: true);
+
+ // Now we complete in `test.dart`, and `a.dart` is already cached.
+ await _compute(r'''
+import 'a.dart';
+^
+''');
+ _assertComputedImportedLibraries([]);
+ _assertHasClass(text: 'A');
+ }
+
void _assertComputedImportedLibraries(
List<String> expected, {
bool hasCore = false,
@@ -328,6 +386,15 @@
return matching.single;
}
+ void _assertHasOperationPerformance(List<String> path) {
+ var current = _completionResult.performance.operations;
+ for (var name in path) {
+ var child = current.getChild(name);
+ expect(child, isNotNull, reason: "No '$name' in $current");
+ current = child;
+ }
+ }
+
CompletionSuggestion _assertHasTopLevelVariable({@required String text}) {
var matching = _matchingCompletions(
text: text,
diff --git a/pkg/analysis_server/test/src/services/completion/dart/feature_computer_test.dart b/pkg/analysis_server/test/src/services/completion/dart/feature_computer_test.dart
index cd03280..6684dc5 100644
--- a/pkg/analysis_server/test/src/services/completion/dart/feature_computer_test.dart
+++ b/pkg/analysis_server/test/src/services/completion/dart/feature_computer_test.dart
@@ -268,7 +268,7 @@
Future<void> test_assignmentExpression_withoutType() async {
await assertContextType('''
void g(String s) {
- var i = ^s.length;
+ var x = ^s.length;
}
''', null);
}
@@ -332,15 +332,39 @@
Future<void> test_fieldDeclaration_var() async {
await assertContextType('''
class Foo {
- var i =^;
+ var x =^;
}
''', null);
}
+ Future<void> test_fieldDeclaration_var_impliedType_int() async {
+ await assertContextType('''
+class Foo {
+ var i = ^ ;
+}
+''', 'int');
+ }
+
+ Future<void> test_fieldDeclaration_var_impliedType_list() async {
+ await assertContextType('''
+class Foo {
+ var list = ^ ;
+}
+''', 'List<dynamic>');
+ }
+
+ Future<void> test_fieldDeclaration_var_impliedType_string() async {
+ await assertContextType('''
+class Foo {
+ var string = ^ ;
+}
+''', 'String');
+ }
+
Future<void> test_fieldDeclaration_var_whitespace() async {
await assertContextType('''
class Foo {
- var i = ^ ;
+ var x = ^ ;
}
''', null);
}
@@ -534,13 +558,19 @@
Future<void> test_topLevelVariableDeclaration_var() async {
await assertContextType('''
-var i=^;
+var x=^;
+''', null);
+ }
+
+ Future<void> test_topLevelVariableDeclaration_var_noEqual() async {
+ await assertContextType('''
+int x^;
''', null);
}
Future<void> test_topLevelVariableDeclaration_var_whitespace() async {
await assertContextType('''
-var i= ^ ;
+var x= ^ ;
''', null);
}
}
diff --git a/pkg/analysis_server/test/src/services/correction/assist/add_not_null_assert.dart b/pkg/analysis_server/test/src/services/correction/assist/add_not_null_assert_test.dart
similarity index 93%
rename from pkg/analysis_server/test/src/services/correction/assist/add_not_null_assert.dart
rename to pkg/analysis_server/test/src/services/correction/assist/add_not_null_assert_test.dart
index bd8122e..92c6865 100644
--- a/pkg/analysis_server/test/src/services/correction/assist/add_not_null_assert.dart
+++ b/pkg/analysis_server/test/src/services/correction/assist/add_not_null_assert_test.dart
@@ -10,12 +10,12 @@
void main() {
defineReflectiveSuite(() {
- defineReflectiveTests(AddNotNullAssert);
+ defineReflectiveTests(AddNotNullAssertTest);
});
}
@reflectiveTest
-class AddNotNullAssert extends AssistProcessorTest {
+class AddNotNullAssertTest extends AssistProcessorTest {
@override
AssistKind get kind => DartAssistKind.ADD_NOT_NULL_ASSERT;
diff --git a/pkg/analysis_server/test/src/services/correction/assist/import_add_show_test.dart b/pkg/analysis_server/test/src/services/correction/assist/import_add_show_test.dart
index a196bfb..bb801c2 100644
--- a/pkg/analysis_server/test/src/services/correction/assist/import_add_show_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/assist/import_add_show_test.dart
@@ -21,9 +21,9 @@
Future<void> test_hasShow() async {
await resolveTestUnit('''
-import 'dart:math' show PI;
+import 'dart:math' show pi;
main() {
- PI;
+ pi;
}
''');
await assertNoAssistAt('import ');
@@ -33,14 +33,14 @@
await resolveTestUnit('''
import 'dart:math';
main(x) {
- PI;
+ pi;
return x.foo();
}
''');
await assertHasAssistAt('import ', '''
-import 'dart:math' show PI;
+import 'dart:math' show pi;
main(x) {
- PI;
+ pi;
return x.foo();
}
''');
@@ -50,16 +50,16 @@
await resolveTestUnit('''
import 'dart:math';
main() {
- PI;
- E;
+ pi;
+ e;
max(1, 2);
}
''');
await assertHasAssistAt('import ', '''
-import 'dart:math' show E, PI, max;
+import 'dart:math' show e, max, pi;
main() {
- PI;
- E;
+ pi;
+ e;
max(1, 2);
}
''');
@@ -69,16 +69,16 @@
await resolveTestUnit('''
import 'dart:math';
main() {
- PI;
- E;
+ pi;
+ e;
max(1, 2);
}
''');
await assertHasAssistAt('art:math', '''
-import 'dart:math' show E, PI, max;
+import 'dart:math' show e, max, pi;
main() {
- PI;
- E;
+ pi;
+ e;
max(1, 2);
}
''');
diff --git a/pkg/analysis_server/test/src/services/correction/assist/join_if_with_inner_test.dart b/pkg/analysis_server/test/src/services/correction/assist/join_if_with_inner_test.dart
index ec1c3df..bd1cf73 100644
--- a/pkg/analysis_server/test/src/services/correction/assist/join_if_with_inner_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/assist/join_if_with_inner_test.dart
@@ -10,12 +10,12 @@
void main() {
defineReflectiveSuite(() {
- defineReflectiveTests(JoinInWithInnerTest);
+ defineReflectiveTests(JoinIfWithInnerTest);
});
}
@reflectiveTest
-class JoinInWithInnerTest extends AssistProcessorTest {
+class JoinIfWithInnerTest extends AssistProcessorTest {
@override
AssistKind get kind => DartAssistKind.JOIN_IF_WITH_INNER;
diff --git a/pkg/analysis_server/test/src/services/correction/assist/test_all.dart b/pkg/analysis_server/test/src/services/correction/assist/test_all.dart
index 4280ef9..18c3539 100644
--- a/pkg/analysis_server/test/src/services/correction/assist/test_all.dart
+++ b/pkg/analysis_server/test/src/services/correction/assist/test_all.dart
@@ -5,7 +5,7 @@
import 'package:test_reflective_loader/test_reflective_loader.dart';
import 'add_diagnostic_property_reference_test.dart' as add_diagnostic_property;
-import 'add_not_null_assert.dart' as add_not_null_assert;
+import 'add_not_null_assert_test.dart' as add_not_null_assert;
import 'add_return_type_test.dart' as add_return_type;
import 'add_type_annotation_test.dart' as add_type_annotation;
import 'assign_to_local_variable_test.dart' as assign_to_local_variable;
diff --git a/pkg/analysis_server/test/src/services/correction/fix/add_late_test.dart b/pkg/analysis_server/test/src/services/correction/fix/add_late_test.dart
new file mode 100644
index 0000000..9a67e6e
--- /dev/null
+++ b/pkg/analysis_server/test/src/services/correction/fix/add_late_test.dart
@@ -0,0 +1,76 @@
+// 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:analysis_server/src/services/correction/fix.dart';
+import 'package:analyzer/src/dart/analysis/experiments.dart';
+import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'fix_processor.dart';
+
+void main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(AddLatePreNnbdTest);
+ defineReflectiveTests(AddLateTest);
+ });
+}
+
+@reflectiveTest
+class AddLatePreNnbdTest extends FixProcessorTest {
+ @override
+ FixKind get kind => DartFixKind.ADD_LATE;
+
+ Future<void> test_withFinal() async {
+ await resolveTestUnit('''
+class C {
+ final String s;
+}
+''');
+ await assertNoFix();
+ }
+}
+
+@reflectiveTest
+class AddLateTest extends FixProcessorTest {
+ @override
+ List<String> get experiments => [EnableString.non_nullable];
+
+ @override
+ FixKind get kind => DartFixKind.ADD_LATE;
+
+ Future<void> test_withFinal() async {
+ await resolveTestUnit('''
+class C {
+ final String s;
+}
+''');
+ await assertHasFix('''
+class C {
+ final late String s;
+}
+''');
+ }
+
+ Future<void> test_withLate() async {
+ await resolveTestUnit('''
+class C {
+ late s;
+}
+''');
+ await assertNoFix();
+ }
+
+ Future<void> test_withType() async {
+ await resolveTestUnit('''
+class C {
+ String s;
+}
+''');
+ await assertHasFix('''
+class C {
+ late String s;
+}
+''');
+ }
+}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/import_library_prefix_test.dart b/pkg/analysis_server/test/src/services/correction/fix/import_library_prefix_test.dart
index b5979a9..73e5996 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/import_library_prefix_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/import_library_prefix_test.dart
@@ -63,15 +63,15 @@
await resolveTestUnit('''
import 'dart:math' as pref;
main() {
- print(pref.E);
- print(PI);
+ print(pref.e);
+ print(pi);
}
''');
await assertHasFix('''
import 'dart:math' as pref;
main() {
- print(pref.E);
- print(pref.PI);
+ print(pref.e);
+ print(pref.pi);
}
''');
}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/import_library_sdk_test.dart b/pkg/analysis_server/test/src/services/correction/fix/import_library_sdk_test.dart
index b504421..b64f017 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/import_library_sdk_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/import_library_sdk_test.dart
@@ -278,28 +278,28 @@
Future<void> test_withTopLevelVariable() async {
await resolveTestUnit('''
main() {
- print(PI);
+ print(pi);
}
''');
await assertHasFix('''
import 'dart:math';
main() {
- print(PI);
+ print(pi);
}
''');
}
Future<void> test_withTopLevelVariable_annotation() async {
await resolveTestUnit('''
-@PI
+@pi
main() {
}
''');
await assertHasFix('''
import 'dart:math';
-@PI
+@pi
main() {
}
''');
diff --git a/pkg/analysis_server/test/src/services/correction/fix/test_all.dart b/pkg/analysis_server/test/src/services/correction/fix/test_all.dart
index 00a1f08..0185dd0 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/test_all.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/test_all.dart
@@ -12,6 +12,7 @@
as add_diagnostic_property_reference;
import 'add_explicit_cast_test.dart' as add_explicit_cast;
import 'add_field_formal_parameters_test.dart' as add_field_formal_parameters;
+import 'add_late_test.dart' as add_late;
import 'add_missing_enum_case_clauses_test.dart'
as add_missing_enum_case_clauses;
import 'add_missing_parameter_named_test.dart' as add_missing_parameter_named;
@@ -170,6 +171,7 @@
add_diagnostic_property_reference.main();
add_explicit_cast.main();
add_field_formal_parameters.main();
+ add_late.main();
add_missing_enum_case_clauses.main();
add_missing_parameter_named.main();
add_missing_parameter_positional.main();
diff --git a/pkg/analysis_server/tool/completion_metrics/implicit_type_declarations.dart b/pkg/analysis_server/tool/completion_metrics/implicit_type_declarations.dart
new file mode 100644
index 0000000..a2c0c4b
--- /dev/null
+++ b/pkg/analysis_server/tool/completion_metrics/implicit_type_declarations.dart
@@ -0,0 +1,245 @@
+// 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' as io;
+
+import 'package:analysis_server/src/status/pages.dart';
+import 'package:analyzer/dart/analysis/analysis_context_collection.dart';
+import 'package:analyzer/dart/analysis/context_root.dart';
+import 'package:analyzer/dart/analysis/results.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/dart/ast/visitor.dart';
+import 'package:analyzer/dart/element/type.dart';
+import 'package:analyzer/diagnostic/diagnostic.dart';
+import 'package:analyzer/file_system/physical_file_system.dart';
+import 'package:analyzer/src/generated/engine.dart';
+import 'package:args/args.dart';
+import 'package:meta/meta.dart';
+
+/// Compute metrics to determine which untyped variable declarations can be used
+/// to imply an expected context type, i.e. the RHS of 'var string = ^' could be
+/// assumed to be a [String].
+Future<void> main(List<String> args) async {
+ var parser = createArgParser();
+ var result = parser.parse(args);
+
+ if (validArguments(parser, result)) {
+ var out = io.stdout;
+ var rootPath = result.rest[0];
+ out.writeln('Analyzing root: "$rootPath"');
+
+ var computer = ImpliedTypeComputer();
+ var stopwatch = Stopwatch();
+ stopwatch.start();
+ await computer.compute(rootPath, verbose: result['verbose']);
+ stopwatch.stop();
+
+ var duration = Duration(milliseconds: stopwatch.elapsedMilliseconds);
+ out.writeln('Metrics computed in $duration');
+ computer.writeMetrics(out);
+ await out.flush();
+ }
+ io.exit(0);
+}
+
+/// Create a parser that can be used to parse the command-line arguments.
+ArgParser createArgParser() {
+ var parser = ArgParser();
+ parser.addOption(
+ 'help',
+ abbr: 'h',
+ help: 'Print this help message.',
+ );
+ parser.addFlag(
+ 'verbose',
+ abbr: 'v',
+ help: 'Print additional information about the analysis',
+ negatable: false,
+ );
+ return parser;
+}
+
+/// Print usage information for this tool.
+void printUsage(ArgParser parser, {String error}) {
+ if (error != null) {
+ print(error);
+ print('');
+ }
+ print('usage: dart implicit_type_declarations.dart [options] packagePath');
+ print('');
+ print('Compute implicit types in field declaration locations without a '
+ 'specified type.');
+ print('');
+ print(parser.usage);
+}
+
+/// Return `true` if the command-line arguments (represented by the [result] and
+/// parsed by the [parser]) are valid.
+bool validArguments(ArgParser parser, ArgResults result) {
+ if (result.wasParsed('help')) {
+ printUsage(parser);
+ return false;
+ } else if (result.rest.length != 1) {
+ printUsage(parser, error: 'No package path specified.');
+ return false;
+ }
+ var rootPath = result.rest[0];
+ if (!io.Directory(rootPath).existsSync()) {
+ printUsage(parser, error: 'The directory "$rootPath" does not exist.');
+ return false;
+ }
+ return true;
+}
+
+/// An object that visits a compilation unit in order to record the data used to
+/// compute the metrics.
+class ImpliedTypeCollector extends RecursiveAstVisitor<void> {
+ /// The implied type data being collected.
+ ImpliedTypeData data;
+
+ /// Initialize a newly created collector to add data points to the given
+ /// [data].
+ ImpliedTypeCollector(this.data);
+
+ void handleVariableDeclaration(VariableDeclaration node, DartType dartType) {
+ // If some untyped variable declaration
+ if (node.equals != null && dartType == null ||
+ (dartType != null && (dartType.isDynamic || dartType.isVoid))) {
+ // And if we can determine the type on the RHS of the variable declaration
+ var rhsType = node.initializer?.staticType;
+ if (rhsType != null && !rhsType.isDynamic) {
+ // Record the name with the type.
+ data.recordImpliedType(node.name.name, rhsType.getDisplayString());
+ }
+ }
+ }
+
+ @override
+ void visitVariableDeclarationList(VariableDeclarationList node) {
+ for (var varDecl in node.variables) {
+ handleVariableDeclaration(varDecl, node.type?.type);
+ }
+ return null;
+ }
+}
+
+/// An object used to compute metrics for a single file or directory.
+class ImpliedTypeComputer {
+ /// The metrics data that was computed.
+ final ImpliedTypeData data = ImpliedTypeData();
+
+ /// Initialize a newly created metrics computer that can compute the metrics
+ /// in one or more files and directories.
+ ImpliedTypeComputer();
+
+ /// Compute the metrics for the file(s) in the [rootPath].
+ /// If [corpus] is true, treat rootPath as a container of packages, creating
+ /// a new context collection for each subdirectory.
+ void compute(String rootPath, {@required bool verbose}) async {
+ final collection = AnalysisContextCollection(
+ includedPaths: [rootPath],
+ resourceProvider: PhysicalResourceProvider.INSTANCE,
+ );
+ final collector = ImpliedTypeCollector(data);
+ for (var context in collection.contexts) {
+ await _computeInContext(context.contextRoot, collector, verbose: verbose);
+ }
+ }
+
+ /// Write a report of the metrics that were computed to the [sink].
+ void writeMetrics(StringSink sink) {
+ data.impliedTypesMap.forEach((String name, Map<String, int> displayStrMap) {
+ var sum = 0;
+ displayStrMap.forEach((String displayStr, int count) {
+ sum += count;
+ });
+ if (sum >= 5) {
+ sink.writeln('$name $sum:');
+ displayStrMap.forEach((String displayStr, int count) {
+ sink.writeln(' $displayStr $count ${printPercentage(count / sum)}');
+ });
+ }
+ });
+ }
+
+ /// Compute the metrics for the files in the context [root], creating a
+ /// separate context collection to prevent accumulating memory. The metrics
+ /// should be captured in the [collector]. Include additional details in the
+ /// output if [verbose] is `true`.
+ void _computeInContext(ContextRoot root, ImpliedTypeCollector collector,
+ {@required bool verbose}) async {
+ // Create a new collection to avoid consuming large quantities of memory.
+ final collection = AnalysisContextCollection(
+ includedPaths: root.includedPaths.toList(),
+ excludedPaths: root.excludedPaths.toList(),
+ resourceProvider: PhysicalResourceProvider.INSTANCE,
+ );
+ var context = collection.contexts[0];
+ for (var filePath in context.contextRoot.analyzedFiles()) {
+ if (AnalysisEngine.isDartFileName(filePath)) {
+ try {
+ var resolvedUnitResult =
+ await context.currentSession.getResolvedUnit(filePath);
+ //
+ // Check for errors that cause the file to be skipped.
+ //
+ if (resolvedUnitResult == null) {
+ print('File $filePath skipped because resolved unit was null.');
+ if (verbose) {
+ print('');
+ }
+ continue;
+ } else if (resolvedUnitResult.state != ResultState.VALID) {
+ print('File $filePath skipped because it could not be analyzed.');
+ if (verbose) {
+ print('');
+ }
+ continue;
+ } else if (hasError(resolvedUnitResult)) {
+ if (verbose) {
+ print('File $filePath skipped due to errors:');
+ for (var error in resolvedUnitResult.errors
+ .where((e) => e.severity == Severity.error)) {
+ print(' ${error.toString()}');
+ }
+ print('');
+ } else {
+ print('File $filePath skipped due to analysis errors.');
+ }
+ continue;
+ }
+
+ resolvedUnitResult.unit.accept(collector);
+ } catch (exception, stacktrace) {
+ print('Exception caught analyzing: "$filePath"');
+ print(exception);
+ print(stacktrace);
+ }
+ }
+ }
+ }
+
+ /// Return `true` if the [result] contains an error.
+ static bool hasError(ResolvedUnitResult result) {
+ for (var error in result.errors) {
+ if (error.severity == Severity.error) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
+
+class ImpliedTypeData {
+ Map<String, Map<String, int>> impliedTypesMap = {};
+
+ /// Record the variable name with the type.
+ void recordImpliedType(String name, String displayString) {
+ assert(name != null);
+ assert(displayString != null);
+ var nameMap = impliedTypesMap.putIfAbsent(name, () => {});
+ nameMap[displayString] = (nameMap[displayString] ?? 0) + 1;
+ }
+}
diff --git a/pkg/analysis_server/tool/completion_metrics/relevance_table_generator.dart b/pkg/analysis_server/tool/completion_metrics/relevance_table_generator.dart
index 5a89a32..4b6d1dc 100644
--- a/pkg/analysis_server/tool/completion_metrics/relevance_table_generator.dart
+++ b/pkg/analysis_server/tool/completion_metrics/relevance_table_generator.dart
@@ -122,33 +122,29 @@
/// An object that records the data used to compute the tables.
class RelevanceData {
- /// A table mapping element kinds to counts by context.
- Map<String, Map<String, int>> byElementKind = {};
-
- /// A table mapping token types to counts by context.
- Map<String, Map<String, int>> byTokenType = {};
+ /// A table mapping element kinds and keywords to counts by context.
+ Map<String, Map<_Kind, int>> byKind = {};
/// Initialize a newly created set of relevance data to be empty.
RelevanceData();
/// Add the data from the given relevance [data] to this set of data.
void addDataFrom(RelevanceData data) {
- _addToMap(byElementKind, data.byElementKind);
- _addToMap(byTokenType, data.byTokenType);
+ _addToMap(byKind, data.byKind);
}
/// Record that an element of the given [kind] was found in the given
/// [context].
void recordElementKind(String context, ElementKind kind) {
- var contextMap = byElementKind.putIfAbsent(context, () => {});
- var key = kind.name;
+ var contextMap = byKind.putIfAbsent(context, () => {});
+ var key = _ElementKind(kind);
contextMap[key] = (contextMap[key] ?? 0) + 1;
}
- /// Record that a token of the given [type] was found in the given [context].
- void recordTokenType(String context, TokenType type) {
- var contextMap = byTokenType.putIfAbsent(context, () => {});
- var key = type.name;
+ /// Record that the given [keyword] was found in the given [context].
+ void recordKeyword(String context, Keyword keyword) {
+ var contextMap = byKind.putIfAbsent(context, () => {});
+ var key = _Keyword(keyword);
contextMap[key] = (contextMap[key] ?? 0) + 1;
}
@@ -334,14 +330,14 @@
@override
void visitBlockFunctionBody(BlockFunctionBody node) {
- _recordTokenType('BlockFunctionBody_start', node,
+ _recordKeyword('BlockFunctionBody_start', node,
allowedKeywords: functionBodyKeywords);
super.visitBlockFunctionBody(node);
}
@override
void visitBooleanLiteral(BooleanLiteral node) {
- _recordTokenType('BooleanLiteral_start', node);
+ _recordKeyword('BooleanLiteral_start', node);
super.visitBooleanLiteral(node);
}
@@ -369,15 +365,15 @@
void visitClassDeclaration(ClassDeclaration node) {
var context = 'name';
if (node.extendsClause != null) {
- _recordTokenType('ClassDeclaration_$context', node.extendsClause,
+ _recordKeyword('ClassDeclaration_$context', node.extendsClause,
allowedKeywords: [Keyword.EXTENDS]);
context = 'extends';
}
if (node.withClause != null) {
- _recordTokenType('ClassDeclaration_$context', node.withClause);
+ _recordKeyword('ClassDeclaration_$context', node.withClause);
context = 'with';
}
- _recordTokenType('ClassDeclaration_$context', node.implementsClause,
+ _recordKeyword('ClassDeclaration_$context', node.implementsClause,
allowedKeywords: [Keyword.IMPLEMENTS]);
for (var member in node.members) {
@@ -392,10 +388,10 @@
_recordDataForNode('ClassTypeAlias_superclass', node.superclass);
var context = 'superclass';
if (node.withClause != null) {
- _recordTokenType('ClassTypeAlias_$context', node.withClause);
+ _recordKeyword('ClassTypeAlias_$context', node.withClause);
context = 'with';
}
- _recordTokenType('ClassTypeAlias_$context', node.implementsClause);
+ _recordKeyword('ClassTypeAlias_$context', node.implementsClause);
super.visitClassTypeAlias(node);
}
@@ -420,7 +416,7 @@
featureComputer = FeatureComputer(typeSystem, typeProvider);
for (var directive in node.directives) {
- _recordTokenType('CompilationUnit_directive', directive,
+ _recordKeyword('CompilationUnit_directive', directive,
allowedKeywords: directiveKeywords);
}
for (var declaration in node.declarations) {
@@ -544,16 +540,16 @@
void visitExportDirective(ExportDirective node) {
var context = 'uri';
if (node.configurations.isNotEmpty) {
- _recordTokenType('ImportDirective_$context', node.configurations[0],
+ _recordKeyword('ImportDirective_$context', node.configurations[0],
allowedKeywords: exportKeywords);
context = 'configurations';
}
if (node.combinators.isNotEmpty) {
- _recordTokenType('ImportDirective_$context', node.combinators[0],
+ _recordKeyword('ImportDirective_$context', node.combinators[0],
allowedKeywords: exportKeywords);
}
for (var combinator in node.combinators) {
- _recordTokenType('ImportDirective_combinator', combinator,
+ _recordKeyword('ImportDirective_combinator', combinator,
allowedKeywords: exportKeywords);
}
super.visitExportDirective(node);
@@ -561,7 +557,7 @@
@override
void visitExpressionFunctionBody(ExpressionFunctionBody node) {
- _recordTokenType('ExpressionFunctionBody_start', node,
+ _recordKeyword('ExpressionFunctionBody_start', node,
allowedKeywords: functionBodyKeywords);
_recordDataForNode('ExpressionFunctionBody_expression', node.expression,
allowedKeywords: expressionKeywords);
@@ -763,25 +759,24 @@
void visitImportDirective(ImportDirective node) {
var context = 'uri';
if (node.deferredKeyword != null) {
- data.recordTokenType(
- 'ImportDirective_$context', node.deferredKeyword.type);
+ data.recordKeyword('ImportDirective_$context', node.deferredKeyword.type);
context = 'deferred';
}
if (node.asKeyword != null) {
- data.recordTokenType('ImportDirective_$context', node.asKeyword.type);
+ data.recordKeyword('ImportDirective_$context', node.asKeyword.type);
context = 'prefix';
}
if (node.configurations.isNotEmpty) {
- _recordTokenType('ImportDirective_$context', node.configurations[0],
+ _recordKeyword('ImportDirective_$context', node.configurations[0],
allowedKeywords: importKeywords);
context = 'configurations';
}
if (node.combinators.isNotEmpty) {
- _recordTokenType('ImportDirective_$context', node.combinators[0],
+ _recordKeyword('ImportDirective_$context', node.combinators[0],
allowedKeywords: importKeywords);
}
for (var combinator in node.combinators) {
- _recordTokenType('ImportDirective_combinator', combinator,
+ _recordKeyword('ImportDirective_combinator', combinator,
allowedKeywords: importKeywords);
}
super.visitImportDirective(node);
@@ -885,11 +880,11 @@
void visitMixinDeclaration(MixinDeclaration node) {
var context = 'name';
if (node.onClause != null) {
- _recordTokenType('MixinDeclaration_$context', node.onClause,
+ _recordKeyword('MixinDeclaration_$context', node.onClause,
allowedKeywords: [Keyword.ON]);
context = 'on';
}
- _recordTokenType('MixinDeclaration_$context', node.implementsClause,
+ _recordKeyword('MixinDeclaration_$context', node.implementsClause,
allowedKeywords: [Keyword.IMPLEMENTS]);
for (var member in node.members) {
@@ -993,9 +988,6 @@
void visitReturnStatement(ReturnStatement node) {
_recordDataForNode('ReturnStatement_expression', node.expression,
allowedKeywords: expressionKeywords);
- if (node.expression == null) {
- data.recordTokenType('ReturnStatement_expression', node.semicolon.type);
- }
super.visitReturnStatement(node);
}
@@ -1115,12 +1107,12 @@
void visitTryStatement(TryStatement node) {
var context = 'try';
for (var clause in node.catchClauses) {
- _recordTokenType('TryStatement_$context', clause,
+ _recordKeyword('TryStatement_$context', clause,
allowedKeywords: [Keyword.ON]);
context = 'catch';
}
if (node.finallyKeyword != null) {
- data.recordTokenType('TryStatement_$context', node.finallyKeyword.type);
+ data.recordKeyword('TryStatement_$context', node.finallyKeyword.type);
}
super.visitTryStatement(node);
}
@@ -1308,7 +1300,7 @@
void _recordDataForNode(String context, AstNode node,
{List<Keyword> allowedKeywords = noKeywords}) {
_recordElementKind(context, node);
- _recordTokenType(context, node, allowedKeywords: allowedKeywords);
+ _recordKeyword(context, node, allowedKeywords: allowedKeywords);
}
/// Record the element kind of the element associated with the left-most
@@ -1327,27 +1319,25 @@
}
}
- /// Record the token type of the left-most token that is a child of the
- /// [node] in the given [context].
- void _recordTokenType(String context, AstNode node,
+ /// If the left-most token of the [node] is a keyword, then record that it
+ /// occurred in the given [context].
+ void _recordKeyword(String context, AstNode node,
{List<Keyword> allowedKeywords = noKeywords}) {
if (node != null) {
var token = _leftMostToken(node);
- if (token != null) {
- var type = token.type;
- if (token.isKeyword && token.keyword.isBuiltInOrPseudo) {
- // These keywords can be used as identifiers, so determine whether it
- // is being used as a keyword or an identifier.
- if (!allowedKeywords.contains(token.keyword)) {
- type = TokenType.IDENTIFIER;
- }
+ if (token.isKeyword) {
+ var keyword = token.type;
+ if (keyword == Keyword.NEW) {
+ // We don't suggest `new`, so we don't care about the frequency with
+ // which it is used.
+ return;
+ } else if (token.keyword.isBuiltInOrPseudo &&
+ !allowedKeywords.contains(token.keyword)) {
+ // These keywords can be used as identifiers, so determine whether
+ // it is being used as a keyword or an identifier.
+ return;
}
- data.recordTokenType(context, type);
- if (node is Expression) {
- data.recordTokenType('Expression', type);
- } else if (node is Statement) {
- data.recordTokenType('Statement', type);
- }
+ data.recordKeyword(context, keyword);
}
}
}
@@ -1452,20 +1442,13 @@
RelevanceTableWriter(this.sink);
- int totalCount(Map<String, int> counts) {
- return counts.values
- .fold(0, (previousValue, value) => previousValue + value);
- }
-
void write(RelevanceData data) {
writeFileHeader();
writeElementKindTable(data);
+ writeKeywordTable(data);
}
void writeElementKindTable(RelevanceData data) {
- var byTokenType = data.byTokenType;
- var byElementKind = data.byElementKind;
-
sink.writeln();
sink.write('''
/// A table keyed by completion location and element kind whose values are the
@@ -1473,46 +1456,39 @@
const elementKindRelevance = {
''');
- var completionLocations = byElementKind.keys.toList()..sort();
+ var byKind = data.byKind;
+ var completionLocations = byKind.keys.toList()..sort();
for (var completionLocation in completionLocations) {
- var elementCounts = byElementKind[completionLocation];
- var identifierPercent = 1.0;
- var tokenCounts = byTokenType[completionLocation];
- if (tokenCounts != null) {
- var identifierCount = tokenCounts['IDENTIFIER'];
- if (identifierCount != null) {
- var totalTokenCount = totalCount(tokenCounts);
- identifierPercent = identifierCount / totalTokenCount;
+ var counts = byKind[completionLocation];
+ if (_hasElementKind(counts)) {
+ var totalCount = _totalCount(counts);
+ // TODO(brianwilkerson) If two element kinds have the same count they
+ // ought to have the same probability. This doesn't correctly do that.
+ var entries = counts.entries.toList()
+ ..sort((first, second) => first.value.compareTo(second.value));
+
+ sink.write(" '");
+ sink.write(completionLocation);
+ sink.writeln("': {");
+ var cumulativeCount = 0;
+ var lowerBound = 0.0;
+ for (var entry in entries) {
+ var kind = entry.key;
+ cumulativeCount += entry.value;
+ var upperBound = cumulativeCount / totalCount;
+ if (kind is _ElementKind) {
+ sink.write(' ElementKind.');
+ sink.write(kind.elementKind.name);
+ sink.write(': ProbabilityRange(lower: ');
+ sink.write(lowerBound.toStringAsFixed(3));
+ sink.write(', upper: ');
+ sink.write(upperBound.toStringAsFixed(3));
+ sink.writeln('),');
+ }
+ lowerBound = upperBound;
}
+ sink.writeln(' },');
}
- // TODO(brianwilkerson) This incorrectly assumes that identifiers are
- // always the most common first token in a completion location.
- var startOfBand = 1.0 - identifierPercent;
- sink.write(" '");
- sink.write(completionLocation);
- sink.writeln("': {");
- var totalElementCount = totalCount(elementCounts);
- var cumulativeCount = 0;
- // TODO(brianwilkerson) If two element kinds have the same count they
- // ought to have the same probability. This doesn't correctly do that.
- var entries = elementCounts.entries.toList()
- ..sort((first, second) => first.value.compareTo(second.value));
- var lowerBound = 0.0;
- for (var entry in entries) {
- var elementKind = entry.key;
- cumulativeCount += entry.value;
- var percentInBand = cumulativeCount / totalElementCount;
- var upperBound = (percentInBand * identifierPercent) + startOfBand;
- sink.write(' ElementKind.');
- sink.write(elementKind);
- sink.write(': ProbabilityRange(lower: ');
- sink.write(lowerBound.toStringAsFixed(3));
- sink.write(', upper: ');
- sink.write(upperBound.toStringAsFixed(3));
- sink.writeln('),');
- lowerBound = upperBound;
- }
- sink.writeln(' },');
}
sink.writeln('};');
}
@@ -1533,25 +1509,107 @@
import 'package:analyzer_plugin/protocol/protocol_common.dart';
''');
}
-}
-class Timer {
- Stopwatch stopwatch = Stopwatch();
+ void writeKeywordTable(RelevanceData data) {
+ sink.writeln();
+ sink.write('''
+/// A table keyed by completion location and keyword whose values are the
+/// ranges of the relevance of those keywords in those locations.
+const keywordRelevance = {
+''');
- int count = 0;
+ var byKind = data.byKind;
+ var completionLocations = byKind.keys.toList()..sort();
+ for (var completionLocation in completionLocations) {
+ var counts = byKind[completionLocation];
+ if (_hasKeyword(counts)) {
+ var totalCount = _totalCount(counts);
+ // TODO(brianwilkerson) If two keywords have the same count they ought to
+ // have the same probability. This doesn't correctly do that.
+ var entries = counts.entries.toList()
+ ..sort((first, second) => first.value.compareTo(second.value));
- Timer();
-
- double get averageTime => count == 0 ? 0 : totalTime / count;
-
- int get totalTime => stopwatch.elapsedMilliseconds;
-
- void start() {
- stopwatch.start();
+ sink.write(" '");
+ sink.write(completionLocation);
+ sink.writeln("': {");
+ var cumulativeCount = 0;
+ var lowerBound = 0.0;
+ for (var entry in entries) {
+ var kind = entry.key;
+ cumulativeCount += entry.value;
+ var upperBound = cumulativeCount / totalCount;
+ if (kind is _Keyword) {
+ sink.write(" '");
+ sink.write(kind.keyword.lexeme);
+ sink.write("': ProbabilityRange(lower: ");
+ sink.write(lowerBound.toStringAsFixed(3));
+ sink.write(', upper: ');
+ sink.write(upperBound.toStringAsFixed(3));
+ sink.writeln('),');
+ }
+ lowerBound = upperBound;
+ }
+ sink.writeln(' },');
+ }
+ }
+ sink.writeln('};');
}
- void stop() {
- stopwatch.stop();
- count++;
+ /// Return `true` if the table of [counts] contains at least one key that is
+ /// an element kind.
+ bool _hasElementKind(Map<_Kind, int> counts) {
+ for (var kind in counts.keys) {
+ if (kind is _ElementKind) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /// Return `true` if the table of [counts] contains at least one key that is a
+ /// keyword.
+ bool _hasKeyword(Map<_Kind, int> counts) {
+ for (var kind in counts.keys) {
+ if (kind is _Keyword) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /// Return the total of the counts in the given table of [counts].
+ int _totalCount(Map<_Kind, int> counts) {
+ return counts.values
+ .fold(0, (previousValue, value) => previousValue + value);
}
}
+
+/// A wrapper for an element kind to allow keywords and element kinds to be used
+/// as keys in a single table.
+class _ElementKind extends _Kind {
+ static final Map<ElementKind, _ElementKind> instances = {};
+
+ final ElementKind elementKind;
+
+ factory _ElementKind(ElementKind elementKind) =>
+ instances.putIfAbsent(elementKind, () => _ElementKind._(elementKind));
+
+ _ElementKind._(this.elementKind);
+}
+
+/// A wrapper for a keyword to allow keywords and element kinds to be used as
+/// keys in a single table.
+class _Keyword extends _Kind {
+ static final Map<Keyword, _Keyword> instances = {};
+
+ final Keyword keyword;
+
+ factory _Keyword(Keyword keyword) =>
+ instances.putIfAbsent(keyword, () => _Keyword._(keyword));
+
+ _Keyword._(this.keyword);
+}
+
+/// A superclass for [_ElementKind] and [_Keyword] to allow keywords and element
+/// kinds to be used as keys in a single table.
+class _Kind {}
diff --git a/pkg/analysis_tool/pubspec.yaml b/pkg/analysis_tool/pubspec.yaml
index 57ab3ee..32199b3 100644
--- a/pkg/analysis_tool/pubspec.yaml
+++ b/pkg/analysis_tool/pubspec.yaml
@@ -1,4 +1,5 @@
name: analysis_tool
+# This package is not intended for consumption on pub.dev. DO NOT publish.
publish_to: none
environment:
diff --git a/pkg/analyzer/lib/src/context/source.dart b/pkg/analyzer/lib/src/context/source.dart
index 0ced545..8f31594 100644
--- a/pkg/analyzer/lib/src/context/source.dart
+++ b/pkg/analyzer/lib/src/context/source.dart
@@ -11,6 +11,7 @@
import 'package:analyzer/src/generated/source.dart';
import 'package:analyzer/src/generated/utilities_dart.dart' as utils;
import 'package:analyzer/src/source/package_map_resolver.dart';
+import 'package:analyzer/src/workspace/package_build.dart';
/**
* Instances of the class `SourceFactory` resolve possibly relative URI's
@@ -49,9 +50,15 @@
@override
Map<String, List<Folder>> get packageMap {
- PackageMapUriResolver resolver = resolvers
- .firstWhere((r) => r is PackageMapUriResolver, orElse: () => null);
- return resolver?.packageMap;
+ for (var resolver in resolvers) {
+ if (resolver is PackageMapUriResolver) {
+ return resolver.packageMap;
+ }
+ if (resolver is PackageBuildPackageUriResolver) {
+ return resolver.packageMap;
+ }
+ }
+ return null;
}
@override
diff --git a/pkg/analyzer/lib/src/dart/analysis/feature_set_provider.dart b/pkg/analyzer/lib/src/dart/analysis/feature_set_provider.dart
index b73866b..8604562 100644
--- a/pkg/analyzer/lib/src/dart/analysis/feature_set_provider.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/feature_set_provider.dart
@@ -44,19 +44,13 @@
}
}
- if (uri.isScheme('package')) {
- var pathSegments = uri.pathSegments;
- if (pathSegments.isNotEmpty) {
- var packageName = pathSegments.first;
- var experiments = _allowedExperiments.forPackage(packageName);
- if (experiments != null) {
- return FeatureSet.fromEnableFlags(experiments);
- }
- }
- }
-
var package = _packages.packageForPath(path);
if (package != null) {
+ var experiments = _allowedExperiments.forPackage(package.name);
+ if (experiments != null) {
+ return FeatureSet.fromEnableFlags(experiments);
+ }
+
return _packageDefaultFeatureSet;
}
diff --git a/pkg/analyzer/lib/src/dart/error/hint_codes.dart b/pkg/analyzer/lib/src/dart/error/hint_codes.dart
index a9b2866..586a0a1 100644
--- a/pkg/analyzer/lib/src/dart/error/hint_codes.dart
+++ b/pkg/analyzer/lib/src/dart/error/hint_codes.dart
@@ -373,6 +373,46 @@
/**
* No parameters.
*/
+ // #### Description
+ //
+ // The analyzer produces this diagnostic when an element in a non-constant set
+ // is the same as a previous element in the same set. If two elements are the
+ // same, then the second value is ignored, which makes having both elements
+ // pointless and likely signals a bug.
+ //
+ // #### Example
+ //
+ // The following code produces this diagnostic because the element `1` appears
+ // twice:
+ //
+ // ```dart
+ // const a = 1;
+ // const b = 1;
+ // var s = <int>{a, [!b!]};
+ // ```
+ //
+ // #### Common fixes
+ //
+ // If both elements should be included in the set, then change one of the
+ // elements:
+ //
+ // ```dart
+ // const a = 1;
+ // const b = 2;
+ // var s = <int>{a, b};
+ // ```
+ //
+ // If only one of the elements is needed, then remove the one that isn't
+ // needed:
+ //
+ // ```dart
+ // const a = 1;
+ // var s = <int>{a};
+ // ```
+ //
+ // Note that literal sets preserve the order of their elements, so the choice
+ // of which element to remove might affect the order in which elements are
+ // returned by an iterator.
static const HintCode EQUAL_ELEMENTS_IN_SET = HintCode(
'EQUAL_ELEMENTS_IN_SET',
"Two elements in a set literal shouldn't be equal.",
@@ -381,6 +421,45 @@
/**
* No parameters.
*/
+ // #### Description
+ //
+ // The analyzer produces this diagnostic when a key in a non-constant map is
+ // the same as a previous key in the same map. If two keys are the same, then
+ // the second value overwrites the first value, which makes having both pairs
+ // pointless and likely signals a bug.
+ //
+ // #### Example
+ //
+ // The following code produces this diagnostic because the keys `a` and `b`
+ // have the same value:
+ //
+ // ```dart
+ // const a = 1;
+ // const b = 1;
+ // var m = <int, String>{a: 'a', [!b!]: 'b'};
+ // ```
+ //
+ // #### Common fixes
+ //
+ // If both entries should be included in the map, then change one of the keys:
+ //
+ // ```dart
+ // const a = 1;
+ // const b = 2;
+ // var m = <int, String>{a: 'a', b: 'b'};
+ // ```
+ //
+ // If only one of the entries is needed, then remove the one that isn't
+ // needed:
+ //
+ // ```dart
+ // const a = 1;
+ // var m = <int, String>{a: 'a'};
+ // ```
+ //
+ // Note that literal maps preserve the order of their entries, so the choice
+ // of which entry to remove might affect the order in which the keys and
+ // values are returned by an iterator.
static const HintCode EQUAL_KEYS_IN_MAP = HintCode(
'EQUAL_KEYS_IN_MAP', "Two keys in a map literal shouldn't be equal.",
correction: "Change or remove the duplicate key.");
diff --git a/pkg/analyzer/lib/src/dart/micro/analysis_context.dart b/pkg/analyzer/lib/src/dart/micro/analysis_context.dart
index 8254469..98faa15 100644
--- a/pkg/analyzer/lib/src/dart/micro/analysis_context.dart
+++ b/pkg/analyzer/lib/src/dart/micro/analysis_context.dart
@@ -190,7 +190,7 @@
@override
Future<ResolvedUnitResult> getResolvedUnit(String path) async {
- return analysisContext.fileResolver.resolve(path);
+ return analysisContext.fileResolver.resolve2(path: path);
}
@override
diff --git a/pkg/analyzer/lib/src/dart/micro/library_analyzer.dart b/pkg/analyzer/lib/src/dart/micro/library_analyzer.dart
index 54f9035..5c56a19 100644
--- a/pkg/analyzer/lib/src/dart/micro/library_analyzer.dart
+++ b/pkg/analyzer/lib/src/dart/micro/library_analyzer.dart
@@ -20,6 +20,7 @@
import 'package:analyzer/src/dart/element/inheritance_manager3.dart';
import 'package:analyzer/src/dart/element/type_provider.dart';
import 'package:analyzer/src/dart/micro/library_graph.dart';
+import 'package:analyzer/src/dart/micro/performance.dart';
import 'package:analyzer/src/dart/resolver/flow_analysis_visitor.dart';
import 'package:analyzer/src/dart/resolver/legacy_type_asserter.dart';
import 'package:analyzer/src/dart/resolver/resolution_visitor.dart';
@@ -45,15 +46,9 @@
import 'package:analyzer/src/summary2/linked_element_factory.dart';
import 'package:analyzer/src/task/strong/checker.dart';
import 'package:analyzer/src/workspace/workspace.dart';
+import 'package:meta/meta.dart';
import 'package:pub_semver/pub_semver.dart';
-var timerLibraryAnalyzer = Stopwatch();
-var timerLibraryAnalyzerConst = Stopwatch();
-var timerLibraryAnalyzerFreshUnit = Stopwatch();
-var timerLibraryAnalyzerResolve = Stopwatch();
-var timerLibraryAnalyzerSplicer = Stopwatch();
-var timerLibraryAnalyzerVerify = Stopwatch();
-
/**
* Analyzer of a single library.
*/
@@ -115,25 +110,17 @@
/**
* Compute analysis results for all units of the library.
*/
- Map<FileState, UnitAnalysisResult> analyze() {
- return PerformanceStatistics.analysis.makeCurrentWhile(() {
- return analyzeSync();
- });
- }
-
- /**
- * Compute analysis results for all units of the library.
- */
- Map<FileState, UnitAnalysisResult> analyzeSync() {
- timerLibraryAnalyzer.start();
+ Map<FileState, UnitAnalysisResult> analyzeSync({
+ @required CiderOperationPerformanceImpl performance,
+ }) {
Map<FileState, CompilationUnit> units = {};
// Parse all files.
- timerLibraryAnalyzerFreshUnit.start();
- for (FileState file in _library.libraryFiles) {
- units[file] = _parse(file);
- }
- timerLibraryAnalyzerFreshUnit.stop();
+ performance.run('parse', (performance) {
+ for (FileState file in _library.libraryFiles) {
+ units[file] = _parse(file);
+ }
+ });
// Resolve URIs in directives to corresponding sources.
FeatureSet featureSet = units[_library].featureSet;
@@ -147,29 +134,30 @@
_libraryScope = LibraryScope(_libraryElement);
- timerLibraryAnalyzerResolve.start();
- _resolveDirectives(units);
-
- units.forEach((file, unit) {
- _resolveFile(file, unit);
+ performance.run('resolveDirectives', (performance) {
+ _resolveDirectives(units);
});
- timerLibraryAnalyzerResolve.stop();
- timerLibraryAnalyzerConst.start();
- units.values.forEach(_findConstants);
- _clearConstantEvaluationResults();
- _computeConstants();
- timerLibraryAnalyzerConst.stop();
+ performance.run('resolveFiles', (performance) {
+ units.forEach((file, unit) {
+ _resolveFile(file, unit);
+ });
+ });
- timerLibraryAnalyzerVerify.start();
- PerformanceStatistics.errors.makeCurrentWhile(() {
+ performance.run('computeConstants', (performance) {
+ units.values.forEach(_findConstants);
+ _clearConstantEvaluationResults();
+ _computeConstants();
+ });
+
+ performance.run('computeVerifyErrors', (performance) {
units.forEach((file, unit) {
_computeVerifyErrors(file, unit);
});
});
if (_analysisOptions.hint) {
- PerformanceStatistics.hints.makeCurrentWhile(() {
+ performance.run('computeHints', (performance) {
units.forEach((file, unit) {
{
var visitor = GatherUsedLocalElementsVisitor(_libraryElement);
@@ -189,13 +177,11 @@
}
if (_analysisOptions.lint) {
- PerformanceStatistics.lints.makeCurrentWhile(() {
- var allUnits = _library.libraryFiles.map(
- (file) {
- var content = _getFileContent(file.path);
- return LinterContextUnit(content, units[file]);
- },
- ).toList();
+ performance.run('computeLints', (performance) {
+ var allUnits = _library.libraryFiles.map((file) {
+ var content = _getFileContent(file.path);
+ return LinterContextUnit(content, units[file]);
+ }).toList();
for (int i = 0; i < allUnits.length; i++) {
_computeLints(_library.libraryFiles[i], allUnits[i], allUnits);
}
@@ -204,8 +190,6 @@
assert(units.values.every(LegacyTypeAsserter.assertLegacyTypes));
- timerLibraryAnalyzerVerify.stop();
-
// Return full results.
Map<FileState, UnitAnalysisResult> results = {};
units.forEach((file, unit) {
@@ -213,7 +197,6 @@
errors = _filterIgnoredErrors(file, errors);
results[file] = UnitAnalysisResult(file, unit, errors);
});
- timerLibraryAnalyzer.stop();
return results;
}
diff --git a/pkg/analyzer/lib/src/dart/micro/performance.dart b/pkg/analyzer/lib/src/dart/micro/performance.dart
new file mode 100644
index 0000000..5a398f2
--- /dev/null
+++ b/pkg/analyzer/lib/src/dart/micro/performance.dart
@@ -0,0 +1,156 @@
+// 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:meta/meta.dart';
+
+/// The performance of an operation.
+abstract class CiderOperationPerformance {
+ /// The children operation, might be empty.
+ List<CiderOperationPerformance> get children;
+
+ /// The duration of the operation itself and its children.
+ Duration get elapsed;
+
+ /// The duration of the operation itself.
+ Duration get elapsedSelf;
+
+ /// The name of the operation.
+ String get name;
+
+ CiderOperationPerformance getChild(String name);
+
+ /// Write this operation and its children into the [buffer].
+ void write({
+ @required StringBuffer buffer,
+ String indent = '',
+ });
+}
+
+class CiderOperationPerformanceFixed implements CiderOperationPerformance {
+ @override
+ final String name;
+
+ @override
+ final Duration elapsedSelf;
+
+ CiderOperationPerformanceFixed(this.name, this.elapsedSelf);
+
+ @override
+ List<CiderOperationPerformance> get children => const [];
+
+ @override
+ Duration get elapsed => elapsedSelf;
+
+ @override
+ CiderOperationPerformance getChild(String name) {
+ return null;
+ }
+
+ @override
+ String toString() {
+ return '(name: $name, elapsed: $elapsed)';
+ }
+
+ @override
+ void write({StringBuffer buffer, String indent = ''}) {
+ buffer.writeln('$indent${toString()}');
+ }
+}
+
+class CiderOperationPerformanceImpl implements CiderOperationPerformance {
+ @override
+ final String name;
+
+ final Stopwatch _timer = Stopwatch();
+ final List<CiderOperationPerformance> _children = [];
+
+ CiderOperationPerformanceImpl(this.name);
+
+ @override
+ List<CiderOperationPerformance> get children {
+ return _children;
+ }
+
+ @override
+ Duration get elapsed {
+ return _timer.elapsed;
+ }
+
+ @override
+ Duration get elapsedSelf {
+ return elapsed - _elapsedChildren;
+ }
+
+ Duration get _elapsedChildren {
+ return children.fold<Duration>(
+ Duration.zero,
+ (sum, child) => sum + child.elapsed,
+ );
+ }
+
+ /// Add a new child with the known elapsed time.
+ ///
+ /// This method is used when we already measure performance using some other
+ /// mechanism, but want to add it to this performance hierarchy.
+ void addChildFixed(String name, Duration elapsed) {
+ _children.add(
+ CiderOperationPerformanceFixed(name, elapsed),
+ );
+ }
+
+ @override
+ CiderOperationPerformance getChild(String name) {
+ return children.firstWhere(
+ (child) => child.name == name,
+ orElse: () => null,
+ );
+ }
+
+ /// Run the [operation] as a new child.
+ T run<T>(
+ String name,
+ T Function(CiderOperationPerformanceImpl) operation,
+ ) {
+ var child = CiderOperationPerformanceImpl(name);
+ _children.add(child);
+ child._timer.start();
+
+ try {
+ return operation(child);
+ } finally {
+ child._timer.stop();
+ }
+ }
+
+ /// Run the [operation] as a new child.
+ Future<T> runAsync<T>(
+ String name,
+ Future<T> Function(CiderOperationPerformanceImpl) operation,
+ ) async {
+ var child = CiderOperationPerformanceImpl(name);
+ _children.add(child);
+ child._timer.start();
+
+ try {
+ return await operation(child);
+ } finally {
+ child._timer.stop();
+ }
+ }
+
+ @override
+ String toString() {
+ return '(name: $name, elapsed: $elapsed, elapsedSelf: $elapsedSelf)';
+ }
+
+ @override
+ void write({StringBuffer buffer, String indent = ''}) {
+ buffer.writeln('$indent${toString()}');
+
+ var childIndent = '$indent ';
+ for (var child in children) {
+ child.write(buffer: buffer, indent: childIndent);
+ }
+ }
+}
diff --git a/pkg/analyzer/lib/src/dart/micro/resolve_file.dart b/pkg/analyzer/lib/src/dart/micro/resolve_file.dart
index 74d6026..0117946 100644
--- a/pkg/analyzer/lib/src/dart/micro/resolve_file.dart
+++ b/pkg/analyzer/lib/src/dart/micro/resolve_file.dart
@@ -22,6 +22,7 @@
import 'package:analyzer/src/dart/micro/cider_byte_store.dart';
import 'package:analyzer/src/dart/micro/library_analyzer.dart';
import 'package:analyzer/src/dart/micro/library_graph.dart';
+import 'package:analyzer/src/dart/micro/performance.dart';
import 'package:analyzer/src/generated/engine.dart'
show AnalysisEngine, AnalysisOptionsImpl;
import 'package:analyzer/src/generated/source.dart';
@@ -146,12 +147,25 @@
_libraryContextReset.dispose();
}
+ @deprecated
ErrorsResult getErrors(String path) {
+ return getErrors2(path: path);
+ }
+
+ ErrorsResult getErrors2({
+ @required String path,
+ CiderOperationPerformanceImpl performance,
+ }) {
_throwIfNotAbsoluteNormalizedPath(path);
+ performance ??= CiderOperationPerformanceImpl('<default>');
+
return _withLibraryContextReset(() {
return logger.run('Get errors for $path', () {
- var fileContext = getFileContext(path);
+ var fileContext = getFileContext(
+ path: path,
+ performance: performance,
+ );
var file = fileContext.file;
var errorsSignatureBuilder = ApiSignature();
@@ -170,7 +184,10 @@
}
if (errors == null) {
- var unitResult = resolve(path);
+ var unitResult = resolve2(
+ path: path,
+ performance: performance,
+ );
errors = unitResult.errors;
bytes = CiderUnitErrorsBuilder(
@@ -192,12 +209,17 @@
});
}
- FileContext getFileContext(String path) {
- var analysisOptions = _getAnalysisOptions(path);
- _createContext(path, analysisOptions);
+ FileContext getFileContext({
+ @required String path,
+ @required CiderOperationPerformanceImpl performance,
+ }) {
+ return performance.run('fileContext', (performance) {
+ var analysisOptions = _getAnalysisOptions(path);
+ _createContext(path, analysisOptions);
- var file = fsState.getFileForPath(path);
- return FileContext(analysisOptions, file);
+ var file = fsState.getFileForPath(path);
+ return FileContext(analysisOptions, file);
+ });
}
String getLibraryLinkedSignature(String path) {
@@ -207,16 +229,31 @@
return file.libraryCycle.signatureStr;
}
+ @deprecated
ResolvedUnitResult resolve(String path) {
+ return resolve2(path: path);
+ }
+
+ ResolvedUnitResult resolve2({
+ @required String path,
+ CiderOperationPerformanceImpl performance,
+ }) {
_throwIfNotAbsoluteNormalizedPath(path);
+ performance ??= CiderOperationPerformanceImpl('<default>');
+
return _withLibraryContextReset(() {
return logger.run('Resolve $path', () {
- var fileContext = getFileContext(path);
+ var fileContext = getFileContext(
+ path: path,
+ performance: performance,
+ );
var file = fileContext.file;
var libraryFile = file.partOfLibrary ?? file;
- libraryContext.load2(libraryFile);
+ performance.run('libraryContext', (performance) {
+ libraryContext.load2(libraryFile);
+ });
testView?.addResolvedFile(path);
@@ -240,7 +277,11 @@
(String path) => resourceProvider.getFile(path).readAsStringSync(),
);
- results = libraryAnalyzer.analyzeSync();
+ results = performance.run('analyze', (performance) {
+ return libraryAnalyzer.analyzeSync(
+ performance: performance,
+ );
+ });
});
UnitAnalysisResult fileResult = results[file];
diff --git a/pkg/analyzer/lib/src/dart/resolver/function_expression_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/function_expression_resolver.dart
index 9dc3e92..bdf9dee 100644
--- a/pkg/analyzer/lib/src/dart/resolver/function_expression_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/function_expression_resolver.dart
@@ -52,7 +52,7 @@
var contextType = InferenceContext.getContext(node);
if (contextType is FunctionType) {
- contextType = _matchFunctionTypeParameters(
+ contextType = _matchTypeParameters(
node.typeParameters,
contextType,
);
@@ -151,44 +151,27 @@
return InferenceContext.getContext(body) ?? DynamicTypeImpl.instance;
}
- /// Given a downward inference type [fnType], and the declared
- /// [typeParameterList] for a function expression, determines if we can enable
- /// downward inference and if so, returns the function type to use for
- /// inference.
+ /// Given the downward inference [type], return the function type expressed
+ /// in terms of the type parameters from [typeParameterList].
///
- /// This will return null if inference is not possible. This happens when
- /// there is no way we can find a subtype of the function type, given the
- /// provided type parameter list.
- FunctionType _matchFunctionTypeParameters(
- TypeParameterList typeParameterList, FunctionType fnType) {
+ /// Return `null` is the number of element in [typeParameterList] is not
+ /// the same as the number of type parameters in the [type].
+ FunctionType _matchTypeParameters(
+ TypeParameterList typeParameterList, FunctionType type) {
if (typeParameterList == null) {
- if (fnType.typeFormals.isEmpty) {
- return fnType;
+ if (type.typeFormals.isEmpty) {
+ return type;
}
-
- // A non-generic function cannot be a subtype of a generic one.
return null;
}
- NodeList<TypeParameter> typeParameters = typeParameterList.typeParameters;
- if (fnType.typeFormals.isEmpty) {
- // TODO(jmesserly): this is a legal subtype. We don't currently infer
- // here, but we could. This is similar to
- // Dart2TypeSystem.inferFunctionTypeInstantiation, but we don't
- // have the FunctionType yet for the current node, so it's not quite
- // straightforward to apply.
+ var typeParameters = typeParameterList.typeParameters;
+ if (typeParameters.length != type.typeFormals.length) {
return null;
}
- if (fnType.typeFormals.length != typeParameters.length) {
- // A subtype cannot have different number of type formals.
- return null;
- }
-
- // Same number of type formals. Instantiate the function type so its
- // parameter and return type are in terms of the surrounding context.
- return fnType.instantiate(typeParameters.map((TypeParameter t) {
- return t.declaredElement.instantiate(
+ return type.instantiate(typeParameters.map((typeParameter) {
+ return typeParameter.declaredElement.instantiate(
nullabilitySuffix: _resolver.noneOrStarSuffix,
);
}).toList());
diff --git a/pkg/analyzer/lib/src/error/codes.dart b/pkg/analyzer/lib/src/error/codes.dart
index 754ed2f..0d7bb149b 100644
--- a/pkg/analyzer/lib/src/error/codes.dart
+++ b/pkg/analyzer/lib/src/error/codes.dart
@@ -43,9 +43,52 @@
"has type '{2}'.");
/**
- * 16.12.2 Const: It is a compile-time error if evaluation of a constant
- * object results in an uncaught exception being thrown.
+ * Parameters:
+ * 0: The type of the runtime value of the argument
+ * 1: The static type of the parameter
*/
+ // #### Description
+ //
+ // The analyzer produces this diagnostic when the runtime type of a constant
+ // value can't be assigned to the static type of a constant constructor's
+ // parameter.
+ //
+ // #### Example
+ //
+ // The following code produces this diagnostic because the runtime type of `i`
+ // is `int`, which can't be assigned to the static type of `s`:
+ //
+ // ```dart
+ // class C {
+ // final String s;
+ //
+ // const C(this.s);
+ // }
+ //
+ // const dynamic i = 0;
+ //
+ // void f() {
+ // const C([!i!]);
+ // }
+ // ```
+ //
+ // #### Common fixes
+ //
+ // Pass a value of the correct type to the constructor:
+ //
+ // ```dart
+ // class C {
+ // final String s;
+ //
+ // const C(this.s);
+ // }
+ //
+ // const dynamic i = 0;
+ //
+ // void f() {
+ // const C('$i');
+ // }
+ // ```
static const CheckedModeCompileTimeErrorCode
CONST_CONSTRUCTOR_PARAM_TYPE_MISMATCH = CheckedModeCompileTimeErrorCode(
'CONST_CONSTRUCTOR_PARAM_TYPE_MISMATCH',
@@ -2086,35 +2129,13 @@
hasPublishedDocs: true);
/**
- * 12.2 Null: It is a compile-time error for a class to attempt to extend or
- * implement Null.
- *
- * 12.3 Numbers: It is a compile-time error for a class to attempt to extend
- * or implement int.
- *
- * 12.3 Numbers: It is a compile-time error for a class to attempt to extend
- * or implement double.
- *
- * 12.3 Numbers: It is a compile-time error for any type other than the types
- * int and double to
- * attempt to extend or implement num.
- *
- * 12.4 Booleans: It is a compile-time error for a class to attempt to extend
- * or implement bool.
- *
- * 12.5 Strings: It is a compile-time error for a class to attempt to extend
- * or implement String.
- *
* Parameters:
- * 0: the name of the type that cannot be extended
- *
- * See [IMPLEMENTS_DISALLOWED_CLASS] and [MIXIN_OF_DISALLOWED_CLASS].
- *
- * TODO(scheglov) We might want to restore specific code with FrontEnd.
- * https://github.com/dart-lang/sdk/issues/31821
+ * 0: The name of the disallowed type
*/
static const CompileTimeErrorCode EXTENDS_DISALLOWED_CLASS =
- CompileTimeErrorCode(
+ // TODO(scheglov) We might want to restore specific code with FrontEnd.
+ // https://github.com/dart-lang/sdk/issues/31821
+ CompileTimeErrorCodeWithUniqueName('SUBTYPE_OF_DISALLOWED_TYPE',
'EXTENDS_DISALLOWED_CLASS', "Classes can't extend '{0}'.",
correction: "Try specifying a different superclass, or "
"removing the extends clause.");
@@ -2719,32 +2740,13 @@
"changing the import to not be deferred.");
/**
- * 12.2 Null: It is a compile-time error for a class to attempt to extend or
- * implement Null.
- *
- * 12.3 Numbers: It is a compile-time error for a class to attempt to extend
- * or implement int.
- *
- * 12.3 Numbers: It is a compile-time error for a class to attempt to extend
- * or implement double.
- *
- * 12.3 Numbers: It is a compile-time error for any type other than the types
- * int and double to
- * attempt to extend or implement num.
- *
- * 12.4 Booleans: It is a compile-time error for a class to attempt to extend
- * or implement bool.
- *
- * 12.5 Strings: It is a compile-time error for a class to attempt to extend
- * or implement String.
- *
* Parameters:
- * 0: the name of the type that cannot be implemented
- *
- * See [EXTENDS_DISALLOWED_CLASS].
+ * 0: The name of the disallowed type
*/
static const CompileTimeErrorCode IMPLEMENTS_DISALLOWED_CLASS =
- CompileTimeErrorCode('IMPLEMENTS_DISALLOWED_CLASS',
+ CompileTimeErrorCodeWithUniqueName(
+ 'SUBTYPE_OF_DISALLOWED_TYPE',
+ 'IMPLEMENTS_DISALLOWED_CLASS',
"Classes and mixins can't implement '{0}'.",
correction: "Try specifying a different interface, or "
"remove the class from the list.");
@@ -2836,7 +2838,8 @@
correction: "Try removing one of the occurrences.");
/**
- * No parameters.
+ * Parameters:
+ * 0: the name of the instance member
*/
// #### Description
//
@@ -2886,15 +2889,33 @@
// ```
static const CompileTimeErrorCode IMPLICIT_THIS_REFERENCE_IN_INITIALIZER =
CompileTimeErrorCode('IMPLICIT_THIS_REFERENCE_IN_INITIALIZER',
- "Only static members can be accessed in initializers.",
+ "The instance member '{0}' can't be accessed in an initializer.",
+ correction:
+ 'Try replacing the reference to the instance member with a '
+ 'different expression',
hasPublishedDocs: true);
/**
- * SDK implementation libraries can be imported only by other SDK libraries.
- *
* Parameters:
* 0: the uri pointing to a library
*/
+ // #### Description
+ //
+ // The analyzer produces this diagnostic when it finds an import whose `dart:`
+ // URI references an internal library.
+ //
+ // #### Example
+ //
+ // The following code produces this diagnostic because `_interceptors` is an
+ // internal library:
+ //
+ // ```dart
+ // import [!'dart:_interceptors'!];
+ // ```
+ //
+ // #### Common fixes
+ //
+ // Remove the import directive.
static const CompileTimeErrorCode IMPORT_INTERNAL_LIBRARY =
CompileTimeErrorCode('IMPORT_INTERNAL_LIBRARY',
"The library '{0}' is internal and can't be imported.");
@@ -2925,17 +2946,53 @@
"Case expressions must have the same types, '{0}' isn't a '{1}'.");
/**
- * If a class declaration does not have a member declaration with a
- * particular name, but some super-interfaces do have a member with that
- * name, it's a compile-time error if there is no signature among the
- * super-interfaces that is a valid override of all the other super-interface
- * signatures with the same name. That "most specific" signature becomes the
- * signature of the class's interface.
- *
* Parameters:
* 0: the name of the instance member with inconsistent inheritance.
* 1: the list of all inherited signatures for this member.
*/
+ // #### Description
+ //
+ // The analyzer produces this diagnostic when a class inherits two or more
+ // conflicting signatures for a member and doesn't provide an implementation
+ // that satisfies all the inherited signatures.
+ //
+ // #### Example
+ //
+ // The following code produces this diagnostic because `C` is inheriting the
+ // declaration of `m` from `A`, and that implementation isn't consistent with
+ // the signature of `m` that's inherited from `B`:
+ //
+ // ```dart
+ // class A {
+ // void m({int a}) {}
+ // }
+ //
+ // class B {
+ // void m({int b}) {}
+ // }
+ //
+ // class [!C!] extends A implements B {
+ // }
+ // ```
+ //
+ // #### Common fixes
+ //
+ // Add an implementation of the method that satisfies all the inherited
+ // signatures:
+ //
+ // ```dart
+ // class A {
+ // void m({int a}) {}
+ // }
+ //
+ // class B {
+ // void m({int b}) {}
+ // }
+ //
+ // class C extends A implements B {
+ // void m({int a, int b}) {}
+ // }
+ // ```
static const CompileTimeErrorCode INCONSISTENT_INHERITANCE =
CompileTimeErrorCode('INCONSISTENT_INHERITANCE',
"Superinterfaces don't have a valid override for '{0}': {1}.",
@@ -4076,31 +4133,11 @@
'MIXIN_INSTANTIATE', "Mixins can't be instantiated.");
/**
- * 12.2 Null: It is a compile-time error for a class to attempt to extend or
- * implement Null.
- *
- * 12.3 Numbers: It is a compile-time error for a class to attempt to extend
- * or implement int.
- *
- * 12.3 Numbers: It is a compile-time error for a class to attempt to extend
- * or implement double.
- *
- * 12.3 Numbers: It is a compile-time error for any type other than the types
- * int and double to attempt to extend or implement num.
- *
- * 12.4 Booleans: It is a compile-time error for a class to attempt to extend
- * or implement bool.
- *
- * 12.5 Strings: It is a compile-time error for a class to attempt to extend
- * or implement String.
- *
* Parameters:
- * 0: the name of the type that cannot be extended
- *
- * See [IMPLEMENTS_DISALLOWED_CLASS].
+ * 0: The name of the disallowed type
*/
static const CompileTimeErrorCode MIXIN_OF_DISALLOWED_CLASS =
- CompileTimeErrorCode(
+ CompileTimeErrorCodeWithUniqueName('SUBTYPE_OF_DISALLOWED_TYPE',
'MIXIN_OF_DISALLOWED_CLASS', "Classes can't mixin '{0}'.");
/**
@@ -4135,14 +4172,23 @@
'MIXIN_OF_NON_CLASS', "Classes can only mix in mixins and classes.",
hasPublishedDocs: true);
+ /**
+ * No parameters.
+ */
static const CompileTimeErrorCode
MIXIN_SUPER_CLASS_CONSTRAINT_DEFERRED_CLASS = CompileTimeErrorCode(
'MIXIN_SUPER_CLASS_CONSTRAINT_DEFERRED_CLASS',
"Deferred classes can't be used as super-class constraints.",
correction: "Try changing the import to not be deferred.");
+ /**
+ * Parameters:
+ * 0: The name of the disallowed type
+ */
static const CompileTimeErrorCode
- MIXIN_SUPER_CLASS_CONSTRAINT_DISALLOWED_CLASS = CompileTimeErrorCode(
+ MIXIN_SUPER_CLASS_CONSTRAINT_DISALLOWED_CLASS =
+ CompileTimeErrorCodeWithUniqueName(
+ 'SUBTYPE_OF_DISALLOWED_TYPE',
'MIXIN_SUPER_CLASS_CONSTRAINT_DISALLOWED_CLASS',
"'{0}' can't be used as a super-class constraint.");
@@ -4646,14 +4692,46 @@
correction: "Try removing the keyword 'const' from the map literal.");
/**
- * 15 Metadata: Metadata consists of a series of annotations, each of which
- * begin with the character @, followed by a constant expression that must be
- * either a reference to a compile-time constant variable, or a call to a
- * constant constructor.
- *
- * "From deferred library" case is covered by
- * [CompileTimeErrorCode.INVALID_ANNOTATION_FROM_DEFERRED_LIBRARY].
+ * No parameters.
*/
+ // #### Description
+ //
+ // The analyzer produces this diagnostic when an annotation is the invocation
+ // of an existing constructor even though the invoked constructor isn't a
+ // const constructor.
+ //
+ // #### Example
+ //
+ // The following code produces this diagnostic because the constructor for `C`
+ // isn't a const constructor:
+ //
+ // ```dart
+ // [!@C()!]
+ // void f() {
+ // }
+ //
+ // class C {
+ // C();
+ // }
+ // ```
+ //
+ // #### Common fixes
+ //
+ // If it's valid for the class to have a const constructor, then create a
+ // const constructor that can be used for the annotation:
+ //
+ // ```dart
+ // @C()
+ // void f() {
+ // }
+ //
+ // class C {
+ // const C();
+ // }
+ // ```
+ //
+ // If it isn't valid for the class to have a const constructor, then either
+ // remove the annotation or use a different class for the annotation.
static const CompileTimeErrorCode NON_CONSTANT_ANNOTATION_CONSTRUCTOR =
CompileTimeErrorCode('NON_CONSTANT_ANNOTATION_CONSTRUCTOR',
"Annotation creation can only call a const constructor.");
@@ -5411,9 +5489,43 @@
"Try renaming either the top-level element or the prefix.");
/**
- * 16.32 Identifier Reference: If d is a prefix p, a compile-time error
- * occurs unless the token immediately following d is '.'.
+ * Parameters:
+ * 0: The name of the prefix
*/
+ // #### Description
+ //
+ // The analyzer produces this diagnostic when an import prefix is used by
+ // itself, without accessing any of the names declared in the libraries
+ // associated with the prefix. Prefixes aren't variables, and therefore can't
+ // be used as a value.
+ //
+ // #### Example
+ //
+ // The following code produces this diagnostic because the prefix `math` is
+ // being used as if it were a variable:
+ //
+ // ```dart
+ // import 'dart:math' as math;
+ //
+ // void f() {
+ // print([!math!]);
+ // }
+ // ```
+ //
+ // #### Common fixes
+ //
+ // If the code is incomplete, then reference something in one of the libraries
+ // associated with the prefix:
+ //
+ // ```dart
+ // import 'dart:math' as math;
+ //
+ // void f() {
+ // print(math.pi);
+ // }
+ // ```
+ //
+ // If the name is wrong, then correct the name.
static const CompileTimeErrorCode PREFIX_IDENTIFIER_NOT_FOLLOWED_BY_DOT =
CompileTimeErrorCode(
'PREFIX_IDENTIFIER_NOT_FOLLOWED_BY_DOT',
@@ -5721,9 +5833,53 @@
"'throw' expression.");
/**
- * 13.12 Return: It is a compile-time error if a return statement of the form
- * <i>return e;</i> appears in a generative constructor.
+ * No parameters.
*/
+ // #### Description
+ //
+ // The analyzer produces this diagnostic when a generative constructor
+ // contains a return statement that specifies a value to be returned.
+ // Generative constructors always return the object that was created, and
+ // therefore can't return a different object.
+ //
+ // #### Example
+ //
+ // The following code produces this diagnostic because the return statement
+ // has an expression:
+ //
+ // ```dart
+ // class C {
+ // C() {
+ // return [!this!];
+ // }
+ // }
+ // ```
+ //
+ // #### Common fixes
+ //
+ // If the constructor should create a new instance, then remove either the
+ // return statement or the expression:
+ //
+ // ```dart
+ // class C {
+ // C();
+ // }
+ // ```
+ //
+ // If the constructor shouldn't create a new instance, then convert it to be a
+ // factory constructor:
+ //
+ // ```dart
+ // class C {
+ // factory C() {
+ // return _instance;
+ // }
+ //
+ // static C _instance = C._();
+ //
+ // C._();
+ // }
+ // ```
static const CompileTimeErrorCode RETURN_IN_GENERATIVE_CONSTRUCTOR =
CompileTimeErrorCode('RETURN_IN_GENERATIVE_CONSTRUCTOR',
"Constructors can't return values.",
@@ -6935,9 +7091,44 @@
"removing the modifier 'async*' from the function body.");
/**
- * 9 Functions: It is a static warning if the declared return type of a
- * function marked async may not be assigned to Future.
+ * No parameters.
*/
+ // #### Description
+ //
+ // The analyzer produces this diagnostic when the body of a function has the
+ // `async` modifier even though the return type of the function isn't
+ // assignable to `Future`.
+ //
+ // #### Example
+ //
+ // The following code produces this diagnostic because the body of the
+ // function `f` has the `async` modifier even though the return type isn't
+ // assignable to `Future`:
+ //
+ // ```dart
+ // [!int!] f() async {
+ // return 0;
+ // }
+ // ```
+ //
+ // #### Common fixes
+ //
+ // If the function should be asynchronous, then change the return type to be
+ // assignable to `Future`:
+ //
+ // ```dart
+ // Future<int> f() async {
+ // return 0;
+ // }
+ // ```
+ //
+ // If the function should be synchronous, then remove the `async` modifier:
+ //
+ // ```dart
+ // int f() {
+ // return 0;
+ // }
+ // ```
static const StaticTypeWarningCode ILLEGAL_ASYNC_RETURN_TYPE =
StaticTypeWarningCode(
'ILLEGAL_ASYNC_RETURN_TYPE',
@@ -7446,17 +7637,49 @@
correction: "Try using a type that is or is a subclass of '{1}'.");
/**
- * 12.17 Getter Invocation: It is a static warning if there is no class
- * <i>C</i> in the enclosing lexical scope of <i>i</i>, or if <i>C</i> does
- * not declare, implicitly or explicitly, a getter named <i>m</i>.
- *
* Parameters:
* 0: the name of the enumeration constant that is not defined
* 1: the name of the enumeration used to access the constant
*/
+ // #### Description
+ //
+ // The analyzer produces this diagnostic when it encounters an identifier that
+ // appears to be the name of an enum constant, and the name either isn't
+ // defined or isn't visible in the scope in which it's being referenced.
+ //
+ // #### Examples
+ //
+ // The following code produces this diagnostic because `E` doesn't define a
+ // constant named `c`:
+ //
+ // ```dart
+ // enum E {a, b}
+ //
+ // var e = E.[!c!];
+ // ```
+ //
+ // #### Common fixes
+ //
+ // If the constant should be defined, then add it to the declaration of the
+ // enum:
+ //
+ // ```dart
+ // enum E {a, b, c}
+ //
+ // var e = E.c;
+ // ```
+ //
+ // If the constant shouldn't be defined, then change the name to the name of
+ // an existing constant:
+ //
+ // ```dart
+ // enum E {a, b}
+ //
+ // var e = E.b;
+ // ```
static const StaticTypeWarningCode UNDEFINED_ENUM_CONSTANT =
StaticTypeWarningCode('UNDEFINED_ENUM_CONSTANT',
- "There is no constant named '{0}' in '{1}'.",
+ "There's no constant named '{0}' in '{1}'.",
correction:
"Try correcting the name to the name of an existing constant, or "
"defining a constant named '{0}'.");
@@ -8613,15 +8836,54 @@
CompileTimeErrorCode.EXTRA_POSITIONAL_ARGUMENTS_COULD_BE_NAMED;
/**
- * 5. Variables: It is a static warning if a final instance variable that has
- * been initialized at its point of declaration is also initialized in a
- * constructor.
+ * No parameters.
*/
+ // #### Description
+ //
+ // The analyzer produces this diagnostic when a final field is initialized in
+ // both the declaration of the field and in an initializer in a constructor.
+ // Final fields can only be assigned once, so it can't be initialized in both
+ // places.
+ //
+ // #### Example
+ //
+ // The following code produces this diagnostic because `f` is :
+ //
+ // ```dart
+ // class C {
+ // final int f = 0;
+ // C() : [!f!] = 1;
+ // }
+ // ```
+ //
+ // #### Common fixes
+ //
+ // If the initialization doesn't depend on any values passed to the
+ // constructor, and if all of the constructors need to initialize the field to
+ // the same value, then remove the initializer from the constructor:
+ //
+ // ```dart
+ // class C {
+ // final int f = 0;
+ // C();
+ // }
+ // ```
+ //
+ // If the initialization depends on a value passed to the constructor, or if
+ // different constructors need to initialize the field differently, then
+ // remove the initializer in the field's declaration:
+ //
+ // ```dart
+ // class C {
+ // final int f;
+ // C() : f = 1;
+ // }
+ // ```
static const StaticWarningCode
FIELD_INITIALIZED_IN_INITIALIZER_AND_DECLARATION = StaticWarningCode(
'FIELD_INITIALIZED_IN_INITIALIZER_AND_DECLARATION',
"Fields can't be initialized in the constructor if they are final "
- "and have already been initialized at their declaration.",
+ "and were already initialized at their declaration.",
correction: "Try removing one of the initializations.");
/**
@@ -8640,22 +8902,52 @@
correction: "Try removing one of the initializations.");
/**
- * 7.6.1 Generative Constructors: Execution of an initializer of the form
- * <b>this</b>.<i>v</i> = <i>e</i> proceeds as follows: First, the expression
- * <i>e</i> is evaluated to an object <i>o</i>. Then, the instance variable
- * <i>v</i> of the object denoted by this is bound to <i>o</i>.
- *
- * 12.14.2 Binding Actuals to Formals: Let <i>T<sub>i</sub></i> be the static
- * type of <i>a<sub>i</sub></i>, let <i>S<sub>i</sub></i> be the type of
- * <i>p<sub>i</sub>, 1 <= i <= n+k</i> and let <i>S<sub>q</sub></i> be
- * the type of the named parameter <i>q</i> of <i>f</i>. It is a static
- * warning if <i>T<sub>j</sub></i> may not be assigned to <i>S<sub>j</sub>, 1
- * <= j <= m</i>.
- *
* Parameters:
* 0: the name of the type of the initializer expression
* 1: the name of the type of the field
*/
+ // #### Description
+ //
+ // The analyzer produces this diagnostic when the initializer list of a
+ // constructor initializes a field to a value that isn't assignable to the
+ // field.
+ //
+ // #### Example
+ //
+ // The following code produces this diagnostic because `0` has the type `int`,
+ // and an `int` can't be assigned to a field of type `String`:
+ //
+ // ```dart
+ // class C {
+ // String s;
+ //
+ // C() : s = [!0!];
+ // }
+ // ```
+ //
+ // #### Common fixes
+ //
+ // If the type of the field is correct, then change the value assigned to it
+ // so that the value has a valid type:
+ //
+ // ```dart
+ // class C {
+ // String s;
+ //
+ // C() : s = '0';
+ // }
+ // ```
+ //
+ // If the type of the value is correct, then change the type of the field to
+ // allow the assignment:
+ //
+ // ```dart
+ // class C {
+ // int s;
+ //
+ // C() : s = 0;
+ // }
+ // ```
static const StaticWarningCode FIELD_INITIALIZER_NOT_ASSIGNABLE =
StaticWarningCode(
'FIELD_INITIALIZER_NOT_ASSIGNABLE',
@@ -9587,14 +9879,41 @@
CompileTimeErrorCode.NOT_ENOUGH_POSITIONAL_ARGUMENTS;
/**
- * 14.3 Parts: It is a static warning if the referenced part declaration
- * <i>p</i> names a library other than the current library as the library to
- * which <i>p</i> belongs.
- *
* Parameters:
* 0: the name of expected library name
* 1: the non-matching actual library name from the "part of" declaration
*/
+ // #### Description
+ //
+ // The analyzer produces this diagnostic when a library attempts to include a
+ // file as a part of itself when the other file is a part of a different
+ // library.
+ //
+ // #### Example
+ //
+ // Given a file named `part.dart` containing
+ //
+ // ```dart
+ // %uri="package:a/part.dart"
+ // part of 'library.dart';
+ // ```
+ //
+ // The following code, in any file other than `library.dart`, produces this
+ // diagnostic because it attempts to include `part.dart` as a part of itself
+ // when `part.dart` is a part of a different library:
+ //
+ // ```dart
+ // part [!'package:a/part.dart'!];
+ // ```
+ //
+ // #### Common fixes
+ //
+ // If the library should be using a different file as a part, then change the
+ // URI in the part directive to be the URI of the other file.
+ //
+ // If the part file should be a part of this library, then update the URI (or
+ // library name) in the part-of directive to be the URI (or name) of the
+ // correct library.
static const StaticWarningCode PART_OF_DIFFERENT_LIBRARY = StaticWarningCode(
'PART_OF_DIFFERENT_LIBRARY',
"Expected this library to be part of '{0}', not '{1}'.",
@@ -9850,9 +10169,55 @@
hasPublishedDocs: true);
/**
- * 13.9 Switch: It is a static warning if the type of <i>e</i> may not be
- * assigned to the type of <i>e<sub>k</sub></i>.
+ * Parameters:
+ * 0: The static type of the switch expression
+ * 1: The static type of the case expressions
*/
+ // #### Description
+ //
+ // The analyzer produces this diagnostic when the type of the expression in a
+ // switch statement isn't assignable to the type of the expressions in the
+ // case clauses.
+ //
+ // #### Example
+ //
+ // The following code produces this diagnostic because the type of `s`
+ // (`String`) isn't assignable to the type of `0` (`int`):
+ //
+ // ```dart
+ // void f(String s) {
+ // switch ([!s!]) {
+ // case 0:
+ // break;
+ // }
+ // }
+ // ```
+ //
+ // #### Common fixes
+ //
+ // If the type of the case expressions is correct, then change the expression
+ // in the switch statement to have the correct type:
+ //
+ // ```dart
+ // void f(String s) {
+ // switch (int.parse(s)) {
+ // case 0:
+ // break;
+ // }
+ // }
+ // ```
+ //
+ // If the type of the switch expression is correct, then change the case
+ // expressions to have the correct type:
+ //
+ // ```dart
+ // void f(String s) {
+ // switch (s) {
+ // case '0':
+ // break;
+ // }
+ // }
+ // ```
static const StaticWarningCode SWITCH_EXPRESSION_NOT_ASSIGNABLE =
StaticWarningCode(
'SWITCH_EXPRESSION_NOT_ASSIGNABLE',
@@ -9992,8 +10357,32 @@
isUnresolvedIdentifier: true);
/**
- * If the identifier is 'await', be helpful about it.
+ * No parameters.
*/
+ // #### Description
+ //
+ // The analyzer produces this diagnostic when the name `await` is used in a
+ // method or function body without being declared, and the body isn't marked
+ // with the `async` keyword. The name `await` only introduces an await
+ // expression in an asynchronous function.
+ //
+ // #### Example
+ //
+ // The following code produces this diagnostic because the name `await` is
+ // used in the body of `f` even though the body of `f` isn't marked with the
+ // `async` keyword:
+ //
+ // ```dart
+ // void f(p) { [!await!] p; }
+ // ```
+ //
+ // #### Common fixes
+ //
+ // Add the keyword `async` to the function body:
+ //
+ // ```dart
+ // void f(p) async { await p; }
+ // ```
static const StaticWarningCode UNDEFINED_IDENTIFIER_AWAIT = StaticWarningCode(
'UNDEFINED_IDENTIFIER_AWAIT',
"Undefined name 'await' in function body not marked with 'async'.",
diff --git a/pkg/analyzer/lib/src/generated/error_verifier.dart b/pkg/analyzer/lib/src/generated/error_verifier.dart
index 17069da..2cab966 100644
--- a/pkg/analyzer/lib/src/generated/error_verifier.dart
+++ b/pkg/analyzer/lib/src/generated/error_verifier.dart
@@ -2869,7 +2869,8 @@
} else {
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.IMPLICIT_THIS_REFERENCE_IN_INITIALIZER,
- identifier);
+ identifier,
+ [identifier.name]);
}
}
diff --git a/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart b/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart
index 0ca7c3f..d44f818 100644
--- a/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart
+++ b/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart
@@ -1020,9 +1020,9 @@
'''
library dart.math;
-const double E = 2.718281828459045;
-const double PI = 3.1415926535897932;
-const double LN10 = 2.302585092994046;
+const double e = 2.718281828459045;
+const double pi = 3.1415926535897932;
+const double ln10 = 2.302585092994046;
T min<T extends num>(T a, T b) => throw 0;
T max<T extends num>(T a, T b) => throw 0;
diff --git a/pkg/analyzer/lib/src/workspace/package_build.dart b/pkg/analyzer/lib/src/workspace/package_build.dart
index 1fc9340..effc784 100644
--- a/pkg/analyzer/lib/src/workspace/package_build.dart
+++ b/pkg/analyzer/lib/src/workspace/package_build.dart
@@ -60,6 +60,8 @@
: _workspace = workspace,
_context = workspace.provider.pathContext;
+ Map<String, List<Folder>> get packageMap => _workspace._packageMap;
+
@override
Source resolveAbsolute(Uri _ignore, [Uri uri]) {
uri ??= _ignore;
diff --git a/pkg/analyzer/test/src/dart/analysis/feature_set_provider_test.dart b/pkg/analyzer/test/src/dart/analysis/feature_set_provider_test.dart
index 0696a67..f5328f8 100644
--- a/pkg/analyzer/test/src/dart/analysis/feature_set_provider_test.dart
+++ b/pkg/analyzer/test/src/dart/analysis/feature_set_provider_test.dart
@@ -85,8 +85,12 @@
);
_assertNonNullableForPath('/packages/aaa/lib/a.dart', true);
+ _assertNonNullableForPath('/packages/aaa/bin/b.dart', true);
+ _assertNonNullableForPath('/packages/aaa/test/c.dart', true);
- _assertNonNullableForPath('/packages/bbb/lib/b.dart', false);
+ _assertNonNullableForPath('/packages/bbb/lib/a.dart', false);
+ _assertNonNullableForPath('/packages/bbb/bin/b.dart', false);
+ _assertNonNullableForPath('/packages/bbb/test/c.dart', false);
_assertNonNullableForPath('/other/file.dart', false);
}
diff --git a/pkg/analyzer/test/src/dart/analysis/search_test.dart b/pkg/analyzer/test/src/dart/analysis/search_test.dart
index 47d262f..ec7fe9f 100644
--- a/pkg/analyzer/test/src/dart/analysis/search_test.dart
+++ b/pkg/analyzer/test/src/dart/analysis/search_test.dart
@@ -541,10 +541,10 @@
test_searchReferences_ImportElement_noPrefix() async {
await _resolveTestUnit('''
-import 'dart:math' show max, PI, Random hide min;
-export 'dart:math' show max, PI, Random hide min;
+import 'dart:math' show max, pi, Random hide min;
+export 'dart:math' show max, pi, Random hide min;
main() {
- print(PI);
+ print(pi);
print(new Random());
print(max(1, 2));
}
@@ -555,7 +555,7 @@
var bar = findElement.function('bar');
var kind = SearchResultKind.REFERENCE;
var expected = [
- _expectId(main, kind, 'PI);', length: 0),
+ _expectId(main, kind, 'pi);', length: 0),
_expectId(main, kind, 'Random()', length: 0),
_expectId(main, kind, 'max(', length: 0),
_expectId(bar, kind, 'Random bar()', length: 0),
@@ -566,10 +566,10 @@
test_searchReferences_ImportElement_noPrefix_inPackage() async {
testFile = convertPath('/aaa/lib/a.dart');
await _resolveTestUnit('''
-import 'dart:math' show max, PI, Random hide min;
-export 'dart:math' show max, PI, Random hide min;
+import 'dart:math' show max, pi, Random hide min;
+export 'dart:math' show max, pi, Random hide min;
main() {
- PI;
+ pi;
new Random();
max(1, 2);
}
@@ -580,7 +580,7 @@
var bar = findElement.function('bar');
var kind = SearchResultKind.REFERENCE;
var expected = [
- _expectId(main, kind, 'PI;', length: 0),
+ _expectId(main, kind, 'pi;', length: 0),
_expectId(main, kind, 'Random();', length: 0),
_expectId(main, kind, 'max(1, 2);', length: 0),
_expectId(bar, kind, 'Random bar()', length: 0),
@@ -590,10 +590,10 @@
test_searchReferences_ImportElement_withPrefix() async {
await _resolveTestUnit('''
-import 'dart:math' as math show max, PI, Random hide min;
-export 'dart:math' show max, PI, Random hide min;
+import 'dart:math' as math show max, pi, Random hide min;
+export 'dart:math' show max, pi, Random hide min;
main() {
- print(math.PI);
+ print(math.pi);
print(new math.Random());
print(math.max(1, 2));
}
@@ -605,7 +605,7 @@
var kind = SearchResultKind.REFERENCE;
var length = 'math.'.length;
var expected = [
- _expectId(main, kind, 'math.PI);', length: length),
+ _expectId(main, kind, 'math.pi);', length: length),
_expectId(main, kind, 'math.Random()', length: length),
_expectId(main, kind, 'math.max(', length: length),
_expectId(bar, kind, 'math.Random bar()', length: length),
@@ -1728,19 +1728,19 @@
class B = Object with A;
mixin C {}
typedef D();
-e() {}
-var f = null;
+f() {}
+var g = null;
class NoMatchABCDEF {}
''');
var a = findElement.class_('A');
var b = findElement.class_('B');
var c = findElement.mixin('C');
var d = findElement.functionTypeAlias('D');
- var e = findElement.function('e');
- var f = findElement.topVar('f');
- RegExp regExp = RegExp(r'^[ABCDef]$');
+ var f = findElement.function('f');
+ var g = findElement.topVar('g');
+ RegExp regExp = RegExp(r'^[ABCDfg]$');
expect(await driver.search.topLevelElements(regExp),
- unorderedEquals([a, b, c, d, e, f]));
+ unorderedEquals([a, b, c, d, f, g]));
}
ExpectedResult _expectId(
diff --git a/pkg/analyzer/test/src/dart/micro/file_resolution.dart b/pkg/analyzer/test/src/dart/micro/file_resolution.dart
index 395f676..27b5316 100644
--- a/pkg/analyzer/test/src/dart/micro/file_resolution.dart
+++ b/pkg/analyzer/test/src/dart/micro/file_resolution.dart
@@ -58,12 +58,12 @@
ErrorsResult getTestErrors() {
var path = convertPath(_testFile);
- return fileResolver.getErrors(path);
+ return fileResolver.getErrors2(path: path);
}
@override
Future<ResolvedUnitResult> resolveFile(String path) async {
- return fileResolver.resolve(path);
+ return fileResolver.resolve2(path: path);
}
@override
diff --git a/pkg/analyzer/test/src/dart/micro/simple_file_resolver_test.dart b/pkg/analyzer/test/src/dart/micro/simple_file_resolver_test.dart
index de3b2ed..40a6e38 100644
--- a/pkg/analyzer/test/src/dart/micro/simple_file_resolver_test.dart
+++ b/pkg/analyzer/test/src/dart/micro/simple_file_resolver_test.dart
@@ -46,20 +46,20 @@
''');
// First time we refresh everything.
- await fileResolver.resolve(cPath);
+ await fileResolver.resolve2(path: cPath);
_assertRefreshedFiles([aPath, bPath, cPath], withSdk: true);
// Without changes we refresh nothing.
- await fileResolver.resolve(cPath);
+ await fileResolver.resolve2(path: cPath);
_assertRefreshedFiles([]);
// We already know a.dart, refresh nothing.
- await fileResolver.resolve(aPath);
+ await fileResolver.resolve2(path: aPath);
_assertRefreshedFiles([]);
// Change a.dart, refresh a.dart and c.dart, but not b.dart
fileResolver.changeFile(aPath);
- await fileResolver.resolve(cPath);
+ await fileResolver.resolve2(path: cPath);
_assertRefreshedFiles([aPath, cPath]);
}
@@ -74,7 +74,7 @@
B b;
''');
- result = fileResolver.resolve(bPath);
+ result = fileResolver.resolve2(path: bPath);
assertErrorsInResolvedUnit(result, [
error(CompileTimeErrorCode.UNDEFINED_CLASS, 22, 1),
]);
@@ -85,7 +85,7 @@
''');
fileResolver.changeFile(aPath);
- result = fileResolver.resolve(bPath);
+ result = fileResolver.resolve2(path: bPath);
assertErrorsInResolvedUnit(result, []);
}
@@ -107,17 +107,17 @@
''');
// First time we refresh everything.
- await fileResolver.resolve(bPath);
+ await fileResolver.resolve2(path: bPath);
_assertRefreshedFiles([aPath, bPath], withSdk: true);
// Change b.dart, refresh a.dart
fileResolver.changeFile(bPath);
- await fileResolver.resolve(bPath);
+ await fileResolver.resolve2(path: bPath);
_assertRefreshedFiles([aPath, bPath]);
// now with c.dart
- await fileResolver.resolve(cPath);
+ await fileResolver.resolve2(path: cPath);
_assertRefreshedFiles([cPath]);
fileResolver.changeFile(bPath);
- await fileResolver.resolve(cPath);
+ await fileResolver.resolve2(path: cPath);
_assertRefreshedFiles([aPath, bPath, cPath]);
}
diff --git a/pkg/analyzer/test/src/dart/resolution/language_version_test.dart b/pkg/analyzer/test/src/dart/resolution/language_version_test.dart
index 685009e..2d30010 100644
--- a/pkg/analyzer/test/src/dart/resolution/language_version_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/language_version_test.dart
@@ -8,6 +8,8 @@
import 'package:analyzer/src/dart/error/syntactic_errors.dart';
import 'package:analyzer/src/error/codes.dart';
import 'package:analyzer/src/generated/engine.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:test_reflective_loader/test_reflective_loader.dart';
@@ -62,7 +64,7 @@
var x = 0;
var y = a;
-var z = PI;
+var z = pi;
''');
assertType(findElement.topVar('x').type, 'int*');
assertType(findElement.topVar('y').type, 'int*');
@@ -99,7 +101,7 @@
var x = 0;
var y = a;
-var z = PI;
+var z = pi;
''');
assertType(findElement.topVar('x').type, 'int');
assertType(findElement.topVar('y').type, 'int');
@@ -115,7 +117,40 @@
@override
bool get typeToStringWithNullability => true;
- test_jsonConfig_disable() async {
+ test_jsonConfig_disable_bin() async {
+ _configureAllowedExperimentsTestNullSafety();
+
+ _configureTestWithJsonConfig('''
+{
+ "configVersion": 2,
+ "packages": [
+ {
+ "name": "test",
+ "rootUri": "../",
+ "packageUri": "lib/",
+ "languageVersion": "2.8"
+ }
+ ]
+}
+''');
+
+ var path = convertPath('/test/bin/a.dart');
+ await _resolveFile(path, r'''
+var x = 0;
+''');
+ assertErrorsInList(result.errors, []);
+ assertType(findElement.topVar('x').type, 'int*');
+
+ // Upgrade the language version to `2.9`, so enabled Null Safety.
+ driver.changeFile(path);
+ await _resolveFile(path, r'''
+// @dart = 2.9
+var x = 0;
+''');
+ assertType(findElement.topVar('x').type, 'int');
+ }
+
+ test_jsonConfig_disable_lib() async {
_configureAllowedExperimentsTestNullSafety();
_configureTestWithJsonConfig('''
@@ -146,7 +181,39 @@
assertType(findElement.topVar('x').type, 'int');
}
- test_jsonConfig_enable() async {
+ test_jsonConfig_enable_bin() async {
+ _configureAllowedExperimentsTestNullSafety();
+
+ _configureTestWithJsonConfig('''
+{
+ "configVersion": 2,
+ "packages": [
+ {
+ "name": "test",
+ "rootUri": "../",
+ "packageUri": "lib/"
+ }
+ ]
+}
+''');
+
+ var path = convertPath('/test/bin/a.dart');
+ await _resolveFile(path, r'''
+var x = 0;
+''');
+ assertErrorsInList(result.errors, []);
+ assertType(findElement.topVar('x').type, 'int');
+
+ // Downgrade the version to `2.8`, so disable Null Safety.
+ driver.changeFile(path);
+ await _resolveFile(path, r'''
+// @dart = 2.8
+var x = 0;
+''');
+ assertType(findElement.topVar('x').type, 'int*');
+ }
+
+ test_jsonConfig_enable_lib() async {
_configureAllowedExperimentsTestNullSafety();
_configureTestWithJsonConfig('''
@@ -203,8 +270,18 @@
}
void _newSdkExperimentsFile(String content) {
- newFile('$sdkRoot/lib/_internal/allowed_experiments.json',
- content: content);
+ newFile(
+ '$sdkRoot/lib/_internal/allowed_experiments.json',
+ content: content,
+ );
+ }
+
+ Future<void> _resolveFile(String path, String content) async {
+ newFile(path, content: content);
+ result = await resolveFile(path);
+
+ findNode = FindNode(result.content, result.unit);
+ findElement = FindElement(result.unit);
}
}
diff --git a/pkg/analyzer/test/src/diagnostics/unnecessary_cast_test.dart b/pkg/analyzer/test/src/diagnostics/unnecessary_cast_test.dart
index 97a4925..79c6f59 100644
--- a/pkg/analyzer/test/src/diagnostics/unnecessary_cast_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/unnecessary_cast_test.dart
@@ -27,13 +27,11 @@
}
test_dynamic_type() async {
- await assertErrorsInCode(r'''
+ await assertNoErrorsInCode(r'''
m(v) {
- var b = v as Object;
+ v as Object;
}
-''', [
- error(HintCode.UNUSED_LOCAL_VARIABLE, 13, 1),
- ]);
+''');
}
test_function() async {
@@ -46,19 +44,16 @@
}
test_function2() async {
- await assertErrorsInCode(r'''
+ await assertNoErrorsInCode(r'''
class A {}
class B<T extends A> {
void foo() {
T Function(T) f;
- A Function(A) g;
- g = f as A Function(A);
+ f as A Function(A);
}
}
-''', [
- error(HintCode.UNUSED_LOCAL_VARIABLE, 89, 1),
- ]);
+''');
}
test_generics() async {
@@ -90,34 +85,30 @@
}
test_type_dynamic() async {
- await assertErrorsInCode(r'''
+ await assertNoErrorsInCode(r'''
m(v) {
- var b = Object as dynamic;
+ Object as dynamic;
}
-''', [
- error(HintCode.UNUSED_LOCAL_VARIABLE, 13, 1),
- ]);
+''');
}
test_type_supertype() async {
await assertErrorsInCode(r'''
m(int i) {
- var b = i as Object;
+ i as Object;
}
''', [
- error(HintCode.UNUSED_LOCAL_VARIABLE, 17, 1),
- error(HintCode.UNNECESSARY_CAST, 21, 11),
+ error(HintCode.UNNECESSARY_CAST, 13, 11),
]);
}
test_type_type() async {
await assertErrorsInCode(r'''
m(num i) {
- var b = i as num;
+ i as num;
}
''', [
- error(HintCode.UNUSED_LOCAL_VARIABLE, 17, 1),
- error(HintCode.UNNECESSARY_CAST, 21, 8),
+ error(HintCode.UNNECESSARY_CAST, 13, 8),
]);
}
}
diff --git a/pkg/analyzer/test/verify_diagnostics_test.dart b/pkg/analyzer/test/verify_diagnostics_test.dart
index 5cd6fd9..ee476e0 100644
--- a/pkg/analyzer/test/verify_diagnostics_test.dart
+++ b/pkg/analyzer/test/verify_diagnostics_test.dart
@@ -40,6 +40,10 @@
static const List<String> unverifiedDocs = [
// Produces two diagnostics when it should only produce one.
'CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_TYPE',
+ // Produces two diagnostics when it should only produce one. We could get
+ // rid of the invalid error by adding a declaration of a top-level variable
+ // (such as `JSBool b;`), but that would complicate the example.
+ 'CompileTimeErrorCode.IMPORT_INTERNAL_LIBRARY',
// Produces two diagnostics when it should only produce one.
'CompileTimeErrorCode.INVALID_URI',
// Need a way to make auxiliary files that (a) are not included in the
@@ -51,6 +55,8 @@
'StaticWarningCode.AMBIGUOUS_IMPORT',
// Produces two diagnostics when it should only produce one.
'StaticWarningCode.INVALID_USE_OF_NULL_VALUE',
+ // Produces the diagnostic HintCode.UNUSED_LOCAL_VARIABLE when it shouldn't.
+ 'StaticWarningCode.UNDEFINED_IDENTIFIER_AWAIT',
];
/// The prefix used on directive lines to specify the experiments that should
diff --git a/pkg/analyzer/tool/diagnostics/diagnostics.md b/pkg/analyzer/tool/diagnostics/diagnostics.md
index a7a82ea..59b565b 100644
--- a/pkg/analyzer/tool/diagnostics/diagnostics.md
+++ b/pkg/analyzer/tool/diagnostics/diagnostics.md
@@ -860,6 +860,53 @@
}
{% endprettify %}
+### const_constructor_param_type_mismatch
+
+_A value of type '{0}' can't be assigned to a parameter of type '{1}'._
+
+#### Description
+
+The analyzer produces this diagnostic when the runtime type of a constant
+value can't be assigned to the static type of a constant constructor's
+parameter.
+
+#### Example
+
+The following code produces this diagnostic because the runtime type of `i`
+is `int`, which can't be assigned to the static type of `s`:
+
+{% prettify dart %}
+class C {
+ final String s;
+
+ const C(this.s);
+}
+
+const dynamic i = 0;
+
+void f() {
+ const C([!i!]);
+}
+{% endprettify %}
+
+#### Common fixes
+
+Pass a value of the correct type to the constructor:
+
+{% prettify dart %}
+class C {
+ final String s;
+
+ const C(this.s);
+}
+
+const dynamic i = 0;
+
+void f() {
+ const C('$i');
+}
+{% endprettify %}
+
### const_constructor_with_non_final_field
_Can't define a const constructor for a class with non-final fields._
@@ -1725,6 +1772,51 @@
of which element to remove might affect the order in which elements are
returned by an iterator.
+### equal_elements_in_set
+
+_Two elements in a set literal shouldn't be equal._
+
+#### Description
+
+The analyzer produces this diagnostic when an element in a non-constant set
+is the same as a previous element in the same set. If two elements are the
+same, then the second value is ignored, which makes having both elements
+pointless and likely signals a bug.
+
+#### Example
+
+The following code produces this diagnostic because the element `1` appears
+twice:
+
+{% prettify dart %}
+const a = 1;
+const b = 1;
+var s = <int>{a, [!b!]};
+{% endprettify %}
+
+#### Common fixes
+
+If both elements should be included in the set, then change one of the
+elements:
+
+{% prettify dart %}
+const a = 1;
+const b = 2;
+var s = <int>{a, b};
+{% endprettify %}
+
+If only one of the elements is needed, then remove the one that isn't
+needed:
+
+{% prettify dart %}
+const a = 1;
+var s = <int>{a};
+{% endprettify %}
+
+Note that literal sets preserve the order of their elements, so the choice
+of which element to remove might affect the order in which elements are
+returned by an iterator.
+
### equal_keys_in_const_map
_Two keys in a constant map literal can't be equal._
@@ -1765,6 +1857,50 @@
of which entry to remove might affect the order in which keys and values
are returned by an iterator.
+### equal_keys_in_map
+
+_Two keys in a map literal shouldn't be equal._
+
+#### Description
+
+The analyzer produces this diagnostic when a key in a non-constant map is
+the same as a previous key in the same map. If two keys are the same, then
+the second value overwrites the first value, which makes having both pairs
+pointless and likely signals a bug.
+
+#### Example
+
+The following code produces this diagnostic because the keys `a` and `b`
+have the same value:
+
+{% prettify dart %}
+const a = 1;
+const b = 1;
+var m = <int, String>{a: 'a', [!b!]: 'b'};
+{% endprettify %}
+
+#### Common fixes
+
+If both entries should be included in the map, then change one of the keys:
+
+{% prettify dart %}
+const a = 1;
+const b = 2;
+var m = <int, String>{a: 'a', b: 'b'};
+{% endprettify %}
+
+If only one of the entries is needed, then remove the one that isn't
+needed:
+
+{% prettify dart %}
+const a = 1;
+var m = <int, String>{a: 'a'};
+{% endprettify %}
+
+Note that literal maps preserve the order of their entries, so the choice
+of which entry to remove might affect the order in which the keys and
+values are returned by an iterator.
+
### export_legacy_symbol
_The symbol '{0}' is defined in a legacy library, and can't be re-exported from
@@ -2322,6 +2458,100 @@
}
{% endprettify %}
+### field_initialized_in_initializer_and_declaration
+
+_Fields can't be initialized in the constructor if they are final and were
+already initialized at their declaration._
+
+#### Description
+
+The analyzer produces this diagnostic when a final field is initialized in
+both the declaration of the field and in an initializer in a constructor.
+Final fields can only be assigned once, so it can't be initialized in both
+places.
+
+#### Example
+
+The following code produces this diagnostic because `f` is :
+
+{% prettify dart %}
+class C {
+ final int f = 0;
+ C() : [!f!] = 1;
+}
+{% endprettify %}
+
+#### Common fixes
+
+If the initialization doesn't depend on any values passed to the
+constructor, and if all of the constructors need to initialize the field to
+the same value, then remove the initializer from the constructor:
+
+{% prettify dart %}
+class C {
+ final int f = 0;
+ C();
+}
+{% endprettify %}
+
+If the initialization depends on a value passed to the constructor, or if
+different constructors need to initialize the field differently, then
+remove the initializer in the field's declaration:
+
+{% prettify dart %}
+class C {
+ final int f;
+ C() : f = 1;
+}
+{% endprettify %}
+
+### field_initializer_not_assignable
+
+_The initializer type '{0}' can't be assigned to the field type '{1}'._
+
+#### Description
+
+The analyzer produces this diagnostic when the initializer list of a
+constructor initializes a field to a value that isn't assignable to the
+field.
+
+#### Example
+
+The following code produces this diagnostic because `0` has the type `int`,
+and an `int` can't be assigned to a field of type `String`:
+
+{% prettify dart %}
+class C {
+ String s;
+
+ C() : s = [!0!];
+}
+{% endprettify %}
+
+#### Common fixes
+
+If the type of the field is correct, then change the value assigned to it
+so that the value has a valid type:
+
+{% prettify dart %}
+class C {
+ String s;
+
+ C() : s = '0';
+}
+{% endprettify %}
+
+If the type of the value is correct, then change the type of the field to
+allow the assignment:
+
+{% prettify dart %}
+class C {
+ int s;
+
+ C() : s = 0;
+}
+{% endprettify %}
+
### final_not_initialized
_The final variable '{0}' must be initialized._
@@ -2494,6 +2724,47 @@
}
{% endprettify %}
+### illegal_async_return_type
+
+_Functions marked 'async' must have a return type assignable to 'Future'._
+
+#### Description
+
+The analyzer produces this diagnostic when the body of a function has the
+`async` modifier even though the return type of the function isn't
+assignable to `Future`.
+
+#### Example
+
+The following code produces this diagnostic because the body of the
+function `f` has the `async` modifier even though the return type isn't
+assignable to `Future`:
+
+{% prettify dart %}
+[!int!] f() async {
+ return 0;
+}
+{% endprettify %}
+
+#### Common fixes
+
+If the function should be asynchronous, then change the return type to be
+assignable to `Future`:
+
+{% prettify dart %}
+Future<int> f() async {
+ return 0;
+}
+{% endprettify %}
+
+If the function should be synchronous, then remove the `async` modifier:
+
+{% prettify dart %}
+int f() {
+ return 0;
+}
+{% endprettify %}
+
### implements_non_class
_Classes and mixins can only implement other classes and mixins._
@@ -2558,7 +2829,7 @@
### implicit_this_reference_in_initializer
-_Only static members can be accessed in initializers._
+_The instance member '{0}' can't be accessed in an initializer._
#### Description
@@ -2607,6 +2878,76 @@
}
{% endprettify %}
+### import_internal_library
+
+_The library '{0}' is internal and can't be imported._
+
+#### Description
+
+The analyzer produces this diagnostic when it finds an import whose `dart:`
+URI references an internal library.
+
+#### Example
+
+The following code produces this diagnostic because `_interceptors` is an
+internal library:
+
+{% prettify dart %}
+import [!'dart:_interceptors'!];
+{% endprettify %}
+
+#### Common fixes
+
+Remove the import directive.
+
+### inconsistent_inheritance
+
+_Superinterfaces don't have a valid override for '{0}': {1}._
+
+#### Description
+
+The analyzer produces this diagnostic when a class inherits two or more
+conflicting signatures for a member and doesn't provide an implementation
+that satisfies all the inherited signatures.
+
+#### Example
+
+The following code produces this diagnostic because `C` is inheriting the
+declaration of `m` from `A`, and that implementation isn't consistent with
+the signature of `m` that's inherited from `B`:
+
+{% prettify dart %}
+class A {
+ void m({int a}) {}
+}
+
+class B {
+ void m({int b}) {}
+}
+
+class [!C!] extends A implements B {
+}
+{% endprettify %}
+
+#### Common fixes
+
+Add an implementation of the method that satisfies all the inherited
+signatures:
+
+{% prettify dart %}
+class A {
+ void m({int a}) {}
+}
+
+class B {
+ void m({int b}) {}
+}
+
+class C extends A implements B {
+ void m({int a, int b}) {}
+}
+{% endprettify %}
+
### initializer_for_non_existent_field
_'{0}' isn't a field in the enclosing class._
@@ -4280,6 +4621,49 @@
bool b = a == 0 || a > 1;
{% endprettify %}
+### non_constant_annotation_constructor
+
+_Annotation creation can only call a const constructor._
+
+#### Description
+
+The analyzer produces this diagnostic when an annotation is the invocation
+of an existing constructor even though the invoked constructor isn't a
+const constructor.
+
+#### Example
+
+The following code produces this diagnostic because the constructor for `C`
+isn't a const constructor:
+
+{% prettify dart %}
+[!@C()!]
+void f() {
+}
+
+class C {
+ C();
+}
+{% endprettify %}
+
+#### Common fixes
+
+If it's valid for the class to have a const constructor, then create a
+const constructor that can be used for the annotation:
+
+{% prettify dart %}
+@C()
+void f() {
+}
+
+class C {
+ const C();
+}
+{% endprettify %}
+
+If it isn't valid for the class to have a const constructor, then either
+remove the annotation or use a different class for the annotation.
+
### non_constant_case_expression
_Case expressions must be constant._
@@ -5292,6 +5676,41 @@
If the member can't be removed, then remove the annotation.
+### part_of_different_library
+
+_Expected this library to be part of '{0}', not '{1}'._
+
+#### Description
+
+The analyzer produces this diagnostic when a library attempts to include a
+file as a part of itself when the other file is a part of a different
+library.
+
+#### Example
+
+Given a file named `part.dart` containing
+
+{% prettify dart %}
+part of 'library.dart';
+{% endprettify %}
+
+The following code, in any file other than `library.dart`, produces this
+diagnostic because it attempts to include `part.dart` as a part of itself
+when `part.dart` is a part of a different library:
+
+{% prettify dart %}
+part [!'package:a/part.dart'!];
+{% endprettify %}
+
+#### Common fixes
+
+If the library should be using a different file as a part, then change the
+URI in the part directive to be the URI of the other file.
+
+If the part file should be a part of this library, then update the URI (or
+library name) in the part-of directive to be the URI (or name) of the
+correct library.
+
### part_of_non_part
_The included part '{0}' must have a part-of directive._
@@ -5334,6 +5753,45 @@
import 'a.dart';
{% endprettify %}
+### prefix_identifier_not_followed_by_dot
+
+_The name '{0}' refers to an import prefix, so it must be followed by '.'._
+
+#### Description
+
+The analyzer produces this diagnostic when an import prefix is used by
+itself, without accessing any of the names declared in the libraries
+associated with the prefix. Prefixes aren't variables, and therefore can't
+be used as a value.
+
+#### Example
+
+The following code produces this diagnostic because the prefix `math` is
+being used as if it were a variable:
+
+{% prettify dart %}
+import 'dart:math' as math;
+
+void f() {
+ print([!math!]);
+}
+{% endprettify %}
+
+#### Common fixes
+
+If the code is incomplete, then reference something in one of the libraries
+associated with the prefix:
+
+{% prettify dart %}
+import 'dart:math' as math;
+
+void f() {
+ print(math.pi);
+}
+{% endprettify %}
+
+If the name is wrong, then correct the name.
+
### redirect_to_invalid_function_type
_The redirected constructor '{0}' has incompatible parameters with '{1}'._
@@ -5552,6 +6010,56 @@
}
{% endprettify %}
+### return_in_generative_constructor
+
+_Constructors can't return values._
+
+#### Description
+
+The analyzer produces this diagnostic when a generative constructor
+contains a return statement that specifies a value to be returned.
+Generative constructors always return the object that was created, and
+therefore can't return a different object.
+
+#### Example
+
+The following code produces this diagnostic because the return statement
+has an expression:
+
+{% prettify dart %}
+class C {
+ C() {
+ return [!this!];
+ }
+}
+{% endprettify %}
+
+#### Common fixes
+
+If the constructor should create a new instance, then remove either the
+return statement or the expression:
+
+{% prettify dart %}
+class C {
+ C();
+}
+{% endprettify %}
+
+If the constructor shouldn't create a new instance, then convert it to be a
+factory constructor:
+
+{% prettify dart %}
+class C {
+ factory C() {
+ return _instance;
+ }
+
+ static C _instance = C._();
+
+ C._();
+}
+{% endprettify %}
+
### return_of_invalid_type
_A value of type '{0}' can't be returned from constructor '{2}' because it has a
@@ -6234,6 +6742,57 @@
Rewrite the code to not use `super`.
+### switch_expression_not_assignable
+
+_Type '{0}' of the switch expression isn't assignable to the type '{1}' of case
+expressions._
+
+#### Description
+
+The analyzer produces this diagnostic when the type of the expression in a
+switch statement isn't assignable to the type of the expressions in the
+case clauses.
+
+#### Example
+
+The following code produces this diagnostic because the type of `s`
+(`String`) isn't assignable to the type of `0` (`int`):
+
+{% prettify dart %}
+void f(String s) {
+ switch ([!s!]) {
+ case 0:
+ break;
+ }
+}
+{% endprettify %}
+
+#### Common fixes
+
+If the type of the case expressions is correct, then change the expression
+in the switch statement to have the correct type:
+
+{% prettify dart %}
+void f(String s) {
+ switch (int.parse(s)) {
+ case 0:
+ break;
+ }
+}
+{% endprettify %}
+
+If the type of the switch expression is correct, then change the case
+expressions to have the correct type:
+
+{% prettify dart %}
+void f(String s) {
+ switch (s) {
+ case '0':
+ break;
+ }
+}
+{% endprettify %}
+
### throw_of_invalid_type
_The type '{0}' of the thrown expression must be assignable to 'Object'._
@@ -6530,6 +7089,47 @@
}
{% endprettify %}
+### undefined_enum_constant
+
+_There's no constant named '{0}' in '{1}'._
+
+#### Description
+
+The analyzer produces this diagnostic when it encounters an identifier that
+appears to be the name of an enum constant, and the name either isn't
+defined or isn't visible in the scope in which it's being referenced.
+
+#### Examples
+
+The following code produces this diagnostic because `E` doesn't define a
+constant named `c`:
+
+{% prettify dart %}
+enum E {a, b}
+
+var e = E.[!c!];
+{% endprettify %}
+
+#### Common fixes
+
+If the constant should be defined, then add it to the declaration of the
+enum:
+
+{% prettify dart %}
+enum E {a, b, c}
+
+var e = E.c;
+{% endprettify %}
+
+If the constant shouldn't be defined, then change the name to the name of
+an existing constant:
+
+{% prettify dart %}
+enum E {a, b}
+
+var e = E.b;
+{% endprettify %}
+
### undefined_extension_getter
_The getter '{0}' isn't defined for the extension '{1}'._
@@ -6943,6 +7543,35 @@
If the identifier is defined but isn't visible, then you probably need to
add an import or re-arrange your code to make the identifier visible.
+### undefined_identifier_await
+
+_Undefined name 'await' in function body not marked with 'async'._
+
+#### Description
+
+The analyzer produces this diagnostic when the name `await` is used in a
+method or function body without being declared, and the body isn't marked
+with the `async` keyword. The name `await` only introduces an await
+expression in an asynchronous function.
+
+#### Example
+
+The following code produces this diagnostic because the name `await` is
+used in the body of `f` even though the body of `f` isn't marked with the
+`async` keyword:
+
+{% prettify dart %}
+void f(p) { [!await!] p; }
+{% endprettify %}
+
+#### Common fixes
+
+Add the keyword `async` to the function body:
+
+{% prettify dart %}
+void f(p) async { await p; }
+{% endprettify %}
+
### undefined_method
_The method '{0}' isn't defined for the type '{1}'._
diff --git a/pkg/analyzer_cli/lib/src/build_mode.dart b/pkg/analyzer_cli/lib/src/build_mode.dart
index 60d403e..bf9eeb1 100644
--- a/pkg/analyzer_cli/lib/src/build_mode.dart
+++ b/pkg/analyzer_cli/lib/src/build_mode.dart
@@ -482,11 +482,18 @@
}
Packages _findPackages(String path) {
- if (path != null) {
- return findPackagesFrom(resourceProvider, resourceProvider.getFile(path));
- } else {
- return Packages.empty;
+ var configPath = options.packageConfigPath;
+ if (configPath != null) {
+ var configFile = resourceProvider.getFile(configPath);
+ return parsePackagesFile(resourceProvider, configFile);
}
+
+ if (path != null) {
+ var file = resourceProvider.getFile(path);
+ return findPackagesFrom(resourceProvider, file);
+ }
+
+ return Packages.empty;
}
/// Ensure that the parsed unit for [absoluteUri] is available.
diff --git a/pkg/analyzer_cli/pubspec.yaml b/pkg/analyzer_cli/pubspec.yaml
index 295f5cf..06a9e27 100644
--- a/pkg/analyzer_cli/pubspec.yaml
+++ b/pkg/analyzer_cli/pubspec.yaml
@@ -1,5 +1,6 @@
name: analyzer_cli
description: Command line interface for the Dart Analyzer.
+# This package is not intended for consumption on pub.dev. DO NOT publish.
publish_to: none
environment:
sdk: "^2.7.0"
diff --git a/pkg/analyzer_cli/test/driver_test.dart b/pkg/analyzer_cli/test/driver_test.dart
index 8a29242..4735d87 100644
--- a/pkg/analyzer_cli/test/driver_test.dart
+++ b/pkg/analyzer_cli/test/driver_test.dart
@@ -622,6 +622,88 @@
});
}
+ Future<void> test_packageConfig_packagesOptions() async {
+ await withTempDirAsync((tempDir) async {
+ var packagesPath = path.join(tempDir, 'aaa.packages');
+
+ var aaaRoot = path.join(tempDir, 'packages', 'aaa');
+ var aPath = path.join(aaaRoot, 'lib', 'a.dart');
+
+ var aUri = 'package:aaa/a.dart';
+
+ File(packagesPath).createSync(recursive: true);
+ File(packagesPath).writeAsStringSync('''
+{
+ "configVersion": 2,
+ "packages": [
+ {
+ "name": "aaa",
+ "rootUri": "${path.toUri(aaaRoot)}",
+ "packageUri": "lib/",
+ "languageVersion": "2.4"
+ }
+ ]
+}
+''');
+
+ File(aPath).createSync(recursive: true);
+ File(aPath).writeAsStringSync(r'''
+extension E on int {}
+''');
+
+ // Analyze package:aaa/a.dart and compute errors.
+ await _doDrive(
+ aPath,
+ fileUri: aUri,
+ additionalArgs: [
+ '--packages=$packagesPath',
+ ],
+ );
+ expect(exitCode, ErrorSeverity.ERROR.ordinal);
+ expect(errorSink.toString(), contains('extension-methods'));
+ });
+ }
+
+ Future<void> test_packageConfig_relativeToFile() async {
+ await withTempDirAsync((tempDir) async {
+ var packagesPath = path.join(tempDir, '.dart_tool/package_config.json');
+
+ var aaaRoot = path.join(tempDir, 'packages', 'aaa');
+ var aPath = path.join(aaaRoot, 'lib', 'a.dart');
+
+ var aUri = 'package:aaa/a.dart';
+
+ File(packagesPath).createSync(recursive: true);
+ File(packagesPath).writeAsStringSync('''
+{
+ "configVersion": 2,
+ "packages": [
+ {
+ "name": "aaa",
+ "rootUri": "${path.toUri(aaaRoot)}",
+ "packageUri": "lib/",
+ "languageVersion": "2.4"
+ }
+ ]
+}
+''');
+
+ File(aPath).createSync(recursive: true);
+ File(aPath).writeAsStringSync(r'''
+extension E on int {}
+''');
+
+ // Analyze package:aaa/a.dart and compute errors.
+ await _doDrive(
+ aPath,
+ fileUri: aUri,
+ additionalArgs: [],
+ );
+ expect(exitCode, ErrorSeverity.ERROR.ordinal);
+ expect(errorSink.toString(), contains('extension-methods'));
+ });
+ }
+
Iterable<String> _linkedLibraryUnitUriList(
LinkedNodeBundle bundle2,
String libraryUriStr,
diff --git a/pkg/async_helper/pubspec.yaml b/pkg/async_helper/pubspec.yaml
index 9497752..817b5fe 100644
--- a/pkg/async_helper/pubspec.yaml
+++ b/pkg/async_helper/pubspec.yaml
@@ -1,5 +1,5 @@
name: async_helper
-# This package is not intended to be published
+# This package is not intended for consumption on pub.dev. DO NOT publish.
publish_to: none
description: >-
Async_helper is used for asynchronous tests that do not want to
diff --git a/pkg/build_integration/pubspec.yaml b/pkg/build_integration/pubspec.yaml
index 722a284..7d5fb5db 100644
--- a/pkg/build_integration/pubspec.yaml
+++ b/pkg/build_integration/pubspec.yaml
@@ -2,7 +2,7 @@
description: >
Libraries for integrating Dart tools with build systems like bazel
and build_runner.
-# This package is not intended to be published - yet
+# This package is not intended for consumption on pub.dev. DO NOT publish.
publish_to: none
environment:
diff --git a/pkg/compiler/lib/src/common/codegen.dart b/pkg/compiler/lib/src/common/codegen.dart
index 44e3980..33de3f1 100644
--- a/pkg/compiler/lib/src/common/codegen.dart
+++ b/pkg/compiler/lib/src/common/codegen.dart
@@ -18,6 +18,7 @@
import '../js/js.dart' as js;
import '../js_backend/backend.dart';
import '../js_backend/namer.dart';
+import '../js_backend/string_reference.dart' show StringReference;
import '../js_backend/type_reference.dart' show TypeReference;
import '../js_emitter/code_emitter_task.dart' show Emitter;
import '../js_model/type_recipe.dart' show TypeRecipe;
@@ -1001,6 +1002,7 @@
expressionStatement,
block,
program,
+ stringReference,
typeReference,
}
@@ -1064,6 +1066,7 @@
static const String expressionStatement = 'js-expressionStatement';
static const String block = 'js-block';
static const String program = 'js-program';
+ static const String stringReference = 'js-stringReference';
static const String typeReference = 'js-typeReference';
}
@@ -1307,6 +1310,12 @@
node.writeToDataSink(sink);
sink.end(JsNodeTags.typeReference);
_writeInfo(node);
+ } else if (node is StringReference) {
+ sink.writeEnum(JsNodeKind.stringReference);
+ sink.begin(JsNodeTags.stringReference);
+ node.writeToDataSink(sink);
+ sink.end(JsNodeTags.stringReference);
+ _writeInfo(node);
} else {
throw new UnsupportedError(
'Unexpected deferred expression: ${node.runtimeType}.');
@@ -2098,6 +2107,11 @@
node = new js.Program(body);
source.end(JsNodeTags.program);
break;
+ case JsNodeKind.stringReference:
+ source.begin(JsNodeTags.stringReference);
+ node = StringReference.readFromDataSource(source);
+ source.end(JsNodeTags.stringReference);
+ break;
case JsNodeKind.typeReference:
source.begin(JsNodeTags.typeReference);
node = TypeReference.readFromDataSource(source);
diff --git a/pkg/compiler/lib/src/common_elements.dart b/pkg/compiler/lib/src/common_elements.dart
index 6e0d183..dfcea46 100644
--- a/pkg/compiler/lib/src/common_elements.dart
+++ b/pkg/compiler/lib/src/common_elements.dart
@@ -412,14 +412,10 @@
FunctionEntity get exceptionUnwrapper;
- FunctionEntity get throwRuntimeError;
-
FunctionEntity get throwUnsupportedError;
FunctionEntity get throwTypeError;
- FunctionEntity get throwAbstractClassInstantiationError;
-
/// Recognizes the `checkConcurrentModificationError` helper without needing
/// it to be resolved.
bool isCheckConcurrentModificationError(MemberEntity member);
@@ -1698,10 +1694,6 @@
_findHelperFunction('unwrapException');
@override
- FunctionEntity get throwRuntimeError =>
- _findHelperFunction('throwRuntimeError');
-
- @override
FunctionEntity get throwUnsupportedError =>
_findHelperFunction('throwUnsupportedError');
@@ -1709,10 +1701,6 @@
FunctionEntity get throwTypeError => _findRtiFunction('throwTypeError');
@override
- FunctionEntity get throwAbstractClassInstantiationError =>
- _findHelperFunction('throwAbstractClassInstantiationError');
-
- @override
bool isCheckConcurrentModificationError(MemberEntity member) {
return member.name == 'checkConcurrentModificationError' &&
member.isFunction &&
diff --git a/pkg/compiler/lib/src/ir/scope_visitor.dart b/pkg/compiler/lib/src/ir/scope_visitor.dart
index 22be758..7ef94b7 100644
--- a/pkg/compiler/lib/src/ir/scope_visitor.dart
+++ b/pkg/compiler/lib/src/ir/scope_visitor.dart
@@ -143,7 +143,7 @@
for (ir.Node variable in _scopeVariables) {
// No need to box non-assignable elements.
if (variable is ir.VariableDeclaration) {
- if (variable.isFinal || variable.isConst) continue;
+ if (variable.isConst) continue;
if (!_mutatedVariables.contains(variable)) continue;
if (_capturedVariables.contains(variable)) {
capturedVariablesForScope.add(variable);
@@ -345,6 +345,9 @@
typeVariable(context.enclosingLibrary), _currentTypeUsage);
}
}
+
+ visitNode(typeParameter.bound);
+
return const InitializerComplexity.constant();
}
diff --git a/pkg/compiler/lib/src/js_backend/backend_impact.dart b/pkg/compiler/lib/src/js_backend/backend_impact.dart
index 6fd3844..cbb0bdd 100644
--- a/pkg/compiler/lib/src/js_backend/backend_impact.dart
+++ b/pkg/compiler/lib/src/js_backend/backend_impact.dart
@@ -165,14 +165,6 @@
]);
}
- BackendImpact _abstractClassInstantiation;
-
- BackendImpact get abstractClassInstantiation {
- return _abstractClassInstantiation ??= new BackendImpact(
- staticUses: [_commonElements.throwAbstractClassInstantiationError],
- otherImpacts: [_needsString('Needed to encode the message.')]);
- }
-
BackendImpact _fallThroughError;
BackendImpact get fallThroughError {
@@ -183,9 +175,7 @@
BackendImpact _asCheck;
BackendImpact get asCheck {
- return _asCheck ??= new BackendImpact(staticUses: [
- _commonElements.throwRuntimeError,
- ], otherImpacts: [
+ return _asCheck ??= new BackendImpact(staticUses: [], otherImpacts: [
newRtiImpact,
]);
}
@@ -256,9 +246,7 @@
BackendImpact _throwRuntimeError;
BackendImpact get throwRuntimeError {
- return _throwRuntimeError ??= new BackendImpact(staticUses: [
- _commonElements.throwRuntimeError,
- ], otherImpacts: [
+ return _throwRuntimeError ??= new BackendImpact(otherImpacts: [
// Also register the types of the arguments passed to this method.
stringValues
]);
diff --git a/pkg/compiler/lib/src/js_backend/constant_emitter.dart b/pkg/compiler/lib/src/js_backend/constant_emitter.dart
index 80b45fa..95166e4 100644
--- a/pkg/compiler/lib/src/js_backend/constant_emitter.dart
+++ b/pkg/compiler/lib/src/js_backend/constant_emitter.dart
@@ -13,6 +13,8 @@
import '../js/js.dart' show js;
import '../js_backend/field_analysis.dart';
import '../js_backend/type_reference.dart' show TypeReference;
+import '../js_backend/string_reference.dart'
+ show StringReference, StringReferencePolicy;
import '../js_emitter/code_emitter_task.dart';
import '../js_model/type_recipe.dart' show TypeExpressionRecipe;
import '../options.dart';
@@ -143,7 +145,11 @@
/// The string is assumed quoted by double quote characters.
@override
jsAst.Expression visitString(StringConstantValue constant, [_]) {
- return js.escapedString(constant.stringValue, ascii: true);
+ String value = constant.stringValue;
+ if (value.length < StringReferencePolicy.minimumLength) {
+ return js.escapedString(value, ascii: true);
+ }
+ return StringReference(constant);
}
@override
@@ -276,7 +282,8 @@
}
// Keys in literal maps must be emitted in place.
- jsAst.Literal keyExpression = _visit(key);
+ jsAst.Literal keyExpression =
+ js.escapedString(key.stringValue, ascii: true);
jsAst.Expression valueExpression =
_constantReferenceGenerator(constant.values[i]);
properties.add(new jsAst.Property(keyExpression, valueExpression));
diff --git a/pkg/compiler/lib/src/js_backend/impact_transformer.dart b/pkg/compiler/lib/src/js_backend/impact_transformer.dart
index f0de179..842a94e 100644
--- a/pkg/compiler/lib/src/js_backend/impact_transformer.dart
+++ b/pkg/compiler/lib/src/js_backend/impact_transformer.dart
@@ -72,9 +72,6 @@
for (Feature feature in worldImpact.features) {
switch (feature) {
- case Feature.ABSTRACT_CLASS_INSTANTIATION:
- registerImpact(_impacts.abstractClassInstantiation);
- break;
case Feature.ASSERT:
registerImpact(_impacts.assertWithoutMessage);
break;
diff --git a/pkg/compiler/lib/src/js_backend/name_sequence.dart b/pkg/compiler/lib/src/js_backend/name_sequence.dart
new file mode 100644
index 0000000..1f7bdf1
--- /dev/null
+++ b/pkg/compiler/lib/src/js_backend/name_sequence.dart
@@ -0,0 +1,48 @@
+// 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:front_end/src/api_unstable/dart2js.dart'
+ show $0, $9, $A, $Z, $_, $a, $z;
+
+/// Returns an infinite sequence of property names in increasing size.
+Iterable<String> generalMinifiedNameSequence() sync* {
+ List<int> nextName = [$a];
+
+ /// Increments the letter at [pos] in the current name. Also takes care of
+ /// overflows to the left. Returns the carry bit, i.e., it returns `true`
+ /// if all positions to the left have wrapped around.
+ ///
+ /// If [nextName] is initially 'a', this will generate the sequence
+ ///
+ /// [a-zA-Z_]
+ /// [a-zA-Z_][0-9a-zA-Z_]
+ /// [a-zA-Z_][0-9a-zA-Z_][0-9a-zA-Z_]
+ /// ...
+ bool incrementPosition(int pos) {
+ bool overflow = false;
+ if (pos < 0) return true;
+ int value = nextName[pos];
+ if (value == $9) {
+ value = $a;
+ } else if (value == $z) {
+ value = $A;
+ } else if (value == $Z) {
+ value = $_;
+ } else if (value == $_) {
+ overflow = incrementPosition(pos - 1);
+ value = (pos > 0) ? $0 : $a;
+ } else {
+ value++;
+ }
+ nextName[pos] = value;
+ return overflow;
+ }
+
+ while (true) {
+ yield String.fromCharCodes(nextName);
+ if (incrementPosition(nextName.length - 1)) {
+ nextName.add($0);
+ }
+ }
+}
diff --git a/pkg/compiler/lib/src/js_backend/namer.dart b/pkg/compiler/lib/src/js_backend/namer.dart
index 9b4e0ee..e44beff 100644
--- a/pkg/compiler/lib/src/js_backend/namer.dart
+++ b/pkg/compiler/lib/src/js_backend/namer.dart
@@ -1614,6 +1614,7 @@
String getTypeRepresentationForTypeConstant(DartType type) {
type = type.withoutNullability;
if (type is DynamicType) return "dynamic";
+ if (type is NeverType) return "Never";
if (type is FutureOrType) {
return "FutureOr<dynamic>";
}
@@ -2246,8 +2247,6 @@
String get callPrefix => 'call';
String get callCatchAllName => r'call*';
String get callNameField => r'$callName';
- String get reflectableField => r'$reflectable';
- String get classDescriptorProperty => r'^';
String get defaultValuesField => r'$defaultValues';
String get deferredAction => r'$deferredAction';
String get operatorIsPrefix => r'$is';
@@ -2544,10 +2543,6 @@
return asName('${fixedNames.callPrefix}\$5');
case JsGetName.CALL_CATCH_ALL:
return asName(fixedNames.callCatchAllName);
- case JsGetName.REFLECTABLE:
- return asName(fixedNames.reflectableField);
- case JsGetName.CLASS_DESCRIPTOR_PROPERTY:
- return asName(fixedNames.classDescriptorProperty);
case JsGetName.REQUIRED_PARAMETER_PROPERTY:
return asName(fixedNames.requiredParameterField);
case JsGetName.DEFAULT_VALUES_PROPERTY:
@@ -2570,10 +2565,6 @@
return runtimeTypeName(_commonElements.nullClass);
case JsGetName.OBJECT_CLASS_TYPE_NAME:
return runtimeTypeName(_commonElements.objectClass);
- case JsGetName.FUNCTION_CLASS_TYPE_NAME:
- return runtimeTypeName(_commonElements.functionClass);
- case JsGetName.JS_FUNCTION_CLASS_TYPE_NAME:
- return runtimeTypeName(_commonElements.jsJavaScriptFunctionClass);
case JsGetName.FUTURE_CLASS_TYPE_NAME:
return runtimeTypeName(_commonElements.futureClass);
case JsGetName.RTI_FIELD_AS:
diff --git a/pkg/compiler/lib/src/js_backend/string_abbreviation.dart b/pkg/compiler/lib/src/js_backend/string_abbreviation.dart
new file mode 100644
index 0000000..0e442bd
--- /dev/null
+++ b/pkg/compiler/lib/src/js_backend/string_abbreviation.dart
@@ -0,0 +1,169 @@
+// 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:front_end/src/api_unstable/dart2js.dart'
+ show $0, $9, $A, $Z, $a, $z;
+
+/// Returns a list of strings that are short valid identifiers based on the
+/// corresponding full strings in [strings].
+///
+/// [strings] must not contain the empty string.
+/// [strings] must have no duplicates.
+List<String> abbreviateToIdentifiers(Iterable<String> strings,
+ {int minLength = 6}) {
+ var nodes = [for (final string in strings) _Node(string)];
+ _partition(nodes, minLength, [], 0);
+ return [for (final node in nodes) node.assignment];
+}
+
+class _Node {
+ final String string;
+ String assignment;
+ _Node(this.string);
+}
+
+/// Walk the prefix tree or TRIE of [nodes], assigning compressed names built
+/// from the distinguishing characters along the path.
+///
+/// - [index] is the position of the first potentially different character,
+/// i.e. the current TRIE depth.
+/// - [path] contains the prefix of the compressed name at this depth.
+/// - Path compression starts after the first [minLength] characters.
+void _partition(
+ List<_Node> nodes, int minLength, List<String> path, int index) {
+ while (true) {
+ // Handle trivial partitions.
+ if (nodes.length == 0) return;
+ if (nodes.length == 1 && path.length >= minLength) {
+ String name = path.join();
+ assert(name.isNotEmpty);
+ nodes.single.assignment = name;
+ return;
+ }
+
+ // Partition on the code unit at position [index], setting [terminating] if
+ // some string ends at this length;
+ Map<int, List<_Node>> partition = {};
+ _Node terminating;
+
+ for (final node in nodes) {
+ String string = node.string;
+ assert(string.length > 0);
+ if (index < string.length) {
+ int codeUnit = string.codeUnitAt(index);
+ (partition[codeUnit] ??= []).add(node);
+ } else {
+ assert(terminating == null); // i.e. no duplicates.
+ terminating = node;
+ }
+ }
+
+ if (terminating != null) {
+ terminating.assignment = path.join();
+ }
+
+ if (partition.length == 0) return;
+
+ if (partition.length > 1) {
+ var keys = partition.keys.toList();
+ var keyEncodings = _discriminators(keys, path.isEmpty);
+ for (int key in keys) {
+ var children = partition[key];
+ var discriminator = keyEncodings[key];
+ _partition(children, minLength, [...path, discriminator], index + 1);
+ }
+ return;
+ }
+
+ assert(partition.length == 1);
+ // All the strings have the same code unit at [index]. Use iteration to
+ // find next partition point to avoid recursing down the whole string
+ // length. The path is compressed by omitting to add fragments to [path]
+ // unless we are near the start of the string.
+
+ int codeUnit = partition.keys.single;
+ // Add some characters to name to distinguish from terminating strings.
+ // Add the first few legal identifier characters of the string regardless.
+ if (terminating != null || path.length < minLength) {
+ path.add(_isIdentifier(codeUnit, path.isEmpty)
+ ? String.fromCharCode(codeUnit)
+ : '_');
+ }
+ nodes = partition.values.single;
+ index += 1;
+ }
+}
+
+Map<int, String> _discriminators(List<int> keys, bool atStart) {
+ // Assign each partition a distinguishing short string. If the partition key
+ // is a valid identifier character, it can be used, otherwise we could use
+ // `'_'` or an escaped code like `'x3b'` or `'u12ef'`. If we use an escape
+ // like `'x3b'` then we need to be careful with the partition key `x`, as it
+ // might be followed by `3b`. We avoid this problem without lookahead by
+ // encoding `x` as an escape (i.e. `'x78'`) if there is another `x`-escape.
+
+ const xCode = 0x78;
+ const uCode = 0x75;
+
+ bool hasX = false;
+ bool hasU = false;
+
+ int xEscapes = 0;
+ int uEscapes = 0;
+ for (int key in keys) {
+ if (_isIdentifier(key, atStart)) {
+ if (key == xCode) hasX = true;
+ if (key == uCode) hasU = true;
+ } else if (key < 256) {
+ xEscapes++;
+ } else {
+ uEscapes++;
+ }
+ }
+
+ Map<int, String> encoding = {};
+ bool escapeToUnderscore = false;
+
+ if (uEscapes + xEscapes <= 1) {
+ escapeToUnderscore = true;
+ xEscapes = uEscapes = 0;
+ }
+
+ if (uEscapes > 0 && hasU) {
+ encoding[uCode] = 'x75';
+ xEscapes++;
+ }
+ if (xEscapes > 0 && hasX) {
+ encoding[xCode] = 'x78';
+ }
+
+ for (int key in keys) {
+ if (encoding.containsKey(key)) continue;
+ if (_isIdentifier(key, atStart)) {
+ encoding[key] = String.fromCharCode(key);
+ } else {
+ if (escapeToUnderscore) {
+ encoding[key] = '_';
+ } else if (key < 256) {
+ encoding[key] = 'x' + key.toRadixString(16).padLeft(2, '0');
+ } else {
+ encoding[key] = 'u' + key.toRadixString(16).padLeft(4, '0');
+ }
+ }
+ }
+
+ return encoding;
+}
+
+bool _isIdentifier(int codeUnit, bool atStart) {
+ return atStart ? _isAsciiAlpha(codeUnit) : _isAsciiAlphanumeric(codeUnit);
+}
+
+bool _isAsciiAlphanumeric(int codeUnit) {
+ return $0 <= codeUnit && codeUnit <= $9 || _isAsciiAlpha(codeUnit);
+}
+
+bool _isAsciiAlpha(int codeUnit) {
+ return $A <= codeUnit && codeUnit <= $Z || $a <= codeUnit && codeUnit <= $z;
+}
diff --git a/pkg/compiler/lib/src/js_backend/string_reference.dart b/pkg/compiler/lib/src/js_backend/string_reference.dart
new file mode 100644
index 0000000..95fa795
--- /dev/null
+++ b/pkg/compiler/lib/src/js_backend/string_reference.dart
@@ -0,0 +1,435 @@
+// 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.
+
+/// StringReferences are 'holes' in the generated JavaScript that are filled in
+/// by the emitter with code to access a large string.
+///
+/// The Dart code
+///
+/// foo1() => 'A very long string';
+///
+/// might be compiled to something like the following, where StringReference1 is
+/// assocatied with the string `'A very long string'`.
+///
+/// foo1: function() {
+/// return StringReference1;
+/// }
+///
+/// The dart method `foo2` would be compiled separately, with the generated code
+/// containing StringReference2, also 3 referring to `int`:
+///
+/// foo2() => 'A very long string';
+/// -->
+/// foo2: function() {
+/// return StringReference2;
+/// }
+///
+/// When the code for an output unit (main unit or deferred loaded unit) is
+/// assembled, there will also be a StringReferenceResource 'hole', so the
+/// assembled looks something like
+///
+/// foo: function() {
+/// return StringReference1;
+/// }
+/// foo2: function() {
+/// return StringReference2;
+/// }
+/// ...
+/// StringReferenceResource
+///
+/// The StringReferenceFinalizer decides on a strategy for accessing the
+/// strings. In most cases a string will have one reference and it should be
+/// generated in-place. Shared strings can be referenced via a object. The
+/// StringReference nodes are filled in with property access expressions and the
+/// StringReferenceResource is filled in with the precomputed data, something
+/// like:
+///
+/// foo1: function() {
+/// return string$.A_very;
+/// }
+/// foo2: function() {
+/// return string$.A_very;
+/// }
+/// ...
+/// var string$ = {
+/// A_very: "A very long string",
+/// };
+///
+/// In minified mode, the properties (`A_very`) can be replaced by shorter
+/// names.
+library js_backend.string_reference;
+
+import '../constants/values.dart' show StringConstantValue;
+import '../js/js.dart' as js;
+import '../serialization/serialization.dart';
+import '../util/util.dart' show Hashing;
+import 'frequency_assignment.dart';
+import 'name_sequence.dart';
+import 'string_abbreviation.dart';
+
+class StringReferencePolicy {
+ /// Minimum length to generate a StringReference for further processing.
+ static const int minimumLength = 11;
+
+ /// Strings shorter that [shortestSharedLength] are not shared.
+ // TODO(sra): Split this into different settings depending on code contexts
+ // (hot, cold, execute-once, etc).
+ static const int shortestSharedLength = 40;
+
+ // TODO(sra): Add policy for huge non-shared strings, strings occuring in
+ // run-once code, etc. Maybe make policy settings assignable for testing or
+ // command-line configuration.
+}
+
+/// A [StringReference] is a deferred JavaScript expression that refers to the
+/// runtime representation of a ground type or ground type environment. The
+/// deferred expression is filled in by the StringReferenceFinalizer which is
+/// called from the fragment emitter. The replacement expression could be any
+/// expression, e.g. a call, or a reference to a variable, or property of a
+/// variable.
+class StringReference extends js.DeferredExpression implements js.AstContainer {
+ static const String tag = 'string-reference';
+
+ final StringConstantValue constant;
+
+ js.Expression _value;
+
+ @override
+ final js.JavaScriptNodeSourceInformation sourceInformation;
+
+ StringReference(this.constant) : sourceInformation = null;
+ StringReference._(this.constant, this._value, this.sourceInformation);
+
+ factory StringReference.readFromDataSource(DataSource source) {
+ source.begin(tag);
+ StringConstantValue constant = source.readConstant() as StringConstantValue;
+ source.end(tag);
+ return StringReference(constant);
+ }
+
+ void writeToDataSink(DataSink sink) {
+ sink.begin(tag);
+ sink.writeConstant(constant);
+ sink.end(tag);
+ }
+
+ set value(js.Expression value) {
+ assert(_value == null && value != null);
+ _value = value;
+ }
+
+ @override
+ js.Expression get value {
+ assert(_value != null, 'StringReference is unassigned');
+ return _value;
+ }
+
+ // Precedence will be CALL or LEFT_HAND_SIDE depending on what expression the
+ // reference is resolved to.
+ @override
+ int get precedenceLevel => value.precedenceLevel;
+
+ @override
+ StringReference withSourceInformation(
+ js.JavaScriptNodeSourceInformation newSourceInformation) {
+ if (newSourceInformation == sourceInformation) return this;
+ if (newSourceInformation == null) return this;
+ return StringReference._(constant, _value, newSourceInformation);
+ }
+
+ @override
+ Iterable<js.Node> get containedNodes => _value == null ? const [] : [_value];
+}
+
+/// A [StringReferenceResource] is a deferred JavaScript expression determined
+/// by the finalization of string references. It is the injection point for data
+/// or code to support string references. For example, if the
+/// [StringReferenceFinalizer] decides that a string should be referred to via a
+/// variable, the [StringReferenceResource] would be set to code that declares
+/// and initializes the variable.
+class StringReferenceResource extends js.DeferredExpression
+ implements js.AstContainer {
+ js.Expression _value;
+
+ @override
+ final js.JavaScriptNodeSourceInformation sourceInformation;
+
+ StringReferenceResource() : sourceInformation = null;
+ StringReferenceResource._(this._value, this.sourceInformation);
+
+ set value(js.Expression value) {
+ assert(_value == null && value != null);
+ _value = value;
+ }
+
+ @override
+ js.Expression get value {
+ assert(_value != null, 'StringReferenceResource is unassigned');
+ return _value;
+ }
+
+ @override
+ int get precedenceLevel => value.precedenceLevel;
+
+ @override
+ StringReferenceResource withSourceInformation(
+ js.JavaScriptNodeSourceInformation newSourceInformation) {
+ if (newSourceInformation == sourceInformation) return this;
+ if (newSourceInformation == null) return this;
+ return StringReferenceResource._(_value, newSourceInformation);
+ }
+
+ @override
+ Iterable<js.Node> get containedNodes => _value == null ? const [] : [_value];
+
+ @override
+ void visitChildren<T>(js.NodeVisitor<T> visitor) {
+ _value?.accept<T>(visitor);
+ }
+
+ @override
+ void visitChildren1<R, A>(js.NodeVisitor1<R, A> visitor, A arg) {
+ _value?.accept1<R, A>(visitor, arg);
+ }
+}
+
+abstract class StringReferenceFinalizer {
+ /// Collects StringReference and StringReferenceResource nodes from the
+ /// JavaScript AST [code];
+ void addCode(js.Node code);
+
+ /// Performs analysis on all collected StringReference nodes finalizes the
+ /// values to expressions to access the types.
+ void finalize();
+}
+
+class StringReferenceFinalizerImpl implements StringReferenceFinalizer {
+ final bool _minify;
+ final int shortestSharedLength; // Configurable for testing.
+
+ /*late final*/ _StringReferenceCollectorVisitor _visitor;
+ StringReferenceResource _resource;
+
+ /// Maps the recipe (type expression) to the references with the same recipe.
+ /// Much of the algorithm's state is stored in the _ReferenceSet objects.
+ Map<StringConstantValue, _ReferenceSet> _referencesByString = {};
+
+ StringReferenceFinalizerImpl(this._minify,
+ {this.shortestSharedLength =
+ StringReferencePolicy.shortestSharedLength}) {
+ _visitor = _StringReferenceCollectorVisitor(this);
+ }
+
+ @override
+ void addCode(js.Node code) {
+ code.accept(_visitor);
+ }
+
+ @override
+ void finalize() {
+ assert(_resource != null, 'StringReferenceFinalizer needs resource');
+ _allocateNames();
+ _updateReferences();
+ }
+
+ // Called from collector visitor.
+ void registerStringReference(StringReference node) {
+ StringConstantValue constant = node.constant;
+ _ReferenceSet refs =
+ _referencesByString[constant] ??= _ReferenceSet(constant);
+ refs.count++;
+ refs._references.add(node);
+ }
+
+ // Called from collector visitor.
+ void registerStringReferenceResource(StringReferenceResource node) {
+ assert(_resource == null);
+ _resource = node;
+ }
+
+ void _updateReferences() {
+ // Emit generate-at-use references.
+ for (_ReferenceSet referenceSet in _referencesByString.values) {
+ if (referenceSet.generateAtUse) {
+ StringConstantValue constant = referenceSet.constant;
+ js.Expression reference =
+ js.js.escapedString(constant.stringValue, ascii: true);
+ for (StringReference ref in referenceSet._references) {
+ ref.value = reference;
+ }
+ }
+ }
+
+ List<_ReferenceSet> referenceSetsUsingProperties =
+ _referencesByString.values.where((ref) => !ref.generateAtUse).toList();
+
+ // Sort by string (which is unique and stable) so that similar strings are
+ // grouped together.
+ referenceSetsUsingProperties.sort(_ReferenceSet.compareByString);
+
+ List<js.Property> properties = [];
+ for (_ReferenceSet referenceSet in referenceSetsUsingProperties) {
+ String string = referenceSet.constant.stringValue;
+ var propertyName = js.string(referenceSet.propertyName);
+ properties.add(
+ js.Property(propertyName, js.js.escapedString(string, ascii: true)));
+ var access = js.js('#.#', [holderLocalName, propertyName]);
+ for (StringReference ref in referenceSet._references) {
+ ref.value = access;
+ }
+ }
+
+ if (properties.isEmpty) {
+ // We don't have a deferred statement sequence. "0;" is the smallest we
+ // can do with an expression statement.
+ // TODO(sra): Add deferred expression statement sequences.
+ _resource.value = js.js('0');
+ } else {
+ js.Expression initializer =
+ js.ObjectInitializer(properties, isOneLiner: false);
+ _resource.value = js.js(
+ r'var # = #', [js.VariableDeclaration(holderLocalName), initializer]);
+ }
+ }
+
+ // This is a top-level local name in the generated JavaScript top-level
+ // function, so will be minified automatically. The name should not collide
+ // with any other locals.
+ static const holderLocalName = r'string$';
+
+ void _allocateNames() {
+ // Filter out generate-at-use cases and allocate unique names to the rest.
+ List<_ReferenceSet> referencesInTable = [];
+
+ for (final referenceSet in _referencesByString.values) {
+ String text = referenceSet.constant.stringValue;
+ if (referenceSet.count == 1) continue;
+ // TODO(sra): We might want to always extract very large strings,
+ // e.g. replace above with:
+ //
+ // if (referenceSet.count == 1 && text.length < 1000) continue;
+ if (text.length <= shortestSharedLength) continue;
+ referencesInTable.add(referenceSet);
+ }
+
+ if (referencesInTable.isEmpty) return;
+
+ List<String> names = abbreviateToIdentifiers(
+ referencesInTable.map((r) => r.constant.stringValue));
+ assert(referencesInTable.length == names.length);
+ for (int i = 0; i < referencesInTable.length; i++) {
+ referencesInTable[i].name = names[i];
+ }
+
+ if (!_minify) {
+ // For unminified code, use the characteristic names as property names.
+ for (final referenceSet in referencesInTable) {
+ referenceSet.propertyName = referenceSet.name;
+ }
+
+ return;
+ }
+
+ // Step 2. Sort by frequency to arrange common entries have shorter property
+ // names.
+ List<_ReferenceSet> referencesByFrequency = referencesInTable.toList()
+ ..sort((a, b) {
+ assert(a.name != b.name);
+ int r = b.count.compareTo(a.count); // Decreasing frequency.
+ if (r != 0) return r;
+ // Tie-break with raw string.
+ return _ReferenceSet.compareByString(a, b);
+ });
+
+ for (final referenceSet in referencesByFrequency) {
+ // TODO(sra): Assess the dispersal of this hash function in the
+ // semistableFrequencyAssignment algorithm.
+ // TODO(sra): Consider a cheaper but stable hash. We are generally hashing
+ // a relatively small set of large strings.
+ referenceSet.hash = Hashing.stringHash(referenceSet.constant.stringValue);
+ }
+
+ int hashOf(int index) => referencesByFrequency[index].hash;
+ int countOf(int index) => referencesByFrequency[index].count;
+ void assign(int index, String name) {
+ if (_minify) {
+ referencesByFrequency[index].propertyName = name;
+ } else {
+ var refSet = referencesByFrequency[index];
+ refSet.propertyName = name + '_' + refSet.name;
+ }
+ }
+
+ semistableFrequencyAssignment(referencesByFrequency.length,
+ generalMinifiedNameSequence(), hashOf, countOf, assign);
+ }
+}
+
+/// Set of references to a single recipe.
+class _ReferenceSet {
+ final StringConstantValue constant;
+
+ // Number of times a StringReference for [constant] occurs in the tree-scan of
+ // the JavaScript ASTs.
+ int count = 0;
+
+ // It is possible for the JavaScript AST to be a DAG, so collect
+ // [StringReference]s as set so we don't try to update one twice.
+ final Set<StringReference> _references = Set.identity();
+
+ /// Characteristic name of the recipe - this can be used as a property name
+ /// for emitting unminified code, and as a stable hash source for minified
+ /// names. [name] is `null` if [recipe] should always be generated at use.
+ String name;
+
+ /// Property name for 'indexing' into the precomputed types.
+ String propertyName;
+
+ /// A stable hash code that can be used for picking stable minified names.
+ int hash = 0;
+
+ _ReferenceSet(this.constant);
+
+ // If we don't assign a name it means we should not precompute the recipe.
+ bool get generateAtUse => name == null;
+
+ static int compareByString(_ReferenceSet a, _ReferenceSet b) {
+ return a.constant.stringValue.compareTo(b.constant.stringValue);
+ }
+}
+
+/// Scans a JavaScript AST to collect all the StringReference nodes.
+///
+/// The state is kept in the finalizer so that this scan could be extended to
+/// look for other deferred expressions in one pass.
+// TODO(sra): Merge with TypeReferenceCollectorVisitor.
+class _StringReferenceCollectorVisitor extends js.BaseVisitor<void> {
+ final StringReferenceFinalizerImpl _finalizer;
+
+ _StringReferenceCollectorVisitor(this._finalizer);
+
+ @override
+ void visitNode(js.Node node) {
+ assert(node is! StringReference);
+ assert(node is! StringReferenceResource);
+ if (node is js.AstContainer) {
+ for (js.Node element in node.containedNodes) {
+ element.accept(this);
+ }
+ } else {
+ super.visitNode(node);
+ }
+ }
+
+ @override
+ void visitDeferredExpression(js.DeferredExpression node) {
+ if (node is StringReference) {
+ _finalizer.registerStringReference(node);
+ } else if (node is StringReferenceResource) {
+ _finalizer.registerStringReferenceResource(node);
+ } else {
+ visitNode(node);
+ }
+ }
+}
diff --git a/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart b/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart
index 2e5779b..8ae8d3a3 100644
--- a/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart
+++ b/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart
@@ -432,6 +432,9 @@
// Adds the variance table for the new RTI.
#variances;
+// Shared strings need to be initialized before constants.
+#sharedStrings;
+
// Shared types need to be initialized before constants.
#sharedTypeRtis;
@@ -536,6 +539,8 @@
// Adds the variance table for the new RTI.
#variances;
+#sharedStrings;
+
#sharedTypeRtis;
// Instantiates all constants of this deferred fragment.
// Note that the constant-holder has been updated earlier and storing the
@@ -695,6 +700,7 @@
'embeddedGlobalsPart2':
emitEmbeddedGlobalsPart2(program, deferredLoadingState),
'typeRules': emitTypeRules(fragment),
+ 'sharedStrings': StringReferenceResource(),
'variances': emitVariances(fragment),
'sharedTypeRtis': TypeReferenceResource(),
'nativeSupport': emitNativeSupport(fragment),
@@ -725,7 +731,7 @@
mainCode
]);
}
- finalizeTypeReferences(mainCode);
+ finalizeStringAndTypeReferences(mainCode);
return mainCode;
}
@@ -827,17 +833,22 @@
'types': deferredTypes,
'nativeSupport': nativeSupport,
'typesOffset': _namer.typesOffsetName,
+ 'sharedStrings': StringReferenceResource(),
'sharedTypeRtis': TypeReferenceResource(),
});
if (_options.experimentStartupFunctions) {
code = js.Parentheses(code);
}
- finalizeTypeReferences(code);
+ finalizeStringAndTypeReferences(code);
return code;
}
- void finalizeTypeReferences(js.Node code) {
+ void finalizeStringAndTypeReferences(js.Node code) {
+ StringReferenceFinalizer stringFinalizer =
+ StringReferenceFinalizerImpl(_options.enableMinification);
+ stringFinalizer.addCode(code);
+ stringFinalizer.finalize();
TypeReferenceFinalizer finalizer = TypeReferenceFinalizerImpl(
_emitter, _commonElements, _recipeEncoder, _options.enableMinification);
finalizer.addCode(code);
diff --git a/pkg/compiler/lib/src/js_emitter/startup_emitter/model_emitter.dart b/pkg/compiler/lib/src/js_emitter/startup_emitter/model_emitter.dart
index 33479d9..f549f60 100644
--- a/pkg/compiler/lib/src/js_emitter/startup_emitter/model_emitter.dart
+++ b/pkg/compiler/lib/src/js_emitter/startup_emitter/model_emitter.dart
@@ -60,6 +60,11 @@
TypeReferenceFinalizer,
TypeReferenceFinalizerImpl,
TypeReferenceResource;
+import '../../js_backend/string_reference.dart'
+ show
+ StringReferenceFinalizer,
+ StringReferenceFinalizerImpl,
+ StringReferenceResource;
import '../../options.dart';
import '../../universe/class_hierarchy.dart' show ClassHierarchy;
import '../../universe/codegen_world_builder.dart' show CodegenWorld;
diff --git a/pkg/compiler/lib/src/ssa/builder_kernel.dart b/pkg/compiler/lib/src/ssa/builder_kernel.dart
index 801f78f..02e6e5c 100644
--- a/pkg/compiler/lib/src/ssa/builder_kernel.dart
+++ b/pkg/compiler/lib/src/ssa/builder_kernel.dart
@@ -3251,6 +3251,7 @@
ir.DartType type = node.type;
if (type is ir.InterfaceType ||
type is ir.DynamicType ||
+ type is ir.NeverType ||
type is ir.TypedefType ||
type is ir.FunctionType ||
type is ir.FutureOrType) {
@@ -4495,16 +4496,6 @@
return js.js
.expressionTemplateYielding(_emitter.typeAccess(closureClass));
- case JsBuiltin.isCheckPropertyToJsConstructorName:
- int isPrefixLength = _namer.fixedNames.operatorIsPrefix.length;
- return js.js.expressionTemplateFor('#.substring($isPrefixLength)');
-
- case JsBuiltin.rawRtiToJsConstructorName:
- return js.js.expressionTemplateFor("#.name");
-
- case JsBuiltin.rawRuntimeType:
- return js.js.expressionTemplateFor("#.constructor");
-
case JsBuiltin.getMetadata:
String metadataAccess =
_emitter.generateEmbeddedGlobalAccessString(METADATA);
diff --git a/pkg/compiler/lib/src/ssa/codegen.dart b/pkg/compiler/lib/src/ssa/codegen.dart
index afcc43f..dc5adf4 100644
--- a/pkg/compiler/lib/src/ssa/codegen.dart
+++ b/pkg/compiler/lib/src/ssa/codegen.dart
@@ -2512,11 +2512,7 @@
bool handledBySpecialCase = false;
if (isGenerateAtUseSite(input)) {
handledBySpecialCase = true;
- if (input is HIs) {
- emitIs(input, '!==', sourceInformation);
- } else if (input is HIsViaInterceptor) {
- emitIsViaInterceptor(input, sourceInformation, negative: true);
- } else if (input is HIsTestSimple) {
+ if (input is HIsTestSimple) {
_emitIsTestSimple(input, negative: true);
} else if (input is HNot) {
use(input.inputs[0]);
@@ -2855,21 +2851,6 @@
push(new js.Binary(cmp, left, or0));
}
- void checkBigInt(
- HInstruction input, String cmp, SourceInformation sourceInformation) {
- use(input);
- js.Expression left = pop();
- use(input);
- js.Expression right = pop();
- // TODO(4984): Deal with infinity and -0.0.
- js.Expression mathFloorCall = js.js('Math.floor(#)',
- <js.Expression>[left]).withSourceInformation(sourceInformation);
- push(js.js('# $cmp #', <js.Expression>[
- mathFloorCall,
- right
- ]).withSourceInformation(sourceInformation));
- }
-
void checkTypeOf(HInstruction input, String cmp, String typeName,
SourceInformation sourceInformation) {
use(input);
@@ -2883,328 +2864,11 @@
return checkTypeOf(input, cmp, 'number', sourceInformation);
}
- void checkDouble(
- HInstruction input, String cmp, SourceInformation sourceInformation) {
- return checkNum(input, cmp, sourceInformation);
- }
-
- void checkString(
- HInstruction input, String cmp, SourceInformation sourceInformation) {
- return checkTypeOf(input, cmp, 'string', sourceInformation);
- }
-
void checkBool(
HInstruction input, String cmp, SourceInformation sourceInformation) {
return checkTypeOf(input, cmp, 'boolean', sourceInformation);
}
- void checkObject(
- HInstruction input, String cmp, SourceInformation sourceInformation) {
- assert(NullConstantValue.JsNull == 'null');
- if (cmp == "===") {
- checkTypeOf(input, '===', 'object', sourceInformation);
- js.Expression left = pop();
- use(input);
- js.Expression notNull = new js.Binary("!==", pop(), new js.LiteralNull());
- push(new js.Binary("&&", left, notNull)
- .withSourceInformation(sourceInformation));
- } else {
- assert(cmp == "!==");
- checkTypeOf(input, '!==', 'object', sourceInformation);
- js.Expression left = pop();
- use(input);
- js.Expression eqNull = new js.Binary("===", pop(), new js.LiteralNull());
- push(new js.Binary("||", left, eqNull)
- .withSourceInformation(sourceInformation));
- }
- }
-
- void checkArray(HInstruction input, String cmp) {
- use(input);
- js.PropertyAccess constructor =
- new js.PropertyAccess.field(pop(), 'constructor');
- push(new js.Binary(cmp, constructor, new js.VariableUse('Array')));
- }
-
- void checkFieldExists(HInstruction input, String fieldName) {
- use(input);
- js.PropertyAccess field = new js.PropertyAccess.field(pop(), fieldName);
- // Double negate to boolify the result.
- push(new js.Prefix('!', new js.Prefix('!', field)));
- }
-
- void checkFieldDoesNotExist(HInstruction input, String fieldName) {
- use(input);
- js.PropertyAccess field = new js.PropertyAccess.field(pop(), fieldName);
- push(new js.Prefix('!', field));
- }
-
- void checkImmutableArray(HInstruction input) {
- checkFieldExists(input, 'immutable\$list');
- }
-
- void checkMutableArray(HInstruction input) {
- checkFieldDoesNotExist(input, 'immutable\$list');
- }
-
- void checkExtendableArray(HInstruction input) {
- checkFieldDoesNotExist(input, 'fixed\$length');
- }
-
- void checkFixedArray(HInstruction input) {
- checkFieldExists(input, 'fixed\$length');
- }
-
- void checkNull(HInstruction input) {
- use(input);
- push(new js.Binary('==', pop(), new js.LiteralNull()));
- }
-
- void checkNonNull(HInstruction input) {
- use(input);
- push(new js.Binary('!=', pop(), new js.LiteralNull()));
- }
-
- void checkType(HInstruction input, HInstruction interceptor, DartType type,
- SourceInformation sourceInformation,
- {bool negative: false}) {
- if (type is InterfaceType) {
- InterfaceType interfaceType = type;
- ClassEntity element = interfaceType.element;
- if (element == _commonElements.jsArrayClass) {
- checkArray(input, negative ? '!==' : '===');
- return;
- } else if (element == _commonElements.jsMutableArrayClass) {
- if (negative) {
- checkImmutableArray(input);
- } else {
- checkMutableArray(input);
- }
- return;
- } else if (element == _commonElements.jsExtendableArrayClass) {
- if (negative) {
- checkFixedArray(input);
- } else {
- checkExtendableArray(input);
- }
- return;
- } else if (element == _commonElements.jsFixedArrayClass) {
- if (negative) {
- checkExtendableArray(input);
- } else {
- checkFixedArray(input);
- }
- return;
- } else if (element == _commonElements.jsUnmodifiableArrayClass) {
- if (negative) {
- checkMutableArray(input);
- } else {
- checkImmutableArray(input);
- }
- return;
- }
- }
- if (interceptor != null) {
- checkTypeViaProperty(interceptor, type, sourceInformation,
- negative: negative);
- } else {
- checkTypeViaProperty(input, type, sourceInformation, negative: negative);
- }
- }
-
- void checkTypeViaProperty(
- HInstruction input, DartType type, SourceInformation sourceInformation,
- {bool negative: false}) {
- _registry.registerTypeUse(new TypeUse.isCheck(type));
-
- use(input);
-
- js.PropertyAccess field =
- new js.PropertyAccess(pop(), _namer.operatorIsType(type))
- .withSourceInformation(sourceInformation);
- // We always negate at least once so that the result is boolified.
- push(new js.Prefix('!', field).withSourceInformation(sourceInformation));
- // If the result is not negated, put another '!' in front.
- if (!negative) {
- push(new js.Prefix('!', pop()).withSourceInformation(sourceInformation));
- }
- }
-
- void checkTypeViaInstanceof(HInstruction input, InterfaceType type,
- SourceInformation sourceInformation,
- {bool negative: false}) {
- _registry.registerTypeUse(new TypeUse.instanceConstructor(type));
-
- use(input);
-
- js.Expression jsClassReference = _emitter.constructorAccess(type.element);
- push(js.js('# instanceof #',
- [pop(), jsClassReference]).withSourceInformation(sourceInformation));
- if (negative) {
- push(new js.Prefix('!', pop()).withSourceInformation(sourceInformation));
- }
- }
-
- void handleNumberOrStringSupertypeCheck(
- HInstruction input,
- HInstruction interceptor,
- InterfaceType type,
- SourceInformation sourceInformation,
- {bool negative: false}) {
- assert(!identical(type.element, _commonElements.listClass) &&
- !_commonElements.isListSupertype(type.element) &&
- !_commonElements.isStringOnlySupertype(type.element));
- String relation = negative ? '!==' : '===';
- checkNum(input, relation, sourceInformation);
- js.Expression numberTest = pop();
- checkString(input, relation, sourceInformation);
- js.Expression stringTest = pop();
- checkObject(input, relation, sourceInformation);
- js.Expression objectTest = pop();
- checkType(input, interceptor, type, sourceInformation, negative: negative);
- String combiner = negative ? '&&' : '||';
- String combiner2 = negative ? '||' : '&&';
- push(new js.Binary(
- combiner,
- new js.Binary(combiner, numberTest, stringTest)
- .withSourceInformation(sourceInformation),
- new js.Binary(combiner2, objectTest, pop())
- .withSourceInformation(sourceInformation))
- .withSourceInformation(sourceInformation));
- }
-
- void handleStringSupertypeCheck(HInstruction input, HInstruction interceptor,
- InterfaceType type, SourceInformation sourceInformation,
- {bool negative: false}) {
- assert(!identical(type.element, _commonElements.listClass) &&
- !_commonElements.isListSupertype(type.element) &&
- !_commonElements.isNumberOrStringSupertype(type.element));
- String relation = negative ? '!==' : '===';
- checkString(input, relation, sourceInformation);
- js.Expression stringTest = pop();
- checkObject(input, relation, sourceInformation);
- js.Expression objectTest = pop();
- checkType(input, interceptor, type, sourceInformation, negative: negative);
- String combiner = negative ? '||' : '&&';
- push(new js.Binary(negative ? '&&' : '||', stringTest,
- new js.Binary(combiner, objectTest, pop())));
- }
-
- void handleListOrSupertypeCheck(HInstruction input, HInstruction interceptor,
- InterfaceType type, SourceInformation sourceInformation,
- {bool negative: false}) {
- assert(!identical(type.element, _commonElements.stringClass) &&
- !_commonElements.isStringOnlySupertype(type.element) &&
- !_commonElements.isNumberOrStringSupertype(type.element));
- String relation = negative ? '!==' : '===';
- checkObject(input, relation, sourceInformation);
- js.Expression objectTest = pop();
- checkArray(input, relation);
- js.Expression arrayTest = pop();
- checkType(input, interceptor, type, sourceInformation, negative: negative);
- String combiner = negative ? '&&' : '||';
- push(new js.Binary(negative ? '||' : '&&', objectTest,
- new js.Binary(combiner, arrayTest, pop()))
- .withSourceInformation(sourceInformation));
- }
-
- @override
- void visitIs(HIs node) {
- emitIs(node, "===", node.sourceInformation);
- }
-
- @override
- void visitIsViaInterceptor(HIsViaInterceptor node) {
- emitIsViaInterceptor(node, node.sourceInformation, negative: false);
- }
-
- void emitIs(HIs node, String relation, SourceInformation sourceInformation) {
- DartType type = node.typeExpression;
- _registry.registerTypeUse(new TypeUse.isCheck(type));
- HInstruction input = node.expression;
-
- // If this is changed to single == there are several places below that must
- // be changed to match.
- assert(relation == '===' || relation == '!==');
- bool negative = relation == '!==';
-
- if (node.isVariableCheck || node.isCompoundCheck) {
- use(node.checkCall);
- if (negative) push(new js.Prefix('!', pop()));
- } else {
- assert(node.isRawCheck);
- HInstruction interceptor = node.interceptor;
- InterfaceType interfaceType = type;
- ClassEntity element = interfaceType.element;
- if (element == _commonElements.nullClass) {
- if (negative) {
- checkNonNull(input);
- } else {
- checkNull(input);
- }
- } else if (element == _commonElements.objectClass /* || type.isTop*/) {
- // The constant folder also does this optimization, but we make
- // it safe by assuming it may have not run.
- push(newLiteralBool(!negative, sourceInformation));
- } else if (element == _commonElements.stringClass) {
- checkString(input, relation, sourceInformation);
- } else if (element == _commonElements.doubleClass) {
- checkDouble(input, relation, sourceInformation);
- } else if (element == _commonElements.numClass) {
- checkNum(input, relation, sourceInformation);
- } else if (element == _commonElements.boolClass) {
- checkBool(input, relation, sourceInformation);
- } else if (element == _commonElements.intClass) {
- // The is check in the code tells us that it might not be an
- // int. So we do a typeof first to avoid possible
- // deoptimizations on the JS engine due to the Math.floor check.
- checkNum(input, relation, sourceInformation);
- js.Expression numTest = pop();
- checkBigInt(input, relation, sourceInformation);
- push(new js.Binary(negative ? '||' : '&&', numTest, pop())
- .withSourceInformation(sourceInformation));
- } else if (node.useInstanceOf) {
- assert(interceptor == null);
- checkTypeViaInstanceof(input, type, sourceInformation,
- negative: negative);
- } else if (_commonElements.isNumberOrStringSupertype(element)) {
- handleNumberOrStringSupertypeCheck(
- input, interceptor, type, sourceInformation,
- negative: negative);
- } else if (_commonElements.isStringOnlySupertype(element)) {
- handleStringSupertypeCheck(input, interceptor, type, sourceInformation,
- negative: negative);
- } else if (element == _commonElements.listClass ||
- _commonElements.isListSupertype(element)) {
- handleListOrSupertypeCheck(input, interceptor, type, sourceInformation,
- negative: negative);
- } else if (type is FunctionType) {
- checkType(input, interceptor, type, sourceInformation,
- negative: negative);
- } else if ((input.isPrimitive(_abstractValueDomain).isPotentiallyTrue &&
- input.isPrimitiveArray(_abstractValueDomain).isDefinitelyFalse) ||
- input.isNull(_abstractValueDomain).isPotentiallyTrue) {
- checkObject(input, relation, node.sourceInformation);
- js.Expression objectTest = pop();
- checkType(input, interceptor, type, sourceInformation,
- negative: negative);
- push(new js.Binary(negative ? '||' : '&&', objectTest, pop())
- .withSourceInformation(sourceInformation));
- } else {
- checkType(input, interceptor, type, sourceInformation,
- negative: negative);
- }
- }
- }
-
- void emitIsViaInterceptor(
- HIsViaInterceptor node, SourceInformation sourceInformation,
- {bool negative: false}) {
- checkTypeViaProperty(
- node.interceptor, node.typeExpression, sourceInformation,
- negative: negative);
- }
-
@override
void visitPrimitiveCheck(HPrimitiveCheck node) {
js.Expression test = _generateReceiverOrArgumentTypeTest(node);
diff --git a/pkg/compiler/lib/src/ssa/codegen_helpers.dart b/pkg/compiler/lib/src/ssa/codegen_helpers.dart
index 05f415f..be25134 100644
--- a/pkg/compiler/lib/src/ssa/codegen_helpers.dart
+++ b/pkg/compiler/lib/src/ssa/codegen_helpers.dart
@@ -170,18 +170,6 @@
}
@override
- HInstruction visitIs(HIs node) {
- if (node.kind == HIs.RAW_CHECK) {
- HInstruction interceptor = node.interceptor;
- if (interceptor != null) {
- return new HIsViaInterceptor(
- node.typeExpression, interceptor, _abstractValueDomain.boolType);
- }
- }
- return node;
- }
-
- @override
HInstruction visitIdentity(HIdentity node) {
node.singleComparisonOp = simpleOp(node.left, node.right);
return node;
@@ -810,23 +798,6 @@
}
}
- @override
- void visitIs(HIs instruction) {
- // In the general case the input might be used multple multiple times, so it
- // must not be set generate at use site.
-
- // If the code will generate 'instanceof' then we can generate at use site.
- if (instruction.useInstanceOf) {
- analyzeInputs(instruction, 0);
- }
-
- // Compound and variable checks use a separate instruction to compute the
- // result.
- if (instruction.isCompoundCheck || instruction.isVariableCheck) {
- analyzeInputs(instruction, 0);
- }
- }
-
// A bounds check method must not have its first input generated at use site,
// because it's using it twice.
@override
@@ -1086,9 +1057,6 @@
// A [HCheck] instruction with control flow uses its input
// multiple times, so we avoid generating it at use site.
if (user is HCheck && user.isControlFlow()) return false;
- // A [HIs] instruction uses its input multiple times, so we
- // avoid generating it at use site.
- if (user is HIs) return false;
// Avoid code motion into a loop.
return user.hasSameLoopHeaderAs(input);
}
diff --git a/pkg/compiler/lib/src/ssa/interceptor_simplifier.dart b/pkg/compiler/lib/src/ssa/interceptor_simplifier.dart
index a1b208c..a484821 100644
--- a/pkg/compiler/lib/src/ssa/interceptor_simplifier.dart
+++ b/pkg/compiler/lib/src/ssa/interceptor_simplifier.dart
@@ -27,9 +27,6 @@
/// the interceptor and then call the method. This saves code size and makes the
/// receiver of an intercepted call a candidate for being generated at use site.
///
-/// 5) Some HIs operations on an interceptor are replaced with a HIs version that
-/// uses 'instanceof' rather than testing a type flag.
-///
class SsaSimplifyInterceptors extends HBaseVisitor
implements OptimizationPhase {
@override
@@ -243,8 +240,8 @@
// Specialize the interceptor with set of classes it intercepts, considering
// all uses. (The specialized interceptor has a shorter dispatch chain).
// This operation applies only where the interceptor is used to dispatch a
- // method. Other uses, e.g. as an ordinary argument or a HIs check use the
- // most general interceptor.
+ // method. Other uses, e.g. as an ordinary argument use the most general
+ // interceptor.
//
// TODO(sra): Take into account the receiver type at each call. e.g:
//
@@ -377,19 +374,7 @@
return false;
}
- if (user is HIs) {
- // See if we can rewrite the is-check to use 'instanceof', i.e. rewrite
- // "getInterceptor(x).$isT" to "x instanceof T".
- if (node == user.interceptor) {
- if (_interceptorData.mayGenerateInstanceofCheck(
- user.typeExpression, _closedWorld)) {
- HInstruction instanceofCheck = new HIs.instanceOf(user.typeExpression,
- user.expression, user.instructionType, user.sourceInformation);
- instanceofCheck.sourceElement = user.sourceElement;
- return replaceUserWith(instanceofCheck);
- }
- }
- } else if (user is HInvokeDynamic) {
+ if (user is HInvokeDynamic) {
if (node == user.inputs[0]) {
// Replace the user with a [HOneShotInterceptor].
HConstant nullConstant = _graph.addConstantNull(_closedWorld);
@@ -439,14 +424,7 @@
}
for (HInstruction user in node.usedBy.toList()) {
- if (user is HIs) {
- if (user.interceptor == node) {
- HInstruction expression = user.expression;
- if (canUseSelfForInterceptor(expression)) {
- user.changeUse(node, expression);
- }
- }
- } else if (user is HInvokeDynamic) {
+ if (user is HInvokeDynamic) {
if (user.isCallOnInterceptor(_closedWorld) &&
node == user.inputs[0] &&
useCount(user, node) == 1) {
diff --git a/pkg/compiler/lib/src/ssa/nodes.dart b/pkg/compiler/lib/src/ssa/nodes.dart
index e654310..86310fe 100644
--- a/pkg/compiler/lib/src/ssa/nodes.dart
+++ b/pkg/compiler/lib/src/ssa/nodes.dart
@@ -65,8 +65,6 @@
R visitInvokeSuper(HInvokeSuper node);
R visitInvokeConstructorBody(HInvokeConstructorBody node);
R visitInvokeGeneratorBody(HInvokeGeneratorBody node);
- R visitIs(HIs node);
- R visitIsViaInterceptor(HIsViaInterceptor node);
R visitLateValue(HLateValue node);
R visitLazyStatic(HLazyStatic node);
R visitLess(HLess node);
@@ -578,12 +576,8 @@
@override
visitTry(HTry node) => visitControlFlow(node);
@override
- visitIs(HIs node) => visitInstruction(node);
- @override
visitLateValue(HLateValue node) => visitInstruction(node);
@override
- visitIsViaInterceptor(HIsViaInterceptor node) => visitInstruction(node);
- @override
visitBoolConversion(HBoolConversion node) => visitCheck(node);
@override
visitNullCheck(HNullCheck node) => visitCheck(node);
@@ -1074,12 +1068,10 @@
static const int TYPE_KNOWN_TYPECODE = 26;
static const int INVOKE_STATIC_TYPECODE = 27;
static const int INDEX_TYPECODE = 28;
- static const int IS_TYPECODE = 29;
- static const int INVOKE_DYNAMIC_TYPECODE = 30;
- static const int SHIFT_RIGHT_TYPECODE = 31;
+ static const int INVOKE_DYNAMIC_TYPECODE = 29;
+ static const int SHIFT_RIGHT_TYPECODE = 30;
static const int TRUNCATING_DIVIDE_TYPECODE = 36;
- static const int IS_VIA_INTERCEPTOR_TYPECODE = 37;
static const int INVOKE_EXTERNAL_TYPECODE = 41;
static const int FOREIGN_CODE_TYPECODE = 42;
@@ -3476,136 +3468,6 @@
receiver.isNull(domain).isPotentiallyTrue;
}
-/// Is-test using legacy constructor based typ representation.
-class HIs extends HInstruction {
- /// A check against a raw type: 'o is int', 'o is A'.
- static const int RAW_CHECK = 0;
-
- /// A check against a type with type arguments: 'o is List<int>', 'o is C<T>'.
- static const int COMPOUND_CHECK = 1;
-
- /// A check against a single type variable: 'o is T'.
- static const int VARIABLE_CHECK = 2;
-
- final DartType typeExpression;
- final int kind;
- final bool useInstanceOf;
-
- HIs.direct(DartType typeExpression, HInstruction expression,
- AbstractValue type, SourceInformation sourceInformation)
- : this.internal(
- typeExpression, [expression], RAW_CHECK, type, sourceInformation);
-
- // Pre-verified that the check can be done using 'instanceof'.
- HIs.instanceOf(DartType typeExpression, HInstruction expression,
- AbstractValue type, SourceInformation sourceInformation)
- : this.internal(
- typeExpression, [expression], RAW_CHECK, type, sourceInformation,
- useInstanceOf: true);
-
- factory HIs.raw(
- DartType typeExpression,
- HInstruction expression,
- HInterceptor interceptor,
- AbstractValue type,
- SourceInformation sourceInformation) {
- // TODO(sigmund): re-add `&& typeExpression.treatAsRaw` or something
- // equivalent (which started failing once we allowed typeExpressions that
- // contain type parameters matching the original bounds of the type).
- assert((typeExpression is FunctionType || typeExpression is InterfaceType),
- "Unexpected raw is-test type: $typeExpression");
- return new HIs.internal(typeExpression, [expression, interceptor],
- RAW_CHECK, type, sourceInformation);
- }
-
- HIs.compound(
- DartType typeExpression,
- HInstruction expression,
- HInstruction call,
- AbstractValue type,
- SourceInformation sourceInformation)
- : this.internal(typeExpression, [expression, call], COMPOUND_CHECK, type,
- sourceInformation);
-
- HIs.variable(
- DartType typeExpression,
- HInstruction expression,
- HInstruction call,
- AbstractValue type,
- SourceInformation sourceInformation)
- : this.internal(typeExpression, [expression, call], VARIABLE_CHECK, type,
- sourceInformation);
-
- HIs.internal(this.typeExpression, List<HInstruction> inputs, this.kind,
- AbstractValue type, SourceInformation sourceInformation,
- {bool this.useInstanceOf: false})
- : super(inputs, type) {
- assert(kind >= RAW_CHECK && kind <= VARIABLE_CHECK);
- setUseGvn();
- this.sourceInformation = sourceInformation;
- }
-
- HInstruction get expression => inputs[0];
-
- HInstruction get interceptor {
- assert(kind == RAW_CHECK);
- return inputs.length > 1 ? inputs[1] : null;
- }
-
- HInstruction get checkCall {
- assert(kind == VARIABLE_CHECK || kind == COMPOUND_CHECK);
- return inputs[1];
- }
-
- bool get isRawCheck => kind == RAW_CHECK;
- bool get isVariableCheck => kind == VARIABLE_CHECK;
- bool get isCompoundCheck => kind == COMPOUND_CHECK;
-
- @override
- accept(HVisitor visitor) => visitor.visitIs(this);
-
- @override
- toString() => "$expression is $typeExpression";
-
- @override
- int typeCode() => HInstruction.IS_TYPECODE;
-
- @override
- bool typeEquals(HInstruction other) => other is HIs;
-
- @override
- bool dataEquals(HIs other) {
- return typeExpression == other.typeExpression && kind == other.kind;
- }
-}
-
-/// HIsViaInterceptor is a late-stage instruction for a type test that can be
-/// done entirely on an interceptor. It is not a HCheck because the checked
-/// input is not one of the inputs.
-class HIsViaInterceptor extends HLateInstruction {
- final DartType typeExpression;
- HIsViaInterceptor(
- this.typeExpression, HInstruction interceptor, AbstractValue type)
- : super(<HInstruction>[interceptor], type) {
- setUseGvn();
- }
-
- HInstruction get interceptor => inputs[0];
-
- @override
- accept(HVisitor visitor) => visitor.visitIsViaInterceptor(this);
- @override
- toString() => "$interceptor is $typeExpression";
- @override
- int typeCode() => HInstruction.IS_VIA_INTERCEPTOR_TYPECODE;
- @override
- bool typeEquals(HInstruction other) => other is HIsViaInterceptor;
- @override
- bool dataEquals(HIs other) {
- return typeExpression == other.typeExpression;
- }
-}
-
/// HLateValue is a late-stage instruction that can be used to force a value
/// into a temporary.
///
diff --git a/pkg/compiler/lib/src/ssa/optimize.dart b/pkg/compiler/lib/src/ssa/optimize.dart
index f075382..e21dd7d 100644
--- a/pkg/compiler/lib/src/ssa/optimize.dart
+++ b/pkg/compiler/lib/src/ssa/optimize.dart
@@ -1223,80 +1223,6 @@
}
@override
- HInstruction visitIs(HIs node) {
- DartType type = node.typeExpression;
-
- if (!node.isRawCheck) {
- return node;
- } else if (type is FunctionType) {
- return node;
- } else if (type is FutureOrType) {
- return node;
- }
-
- if (_closedWorld.dartTypes.isTopType(type)) {
- return _graph.addConstantBool(true, _closedWorld);
- }
- InterfaceType interfaceType = type;
- ClassEntity element = interfaceType.element;
- HInstruction expression = node.expression;
- if (expression.isInteger(_abstractValueDomain).isDefinitelyTrue) {
- if (element == commonElements.intClass ||
- element == commonElements.numClass ||
- commonElements.isNumberOrStringSupertype(element)) {
- return _graph.addConstantBool(true, _closedWorld);
- } else if (element == commonElements.doubleClass) {
- // We let the JS semantics decide for that check. Currently
- // the code we emit will always return true.
- return node;
- } else {
- return _graph.addConstantBool(false, _closedWorld);
- }
- } else if (expression.isDouble(_abstractValueDomain).isDefinitelyTrue) {
- if (element == commonElements.doubleClass ||
- element == commonElements.numClass ||
- commonElements.isNumberOrStringSupertype(element)) {
- return _graph.addConstantBool(true, _closedWorld);
- } else if (element == commonElements.intClass) {
- // We let the JS semantics decide for that check. Currently
- // the code we emit will return true for a double that can be
- // represented as a 31-bit integer and for -0.0.
- return node;
- } else {
- return _graph.addConstantBool(false, _closedWorld);
- }
- } else if (expression.isNumber(_abstractValueDomain).isDefinitelyTrue) {
- if (element == commonElements.numClass) {
- return _graph.addConstantBool(true, _closedWorld);
- } else {
- // We cannot just return false, because the expression may be of
- // type int or double.
- }
- } else if (expression
- .isPrimitiveNumber(_abstractValueDomain)
- .isPotentiallyTrue &&
- element == commonElements.intClass) {
- // We let the JS semantics decide for that check.
- return node;
- // We need the [:hasTypeArguments:] check because we don't have
- // the notion of generics in the backend. For example, [:this:] in
- // a class [:A<T>:], is currently always considered to have the
- // raw type.
- } else if (!RuntimeTypesSubstitutions.hasTypeArguments(
- _closedWorld.dartTypes, type)) {
- AbstractValue expressionMask = expression.instructionType;
- AbstractBool isInstanceOf =
- _abstractValueDomain.isInstanceOf(expressionMask, element);
- if (isInstanceOf.isDefinitelyTrue) {
- return _graph.addConstantBool(true, _closedWorld);
- } else if (isInstanceOf.isDefinitelyFalse) {
- return _graph.addConstantBool(false, _closedWorld);
- }
- }
- return node;
- }
-
- @override
HInstruction visitPrimitiveCheck(HPrimitiveCheck node) {
if (node.isRedundant(_closedWorld)) return node.checkedInput;
return node;
@@ -3057,35 +2983,6 @@
}
@override
- void visitIs(HIs instruction) {
- DartType type = instruction.typeExpression;
- if (!instruction.isRawCheck) {
- return;
- } else if (type is FutureOrType) {
- return;
- }
- InterfaceType interfaceType = type;
- ClassEntity cls = interfaceType.element;
-
- List<HBasicBlock> trueTargets = <HBasicBlock>[];
- List<HBasicBlock> falseTargets = <HBasicBlock>[];
-
- collectTargets(instruction, trueTargets, falseTargets);
-
- if (trueTargets.isEmpty && falseTargets.isEmpty) return;
-
- AbstractValue convertedType =
- _abstractValueDomain.createNonNullSubtype(cls);
- HInstruction input = instruction.expression;
-
- for (HBasicBlock block in trueTargets) {
- insertTypePropagationForDominatedUsers(block, input, convertedType);
- }
- // TODO(sra): Also strengthen uses for when the condition is known
- // false. Avoid strengthening to `null`.
- }
-
- @override
void visitIsTest(HIsTest instruction) {
List<HBasicBlock> trueTargets = <HBasicBlock>[];
List<HBasicBlock> falseTargets = <HBasicBlock>[];
@@ -3464,10 +3361,6 @@
@override
void visitInterceptor(HInterceptor instruction) {}
@override
- void visitIs(HIs instruction) {}
- @override
- void visitIsViaInterceptor(HIsViaInterceptor instruction) {}
- @override
void visitNot(HNot instruction) {}
@override
void visitNullCheck(HNullCheck instruction) {}
diff --git a/pkg/compiler/lib/src/ssa/ssa_tracer.dart b/pkg/compiler/lib/src/ssa/ssa_tracer.dart
index 053beb2f..ef1ad6a 100644
--- a/pkg/compiler/lib/src/ssa/ssa_tracer.dart
+++ b/pkg/compiler/lib/src/ssa/ssa_tracer.dart
@@ -597,18 +597,6 @@
}
@override
- String visitIs(HIs node) {
- String type = node.typeExpression.toString();
- return "Is: ${temporaryId(node.expression)} is $type";
- }
-
- @override
- String visitIsViaInterceptor(HIsViaInterceptor node) {
- String type = node.typeExpression.toString();
- return "IsViaInterceptor: ${temporaryId(node.inputs[0])} is $type";
- }
-
- @override
String visitPrimitiveCheck(HPrimitiveCheck node) {
String checkedInput = temporaryId(node.checkedInput);
assert(node.inputs.length == 1);
diff --git a/pkg/compiler/lib/src/universe/feature.dart b/pkg/compiler/lib/src/universe/feature.dart
index 1cb6aff..7ea60b7 100644
--- a/pkg/compiler/lib/src/universe/feature.dart
+++ b/pkg/compiler/lib/src/universe/feature.dart
@@ -16,9 +16,6 @@
/// A language feature that may be seen in the program.
// TODO(johnniwinther): Should mirror usage be part of this?
enum Feature {
- /// Invocation of a generative construction on an abstract class.
- ABSTRACT_CLASS_INSTANTIATION,
-
/// An assert statement with no message.
ASSERT,
diff --git a/pkg/compiler/lib/src/util/util.dart b/pkg/compiler/lib/src/util/util.dart
index a4e0f16..423c0b2 100644
--- a/pkg/compiler/lib/src/util/util.dart
+++ b/pkg/compiler/lib/src/util/util.dart
@@ -36,6 +36,15 @@
return ((high * 13) ^ (low * 997) ^ h) & SMI_MASK;
}
+ /// Returns a hash value computed from all the characters in the string.
+ static int stringHash(String s) {
+ int hash = mixHashCodeBits(0, s.length);
+ for (int i = 0; i < s.length; i++) {
+ hash = mixHashCodeBits(hash, s.codeUnitAt(i));
+ }
+ return hash;
+ }
+
/// Mix the bits of `object.hashCode` with [existing].
static int objectHash(Object object, [int existing = 0]) {
return mixHashCodeBits(existing, object.hashCode);
diff --git a/pkg/compiler/pubspec.yaml b/pkg/compiler/pubspec.yaml
index 2bc338e..80667e6 100644
--- a/pkg/compiler/pubspec.yaml
+++ b/pkg/compiler/pubspec.yaml
@@ -1,6 +1,7 @@
# This pubspec is currently mainly used to make it easier to develop on dart2js
# by making it a standalone package.
name: compiler
+# This package is not intended for consumption on pub.dev. DO NOT publish.
publish_to: none
environment:
sdk: '>=2.6.0 <3.0.0'
diff --git a/pkg/compiler/test/codegen/string_abbreviation_test.dart b/pkg/compiler/test/codegen/string_abbreviation_test.dart
new file mode 100644
index 0000000..00ed589
--- /dev/null
+++ b/pkg/compiler/test/codegen/string_abbreviation_test.dart
@@ -0,0 +1,111 @@
+// 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.
+
+// @dart = 2.7
+
+import 'package:expect/expect.dart';
+import 'package:compiler/src/js_backend/string_abbreviation.dart';
+
+void test(List<String> inputs, List<String> expected, {int minLength = 6}) {
+ Expect.listEquals(
+ expected, abbreviateToIdentifiers(inputs, minLength: minLength));
+}
+
+void main() {
+ // No strings yields an empty pool.
+ test([], []);
+
+ // Results identical stretches compressed-out.
+ test(
+ [
+ 'Greetings Bob Smith',
+ 'Great work!',
+ 'Greetings Alice',
+ 'Greetings Bob Henry',
+ ],
+ [
+ 'GreetiBS',
+ 'Great_',
+ 'GreetiA',
+ 'GreetiBH',
+ ],
+ );
+
+ test(
+ [
+ 'Greetings Bob Smith',
+ 'Great work!',
+ 'Greetings Alice',
+ 'Greetings Bob Henry',
+ ],
+ [
+ 'GeBS',
+ 'Ga',
+ 'GeA',
+ 'GeBH',
+ ],
+ minLength: 1,
+ );
+
+ // Non-identifiers are replaced with '_' if that is unambiguous.
+ test(
+ ['!pingpong', 'xylograph'],
+ ['_pingp', 'xylogr'],
+ );
+
+ test(['\u1234\xff'], ['__']);
+ test(['\u1234\xff', 'smile'], ['__', 'smile']);
+
+ test(
+ ['a*b+c', '(x,y)', 'a*c-e', '(x,z)'],
+ ['a_b_c', '_x_y_', 'a_c_e', '_x_z_'],
+ );
+
+ // Multiple discriminating non-identifier characters are replaced with an
+ // escape, which causes a potentially ambiguous non-escape to be escaped.
+ test(
+ ['a xylograph', 'a !pingpong', 'a %percent'],
+ ['a_x78ylo', 'a_x21pin', 'a_x25per'],
+ );
+
+ test(
+ ['a\u1234z', 'auz'],
+ ['a_z', 'auz'],
+ );
+ test(
+ ['a\u1234z', 'auz'],
+ ['a_', 'au'],
+ minLength: 1,
+ );
+
+ test(
+ ['a\u1234z', 'auz', 'a&z'],
+ ['au1234z', 'ax75z', 'ax26z'],
+ );
+ test(
+ ['a\u1234z', 'auz', 'a&z'],
+ ['au1234', 'ax75', 'ax26'],
+ minLength: 1,
+ );
+
+ test(
+ ['a\u1234z', 'auz', 'a&z', 'a\u2345z'],
+ ['au1234z', 'ax75z', 'ax26z', 'au2345z'],
+ );
+ test(
+ ['a\u1234z', 'auz', 'a&z', 'a\u2345z'],
+ ['au1234', 'ax75', 'ax26', 'au2345'],
+ minLength: 1,
+ );
+
+ test(
+ ['a\u1234z', 'auz', 'a&z', 'a\u2345z', 'axe'],
+ ['au1234z', 'ax75z', 'ax26z', 'au2345z', 'ax78e'],
+ );
+ test(
+ ['a\u1234z', 'auz', 'a&z', 'a\u2345z', 'axe'],
+ ['au1234', 'ax75', 'ax26', 'au2345', 'ax78'],
+ minLength: 1,
+ );
+}
diff --git a/pkg/compiler/test/codegen/string_reference_test.dart b/pkg/compiler/test/codegen/string_reference_test.dart
new file mode 100644
index 0000000..f7e20d2
--- /dev/null
+++ b/pkg/compiler/test/codegen/string_reference_test.dart
@@ -0,0 +1,119 @@
+// 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.
+
+// @dart = 2.7
+
+import "package:expect/expect.dart";
+
+import 'package:compiler/src/constants/values.dart' show StringConstantValue;
+import 'package:compiler/src/js_backend/string_reference.dart'
+ show StringReference, StringReferenceResource, StringReferenceFinalizerImpl;
+
+import 'package:compiler/src/js/js.dart' show prettyPrint;
+
+void test(List<String> strings, String expected, {bool minified: false}) {
+ var finalizer =
+ StringReferenceFinalizerImpl(minified, shortestSharedLength: 5);
+
+ for (var string in strings) {
+ finalizer.addCode(StringReference(StringConstantValue(string)));
+ }
+
+ StringReferenceResource resource = StringReferenceResource();
+ finalizer.registerStringReferenceResource(resource);
+ finalizer.finalize();
+
+ Expect.equals(expected.trim(), prettyPrint(resource).trim());
+}
+
+extension on List<String> {
+ // TODO(42122): Remove when analyzer doesn't think `*` is unused.
+ // ignore: unused_element
+ List<String> operator *(int count) {
+ return List.filled(count, this).expand((list) => list).toList();
+ }
+}
+
+void main() {
+ // No strings yields an empty pool.
+ test([], '0');
+
+ // Single occurrence strings are not pooled.
+ test(
+ ['Yellow', 'Blue', 'Crimson'],
+ '0',
+ );
+
+ // Repeated strings that are long enough are pooled.
+ test(
+ ['Yellow', 'Blue', 'Blue', 'Crimson', 'Crimson'],
+ r'''
+var string$ = {
+ Crimso: "Crimson"
+}''',
+ );
+
+ // Readable property names have identical stretches compressed-out.
+ var greets = [
+ 'Greetings Bob Smith',
+ 'Great work!',
+ 'Greetings Alice',
+ 'Greetings Bob Henry'
+ ];
+
+ test(
+ greets * 2,
+ r'''
+var string$ = {
+ Great_: "Great work!",
+ GreetiA: "Greetings Alice",
+ GreetiBH: "Greetings Bob Henry",
+ GreetiBS: "Greetings Bob Smith"
+}''',
+ );
+
+ // Non-identifiers are replaced with '_' if that is unambiguous.
+ test(
+ ['xylograph', '!pingpong'] * 2,
+ r'''
+var string$ = {
+ _pingp: "!pingpong",
+ xylogr: "xylograph"
+}''',
+ );
+
+ final strings1 = [
+ ...['a xylograph'] * 2,
+ ...['a !pingpong'] * 4,
+ ...['a %percent'] * 6,
+ ];
+
+ // Multiple discriminating non-identifier characters are replaced with an
+ // escape, which causes a potentially ambiguous non-escape to be escaped.
+ test(
+ strings1,
+ r'''
+var string$ = {
+ a_x21pin: "a !pingpong",
+ a_x25per: "a %percent",
+ a_x78ylo: "a xylograph"
+}''',
+ );
+
+ // Minified version keeps the strings in the same order as unminified, and
+ // tries to allocate the same minified name.
+ const minified1 = r'''
+var string$ = {
+ l: "a !pingpong",
+ o: "a %percent",
+ n: "a xylograph"
+}''';
+
+ final strings2 = [
+ ...['a xylograph'] * 20, // now most frequent.
+ ...strings1
+ ];
+ test(strings1, minified1, minified: true);
+ test(strings2, minified1, minified: true);
+}
diff --git a/pkg/compiler/test/impact/data/expressions.dart b/pkg/compiler/test/impact/data/expressions.dart
index 6b3d103..1f63b48 100644
--- a/pkg/compiler/test/impact/data/expressions.dart
+++ b/pkg/compiler/test/impact/data/expressions.dart
@@ -1329,8 +1329,7 @@
_isString(1),
_isTop(1),
findType(1),
- instanceType(1),
- throwRuntimeError(1)],
+ instanceType(1)],
type=[
as:Class,
inst:Closure,
@@ -1372,8 +1371,7 @@
_isString(1),
_isTop(1),
findType(1),
- instanceType(1),
- throwRuntimeError(1)],
+ instanceType(1)],
type=[
as:Class*,
inst:Closure,
@@ -1416,8 +1414,7 @@
_isTop(1),
findType(1),
instanceType(1),
- setRuntimeTypeInfo(2),
- throwRuntimeError(1)],
+ setRuntimeTypeInfo(2)],
type=[
as:GenericClass<int,String>,
inst:Closure,
@@ -1465,8 +1462,7 @@
_isTop(1),
findType(1),
instanceType(1),
- setRuntimeTypeInfo(2),
- throwRuntimeError(1)],
+ setRuntimeTypeInfo(2)],
type=[
as:GenericClass<int*,String*>*,
inst:Closure,
@@ -1513,8 +1509,7 @@
_isString(1),
_isTop(1),
findType(1),
- instanceType(1),
- throwRuntimeError(1)],
+ instanceType(1)],
type=[
as:GenericClass<dynamic,dynamic>,
inst:Closure,
@@ -1556,8 +1551,7 @@
_isString(1),
_isTop(1),
findType(1),
- instanceType(1),
- throwRuntimeError(1)],
+ instanceType(1)],
type=[
as:GenericClass<dynamic,dynamic>*,
inst:Closure,
@@ -1599,8 +1593,7 @@
_isString(1),
_isTop(1),
findType(1),
- instanceType(1),
- throwRuntimeError(1)],
+ instanceType(1)],
type=[
as:GenericClass<dynamic,dynamic>,
inst:Closure,
@@ -1642,8 +1635,7 @@
_isString(1),
_isTop(1),
findType(1),
- instanceType(1),
- throwRuntimeError(1)],
+ instanceType(1)],
type=[
as:GenericClass<dynamic,dynamic>*,
inst:Closure,
diff --git a/pkg/compiler/test/inference/data/foreign.dart b/pkg/compiler/test/inference/data/foreign.dart
index 2f304f7..e0b7078 100644
--- a/pkg/compiler/test/inference/data/foreign.dart
+++ b/pkg/compiler/test/inference/data/foreign.dart
@@ -20,10 +20,7 @@
jsCallVoid();
jsCallUnion();
- jsBuiltin_rawRtiToJsConstructorName();
-
jsEmbeddedGlobal_getTypeFromName();
- jsEmbeddedGlobal_libraries();
jsStringConcat();
@@ -42,21 +39,11 @@
/*member: jsCallUnion:Union([exact=JSString], [subclass=JSInt])*/
jsCallUnion() => JS('int|String', '#', 0);
-/*member: jsBuiltin_rawRtiToJsConstructorName:[exact=JSString]*/
-jsBuiltin_rawRtiToJsConstructorName() {
- return JS_BUILTIN('String', JsBuiltin.rawRtiToJsConstructorName, null);
-}
-
/*member: jsEmbeddedGlobal_getTypeFromName:[null|subclass=Object]*/
jsEmbeddedGlobal_getTypeFromName() {
return JS_EMBEDDED_GLOBAL('', GET_TYPE_FROM_NAME);
}
-/*member: jsEmbeddedGlobal_libraries:[null|exact=JSExtendableArray]*/
-jsEmbeddedGlobal_libraries() {
- return JS_EMBEDDED_GLOBAL('JSExtendableArray|Null', LIBRARIES);
-}
-
/*member: jsStringConcat:[exact=JSString]*/
jsStringConcat() => JS_STRING_CONCAT('a', 'b');
diff --git a/pkg/compiler/test/inference/side_effects/foreign.dart b/pkg/compiler/test/inference/side_effects/foreign.dart
index d73e3a4..9884d38 100644
--- a/pkg/compiler/test/inference/side_effects/foreign.dart
+++ b/pkg/compiler/test/inference/side_effects/foreign.dart
@@ -26,21 +26,11 @@
jsCallEffectsNoInstanceDependsNoStatic() =>
JS('effects:no-instance;depends:no-static', '#', 0);
-/*member: jsBuiltin_rawRtiToJsConstructorName:SideEffects(reads anything; writes anything)*/
-jsBuiltin_rawRtiToJsConstructorName() {
- return JS_BUILTIN('String', JsBuiltin.rawRtiToJsConstructorName, null);
-}
-
/*member: jsEmbeddedGlobal_getTypeFromName:SideEffects(reads nothing; writes nothing)*/
jsEmbeddedGlobal_getTypeFromName() {
return JS_EMBEDDED_GLOBAL('', GET_TYPE_FROM_NAME);
}
-/*member: jsEmbeddedGlobal_libraries:SideEffects(reads nothing; writes nothing)*/
-jsEmbeddedGlobal_libraries() {
- return JS_EMBEDDED_GLOBAL('JSExtendableArray|Null', LIBRARIES);
-}
-
/*member: jsStringConcat:SideEffects(reads nothing; writes nothing)*/
jsStringConcat() => JS_STRING_CONCAT('a', 'b');
@@ -54,10 +44,7 @@
jsCallEffectsAllDependsNoIndex();
jsCallEffectsNoInstanceDependsNoStatic();
- jsBuiltin_rawRtiToJsConstructorName();
-
jsEmbeddedGlobal_getTypeFromName();
- jsEmbeddedGlobal_libraries();
jsStringConcat();
diff --git a/pkg/dart2js_tools/pubspec.yaml b/pkg/dart2js_tools/pubspec.yaml
index 294d05f..d007dbe 100644
--- a/pkg/dart2js_tools/pubspec.yaml
+++ b/pkg/dart2js_tools/pubspec.yaml
@@ -1,4 +1,5 @@
name: dart2js_tools
+# This package is not intended for consumption on pub.dev. DO NOT publish.
publish_to: none
description: >
Collection of tools used with dart2js including analyzing compilation
diff --git a/pkg/dart2native/analysis_options.yaml b/pkg/dart2native/analysis_options.yaml
new file mode 100644
index 0000000..c84f27c
--- /dev/null
+++ b/pkg/dart2native/analysis_options.yaml
@@ -0,0 +1,13 @@
+include: package:pedantic/analysis_options.1.8.0.yaml
+
+analyzer:
+ errors:
+ # Increase the severity of several hints.
+ prefer_single_quotes: warning
+ unused_import: warning
+
+linter:
+ rules:
+ - directives_ordering
+ - prefer_relative_imports
+ - prefer_single_quotes
diff --git a/pkg/dart2native/bin/dart2native.dart b/pkg/dart2native/bin/dart2native.dart
index 19015f6..57c6f21 100644
--- a/pkg/dart2native/bin/dart2native.dart
+++ b/pkg/dart2native/bin/dart2native.dart
@@ -6,83 +6,7 @@
import 'dart:io';
import 'package:args/args.dart';
-import 'package:dart2native/dart2native.dart';
-import 'package:path/path.dart' as path;
-
-final String executableSuffix = Platform.isWindows ? '.exe' : '';
-final String snapshotDir = path.dirname(Platform.script.toFilePath());
-final String binDir = path.canonicalize(path.join(snapshotDir, '..'));
-final String sdkDir = path.canonicalize(path.join(binDir, '..'));
-final String dart = path.join(binDir, 'dart${executableSuffix}');
-final String genKernel = path.join(snapshotDir, 'gen_kernel.dart.snapshot');
-final String dartaotruntime =
- path.join(binDir, 'dartaotruntime${executableSuffix}');
-final String genSnapshot =
- path.join(binDir, 'utils', 'gen_snapshot${executableSuffix}');
-final String productPlatformDill =
- path.join(sdkDir, 'lib', '_internal', 'vm_platform_strong_product.dill');
-
-Future<void> generateNative(
- Kind kind,
- String sourceFile,
- String outputFile,
- String debugFile,
- String packages,
- List<String> defines,
- bool enableAsserts,
- bool verbose,
- List<String> extraGenSnapshotOptions) async {
- final Directory tempDir = Directory.systemTemp.createTempSync();
- try {
- final String kernelFile = path.join(tempDir.path, 'kernel.dill');
- final String snapshotFile = (kind == Kind.aot
- ? outputFile
- : path.join(tempDir.path, 'snapshot.aot'));
-
- if (verbose) {
- print('Generating AOT kernel dill.');
- }
-
- final kernelResult = await generateAotKernel(dart, genKernel,
- productPlatformDill, sourceFile, kernelFile, packages, defines);
- if (kernelResult.exitCode != 0) {
- stderr.writeln(kernelResult.stdout);
- stderr.writeln(kernelResult.stderr);
- await stderr.flush();
- throw 'Generating AOT kernel dill failed!';
- }
-
- if (verbose) {
- print('Generating AOT snapshot.');
- }
- final snapshotResult = await generateAotSnapshot(genSnapshot, kernelFile,
- snapshotFile, debugFile, enableAsserts, extraGenSnapshotOptions);
- if (snapshotResult.exitCode != 0) {
- stderr.writeln(snapshotResult.stdout);
- stderr.writeln(snapshotResult.stderr);
- await stderr.flush();
- throw 'Generating AOT snapshot failed!';
- }
-
- if (kind == Kind.exe) {
- if (verbose) {
- print('Generating executable.');
- }
- await writeAppendedExecutable(dartaotruntime, snapshotFile, outputFile);
-
- if (Platform.isLinux || Platform.isMacOS) {
- if (verbose) {
- print('Marking binary executable.');
- }
- await markExecutable(outputFile);
- }
- }
-
- print('Generated: ${outputFile}');
- } finally {
- tempDir.deleteSync(recursive: true);
- }
-}
+import 'package:dart2native/generate.dart';
void printUsage(final ArgParser parser) {
print('''
@@ -95,7 +19,7 @@
Future<void> main(List<String> args) async {
// If we're outputting to a terminal, wrap usage text to that width.
- int outputLineWidth = null;
+ int outputLineWidth;
try {
outputLineWidth = stdout.terminalColumns;
} catch (_) {/* Ignore. */}
@@ -159,42 +83,25 @@
exit(1);
}
- final Kind kind = {
- 'aot': Kind.aot,
- 'exe': Kind.exe,
- }[parsedArgs['output-kind']];
-
- final sourcePath = path.canonicalize(path.normalize(parsedArgs.rest[0]));
- final sourceWithoutDart = sourcePath.replaceFirst(new RegExp(r'\.dart$'), '');
- final outputPath =
- path.canonicalize(path.normalize(parsedArgs['output'] != null
- ? parsedArgs['output']
- : {
- Kind.aot: '${sourceWithoutDart}.aot',
- Kind.exe: '${sourceWithoutDart}.exe',
- }[kind]));
- final debugPath = parsedArgs['save-debugging-info'] != null
- ? path.canonicalize(path.normalize(parsedArgs['save-debugging-info']))
- : null;
-
- if (!FileSystemEntity.isFileSync(sourcePath)) {
+ final String sourceFile = parsedArgs.rest[0];
+ if (!FileSystemEntity.isFileSync(sourceFile)) {
stderr.writeln(
- '"${sourcePath}" is not a file. See \'--help\' for more information.');
+ '"${sourceFile}" is not a file. See \'--help\' for more information.');
await stderr.flush();
exit(1);
}
try {
await generateNative(
- kind,
- sourcePath,
- outputPath,
- debugPath,
- parsedArgs['packages'],
- parsedArgs['define'],
- parsedArgs['enable-asserts'],
- parsedArgs['verbose'],
- parsedArgs['extra-gen-snapshot-options']);
+ kind: parsedArgs['output-kind'],
+ sourceFile: sourceFile,
+ outputFile: parsedArgs['output'],
+ debugFile: parsedArgs['save-debugging-info'],
+ packages: parsedArgs['packages'],
+ defines: parsedArgs['define'],
+ enableAsserts: parsedArgs['enable-asserts'],
+ verbose: parsedArgs['verbose'],
+ extraOptions: parsedArgs['extra-gen-snapshot-options']);
} catch (e) {
stderr.writeln('Failed to generate native files:');
stderr.writeln(e);
diff --git a/pkg/dart2native/lib/dart2native.dart b/pkg/dart2native/lib/dart2native.dart
index 5bcceb1..06aaeec 100644
--- a/pkg/dart2native/lib/dart2native.dart
+++ b/pkg/dart2native/lib/dart2native.dart
@@ -21,7 +21,7 @@
final offset = dartaotruntimeLength + padding;
// Note: The offset is always Little Endian regardless of host.
- final offsetBytes = new ByteData(8) // 64 bit in bytes.
+ final offsetBytes = ByteData(8) // 64 bit in bytes.
..setUint64(0, offset, Endian.little);
final outputFile = File(outputPath).openWrite();
diff --git a/pkg/dart2native/lib/generate.dart b/pkg/dart2native/lib/generate.dart
new file mode 100644
index 0000000..c4cbbcb
--- /dev/null
+++ b/pkg/dart2native/lib/generate.dart
@@ -0,0 +1,97 @@
+// 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:path/path.dart' as path;
+import 'dart2native.dart';
+
+final Directory binDir = File(Platform.resolvedExecutable).parent;
+final String executableSuffix = Platform.isWindows ? '.exe' : '';
+final String dartaotruntime =
+ path.join(binDir.path, 'dartaotruntime${executableSuffix}');
+final String genKernel =
+ path.join(binDir.path, 'snapshots', 'gen_kernel.dart.snapshot');
+final String genSnapshot =
+ path.join(binDir.path, 'utils', 'gen_snapshot${executableSuffix}');
+final String productPlatformDill = path.join(
+ binDir.parent.path, 'lib', '_internal', 'vm_platform_strong_product.dill');
+
+Future<void> generateNative({
+ String kind = 'exe',
+ String sourceFile,
+ String outputFile,
+ String debugFile,
+ String packages,
+ List<String> defines,
+ bool enableAsserts = false,
+ bool verbose = false,
+ List<String> extraOptions = const [],
+}) async {
+ final Directory tempDir = Directory.systemTemp.createTempSync();
+ try {
+ final sourcePath = path.canonicalize(path.normalize(sourceFile));
+ final Kind outputKind = {
+ 'aot': Kind.aot,
+ 'exe': Kind.exe,
+ }[kind];
+ final sourceWithoutDart = sourcePath.replaceFirst(RegExp(r'\.dart$'), '');
+ final outputPath = path.canonicalize(path.normalize(outputFile != null
+ ? outputFile
+ : {
+ Kind.aot: '${sourceWithoutDart}.aot',
+ Kind.exe: '${sourceWithoutDart}.exe',
+ }[outputKind]));
+ final debugPath =
+ debugFile != null ? path.canonicalize(path.normalize(debugFile)) : null;
+
+ if (verbose) {
+ print('Compiling $sourcePath to $outputPath using format $kind:');
+ print('Generating AOT kernel dill.');
+ }
+
+ final String kernelFile = path.join(tempDir.path, 'kernel.dill');
+ final kernelResult = await generateAotKernel(Platform.executable, genKernel,
+ productPlatformDill, sourcePath, kernelFile, packages, defines);
+ if (kernelResult.exitCode != 0) {
+ stderr.writeln(kernelResult.stdout);
+ stderr.writeln(kernelResult.stderr);
+ await stderr.flush();
+ throw 'Generating AOT kernel dill failed!';
+ }
+
+ if (verbose) {
+ print('Generating AOT snapshot.');
+ }
+
+ final String snapshotFile = (outputKind == Kind.aot
+ ? outputPath
+ : path.join(tempDir.path, 'snapshot.aot'));
+ final snapshotResult = await generateAotSnapshot(genSnapshot, kernelFile,
+ snapshotFile, debugPath, enableAsserts, extraOptions);
+ if (snapshotResult.exitCode != 0) {
+ stderr.writeln(snapshotResult.stdout);
+ stderr.writeln(snapshotResult.stderr);
+ await stderr.flush();
+ throw 'Generating AOT snapshot failed!';
+ }
+
+ if (outputKind == Kind.exe) {
+ if (verbose) {
+ print('Generating executable.');
+ }
+ await writeAppendedExecutable(dartaotruntime, snapshotFile, outputPath);
+
+ if (Platform.isLinux || Platform.isMacOS) {
+ if (verbose) {
+ print('Marking binary executable.');
+ }
+ await markExecutable(outputPath);
+ }
+ }
+
+ print('Generated: ${outputPath}');
+ } finally {
+ tempDir.deleteSync(recursive: true);
+ }
+}
diff --git a/pkg/dart2native/pubspec.yaml b/pkg/dart2native/pubspec.yaml
index c89834f..3bdd2c5 100644
--- a/pkg/dart2native/pubspec.yaml
+++ b/pkg/dart2native/pubspec.yaml
@@ -1,4 +1,5 @@
name: dart2native
+# This package is not intended for consumption on pub.dev. DO NOT publish.
publish_to: none
environment:
diff --git a/pkg/dartdev/analysis_options.yaml b/pkg/dartdev/analysis_options.yaml
index 0f9f6b8..b1e4858 100644
--- a/pkg/dartdev/analysis_options.yaml
+++ b/pkg/dartdev/analysis_options.yaml
@@ -1,4 +1,4 @@
-include: package:pedantic/analysis_options.1.8.0.yaml
+include: package:pedantic/analysis_options.1.9.0.yaml
analyzer:
errors:
@@ -7,9 +7,59 @@
unused_import: warning
unnecessary_brace_in_string_interps: warning
+ # Ignore one pedantic lint
+ omit_local_variable_types: ignore
+
linter:
rules:
+ - avoid_function_literals_in_foreach_calls
+ - avoid_private_typedef_functions
+ - avoid_redundant_argument_values
+ - avoid_renaming_method_parameters
+ - avoid_returning_null_for_void
+ - avoid_void_async
+ - await_only_futures
+ - camel_case_types
+ - constant_identifier_names
- directives_ordering
+ - empty_statements
+ - file_names
+ - iterable_contains_unrelated_type
+ - lines_longer_than_80_chars
+ - list_remove_unrelated_type
+ - missing_whitespace_between_adjacent_strings
+ - no_runtimeType_toString
+ - non_constant_identifier_names
+ - only_throw_errors
+ - overridden_fields
+ - package_names
+ - package_prefixed_library_names
+ - prefer_asserts_in_initializer_lists
+ - prefer_const_constructors
+ - prefer_const_declarations
+ - prefer_expression_function_bodies
+ - prefer_function_declarations_over_variables
+ - prefer_initializing_formals
+ - prefer_inlined_adds
+ - prefer_interpolation_to_compose_strings
+ - prefer_is_not_operator
+ - prefer_final_fields
+ - prefer_null_aware_operators
- prefer_relative_imports
- - prefer_single_quotes
+ - prefer_typing_uninitialized_variables
+ - prefer_void_to_null
+ - provide_deprecation_message
+ - sort_pub_dependencies
+ - test_types_in_equals
+ - throw_in_finally
+ - type_annotate_public_apis
- unnecessary_brace_in_string_interps
+ - unnecessary_lambdas
+ - unnecessary_null_aware_assignments
+ - unnecessary_overrides
+ - unnecessary_parenthesis
+ - unnecessary_statements
+ - unnecessary_string_interpolations
+ - use_is_even_rather_than_modulo
+ - use_string_buffers
+ - void_checks
diff --git a/pkg/dartdev/bin/dartdev.dart b/pkg/dartdev/bin/dartdev.dart
index c0a850e9..1035f4e 100644
--- a/pkg/dartdev/bin/dartdev.dart
+++ b/pkg/dartdev/bin/dartdev.dart
@@ -5,6 +5,6 @@
import 'package:dartdev/dartdev.dart';
/// The entry point for dartdev.
-void main(List<String> args) async {
+Future<void> main(List<String> args) async {
await runDartdev(args);
}
diff --git a/pkg/dartdev/lib/dartdev.dart b/pkg/dartdev/lib/dartdev.dart
index c0afd77..c1cc224 100644
--- a/pkg/dartdev/lib/dartdev.dart
+++ b/pkg/dartdev/lib/dartdev.dart
@@ -12,6 +12,7 @@
import 'src/analytics.dart';
import 'src/commands/analyze.dart';
+import 'src/commands/compile.dart';
import 'src/commands/create.dart';
import 'src/commands/format.dart';
import 'src/commands/pub.dart';
@@ -22,7 +23,7 @@
/// This is typically called from bin/, but given the length of the method and
/// analytics logic, it has been moved here. Also note that this method calls
/// [io.exit(code)] directly.
-void runDartdev(List<String> args) async {
+Future<void> runDartdev(List<String> args) async {
final stopwatch = Stopwatch();
dynamic result;
@@ -35,11 +36,9 @@
int exitCode;
// Any caught non-UsageExceptions when running the sub command
- Exception exception;
+ Object exception;
StackTrace stackTrace;
- var runner;
-
analytics =
createAnalyticsInstance(args.contains('--disable-dartdev-analytics'));
@@ -67,11 +66,11 @@
io.exit(0);
}
- var commandName;
+ String commandName;
try {
stopwatch.start();
- runner = DartdevRunner(args);
+ final runner = DartdevRunner(args);
// Run can't be called with the '--disable-dartdev-analytics' flag, remove
// it if it is contained in args.
if (args.contains('--disable-dartdev-analytics')) {
@@ -105,9 +104,7 @@
stopwatch.stop();
// Set the exitCode, if it wasn't set in the catch block above.
- if (exitCode == null) {
- exitCode = result is int ? result : 0;
- }
+ exitCode ??= result is int ? result : 0;
// Send analytics before exiting
if (analytics.enabled) {
@@ -124,7 +121,8 @@
fatal: true);
}
- await analytics.waitForLastPing(timeout: Duration(milliseconds: 200));
+ await analytics.waitForLastPing(
+ timeout: const Duration(milliseconds: 200));
}
// As the notification to the user read on the first run, analytics are
@@ -153,6 +151,9 @@
argParser.addFlag('disable-analytics',
negatable: false, help: 'Disable anonymous analytics.');
+ // TODO(jwren): hook up.
+ argParser.addMultiOption('enable-experiment', hide: true);
+
// A hidden flag to disable analytics on this run, this constructor can be
// called with this flag, but should be removed before run() is called as
// the flag has not been added to all sub-commands.
@@ -163,6 +164,7 @@
addCommand(AnalyzeCommand(verbose: verbose));
addCommand(CreateCommand(verbose: verbose));
+ addCommand(CompileCommand(verbose: verbose));
addCommand(FormatCommand(verbose: verbose));
addCommand(MigrateCommand(verbose: verbose));
addCommand(PubCommand(verbose: verbose));
@@ -175,10 +177,11 @@
'dart [<vm-flags>] <command|dart-file> [<arguments>]';
@override
- Future<int> runCommand(ArgResults results) async {
- assert(!results.arguments.contains('--disable-dartdev-analytics'));
- if (results.command == null && results.arguments.isNotEmpty) {
- final firstArg = results.arguments.first;
+ Future<int> runCommand(ArgResults topLevelResults) async {
+ assert(!topLevelResults.arguments.contains('--disable-dartdev-analytics'));
+ if (topLevelResults.command == null &&
+ topLevelResults.arguments.isNotEmpty) {
+ final firstArg = topLevelResults.arguments.first;
// If we make it this far, it means the VM couldn't find the file on disk.
if (firstArg.endsWith('.dart')) {
io.stderr.writeln(
@@ -187,11 +190,11 @@
io.exit(254);
}
}
- isVerbose = results['verbose'];
+ isVerbose = topLevelResults['verbose'];
final Ansi ansi = Ansi(Ansi.terminalSupportsAnsi);
log = isVerbose ? Logger.verbose(ansi: ansi) : Logger.standard(ansi: ansi);
- return await super.runCommand(results);
+ return await super.runCommand(topLevelResults);
}
}
diff --git a/pkg/dartdev/lib/src/analytics.dart b/pkg/dartdev/lib/src/analytics.dart
index 112eb70..b00dca4 100644
--- a/pkg/dartdev/lib/src/analytics.dart
+++ b/pkg/dartdev/lib/src/analytics.dart
@@ -26,7 +26,7 @@
║ `dart --enable-analytics` ║
╚════════════════════════════════════════════════════════════════════════════╝
''';
-const String _unknown_command = '<unknown>';
+const String _unknownCommand = '<unknown>';
const String _appName = 'dartdev';
const String _dartDirectoryName = '.dart';
const String _settingsFileName = 'dartdev.json';
@@ -87,7 +87,7 @@
return 'help';
}
return args.firstWhere((arg) => allCommands.contains(arg),
- orElse: () => _unknown_command);
+ orElse: () => _unknownCommand);
}
/// Given some set of arguments and parameters, this returns a proper subset
diff --git a/pkg/dartdev/lib/src/commands/analyze.dart b/pkg/dartdev/lib/src/commands/analyze.dart
index 30b096f..ccf9a57 100644
--- a/pkg/dartdev/lib/src/commands/analyze.dart
+++ b/pkg/dartdev/lib/src/commands/analyze.dart
@@ -17,13 +17,9 @@
: super('analyze', "Analyze the project's Dart code.") {
argParser
..addFlag('fatal-infos',
- help: 'Treat info level issues as fatal.',
- defaultsTo: false,
- negatable: false)
+ help: 'Treat info level issues as fatal.', negatable: false)
..addFlag('fatal-warnings',
- help: 'Treat warning level issues as fatal.',
- defaultsTo: true,
- negatable: true);
+ help: 'Treat warning level issues as fatal.', defaultsTo: true);
}
@override
@@ -69,7 +65,7 @@
await server.start();
// Completing the future in the callback can't fail.
//ignore: unawaited_futures
- server.onExit.then<void>((int exitCode) {
+ server.onExit.then((int exitCode) {
if (!analysisCompleter.isCompleted) {
analysisCompleter.completeError('analysis server exited: $exitCode');
}
diff --git a/pkg/dartdev/lib/src/commands/analyze_impl.dart b/pkg/dartdev/lib/src/commands/analyze_impl.dart
index c32eae9..76e1fcb 100644
--- a/pkg/dartdev/lib/src/commands/analyze_impl.dart
+++ b/pkg/dartdev/lib/src/commands/analyze_impl.dart
@@ -38,7 +38,7 @@
Future<void> start() async {
final List<String> command = <String>[
- sdk.analysis_server_snapshot,
+ sdk.analysisServerSnapshot,
'--disable-server-feature-completion',
'--disable-server-feature-search',
'--sdk',
@@ -116,7 +116,9 @@
final Map<String, dynamic> error =
castStringKeyedMap(response['error']);
log.stderr(
- 'Error response from the server: ${error['code']} ${error['message']}');
+ 'Error response from the server: '
+ '${error['code']} ${error['message']}',
+ );
if (error['stackTrace'] != null) {
log.stderr(error['stackTrace'] as String);
}
@@ -235,11 +237,9 @@
}
@override
- String toString() {
- return '${severity.toLowerCase()} • '
- '$messageSentenceFragment at $file:$startLine:$startColumn • '
- '($code)';
- }
+ String toString() => '${severity.toLowerCase()} • '
+ '$messageSentenceFragment at $file:$startLine:$startColumn • '
+ '($code)';
}
class FileAnalysisErrors {
diff --git a/pkg/dartdev/lib/src/commands/compile.dart b/pkg/dartdev/lib/src/commands/compile.dart
new file mode 100644
index 0000000..025d5c6
--- /dev/null
+++ b/pkg/dartdev/lib/src/commands/compile.dart
@@ -0,0 +1,244 @@
+// 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';
+
+import 'package:args/command_runner.dart';
+import 'package:dart2native/generate.dart';
+import 'package:path/path.dart' as path;
+
+import '../core.dart';
+import '../sdk.dart';
+
+const int compileErrorExitCode = 64;
+
+class Option {
+ final String flag;
+ final String help;
+ final String abbr;
+ Option({this.flag, this.help, this.abbr});
+}
+
+final Map<String, Option> commonOptions = {
+ 'outputFile': Option(
+ flag: 'output',
+ abbr: 'o',
+ help: '''
+Write the output to <file name>.
+This can be an absolute or reletive path.
+''',
+ ),
+};
+
+bool checkFile(String sourcePath) {
+ if (!FileSystemEntity.isFileSync(sourcePath)) {
+ stderr.writeln(
+ '"$sourcePath" is not a file. See \'--help\' for more information.');
+ stderr.flush();
+ return false;
+ }
+
+ return true;
+}
+
+class CompileJSCommand extends DartdevCommand<int> {
+ bool verbose = false;
+ CompileJSCommand({
+ this.verbose,
+ }) : super('js', 'Compile Dart to JavaScript') {
+ argParser
+ ..addOption(
+ commonOptions['outputFile'].flag,
+ help: commonOptions['outputFile'].help,
+ abbr: commonOptions['outputFile'].abbr,
+ )
+ ..addFlag(
+ 'minified',
+ help: 'Generate minified output.',
+ abbr: 'm',
+ negatable: false,
+ );
+ }
+
+ @override
+ String get invocation => '${super.invocation} <dart entry point>';
+
+ @override
+ FutureOr<int> run() async {
+ // We expect a single rest argument; the dart entry point.
+ if (argResults.rest.length != 1) {
+ log.stderr('Missing Dart entry point.');
+ printUsage();
+ return compileErrorExitCode;
+ }
+ final String sourcePath = argResults.rest[0];
+ if (!checkFile(sourcePath)) {
+ return -1;
+ }
+
+ final process = await startProcess(sdk.dart2js, argResults.arguments);
+ routeToStdout(process);
+ return process.exitCode;
+ }
+}
+
+class CompileSnapshotCommand extends DartdevCommand<int> {
+ bool verbose = false;
+ final String commandName;
+ final String help;
+ final String fileExt;
+ final String formatName;
+
+ CompileSnapshotCommand({
+ this.verbose,
+ this.commandName,
+ this.help,
+ this.fileExt,
+ this.formatName,
+ }) : super(commandName, 'Compile Dart $help') {
+ argParser
+ ..addOption(
+ commonOptions['outputFile'].flag,
+ help: commonOptions['outputFile'].help,
+ abbr: commonOptions['outputFile'].abbr,
+ );
+ }
+
+ @override
+ String get invocation => '${super.invocation} <dart entry point>';
+
+ @override
+ FutureOr<int> run() async {
+ // We expect a single rest argument; the dart entry point.
+ if (argResults.rest.length != 1) {
+ log.stderr('Missing Dart entry point.');
+ printUsage();
+ return compileErrorExitCode;
+ }
+ final String sourcePath = argResults.rest[0];
+ if (!checkFile(sourcePath)) {
+ return -1;
+ }
+
+ // Determine output file name.
+ String outputFile = argResults[commonOptions['outputFile'].flag];
+ if (outputFile == null) {
+ final inputWithoutDart = sourcePath.replaceFirst(RegExp(r'\.dart$'), '');
+ outputFile = '$inputWithoutDart.$fileExt';
+ }
+
+ // Build arguments.
+ List<String> args = [];
+ args.add('--snapshot-kind=$formatName');
+ args.add('--snapshot=${path.canonicalize(outputFile)}');
+ if (verbose) {
+ args.add('-v');
+ }
+ args.add(path.canonicalize(sourcePath));
+
+ log.stdout('Compiling $sourcePath to $commandName file $outputFile.');
+ final process = await startProcess(sdk.dart, args);
+ routeToStdout(process);
+ return process.exitCode;
+ }
+}
+
+class CompileNativeCommand extends DartdevCommand<int> {
+ bool verbose = false;
+ final String commandName;
+ final String format;
+ final String help;
+
+ CompileNativeCommand({
+ this.verbose,
+ this.commandName,
+ this.format,
+ this.help,
+ }) : super(commandName, 'Compile Dart $help') {
+ argParser
+ ..addOption(
+ commonOptions['outputFile'].flag,
+ help: commonOptions['outputFile'].help,
+ abbr: commonOptions['outputFile'].abbr,
+ )
+ ..addMultiOption('define', abbr: 'D', valueHelp: 'key=value', help: '''
+Set values of environment variables. To specify multiple variables, use multiple options or use commas to separate key-value pairs.
+E.g.: dart2native -Da=1,b=2 main.dart''')
+ ..addFlag('enable-asserts',
+ negatable: false, help: 'Enable assert statements.')
+ ..addOption('packages', abbr: 'p', valueHelp: 'path', help: '''
+Get package locations from the specified file instead of .packages. <path> can be relative or absolute.
+E.g.: dart2native --packages=/tmp/pkgs main.dart
+''')
+ ..addOption('save-debugging-info', abbr: 'S', valueHelp: 'path', help: '''
+Remove debugging information from the output and save it separately to the specified file. <path> can be relative or absolute.
+''');
+ }
+
+ @override
+ String get invocation => '${super.invocation} <dart entry point>';
+
+ @override
+ FutureOr<int> run() async {
+ // We expect a single rest argument; the dart entry point.
+ if (argResults.rest.length != 1) {
+ log.stderr('Missing Dart entry point.');
+ printUsage();
+ return compileErrorExitCode;
+ }
+ final String sourcePath = argResults.rest[0];
+ if (!checkFile(sourcePath)) {
+ return -1;
+ }
+
+ try {
+ await generateNative(
+ kind: format,
+ sourceFile: sourcePath,
+ outputFile: argResults['output'],
+ defines: argResults['define'],
+ packages: argResults['packages'],
+ enableAsserts: argResults['enable-asserts'],
+ debugFile: argResults['save-debugging-info'],
+ verbose: verbose,
+ );
+ return 0;
+ } catch (e) {
+ log.stderr('Error: AOT compilation failed');
+ log.stderr(e.toString());
+ return compileErrorExitCode;
+ }
+ }
+}
+
+class CompileCommand extends Command {
+ @override
+ String get description => 'Compile Dart to various formats.';
+ @override
+ String get name => 'compile';
+
+ CompileCommand({bool verbose = false}) {
+ addSubcommand(CompileJSCommand());
+ addSubcommand(CompileSnapshotCommand(
+ commandName: 'jit-snapshot',
+ help: 'to a JIT snapshot',
+ fileExt: 'jit',
+ formatName: 'app-jit',
+ verbose: verbose,
+ ));
+ addSubcommand(CompileNativeCommand(
+ commandName: 'exe',
+ help: 'to a self-contained executable',
+ format: 'exe',
+ verbose: verbose,
+ ));
+ addSubcommand(CompileNativeCommand(
+ commandName: 'aot-snapshot',
+ help: 'to an AOT snapshot',
+ format: 'aot',
+ verbose: verbose,
+ ));
+ }
+}
diff --git a/pkg/dartdev/lib/src/commands/create.dart b/pkg/dartdev/lib/src/commands/create.dart
index cb95a1c..55d53d8 100644
--- a/pkg/dartdev/lib/src/commands/create.dart
+++ b/pkg/dartdev/lib/src/commands/create.dart
@@ -7,7 +7,7 @@
import 'dart:io' as io;
import 'dart:math' as math;
-import 'package:path/path.dart' as path;
+import 'package:path/path.dart' as p;
import 'package:stagehand/stagehand.dart' as stagehand;
import '../core.dart';
@@ -24,13 +24,11 @@
'web-simple'
];
- static Iterable<stagehand.Generator> get generators {
- return legalTemplateIds.map(retrieveTemplateGenerator);
- }
+ static Iterable<stagehand.Generator> get generators =>
+ legalTemplateIds.map(retrieveTemplateGenerator);
- static stagehand.Generator retrieveTemplateGenerator(String templateId) {
- return stagehand.getGenerator(templateId);
- }
+ static stagehand.Generator retrieveTemplateGenerator(String templateId) =>
+ stagehand.getGenerator(templateId);
CreateCommand({bool verbose = false})
: super('create', 'Create a new project.') {
@@ -53,8 +51,8 @@
argParser.addFlag(
'force',
negatable: false,
- help:
- 'Force project generation, even if the target directory already exists.',
+ help: 'Force project generation, even if the target directory already '
+ 'exists.',
);
}
@@ -77,19 +75,23 @@
String dir = argResults.rest.first;
var targetDir = io.Directory(dir);
- if (targetDir.existsSync() && !(argResults['force'])) {
+ if (targetDir.existsSync() && !argResults['force']) {
log.stderr(
- "Directory '$dir' already exists (use '--force' to force project generation).");
+ "Directory '$dir' already exists "
+ "(use '--force' to force project generation).",
+ );
return 73;
}
log.stdout(
- 'Creating ${log.ansi.emphasized(path.absolute(dir))} using template $templateId...');
+ 'Creating ${log.ansi.emphasized(p.absolute(dir))} '
+ 'using template $templateId...',
+ );
log.stdout('');
var generator = retrieveTemplateGenerator(templateId);
await generator.generate(
- path.basename(dir),
+ p.basename(dir),
DirectoryGeneratorTarget(generator, io.Directory(dir)),
);
@@ -125,7 +127,7 @@
log.stdout('');
log.stdout('Created project $dir! In order to get started, type:');
log.stdout('');
- log.stdout(log.ansi.emphasized(' cd ${path.relative(dir)}'));
+ log.stdout(log.ansi.emphasized(' cd ${p.relative(dir)}'));
// TODO(devoncarew): Once we have a 'run' command, print out here how to run
// the app.
log.stdout('');
@@ -159,7 +161,7 @@
return m;
});
- JsonEncoder encoder = JsonEncoder.withIndent(' ');
+ JsonEncoder encoder = const JsonEncoder.withIndent(' ');
return encoder.convert(items.toList());
}
}
@@ -173,10 +175,10 @@
}
@override
- Future createFile(String filePath, List<int> contents) async {
- io.File file = io.File(path.join(dir.path, filePath));
+ Future createFile(String path, List<int> contents) async {
+ io.File file = io.File(p.join(dir.path, path));
- String name = path.relative(file.path, from: dir.path);
+ String name = p.relative(file.path, from: dir.path);
log.stdout(' $name');
await file.create(recursive: true);
diff --git a/pkg/dartdev/lib/src/commands/pub.dart b/pkg/dartdev/lib/src/commands/pub.dart
index 2d983df..0019696 100644
--- a/pkg/dartdev/lib/src/commands/pub.dart
+++ b/pkg/dartdev/lib/src/commands/pub.dart
@@ -13,6 +13,7 @@
class PubCommand extends DartdevCommand<int> {
PubCommand({bool verbose = false}) : super('pub', 'Work with packages.');
+ @override
final ArgParser argParser = ArgParser.allowAnything();
@override
diff --git a/pkg/dartdev/lib/src/commands/run.dart b/pkg/dartdev/lib/src/commands/run.dart
index 2f4a942..7496a2a 100644
--- a/pkg/dartdev/lib/src/commands/run.dart
+++ b/pkg/dartdev/lib/src/commands/run.dart
@@ -15,8 +15,10 @@
import '../utils.dart';
class RunCommand extends DartdevCommand<int> {
+ @override
final ArgParser argParser = ArgParser.allowAnything();
final bool verbose;
+
RunCommand({this.verbose = false}) : super('run', '''
Run a Dart file.''');
@@ -90,7 +92,9 @@
}
if (!foundImplicitFileToRun) {
log.stderr(
- 'Could not find the implicit file to run: bin$separator$cwdName.dart.');
+ 'Could not find the implicit file to run: '
+ 'bin$separator$cwdName.dart.',
+ );
}
}
@@ -99,8 +103,9 @@
// service intermediary which implements the VM service protocol and
// provides non-VM specific extensions (e.g., log caching, client
// synchronization).
- if (args.any((element) => (element.startsWith('--observe') ||
- element.startsWith('--enable-vm-service')))) {
+ if (args.any((element) =>
+ element.startsWith('--observe') ||
+ element.startsWith('--enable-vm-service'))) {
return await _DebuggingSession(this, args).start();
}
@@ -137,8 +142,8 @@
try {
_bindAddress = Uri.http(observatoryBindInfo[1], '');
} on FormatException {
- // TODO(bkonyi): log invalid parse? The VM service just ignores bad
- // input flags.
+ // TODO(bkonyi): log invalid parse? The VM service just ignores
+ // bad input flags.
// Ignore.
}
}
@@ -165,9 +170,10 @@
// Strip --observe and --write-service-info from the arguments as we'll be
// providing our own.
_args.removeWhere(
- (arg) => (arg.startsWith('--observe') ||
+ (arg) =>
+ arg.startsWith('--observe') ||
arg.startsWith('--enable-vm-service') ||
- arg.startsWith('--write-service-info')),
+ arg.startsWith('--write-service-info'),
);
}
@@ -195,8 +201,7 @@
// Start DDS once the VM service has finished starting up.
await Future.any([
- _waitForRemoteServiceUri(serviceInfoFile)
- .then((serviceUri) => _startDDS(serviceUri)),
+ _waitForRemoteServiceUri(serviceInfoFile).then(_startDDS),
_process.exitCode,
]);
@@ -214,7 +219,7 @@
Future<Uri> _waitForRemoteServiceUri(File serviceInfoFile) async {
// Wait for VM service to write its connection info to disk.
- while ((await serviceInfoFile.length() <= 5)) {
+ while (await serviceInfoFile.length() <= 5) {
await Future.delayed(const Duration(milliseconds: 50));
}
final serviceInfoStr = await serviceInfoFile.readAsString();
@@ -263,14 +268,14 @@
// to ensure we don't let any unfiltered messages slip through.
// TODO(bkonyi): consider filtering on bytes rather than decoding the UTF8.
_stderrDone = process.stderr
- .transform(Utf8Decoder(allowMalformed: true))
+ .transform(const Utf8Decoder(allowMalformed: true))
.listen((event) async {
await _waitForDDS();
stderr.write(filterObservatoryUri(event));
}).asFuture();
_stdoutDone = process.stdout
- .transform(Utf8Decoder(allowMalformed: true))
+ .transform(const Utf8Decoder(allowMalformed: true))
.listen((event) async {
await _waitForDDS();
stdout.write(filterObservatoryUri(event));
diff --git a/pkg/dartdev/lib/src/commands/test.dart b/pkg/dartdev/lib/src/commands/test.dart
index d56cd11..d52b802 100644
--- a/pkg/dartdev/lib/src/commands/test.dart
+++ b/pkg/dartdev/lib/src/commands/test.dart
@@ -14,6 +14,7 @@
TestCommand({bool verbose = false})
: super('test', 'Runs tests in this project.');
+ @override
final ArgParser argParser = ArgParser.allowAnything();
@override
diff --git a/pkg/dartdev/lib/src/core.dart b/pkg/dartdev/lib/src/core.dart
index ea7cb9b..b0bb75e 100644
--- a/pkg/dartdev/lib/src/core.dart
+++ b/pkg/dartdev/lib/src/core.dart
@@ -31,7 +31,7 @@
@override
String get description => _description;
- Project get project => (_project ??= Project());
+ Project get project => _project ??= Project();
}
/// A utility method to start the given executable as a process, optionally
@@ -48,7 +48,7 @@
void routeToStdout(
Process process, {
bool logToTrace = false,
- void listener(String str),
+ void Function(String str) listener,
}) {
if (isVerbose) {
_streamLineTransform(process.stdout, (String line) {
@@ -72,7 +72,10 @@
}
}
-void _streamLineTransform(Stream<List<int>> stream, handler(String line)) {
+void _streamLineTransform(
+ Stream<List<int>> stream,
+ Function(String line) handler,
+) {
stream
.transform(utf8.decoder)
.transform(const LineSplitter())
@@ -119,7 +122,6 @@
return _packages.map<Map<String, dynamic>>(castStringKeyedMap).toList();
}
- bool hasDependency(String packageName) {
- return packages.any((element) => element['name'] == packageName);
- }
+ bool hasDependency(String packageName) =>
+ packages.any((element) => element['name'] == packageName);
}
diff --git a/pkg/dartdev/lib/src/sdk.dart b/pkg/dartdev/lib/src/sdk.dart
index e7c1c97..8481714 100644
--- a/pkg/dartdev/lib/src/sdk.dart
+++ b/pkg/dartdev/lib/src/sdk.dart
@@ -45,9 +45,11 @@
// if the SDK isn't completely built.
String get dart => Platform.resolvedExecutable;
- String get analysis_server_snapshot => path.absolute(
+ String get analysisServerSnapshot => path.absolute(
sdkPath, 'bin', 'snapshots', 'analysis_server.dart.snapshot');
+ String get dart2js => path.absolute(sdkPath, 'bin', _binName('dart2js'));
+
String get dartfmt => path.absolute(sdkPath, 'bin', _binName('dartfmt'));
String get pub => path.absolute(sdkPath, 'bin', _binName('pub'));
diff --git a/pkg/dartdev/pubspec.yaml b/pkg/dartdev/pubspec.yaml
index 8714110..061f6fa 100644
--- a/pkg/dartdev/pubspec.yaml
+++ b/pkg/dartdev/pubspec.yaml
@@ -1,21 +1,28 @@
name: dartdev
-# This package is not intended to be published
+# This package is not intended for consumption on pub.dev. DO NOT publish.
publish_to: none
environment:
sdk: '>=2.6.0 <3.0.0'
dependencies:
- args: ^1.5.2
- cli_util: ^0.1.0
+ args: ^1.6.0
+ cli_util: ^0.1.4
+ dart2native:
+ path: ../dart2native
dds:
- path: '../dds'
- intl: ^0.16.0
- path: ^1.6.2
+ path: ../dds
+ intl: ^0.16.1
+ nnbd_migration: any # this uses the SDK version
+ path: ^1.0.0
+ stack_trace: ^1.9.3
stagehand: 3.3.7
- watcher: ^0.9.7+13
+ telemetry:
+ path: ../telemetry
+ usage: ^3.4.0
+ watcher: ^0.9.6
yaml: ^2.2.0
dev_dependencies:
test: ^1.0.0
- pedantic: ^1.8.0
+ pedantic: ^1.9.0
diff --git a/pkg/dartdev/test/analytics_test.dart b/pkg/dartdev/test/analytics_test.dart
index 6f7d3d3..8632697 100644
--- a/pkg/dartdev/test/analytics_test.dart
+++ b/pkg/dartdev/test/analytics_test.dart
@@ -6,11 +6,11 @@
import 'package:test/test.dart';
void main() {
- group('DisabledAnalytics', disabledAnalytics_object);
+ group('DisabledAnalytics', disabledAnalyticsObject);
group('utils', utils);
}
-void disabledAnalytics_object() {
+void disabledAnalyticsObject() {
test('object', () {
var diabledAnalytics = DisabledAnalytics('trackingId', 'appName');
expect(diabledAnalytics.trackingId, 'trackingId');
diff --git a/pkg/dartdev/test/commands/compile_test.dart b/pkg/dartdev/test/commands/compile_test.dart
new file mode 100644
index 0000000..489181d
--- /dev/null
+++ b/pkg/dartdev/test/commands/compile_test.dart
@@ -0,0 +1,180 @@
+// 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:path/path.dart' as path;
+import 'package:test/test.dart';
+
+import '../utils.dart';
+
+const int compileErrorExitCode = 64;
+
+void main() {
+ group('compile', defineCompileTests, timeout: longTimeout);
+}
+
+void defineCompileTests() {
+ // *** NOTE ***: These tests *must* be run with the `--use-sdk` option
+ // as they depend on a fully built SDK to resolve various snapshot files
+ // used by compilation.
+ test('Running from built SDK', () {
+ final Directory binDir = File(Platform.resolvedExecutable).parent;
+ expect(binDir.path, contains('bin'));
+ });
+
+ test('Implicit --help', () {
+ final p = project();
+ var result = p.runSync(
+ 'compile',
+ [],
+ );
+ expect(result.stderr, contains('Compile Dart'));
+ expect(result.exitCode, compileErrorExitCode);
+ });
+
+ test('--help', () {
+ final p = project();
+ final result = p.runSync(
+ 'compile',
+ ['--help'],
+ );
+ expect(result.stdout, contains('Compile Dart'));
+ expect(result.exitCode, 0);
+ });
+
+ test('Compile and run jit snapshot', () {
+ final p = project(mainSrc: 'void main() { print("I love jit"); }');
+ final outFile = path.join(p.dirPath, 'main.jit');
+ var result = p.runSync(
+ 'compile',
+ [
+ 'jit-snapshot',
+ '-o',
+ outFile,
+ p.relativeFilePath,
+ ],
+ );
+ expect(File(outFile).existsSync(), true,
+ reason: 'File not found: $outFile');
+ expect(result.stderr, isEmpty);
+ expect(result.exitCode, 0);
+
+ result = p.runSync('run', ['main.jit']);
+ expect(result.stdout, contains('I love jit'));
+ expect(result.stderr, isEmpty);
+ expect(result.exitCode, 0);
+ });
+
+ test('Compile and run executable', () {
+ final p = project(mainSrc: 'void main() { print("I love executables"); }');
+ final inFile = path.canonicalize(path.join(p.dirPath, p.relativeFilePath));
+ final outFile = path.canonicalize(path.join(p.dirPath, 'lib', 'main.exe'));
+
+ var result = p.runSync(
+ 'compile',
+ [
+ 'exe',
+ inFile,
+ ],
+ );
+
+ expect(File(outFile).existsSync(), true,
+ reason: 'File not found: $outFile');
+ expect(result.stderr, isEmpty);
+ expect(result.exitCode, 0);
+
+ result = Process.runSync(
+ outFile,
+ [],
+ );
+
+ expect(result.stdout, contains('I love executables'));
+ expect(result.stderr, isEmpty);
+ expect(result.exitCode, 0);
+ });
+
+ test('Compile and run executable with options', () {
+ final p = project(
+ mainSrc: 'void main() {print(const String.fromEnvironment("life"));}');
+ final inFile = path.canonicalize(path.join(p.dirPath, p.relativeFilePath));
+ final outFile = path.canonicalize(path.join(p.dirPath, 'myexe'));
+
+ var result = p.runSync(
+ 'compile',
+ [
+ 'exe',
+ '--define',
+ 'life=42',
+ '-o',
+ outFile,
+ inFile,
+ ],
+ );
+
+ expect(File(outFile).existsSync(), true,
+ reason: 'File not found: $outFile');
+ expect(result.stderr, isEmpty);
+ expect(result.exitCode, 0);
+
+ result = Process.runSync(
+ outFile,
+ [],
+ );
+
+ expect(result.stdout, contains('42'));
+ expect(result.stderr, isEmpty);
+ expect(result.exitCode, 0);
+ });
+
+ test('Compile and run aot snapshot', () {
+ final p = project(mainSrc: 'void main() { print("I love AOT"); }');
+ final inFile = path.canonicalize(path.join(p.dirPath, p.relativeFilePath));
+ final outFile = path.canonicalize(path.join(p.dirPath, 'main.aot'));
+
+ var result = p.runSync(
+ 'compile',
+ [
+ 'aot-snapshot',
+ '-o',
+ 'main.aot',
+ inFile,
+ ],
+ );
+
+ expect(File(outFile).existsSync(), true,
+ reason: 'File not found: $outFile');
+ expect(result.stderr, isEmpty);
+ expect(result.exitCode, 0);
+
+ final Directory binDir = File(Platform.resolvedExecutable).parent;
+ result = Process.runSync(
+ path.join(binDir.path, 'dartaotruntime'),
+ [outFile],
+ );
+
+ expect(result.stdout, contains('I love AOT'));
+ expect(result.stderr, isEmpty);
+ expect(result.exitCode, 0);
+ });
+
+ test('Compile JS', () {
+ final p = project(mainSrc: "void main() { print('Hello from JS'); }");
+ final inFile = path.canonicalize(path.join(p.dirPath, p.relativeFilePath));
+ final outFile = path.canonicalize(path.join(p.dirPath, 'main.js'));
+
+ final result = p.runSync('compile', [
+ 'js',
+ '-m',
+ '-o',
+ outFile,
+ '-v',
+ inFile,
+ ]);
+ expect(File(outFile).existsSync(), true,
+ reason: 'File not found: $outFile');
+ expect(result.stderr, isEmpty);
+ expect(result.exitCode, 0);
+ });
+}
diff --git a/pkg/dartdev/test/commands/flag_test.dart b/pkg/dartdev/test/commands/flag_test.dart
index 7e35ef8..978c6ae 100644
--- a/pkg/dartdev/test/commands/flag_test.dart
+++ b/pkg/dartdev/test/commands/flag_test.dart
@@ -57,6 +57,7 @@
expect(result.stdout, contains('Available commands:'));
expect(result.stdout, contains('analyze '));
expect(result.stdout, contains('create '));
+ expect(result.stdout, contains('compile '));
expect(result.stdout, contains('format '));
expect(result.stdout, contains('migrate '));
});
diff --git a/pkg/dartdev/test/commands/format_test.dart b/pkg/dartdev/test/commands/format_test.dart
index 15dd356..1b68d98 100644
--- a/pkg/dartdev/test/commands/format_test.dart
+++ b/pkg/dartdev/test/commands/format_test.dart
@@ -71,7 +71,7 @@
test('unknown file', () {
p = project(mainSrc: 'int get foo => 1;\n');
- var unknownFilePath = p.relativeFilePath + '-unknown-file.dart';
+ var unknownFilePath = '${p.relativeFilePath}-unknown-file.dart';
ProcessResult result = p.runSync('format', [unknownFilePath]);
expect(result.exitCode, 0);
expect(result.stderr,
diff --git a/pkg/dartdev/test/commands/run_test.dart b/pkg/dartdev/test/commands/run_test.dart
index fcec9f5..e6b8931 100644
--- a/pkg/dartdev/test/commands/run_test.dart
+++ b/pkg/dartdev/test/commands/run_test.dart
@@ -39,7 +39,7 @@
test('no such file', () {
p = project(mainSrc: "void main() { print('Hello World'); }");
ProcessResult result =
- p.runSync('run', ['no/such/file/' + p.relativeFilePath]);
+ p.runSync('run', ['no/such/file/${p.relativeFilePath}']);
expect(result.stderr, isNotEmpty);
expect(result.exitCode, isNot(0));
diff --git a/pkg/dartdev/test/core_test.dart b/pkg/dartdev/test/core_test.dart
index 23cab3c..6384d9b 100644
--- a/pkg/dartdev/test/core_test.dart
+++ b/pkg/dartdev/test/core_test.dart
@@ -48,7 +48,7 @@
});
}
-final String _packageData = '''{
+const String _packageData = '''{
"configVersion": 2,
"packages": [
{
diff --git a/pkg/dartdev/test/sdk_test.dart b/pkg/dartdev/test/sdk_test.dart
index 8bf7ed9..2fcb0ca 100644
--- a/pkg/dartdev/test/sdk_test.dart
+++ b/pkg/dartdev/test/sdk_test.dart
@@ -21,7 +21,7 @@
});
test('analysis_server_snapshot', () {
- expectFileExists(Sdk().analysis_server_snapshot);
+ expectFileExists(Sdk().analysisServerSnapshot);
});
test('dartfmt', () {
diff --git a/pkg/dartdev/test/test_all.dart b/pkg/dartdev/test/test_all.dart
index 8811904..153ebb9 100644
--- a/pkg/dartdev/test/test_all.dart
+++ b/pkg/dartdev/test/test_all.dart
@@ -6,6 +6,7 @@
import 'analytics_test.dart' as analytics;
import 'commands/analyze_test.dart' as analyze;
+import 'commands/compile_test.dart' as compile;
import 'commands/create_test.dart' as create;
import 'commands/flag_test.dart' as flag;
import 'commands/format_test.dart' as format;
@@ -18,7 +19,7 @@
import 'sdk_test.dart' as sdk;
import 'utils_test.dart' as utils;
-main() {
+void main() {
group('dart', () {
analytics.main();
analyze.main();
@@ -29,6 +30,7 @@
migrate.main();
pub.main();
run.main();
+ compile.main();
test.main();
core.main();
sdk.main();
diff --git a/pkg/dartdev/test/utils.dart b/pkg/dartdev/test/utils.dart
index c1fdab7..7fd5c73 100644
--- a/pkg/dartdev/test/utils.dart
+++ b/pkg/dartdev/test/utils.dart
@@ -12,9 +12,8 @@
/// invocation requires the VM to compile the entire dependency graph.
const Timeout longTimeout = Timeout(Duration(minutes: 5));
-TestProject project({String mainSrc, String analysisOptions}) {
- return TestProject(mainSrc: mainSrc, analysisOptions: analysisOptions);
-}
+TestProject project({String mainSrc, String analysisOptions}) =>
+ TestProject(mainSrc: mainSrc, analysisOptions: analysisOptions);
class TestProject {
static String get defaultProjectName => 'dartdev_temp';
@@ -94,9 +93,8 @@
return _sdkRootPath;
}
- String get absolutePathToDartdevFile {
- return path.join(sdkRootPath, 'pkg', 'dartdev', 'bin', 'dartdev.dart');
- }
+ String get absolutePathToDartdevFile =>
+ path.join(sdkRootPath, 'pkg', 'dartdev', 'bin', 'dartdev.dart');
File findFile(String name) {
var file = File(path.join(dir.path, name));
diff --git a/pkg/dartdev/test/utils_test.dart b/pkg/dartdev/test/utils_test.dart
index 489348c..1a579c8 100644
--- a/pkg/dartdev/test/utils_test.dart
+++ b/pkg/dartdev/test/utils_test.dart
@@ -90,7 +90,7 @@
});
}
-final String _packageData = '''{
+const String _packageData = '''{
"configVersion": 2,
"packages": [
{
diff --git a/pkg/dev_compiler/lib/src/compiler/module_builder.dart b/pkg/dev_compiler/lib/src/compiler/module_builder.dart
index ce17c8c..dd1cc90 100644
--- a/pkg/dev_compiler/lib/src/compiler/module_builder.dart
+++ b/pkg/dev_compiler/lib/src/compiler/module_builder.dart
@@ -191,7 +191,7 @@
}
var resultModule = NamedFunction(
- loadFunctionName(module.name),
+ loadFunctionIdentifier(module.name),
js.fun("function(#) { 'use strict'; #; }", [parameters, statements]),
true);
@@ -299,7 +299,7 @@
statements.add(Return(ObjectInitializer(exportedProps, multiline: true)));
}
var resultModule = NamedFunction(
- loadFunctionName(module.name),
+ loadFunctionIdentifier(module.name),
js.fun("function(#) { 'use strict'; #; }", [fnParams, statements]),
true);
var block = js.statement(
@@ -309,6 +309,17 @@
}
}
+bool isSdkInternalRuntimeUri(Uri importUri) {
+ return importUri.scheme == 'dart' && importUri.path == '_runtime';
+}
+
+String libraryUriToJsIdentifier(Uri importUri) {
+ if (importUri.scheme == 'dart') {
+ return isSdkInternalRuntimeUri(importUri) ? 'dart' : importUri.path;
+ }
+ return pathToJSIdentifier(p.withoutExtension(importUri.pathSegments.last));
+}
+
/// Converts an entire arbitrary path string into a string compatible with
/// JS identifier naming rules while conserving path information.
///
@@ -327,9 +338,13 @@
.replaceAll('-', '_'));
}
+/// Creates function name given [moduleName].
+String loadFunctionName(String moduleName) =>
+ 'load__' + pathToJSIdentifier(moduleName.replaceAll('.', '_'));
+
/// Creates function name identifier given [moduleName].
-Identifier loadFunctionName(String moduleName) =>
- Identifier('load__' + pathToJSIdentifier(moduleName.replaceAll('.', '_')));
+Identifier loadFunctionIdentifier(String moduleName) =>
+ Identifier(loadFunctionName(moduleName));
// Replacement string for path separators (i.e., '/', '\', '..').
final encodedSeparator = '__';
diff --git a/pkg/dev_compiler/lib/src/compiler/shared_command.dart b/pkg/dev_compiler/lib/src/compiler/shared_command.dart
index f1b8d93..1b7b01e 100644
--- a/pkg/dev_compiler/lib/src/compiler/shared_command.dart
+++ b/pkg/dev_compiler/lib/src/compiler/shared_command.dart
@@ -61,6 +61,13 @@
/// runtime can enable synchronous stack trace deobsfuscation.
final bool inlineSourceMap;
+ /// Whether to emit the debug metadata
+ ///
+ /// Debugger uses this information about to construct mapping between
+ /// modules and libraries that otherwise requires expensive communication with
+ /// the browser.
+ final bool emitDebugMetadata;
+
/// Whether to emit a summary file containing API signatures.
///
/// This is required for a modular build process.
@@ -99,6 +106,7 @@
this.summarizeApi = true,
this.enableAsserts = true,
this.replCompile = false,
+ this.emitDebugMetadata = false,
this.summaryModules = const {},
this.moduleFormats = const [],
this.experiments = const {},
@@ -119,32 +127,34 @@
moduleFormats: parseModuleFormatOption(args),
moduleName: _getModuleName(args, moduleRoot),
replCompile: args['repl-compile'] as bool,
- soundNullSafety: args['sound-null-safety'] as bool);
+ soundNullSafety: args['sound-null-safety'] as bool,
+ emitDebugMetadata:
+ args['experimental-emit-debug-metadata'] as bool);
static void addArguments(ArgParser parser, {bool hide = true}) {
addModuleFormatOptions(parser, hide: hide);
parser
+ ..addMultiOption('out', abbr: 'o', help: 'Output file (required).')
..addMultiOption('summary',
abbr: 's',
- help: 'summary file(s) of imported libraries, optionally\n'
- 'with module import path: -s path.sum=js/import/path')
+ help: 'API summary file(s) of imported libraries, optionally\n'
+ 'with module import path: -s path.dill=js/import/path')
..addMultiOption('enable-experiment',
- help: 'used to enable/disable experimental language features',
- hide: hide)
+ help: 'Enable/disable experimental language features.', hide: hide)
..addFlag('summarize',
- help: 'emit an API summary file', defaultsTo: true, hide: hide)
+ help: 'Emit an API summary file.', defaultsTo: true, hide: hide)
..addFlag('source-map',
- help: 'emit source mapping', defaultsTo: true, hide: hide)
+ help: 'Emit source mapping.', defaultsTo: true, hide: hide)
..addFlag('inline-source-map',
- help: 'emit source mapping inline', defaultsTo: false, hide: hide)
+ help: 'Emit source mapping inline.', defaultsTo: false, hide: hide)
..addFlag('enable-asserts',
- help: 'enable assertions', defaultsTo: true, hide: hide)
+ help: 'Enable assertions.', defaultsTo: true, hide: hide)
..addOption('module-name',
help: 'The output module name, used in some JS module formats.\n'
'Defaults to the output file name (without .js).')
..addFlag('repl-compile',
- help: 'compile in a more permissive REPL mode, allowing access'
+ help: 'Compile in a more permissive REPL mode, allowing access'
' to private members across library boundaries. This should'
' only be used by debugging tools.',
defaultsTo: false,
@@ -152,7 +162,21 @@
..addFlag('sound-null-safety',
help: 'Compile for sound null safety at runtime.',
negatable: true,
- defaultsTo: false);
+ defaultsTo: false)
+ ..addOption('multi-root-scheme',
+ help: 'The custom scheme to indicate a multi-root uri.',
+ defaultsTo: 'org-dartlang-app')
+ ..addOption('multi-root-output-path',
+ help: 'Path to set multi-root files relative to when generating'
+ ' source-maps.',
+ hide: true)
+ // TODO(41852) Define a process for breaking changes before graduating from
+ // experimental.
+ ..addFlag('experimental-emit-debug-metadata',
+ help: 'Experimental option for compiler development.\n'
+ 'Output a metadata file for debug tools next to the .js output.',
+ defaultsTo: false,
+ hide: true);
}
static String _getModuleName(ArgResults args, String moduleRoot) {
diff --git a/pkg/dev_compiler/lib/src/kernel/command.dart b/pkg/dev_compiler/lib/src/kernel/command.dart
index c968376..c764b0f 100644
--- a/pkg/dev_compiler/lib/src/kernel/command.dart
+++ b/pkg/dev_compiler/lib/src/kernel/command.dart
@@ -27,6 +27,7 @@
import '../js_ast/js_ast.dart' show js;
import '../js_ast/source_map_printer.dart' show SourceMapPrintingContext;
import 'compiler.dart';
+import 'module_metadata.dart';
import 'target.dart';
const _binaryName = 'dartdevc -k';
@@ -80,27 +81,21 @@
var argParser = ArgParser(allowTrailingOptions: true)
..addFlag('help',
abbr: 'h', help: 'Display this message.', negatable: false)
- ..addMultiOption('out', abbr: 'o', help: 'Output file (required).')
..addOption('packages', help: 'The package spec file to use.')
// TODO(jmesserly): is this still useful for us, or can we remove it now?
..addFlag('summarize-text',
- help: 'emit API summary in a .js.txt file',
+ help: 'Emit API summary in a .js.txt file.',
defaultsTo: false,
hide: true)
..addFlag('track-widget-creation',
- help: 'enable inspecting of Flutter widgets', hide: true)
+ help: 'Enable inspecting of Flutter widgets.', hide: true)
// TODO(jmesserly): add verbose help to show hidden options
..addOption('dart-sdk-summary',
help: 'The path to the Dart SDK summary file.', hide: true)
- ..addOption('multi-root-scheme',
- help: 'The custom scheme to indicate a multi-root uri.',
- defaultsTo: 'org-dartlang-app')
..addMultiOption('multi-root',
help: 'The directories to search when encountering uris with the '
'specified multi-root scheme.',
defaultsTo: [Uri.base.path])
- ..addOption('multi-root-output-path',
- help: 'Path to set multi-root files relative to.', hide: true)
..addOption('dart-sdk',
help: '(unsupported with --kernel) path to the Dart SDK.', hide: true)
..addFlag('compile-sdk',
@@ -109,13 +104,6 @@
help: 'The path to the libraries.json file for the sdk.')
..addOption('used-inputs-file',
help: 'If set, the file to record inputs used.', hide: true)
- // TODO(41852) Define a process for breaking changes before graduating from
- // experimental.
- ..addFlag('experimental-emit-debug-metadata',
- help: 'Experimental option for compiler development.\n'
- 'Output a metadata file for debug tools next to the .js output.',
- defaultsTo: false,
- hide: true)
..addFlag('kernel',
abbr: 'k',
help: 'Deprecated and ignored. To be removed in a future release.',
@@ -214,6 +202,11 @@
throw StateError('Non-dill file detected in input: $summaryPaths');
}
+ var inputs = [for (var arg in argResults.rest) sourcePathToCustomUri(arg)];
+ if (inputs.length == 1 && inputs.single.path.endsWith('.dill')) {
+ return compileSdkFromDill(args);
+ }
+
if (librarySpecPath == null) {
// TODO(jmesserly): the `isSupported` bit should be included in the SDK
// summary, but front_end requires a separate file, so we have to work
@@ -245,8 +238,6 @@
// This needs further investigation.
var packageFile = argResults['packages'] as String ?? _findPackagesFilePath();
- var inputs = argResults.rest.map(sourcePathToCustomUri).toList();
-
var succeeded = true;
void diagnosticMessageHandler(fe.DiagnosticMessage message) {
if (message.severity == fe.Severity.error) {
@@ -268,6 +259,7 @@
fe.WorkerInputComponent cachedSdkInput;
var recordUsedInputs = argResults['used-inputs-file'] != null;
var additionalDills = summaryModules.keys.toList();
+
if (!useIncrementalCompiler) {
compilerState = await fe.initializeCompiler(
oldCompilerState,
@@ -425,6 +417,7 @@
var jsCode = jsProgramToCode(jsModule, moduleFormat,
buildSourceMap: options.sourceMap,
inlineSourceMap: options.inlineSourceMap,
+ emitDebugMetadata: options.emitDebugMetadata,
jsUrl: p.toUri(output).toString(),
mapUrl: mapUrl,
customScheme: multiRootScheme,
@@ -436,21 +429,9 @@
outFiles.add(
File('$output.map').writeAsString(json.encode(jsCode.sourceMap)));
}
-
- if (argResults['experimental-emit-debug-metadata'] as bool) {
- var moduleMetadata = [
- for (var lib in compiledLibraries.libraries)
- {
- 'name': compiler.jsLibraryName(lib),
- 'sourceMapFileUri': mapUrl,
- 'dartFileUris': [
- lib.fileUri.toString(),
- ...lib.parts.map((p) => p.partUri.toString())
- ],
- }
- ];
+ if (jsCode.metadata != null) {
outFiles.add(
- File('$output.metadata').writeAsString(json.encode(moduleMetadata)));
+ File('$output.metadata').writeAsString(json.encode(jsCode.metadata)));
}
}
@@ -487,13 +468,7 @@
// well.
// TODO(sigmund): refactor the underlying pieces to reduce the code duplication.
Future<CompilerResult> compileSdkFromDill(List<String> args) async {
- var argParser = ArgParser(allowTrailingOptions: true)
- ..addMultiOption('out', abbr: 'o', help: 'Output file (required).')
- ..addOption('multi-root-scheme', defaultsTo: 'org-dartlang-sdk')
- ..addOption('multi-root-output-path',
- help: 'Path to set multi-root files relative to when generating'
- ' source-maps.',
- hide: true);
+ var argParser = ArgParser(allowTrailingOptions: true);
SharedCompilerOptions.addArguments(argParser);
ArgResults argResults;
@@ -505,6 +480,18 @@
return CompilerResult(64);
}
+ var inputs = argResults.rest.toList();
+ if (inputs.length != 1) {
+ print('Only a single input file is supported to compile the sdk from dill'
+ 'but found: \n${inputs.join('\n')}');
+ return CompilerResult(64);
+ }
+
+ if (!inputs.single.endsWith('.dill')) {
+ print('Input must be a .dill file: ${inputs.single}');
+ return CompilerResult(64);
+ }
+
var outPaths = argResults['out'] as List<String>;
var moduleFormats = parseModuleFormatOption(argResults);
if (outPaths.isEmpty) {
@@ -517,7 +504,19 @@
return CompilerResult(64);
}
- var component = loadComponentFromBinary(argResults.rest[0]);
+ var component = loadComponentFromBinary(inputs.single);
+ var invalidLibraries = <Uri>[];
+ for (var library in component.libraries) {
+ if (library.importUri.scheme != 'dart') {
+ invalidLibraries.add(library.importUri);
+ }
+ }
+
+ if (invalidLibraries.isNotEmpty) {
+ print('Only the SDK libraries can be compiled from .dill but found:\n'
+ '${invalidLibraries.join('\n')}');
+ return CompilerResult(64);
+ }
var coreTypes = CoreTypes(component);
var hierarchy = ClassHierarchy(component, coreTypes);
var multiRootScheme = argResults['multi-root-scheme'] as String;
@@ -598,7 +597,14 @@
/// using [placeSourceMap].
final Map sourceMap;
- JSCode(this.code, this.sourceMap);
+ /// Module and library information
+ ///
+ /// The [metadata] is a contract between compiler and the debugger,
+ /// helping the debugger map between libraries, modules, source paths.
+ /// see: https://goto.google.com/dart-web-debugger-metadata
+ final ModuleMetadata metadata;
+
+ JSCode(this.code, this.sourceMap, {this.metadata});
}
/// Converts [moduleTree] to [JSCode], using [format].
@@ -608,6 +614,7 @@
JSCode jsProgramToCode(js_ast.Program moduleTree, ModuleFormat format,
{bool buildSourceMap = false,
bool inlineSourceMap = false,
+ bool emitDebugMetadata = false,
String jsUrl,
String mapUrl,
String sourceMapBase,
@@ -666,7 +673,27 @@
};
text = text.replaceFirst(
SharedCompiler.metricsLocationID, '$compileTimeStatistics');
- return JSCode(text, builtMap);
+
+ var debugMetadata = emitDebugMetadata
+ ? _emitMetadata(moduleTree, component, mapUrl, jsUrl)
+ : null;
+
+ return JSCode(text, builtMap, metadata: debugMetadata);
+}
+
+ModuleMetadata _emitMetadata(js_ast.Program program, Component component,
+ String sourceMapUri, String moduleUri) {
+ var metadata = ModuleMetadata(
+ program.name, loadFunctionName(program.name), sourceMapUri, moduleUri);
+
+ for (var lib in component.libraries) {
+ metadata.addLibrary(LibraryMetadata(
+ libraryUriToJsIdentifier(lib.importUri),
+ lib.importUri.toString(),
+ lib.fileUri.toString(),
+ [...lib.parts.map((p) => p.partUri)]));
+ }
+ return metadata;
}
/// Parses Dart's non-standard `-Dname=value` syntax for declared variables,
diff --git a/pkg/dev_compiler/lib/src/kernel/compiler.dart b/pkg/dev_compiler/lib/src/kernel/compiler.dart
index d7fcce0..0e7f31e 100644
--- a/pkg/dev_compiler/lib/src/kernel/compiler.dart
+++ b/pkg/dev_compiler/lib/src/kernel/compiler.dart
@@ -17,7 +17,8 @@
import '../compiler/js_names.dart' as js_ast;
import '../compiler/js_utils.dart' as js_ast;
-import '../compiler/module_builder.dart' show pathToJSIdentifier;
+import '../compiler/module_builder.dart'
+ show isSdkInternalRuntimeUri, libraryUriToJsIdentifier, pathToJSIdentifier;
import '../compiler/shared_command.dart' show SharedCompilerOptions;
import '../compiler/shared_compiler.dart';
import '../js_ast/js_ast.dart' as js_ast;
@@ -371,11 +372,7 @@
@override
String jsLibraryName(Library library) {
- var uri = library.importUri;
- if (uri.scheme == 'dart') {
- return isSdkInternalRuntime(library) ? 'dart' : uri.path;
- }
- return pathToJSIdentifier(p.withoutExtension(uri.pathSegments.last));
+ return libraryUriToJsIdentifier(library.importUri);
}
@override
@@ -405,8 +402,7 @@
@override
bool isSdkInternalRuntime(Library l) {
- var uri = l.importUri;
- return uri.scheme == 'dart' && uri.path == '_runtime';
+ return isSdkInternalRuntimeUri(l.importUri);
}
@override
diff --git a/pkg/dev_compiler/lib/src/kernel/module_metadata.dart b/pkg/dev_compiler/lib/src/kernel/module_metadata.dart
new file mode 100644
index 0000000..7286843
--- /dev/null
+++ b/pkg/dev_compiler/lib/src/kernel/module_metadata.dart
@@ -0,0 +1,173 @@
+// 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.
+
+/// Module metadata format version
+///
+/// Module reader always creates the current version but is able to read
+/// metadata files with later versions as long as the changes are backward
+/// compatible, i.e. only minor or patch versions have changed.
+///
+/// See: https://goto.google.com/dart-web-debugger-metadata
+class ModuleMetadataVersion {
+ final int majorVersion;
+ final int minorVersion;
+ final int patchVersion;
+
+ const ModuleMetadataVersion(
+ this.majorVersion,
+ this.minorVersion,
+ this.patchVersion,
+ );
+
+ /// Current metadata version
+ ///
+ /// Version follows simple semantic versioning format 'major.minor.patch'
+ /// See https://semver.org
+ ///
+ /// TODO(annagrin): create metadata package, make version the same as the
+ /// metadata package version, automate updating with the package update
+ static const ModuleMetadataVersion current = ModuleMetadataVersion(1, 0, 0);
+
+ /// Current metadata version created by the reader
+ String get version => '$majorVersion.$minorVersion.$patchVersion';
+
+ /// Is this metadata version compatible with the given version
+ ///
+ /// The minor and patch version changes never remove any fields that current
+ /// version supports, so the reader can create current metadata version from
+ /// any file created with a later reader, as long as the major version does
+ /// not change.
+ bool isCompatibleWith(String version) {
+ var parts = version.split('.');
+ if (parts.length != 3) {
+ throw FormatException('Version: $version'
+ 'does not follow simple semantic versioning format');
+ }
+ var major = int.parse(parts[0]);
+ var minor = int.parse(parts[1]);
+ var patch = int.parse(parts[2]);
+ return major == majorVersion &&
+ minor >= minorVersion &&
+ patch >= patchVersion;
+ }
+}
+
+/// Library metadata
+///
+/// Represents library metadata used in the debugger,
+/// supports reading from and writing to json
+/// See: https://goto.google.com/dart-web-debugger-metadata
+class LibraryMetadata {
+ /// Library name as defined in pubspec.yaml
+ final String name;
+
+ /// Library importUri
+ ///
+ /// Example package:path/path.dart
+ final String importUri;
+
+ /// Library fileUri
+ ///
+ /// Example file:///path/to/path/path.dart
+ final String fileUri;
+
+ /// All file uris from the library
+ ///
+ /// Can be relative paths to the directory of the fileUri
+ final List<String> partUris;
+
+ LibraryMetadata(this.name, this.importUri, this.fileUri, this.partUris);
+
+ LibraryMetadata.fromJson(Map<String, dynamic> json)
+ : name = json['name'] as String,
+ importUri = json['importUri'] as String,
+ fileUri = json['fileUri'] as String,
+ partUris =
+ List.castFrom<dynamic, String>(json['partUris'] as List<dynamic>);
+
+ Map<String, dynamic> toJson() {
+ return {
+ 'name': name,
+ 'importUri': importUri,
+ 'fileUri': fileUri,
+ 'partUris': [...partUris]
+ };
+ }
+}
+
+/// Module metadata
+///
+/// Represents module metadata used in the debugger,
+/// supports reading from and writing to json
+/// See: https://goto.google.com/dart-web-debugger-metadata
+class ModuleMetadata {
+ /// Metadata format version
+ String version;
+
+ /// Module name
+ ///
+ /// Used as a name of the js module created by the compiler and
+ /// as key to store and load modules in the debugger and the browser
+ final String name;
+
+ /// Name of the function enclosing the module
+ ///
+ /// Used by debugger to determine the top dart scope
+ final String closureName;
+
+ /// Source map uri
+ final String sourceMapUri;
+
+ /// Module uri
+ final String moduleUri;
+
+ final Map<String, LibraryMetadata> libraries = {};
+
+ ModuleMetadata(this.name, this.closureName, this.sourceMapUri, this.moduleUri,
+ {this.version}) {
+ version ??= ModuleMetadataVersion.current.version;
+ }
+
+ /// Add [library] to metadata
+ ///
+ /// Used for filling the metadata in the compiler or for reading from
+ /// stored metadata files.
+ void addLibrary(LibraryMetadata library) {
+ if (!libraries.containsKey(library.importUri)) {
+ libraries[library.importUri] = library;
+ } else {
+ throw ('Metadata creation error: '
+ 'Cannot add library $library with uri ${library.importUri}: '
+ 'another library "${libraries[library.importUri]}" is found '
+ 'with the same uri');
+ }
+ }
+
+ ModuleMetadata.fromJson(Map<String, dynamic> json)
+ : version = json['version'] as String,
+ name = json['name'] as String,
+ closureName = json['closureName'] as String,
+ sourceMapUri = json['sourceMapUri'] as String,
+ moduleUri = json['moduleUri'] as String {
+ var fileVersion = json['version'] as String;
+ if (!ModuleMetadataVersion.current.isCompatibleWith(version)) {
+ throw Exception('Unsupported metadata version $fileVersion');
+ }
+
+ for (var l in json['libraries'] as List<dynamic>) {
+ addLibrary(LibraryMetadata.fromJson(l as Map<String, dynamic>));
+ }
+ }
+
+ Map<String, dynamic> toJson() {
+ return {
+ 'version': version,
+ 'name': name,
+ 'closureName': closureName,
+ 'sourceMapUri': sourceMapUri,
+ 'moduleUri': moduleUri,
+ 'libraries': [for (var lib in libraries.values) lib.toJson()]
+ };
+ }
+}
diff --git a/pkg/dev_compiler/pubspec.yaml b/pkg/dev_compiler/pubspec.yaml
index 4dc3d73..a4e9b89 100644
--- a/pkg/dev_compiler/pubspec.yaml
+++ b/pkg/dev_compiler/pubspec.yaml
@@ -1,4 +1,5 @@
name: dev_compiler
+# This package is not intended for consumption on pub.dev. DO NOT publish.
publish_to: none
environment:
diff --git a/pkg/dev_compiler/test/module_metadata_test.dart b/pkg/dev_compiler/test/module_metadata_test.dart
new file mode 100644
index 0000000..93f9d7b
--- /dev/null
+++ b/pkg/dev_compiler/test/module_metadata_test.dart
@@ -0,0 +1,135 @@
+// 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 'dart:io';
+
+import 'package:dev_compiler/src/kernel/module_metadata.dart';
+import 'package:test/test.dart';
+
+// Test creating, reading and writing debugger metadata
+void main() {
+ group('Module metadata', () {
+ Directory tempDir;
+ File file;
+
+ setUpAll(() {
+ var systemTempDir = Directory.systemTemp;
+ tempDir = systemTempDir.createTempSync('foo bar');
+ var input = tempDir.uri.resolve('module.metadata');
+ file = File.fromUri(input)..createSync();
+ });
+
+ tearDownAll(() {
+ tempDir.delete(recursive: true);
+ });
+
+ test('create, write, and read', () async {
+ // create metadata
+ var version = ModuleMetadataVersion.current.version;
+ var module = createMetadata(version);
+ testMetadataFields(module, version);
+
+ // write metadata
+ file.writeAsBytesSync(utf8.encode(json.encode(module)));
+ expect(file.existsSync(), true);
+
+ // read metadata
+ var moduleJson = json.decode(utf8.decode(file.readAsBytesSync()));
+ var newModule =
+ ModuleMetadata.fromJson(moduleJson as Map<String, dynamic>);
+ testMetadataFields(newModule, version);
+ });
+
+ test('read later backward-compatible patch version', () async {
+ // create metadata with next patch version
+ var version = ModuleMetadataVersion(
+ ModuleMetadataVersion.current.majorVersion,
+ ModuleMetadataVersion.current.minorVersion,
+ ModuleMetadataVersion.current.patchVersion + 1)
+ .version;
+
+ var module = createMetadata(version);
+
+ // write metadata
+ file.writeAsBytesSync(utf8.encode(json.encode(module)));
+ expect(file.existsSync(), true);
+
+ // read metadata
+ var moduleJson = json.decode(utf8.decode(file.readAsBytesSync()));
+ var newModule =
+ ModuleMetadata.fromJson(moduleJson as Map<String, dynamic>);
+ testMetadataFields(newModule, version);
+ });
+
+ test('read later backward-compatible minor version', () async {
+ // create metadata with next minor version
+ var version = ModuleMetadataVersion(
+ ModuleMetadataVersion.current.majorVersion,
+ ModuleMetadataVersion.current.minorVersion + 1,
+ ModuleMetadataVersion.current.patchVersion + 1)
+ .version;
+ var module = createMetadata(version);
+
+ // write metadata
+ file.writeAsBytesSync(utf8.encode(json.encode(module)));
+ expect(file.existsSync(), true);
+
+ // read metadata
+ var moduleJson = json.decode(utf8.decode(file.readAsBytesSync()));
+ var newModule =
+ ModuleMetadata.fromJson(moduleJson as Map<String, dynamic>);
+ testMetadataFields(newModule, version);
+ });
+
+ test('fail to read later non-backward-compatible major version', () async {
+ // create metadata with next minor version
+ var version = ModuleMetadataVersion(
+ ModuleMetadataVersion.current.majorVersion + 1,
+ ModuleMetadataVersion.current.minorVersion + 1,
+ ModuleMetadataVersion.current.patchVersion + 1)
+ .version;
+ var module = createMetadata(version);
+
+ // write metadata
+ file.writeAsBytesSync(utf8.encode(json.encode(module)));
+ expect(file.existsSync(), true);
+
+ // try read metadata, expect to fail
+ var moduleJson = json.decode(utf8.decode(file.readAsBytesSync()));
+ ModuleMetadata newModule;
+ try {
+ newModule = ModuleMetadata.fromJson(moduleJson as Map<String, dynamic>);
+ } catch (e) {
+ expect(
+ e.toString(), 'Exception: Unsupported metadata version $version');
+ }
+
+ expect(newModule, null);
+ });
+ });
+}
+
+ModuleMetadata createMetadata(String version) => ModuleMetadata(
+ 'module', 'closure', 'module.map', 'module.js', version: version)
+ ..addLibrary(LibraryMetadata('library', 'package:library/test.dart',
+ 'file:///source/library/lib/test.dart', ['src/test2.dart']));
+
+void testMetadataFields(ModuleMetadata module, String version) {
+ // reader always creates current metadata version
+ expect(module.version, version);
+ expect(module.name, 'module');
+ expect(module.closureName, 'closure');
+ expect(module.sourceMapUri, 'module.map');
+ expect(module.moduleUri, 'module.js');
+
+ var libUri = module.libraries.keys.first;
+ var lib = module.libraries[libUri];
+
+ expect(libUri, 'package:library/test.dart');
+ expect(lib.name, 'library');
+ expect(lib.importUri, 'package:library/test.dart');
+ expect(lib.fileUri, 'file:///source/library/lib/test.dart');
+ expect(lib.partUris[0], 'src/test2.dart');
+}
diff --git a/pkg/diagnostic/pubspec.yaml b/pkg/diagnostic/pubspec.yaml
index 98411d4..b42e69b 100644
--- a/pkg/diagnostic/pubspec.yaml
+++ b/pkg/diagnostic/pubspec.yaml
@@ -1,4 +1,5 @@
name: diagnostic
+# This package is not intended for consumption on pub.dev. DO NOT publish.
publish_to: none
author: Dart Team <misc@dartlang.org>
description: Diagnostics for Dart
diff --git a/pkg/expect/pubspec.yaml b/pkg/expect/pubspec.yaml
index 595d26c..60226bb 100644
--- a/pkg/expect/pubspec.yaml
+++ b/pkg/expect/pubspec.yaml
@@ -1,4 +1,5 @@
name: expect
+# This package is not intended for consumption on pub.dev. DO NOT publish.
publish_to: none
description: >
Expect is used for tests that do not want to make use of the
diff --git a/pkg/front_end/lib/src/fasta/builder/class_builder.dart b/pkg/front_end/lib/src/fasta/builder/class_builder.dart
index 1bfd961..7dcfad3 100644
--- a/pkg/front_end/lib/src/fasta/builder/class_builder.dart
+++ b/pkg/front_end/lib/src/fasta/builder/class_builder.dart
@@ -1252,7 +1252,9 @@
declaredParameter.isCovariant,
declaredParameter,
isInterfaceCheck);
- if (declaredParameter.isRequired &&
+ if (declaredMember.isNonNullableByDefault &&
+ declaredParameter.isRequired &&
+ interfaceMember.isNonNullableByDefault &&
!interfaceNamedParameters.current.isRequired) {
reportInvalidOverride(
isInterfaceCheck,
diff --git a/pkg/front_end/lib/src/fasta/builder/field_builder.dart b/pkg/front_end/lib/src/fasta/builder/field_builder.dart
index 81af315..a04c39f 100644
--- a/pkg/front_end/lib/src/fasta/builder/field_builder.dart
+++ b/pkg/front_end/lib/src/fasta/builder/field_builder.dart
@@ -392,7 +392,7 @@
}
if (needsCheckVisitor != null) {
if (fieldType.accept(needsCheckVisitor)) {
- field.isGenericCovariantImpl = true;
+ _fieldEncoding.setGenericCovariantImpl();
}
}
}
@@ -843,9 +843,11 @@
@override
void setGenericCovariantImpl() {
- // TODO(johnniwinther): Is this correct? Should the [_lateSetter] be
- // annotated instead?
_field.isGenericCovariantImpl = true;
+ if (_lateSetter != null) {
+ _lateSetter.function.positionalParameters.single.isGenericCovariantImpl =
+ true;
+ }
}
@override
diff --git a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
index 2d1f0a2..12b828d 100644
--- a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
@@ -317,6 +317,16 @@
/// invocations are to be resolved in a separate step.
final List<Expression> redirectingFactoryInvocations = <Expression>[];
+ /// List of built type aliased generative constructor invocations that
+ /// require unaliasing.
+ final List<ConstructorInvocation> typeAliasedConstructorInvocations =
+ <ConstructorInvocation>[];
+
+ /// List of built type aliased factory constructor invocations that require
+ /// unaliasing.
+ final List<StaticInvocation> typeAliasedFactoryInvocations =
+ <StaticInvocation>[];
+
/// Variables with metadata. Their types need to be inferred late, for
/// example, in [finishFunction].
List<VariableDeclaration> variablesWithMetadata;
@@ -1129,7 +1139,14 @@
return true;
}
+ // TODO(eernst): Rename this method now that it handles more tasks.
void resolveRedirectingFactoryTargets() {
+ _unaliasTypeAliasedConstructorInvocations();
+ _unaliasTypeAliasedFactoryInvocations();
+ _resolveRedirectingFactoryTargets();
+ }
+
+ void _resolveRedirectingFactoryTargets() {
for (StaticInvocation invocation in redirectingFactoryInvocations) {
// If the invocation was invalid, it or its parent has already been
// desugared into an exception throwing expression. There is nothing to
@@ -1221,6 +1238,25 @@
redirectingFactoryInvocations.clear();
}
+ void _unaliasTypeAliasedConstructorInvocations() {
+ for (ConstructorInvocation invocation
+ in typeAliasedConstructorInvocations) {
+ // TODO(eernst): Should replace aliased constructor invocations,
+ // such that back ends don't see instance creations on type aliases.
+ invocation.replaceWith(new NullLiteral());
+ }
+ typeAliasedConstructorInvocations.clear();
+ }
+
+ void _unaliasTypeAliasedFactoryInvocations() {
+ for (StaticInvocation invocation in typeAliasedFactoryInvocations) {
+ // TODO(eernst): Should replace aliased factory invocations,
+ // such that back ends don't see instance creations on type aliases.
+ invocation.replaceWith(new NullLiteral());
+ }
+ typeAliasedFactoryInvocations.clear();
+ }
+
void finishVariableMetadata() {
List<VariableDeclaration> variablesWithMetadata =
this.variablesWithMetadata;
@@ -3907,6 +3943,7 @@
@override
Expression buildStaticInvocation(Member target, Arguments arguments,
{Constness constness: Constness.implicit,
+ TypeAliasBuilder typeAliasBuilder,
int charOffset: -1,
int charLength: noLength}) {
// The argument checks for the initial target of redirecting factories
@@ -3935,11 +3972,21 @@
return buildProblem(
fasta.messageNonConstConstructor, charOffset, charLength);
}
- ConstructorInvocation node =
- new ConstructorInvocation(target, arguments, isConst: isConst)
- ..fileOffset = charOffset;
- libraryBuilder.checkBoundsInConstructorInvocation(
- node, typeEnvironment, uri);
+ ConstructorInvocation node;
+ if (typeAliasBuilder == null) {
+ node = new ConstructorInvocation(target, arguments, isConst: isConst)
+ ..fileOffset = charOffset;
+ libraryBuilder.checkBoundsInConstructorInvocation(
+ node, typeEnvironment, uri);
+ } else {
+ node = new TypeAliasedConstructorInvocationJudgment(
+ typeAliasBuilder, target, arguments,
+ isConst: isConst)
+ ..fileOffset = charOffset;
+ // No type arguments were passed, so we need not check bounds.
+ assert(arguments.types.isEmpty);
+ typeAliasedConstructorInvocations.add(node);
+ }
return node;
} else {
Procedure procedure = target;
@@ -3952,12 +3999,22 @@
return buildProblem(
fasta.messageNonConstFactory, charOffset, charLength);
}
- StaticInvocation node = new FactoryConstructorInvocationJudgment(
- target, arguments,
- isConst: isConst)
- ..fileOffset = charOffset;
- libraryBuilder.checkBoundsInFactoryInvocation(
- node, typeEnvironment, uri);
+ StaticInvocation node;
+ if (typeAliasBuilder == null) {
+ node = new FactoryConstructorInvocationJudgment(target, arguments,
+ isConst: isConst)
+ ..fileOffset = charOffset;
+ libraryBuilder.checkBoundsInFactoryInvocation(
+ node, typeEnvironment, uri);
+ } else {
+ node = new TypeAliasedFactoryInvocationJudgment(
+ typeAliasBuilder, target, arguments,
+ isConst: isConst)
+ ..fileOffset = charOffset;
+ // No type arguments were passed, so we need not check bounds.
+ assert(arguments.types.isEmpty);
+ typeAliasedFactoryInvocations.add(node);
+ }
return node;
} else {
assert(constness == Constness.implicit);
@@ -4248,13 +4305,83 @@
}
} else {
if (aliasBuilder.typeVariables?.isNotEmpty ?? false) {
- // No type arguments provided to alias, but it is generic.
- typeArgumentBuilders = new List<TypeBuilder>.filled(
- aliasBuilder.typeVariables.length, null,
- growable: true);
- for (int i = 0; i < typeArgumentBuilders.length; ++i) {
- // TODO(eernst): We must use inferred types, for now use defaults.
- typeArgumentBuilders[i] = aliasBuilder.typeVariables[i].defaultType;
+ // Raw generic type alias used for instance creation, needs inference.
+ ClassBuilder classBuilder;
+ if (type is ClassBuilder) {
+ classBuilder = type;
+ } else {
+ if (type is InvalidTypeDeclarationBuilder) {
+ LocatedMessage message = type.message;
+ return evaluateArgumentsBefore(
+ arguments,
+ buildProblem(message.messageObject, nameToken.charOffset,
+ nameToken.lexeme.length));
+ } else {
+ errorName ??= debugName(type.fullNameForErrors, name);
+ }
+ errorName ??= name;
+
+ return throwNoSuchMethodError(forest.createNullLiteral(charOffset),
+ errorName, arguments, nameLastToken.charOffset,
+ message: message);
+ }
+ MemberBuilder b = classBuilder.findConstructorOrFactory(
+ name, charOffset, uri, libraryBuilder);
+ Member target = b?.member;
+ if (b == null) {
+ // Not found. Reported below.
+ } else if (b is AmbiguousMemberBuilder) {
+ message = b.message.withLocation(uri, charOffset, noLength);
+ } else if (b.isConstructor) {
+ if (classBuilder.isAbstract) {
+ return evaluateArgumentsBefore(
+ arguments,
+ buildAbstractClassInstantiationError(
+ fasta.templateAbstractClassInstantiation
+ .withArguments(type.name),
+ type.name,
+ nameToken.charOffset));
+ }
+ }
+ if (target is Constructor ||
+ (target is Procedure && target.kind == ProcedureKind.Factory)) {
+ Expression invocation;
+ invocation = buildStaticInvocation(target, arguments,
+ constness: constness,
+ typeAliasBuilder: aliasBuilder,
+ charOffset: nameToken.charOffset,
+ charLength: nameToken.length);
+ return invocation;
+ } else {
+ errorName ??= debugName(type.name, name);
+ return throwNoSuchMethodError(forest.createNullLiteral(charOffset),
+ errorName, arguments, nameLastToken.charOffset,
+ message: message);
+ }
+ } else {
+ // Empty `typeArguments` and `aliasBuilder``is non-generic, but it
+ // may still unalias to a class type with some type arguments.
+ if (type is ClassBuilder) {
+ List<TypeBuilder> unaliasedTypeArgumentBuilders =
+ aliasBuilder.unaliasTypeArguments(const []);
+ if (unaliasedTypeArgumentBuilders == null) {
+ // TODO(eernst): This is a wrong number of type arguments,
+ // occurring indirectly (in an alias of an alias, etc.).
+ return evaluateArgumentsBefore(
+ arguments,
+ buildProblem(
+ fasta.templateTypeArgumentMismatch
+ .withArguments(numberOfTypeParameters),
+ nameToken.charOffset,
+ nameToken.length,
+ suppressMessage: true));
+ }
+ List<DartType> dartTypeArguments = [];
+ for (TypeBuilder typeBuilder in unaliasedTypeArgumentBuilders) {
+ dartTypeArguments.add(typeBuilder.build(libraryBuilder));
+ }
+ assert(forest.argumentsTypeArguments(arguments).isEmpty);
+ forest.argumentsSetTypeArguments(arguments, dartTypeArguments);
}
}
}
@@ -4296,16 +4423,17 @@
assert(forest.argumentsTypeArguments(arguments).isEmpty);
forest.argumentsSetTypeArguments(arguments, []);
} else {
- // No type arguments provided to unaliased class, use defaults.
- List<DartType> result = new List<DartType>.filled(
- type.typeVariables.length, null,
- growable: true);
- for (int i = 0; i < result.length; ++i) {
- result[i] =
- type.typeVariables[i].defaultType?.build(type.library);
+ if (forest.argumentsTypeArguments(arguments).isEmpty) {
+ // No type arguments provided to unaliased class, use defaults.
+ List<DartType> result = new List<DartType>.filled(
+ type.typeVariables.length, null,
+ growable: true);
+ for (int i = 0; i < result.length; ++i) {
+ result[i] =
+ type.typeVariables[i].defaultType?.build(type.library);
+ }
+ forest.argumentsSetTypeArguments(arguments, result);
}
- assert(forest.argumentsTypeArguments(arguments).isEmpty);
- forest.argumentsSetTypeArguments(arguments, result);
}
}
}
diff --git a/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart b/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
index 3f4c1e8..9644894 100644
--- a/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
@@ -743,6 +743,41 @@
result.inferredType, result.applyResult(resultNode));
}
+ ExpressionInferenceResult visitTypeAliasedConstructorInvocationJudgment(
+ TypeAliasedConstructorInvocationJudgment node, DartType typeContext) {
+ assert(getExplicitTypeArguments(node.arguments) == null);
+ Typedef typedef = node.typeAliasBuilder.typedef;
+ FunctionType calleeType = node.target.function
+ .computeAliasedFunctionType(typedef, inferrer.library.library);
+ InvocationInferenceResult result = inferrer.inferInvocation(
+ typeContext, node.fileOffset, calleeType, node.arguments,
+ returnType: calleeType.returnType.unalias, isConst: node.isConst);
+ node.hasBeenInferred = true;
+ Expression resultNode = node;
+ if (!inferrer.isTopLevel) {
+ SourceLibraryBuilder library = inferrer.library;
+ library.checkBoundsInType(result.inferredType,
+ inferrer.typeSchemaEnvironment, inferrer.helper.uri, node.fileOffset,
+ inferred: true);
+ if (inferrer.isNonNullableByDefault) {
+ if (node.target == inferrer.coreTypes.listDefaultConstructor) {
+ resultNode = inferrer.helper.wrapInProblem(node,
+ messageDefaultListConstructorError, node.fileOffset, noLength);
+ }
+ }
+ }
+ return new ExpressionInferenceResult(
+ result.inferredType, result.applyResult(resultNode));
+ }
+
+ ExpressionInferenceResult visitTypeAliasedFactoryInvocationJudgment(
+ TypeAliasedFactoryInvocationJudgment node, DartType typeContext) {
+ // TODO(eernst): See visitTypeAliasedConstructorInvocationJudgment
+ // for an implementation handling a similar task.
+ throw "visitTypeAliasedFactoryInvocationJudgment: "
+ "${node.typeAliasBuilder}, ${node.target}, $typeContext";
+ }
+
@override
void visitFieldInitializer(FieldInitializer node) {
ExpressionInferenceResult initializerResult =
@@ -4887,9 +4922,7 @@
StatementInferenceResult visitReturnStatement(
covariant ReturnStatementImpl node) {
ClosureContext closureContext = inferrer.closureContext;
- DartType typeContext = !closureContext.isGenerator
- ? closureContext.returnOrYieldContext
- : const UnknownType();
+ DartType typeContext = closureContext.returnContext;
DartType inferredType;
if (node.expression != null) {
ExpressionInferenceResult expressionResult = inferrer.inferExpression(
@@ -5682,24 +5715,18 @@
StatementInferenceResult visitYieldStatement(YieldStatement node) {
ClosureContext closureContext = inferrer.closureContext;
ExpressionInferenceResult expressionResult;
- if (closureContext.isGenerator) {
- DartType typeContext = closureContext.returnOrYieldContext;
- if (node.isYieldStar && typeContext != null) {
- typeContext = inferrer.wrapType(
- typeContext,
- closureContext.isAsync
- ? inferrer.coreTypes.streamClass
- : inferrer.coreTypes.iterableClass,
- inferrer.library.nonNullable);
- }
- expressionResult = inferrer.inferExpression(
- node.expression, typeContext, true,
- isVoidAllowed: true);
- } else {
- expressionResult = inferrer.inferExpression(
- node.expression, const UnknownType(), true,
- isVoidAllowed: true);
+ DartType typeContext = closureContext.yieldContext;
+ if (node.isYieldStar && typeContext is! UnknownType) {
+ typeContext = inferrer.wrapType(
+ typeContext,
+ closureContext.isAsync
+ ? inferrer.coreTypes.streamClass
+ : inferrer.coreTypes.iterableClass,
+ inferrer.library.nonNullable);
}
+ expressionResult = inferrer.inferExpression(
+ node.expression, typeContext, true,
+ isVoidAllowed: true);
closureContext.handleYield(inferrer, node, expressionResult);
return const StatementInferenceResult();
}
diff --git a/pkg/front_end/lib/src/fasta/kernel/internal_ast.dart b/pkg/front_end/lib/src/fasta/kernel/internal_ast.dart
index 991d0cd..377dace 100644
--- a/pkg/front_end/lib/src/fasta/kernel/internal_ast.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/internal_ast.dart
@@ -25,6 +25,8 @@
import 'package:kernel/src/printer.dart';
import 'package:kernel/core_types.dart';
+import '../builder/type_alias_builder.dart';
+
import '../fasta_codes.dart'
show noLength, templateWebLiteralCannotBeRepresentedExactly;
@@ -704,6 +706,65 @@
}
}
+/// Shadow object for [ConstructorInvocation] when the procedure being invoked
+/// is a type aliased constructor.
+class TypeAliasedConstructorInvocationJudgment extends ConstructorInvocation
+ implements ExpressionJudgment {
+ bool hasBeenInferred = false;
+ final TypeAliasBuilder typeAliasBuilder;
+
+ TypeAliasedConstructorInvocationJudgment(
+ this.typeAliasBuilder, Constructor target, ArgumentsImpl arguments,
+ {bool isConst: false})
+ : super(target, arguments, isConst: isConst);
+
+ @override
+ ExpressionInferenceResult acceptInference(
+ InferenceVisitor visitor, DartType typeContext) {
+ return visitor.visitTypeAliasedConstructorInvocationJudgment(
+ this, typeContext);
+ }
+
+ @override
+ String toString() {
+ return "TypeAliasedConstructorInvocationJudgment(${toStringInternal()})";
+ }
+
+ @override
+ String toStringInternal() {
+ return "";
+ }
+}
+
+/// Shadow object for [StaticInvocation] when the procedure being invoked is a
+/// type aliased factory constructor.
+class TypeAliasedFactoryInvocationJudgment extends StaticInvocation
+ implements ExpressionJudgment {
+ bool hasBeenInferred = false;
+ final TypeAliasBuilder typeAliasBuilder;
+
+ TypeAliasedFactoryInvocationJudgment(
+ this.typeAliasBuilder, Procedure target, ArgumentsImpl arguments,
+ {bool isConst: false})
+ : super(target, arguments, isConst: isConst);
+
+ @override
+ ExpressionInferenceResult acceptInference(
+ InferenceVisitor visitor, DartType typeContext) {
+ return visitor.visitTypeAliasedFactoryInvocationJudgment(this, typeContext);
+ }
+
+ @override
+ String toString() {
+ return "TypeAliasedConstructorInvocationJudgment(${toStringInternal()})";
+ }
+
+ @override
+ String toStringInternal() {
+ return "";
+ }
+}
+
/// Front end specific implementation of [FunctionDeclaration].
class FunctionDeclarationImpl extends FunctionDeclaration {
bool hasImplicitReturnType = false;
diff --git a/pkg/front_end/lib/src/fasta/type_inference/closure_context.dart b/pkg/front_end/lib/src/fasta/type_inference/closure_context.dart
new file mode 100644
index 0000000..1ff344f
--- /dev/null
+++ b/pkg/front_end/lib/src/fasta/type_inference/closure_context.dart
@@ -0,0 +1,856 @@
+// 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.md file.
+
+part of 'type_inferrer.dart';
+
+/// Keeps track of information about the innermost function or closure being
+/// inferred.
+abstract class ClosureContext {
+ /// Returns `true` if this is an `async` or an `async*` function.
+ bool get isAsync;
+
+ /// The typing expectation for the subexpression of a `return` statement
+ /// inside the function.
+ ///
+ /// For an `async` function, this is a "FutureOr" type (since it is
+ /// permissible for such a function to return either a direct value or a
+ /// future).
+ ///
+ /// For generator functions (which do not allow return statements) this is the
+ /// unknown type.
+ DartType get returnContext;
+
+ /// The typing expectation for the subexpression of a `yield` statement inside
+ /// the function.
+ ///
+ /// For `sync*` and `async*` functions, the expected type is the element type
+ /// of the generated `Iterable` or `Stream`, respectively.
+ ///
+ /// For non-generator functions (which do not allow yield statements) this is
+ /// the unknown type.
+ DartType get yieldContext;
+
+ factory ClosureContext(TypeInferrerImpl inferrer, AsyncMarker asyncMarker,
+ DartType returnContext, bool needToInferReturnType) {
+ assert(returnContext != null);
+ DartType declaredReturnType =
+ inferrer.computeGreatestClosure(returnContext);
+ bool isAsync = asyncMarker == AsyncMarker.Async ||
+ asyncMarker == AsyncMarker.AsyncStar;
+ bool isGenerator = asyncMarker == AsyncMarker.SyncStar ||
+ asyncMarker == AsyncMarker.AsyncStar;
+ if (isGenerator) {
+ if (isAsync) {
+ DartType yieldContext = inferrer.getTypeArgumentOf(
+ returnContext, inferrer.coreTypes.streamClass);
+ return new _AsyncStarClosureContext(
+ yieldContext, declaredReturnType, needToInferReturnType);
+ } else {
+ DartType yieldContext = inferrer.getTypeArgumentOf(
+ returnContext, inferrer.coreTypes.iterableClass);
+ return new _SyncStarClosureContext(
+ yieldContext, declaredReturnType, needToInferReturnType);
+ }
+ } else if (isAsync) {
+ returnContext = inferrer.wrapFutureOrType(
+ inferrer.typeSchemaEnvironment.unfutureType(returnContext));
+ return new _AsyncClosureContext(
+ returnContext, declaredReturnType, needToInferReturnType);
+ } else {
+ return new _SyncClosureContext(
+ returnContext, declaredReturnType, needToInferReturnType);
+ }
+ }
+
+ /// Handles an explicit return statement.
+ ///
+ /// If the return type is declared, the expression type is checked. If the
+ /// return type is inferred the expression type registered for inference
+ /// in [inferReturnType].
+ void handleReturn(TypeInferrerImpl inferrer, ReturnStatement statement,
+ DartType type, bool isArrow);
+
+ /// Handles an explicit yield statement.
+ ///
+ /// If the return type is declared, the expression type is checked. If the
+ /// return type is inferred the expression type registered for inference
+ /// in [inferReturnType].
+ void handleYield(TypeInferrerImpl inferrer, YieldStatement node,
+ ExpressionInferenceResult expressionResult);
+
+ /// Handles an implicit return statement.
+ ///
+ /// If the return type is declared, the expression type is checked. If the
+ /// return type is inferred the expression type registered for inference
+ /// in [inferReturnType].
+ StatementInferenceResult handleImplicitReturn(TypeInferrerImpl inferrer,
+ Statement body, StatementInferenceResult inferenceResult, int fileOffset);
+
+ /// Infers the return type for the function.
+ ///
+ /// If the function is a non-generator function this is based on the explicit
+ /// and implicit return statements registered in [handleReturn] and
+ /// [handleImplicitReturn].
+ ///
+ /// If the function is a generator function this is based on the explicit
+ /// yield statements registered in [handleYield].
+ DartType inferReturnType(TypeInferrerImpl inferrer, {bool hasImplicitReturn});
+}
+
+class _SyncClosureContext implements ClosureContext {
+ bool get isAsync => false;
+
+ /// The typing expectation for the subexpression of a `return` or `yield`
+ /// statement inside the function.
+ ///
+ /// For non-generator async functions, this will be a "FutureOr" type (since
+ /// it is permissible for such a function to return either a direct value or
+ /// a future).
+ ///
+ /// For generator functions containing a `yield*` statement, the expected type
+ /// for the subexpression of the `yield*` statement is the result of wrapping
+ /// this typing expectation in `Stream` or `Iterator`, as appropriate.
+ final DartType _returnContext;
+
+ @override
+ DartType get returnContext => _returnContext;
+
+ @override
+ DartType get yieldContext => const UnknownType();
+
+ final DartType _declaredReturnType;
+
+ final bool _needToInferReturnType;
+
+ DartType _inferredReturnType;
+
+ /// Whether the function is an arrow function.
+ bool _isArrow;
+
+ /// A list of return statements in functions whose return type is being
+ /// inferred.
+ ///
+ /// The returns are checked for validity after the return type is inferred.
+ List<ReturnStatement> _returnStatements;
+
+ /// A list of return expression types in functions whose return type is
+ /// being inferred.
+ List<DartType> _returnExpressionTypes;
+
+ _SyncClosureContext(this._returnContext, this._declaredReturnType,
+ this._needToInferReturnType) {
+ if (_needToInferReturnType) {
+ _returnStatements = [];
+ _returnExpressionTypes = [];
+ }
+ }
+
+ void _checkValidReturn(TypeInferrerImpl inferrer, DartType returnType,
+ ReturnStatement statement, DartType expressionType) {
+ // The rules for valid returns for functions with [returnType] `T` and
+ // a return expression with static [expressionType] `S`.
+ if (statement.expression == null) {
+ // `return;` is a valid return if T is void, dynamic, or Null.
+ if (returnType is VoidType ||
+ returnType is DynamicType ||
+ returnType == inferrer.coreTypes.nullType) {
+ // Valid return;
+ } else {
+ statement.expression = inferrer.helper.wrapInProblem(
+ new NullLiteral()..fileOffset = statement.fileOffset,
+ messageReturnWithoutExpression,
+ statement.fileOffset,
+ noLength)
+ ..parent = statement;
+ }
+ } else {
+ void ensureAssignability() {
+ Expression expression = inferrer.ensureAssignable(
+ _returnContext, expressionType, statement.expression,
+ fileOffset: statement.fileOffset, isVoidAllowed: true);
+ statement.expression = expression..parent = statement;
+ }
+
+ if (_isArrow && returnType is VoidType) {
+ // Arrow functions are valid if: T is void or return exp; is a valid for
+ // a block-bodied function.
+ ensureAssignability();
+ } else if (returnType is VoidType &&
+ expressionType is! VoidType &&
+ expressionType is! DynamicType &&
+ expressionType != inferrer.coreTypes.nullType) {
+ // Invalid if T is void and S is not void, dynamic, or Null
+ statement.expression = inferrer.helper.wrapInProblem(
+ statement.expression,
+ messageReturnFromVoidFunction,
+ statement.expression.fileOffset,
+ noLength)
+ ..parent = statement;
+ } else if (expressionType is VoidType &&
+ returnType is! VoidType &&
+ returnType is! DynamicType &&
+ returnType != inferrer.coreTypes.nullType) {
+ // Invalid if S is void and T is not void, dynamic, or Null.
+ statement.expression = inferrer.helper.wrapInProblem(
+ statement.expression,
+ messageVoidExpression,
+ statement.expression.fileOffset,
+ noLength)
+ ..parent = statement;
+ } else {
+ ensureAssignability();
+ }
+ }
+ }
+
+ /// Updates the inferred return type based on the presence of a return
+ /// statement returning the given [type].
+ @override
+ void handleReturn(TypeInferrerImpl inferrer, ReturnStatement statement,
+ DartType type, bool isArrow) {
+ // The first return we see tells us if we have an arrow function.
+ if (this._isArrow == null) {
+ this._isArrow = isArrow;
+ } else {
+ assert(this._isArrow == isArrow);
+ }
+
+ if (_needToInferReturnType) {
+ // Add the return to a list to be checked for validity after we've
+ // inferred the return type.
+ _returnStatements.add(statement);
+ _returnExpressionTypes.add(type);
+ } else {
+ _checkValidReturn(inferrer, _declaredReturnType, statement, type);
+ }
+ }
+
+ @override
+ void handleYield(TypeInferrerImpl inferrer, YieldStatement node,
+ ExpressionInferenceResult expressionResult) {
+ node.expression = expressionResult.expression..parent = node;
+ }
+
+ @override
+ DartType inferReturnType(TypeInferrerImpl inferrer,
+ {bool hasImplicitReturn}) {
+ assert(_needToInferReturnType);
+ assert(hasImplicitReturn != null);
+ DartType inferredType;
+ if (_returnStatements.isNotEmpty) {
+ // Use the types seen from the explicit return statements.
+ for (int i = 0; i < _returnStatements.length; i++) {
+ ReturnStatement statement = _returnStatements[i];
+ DartType type = _returnExpressionTypes[i];
+ // The return expression has to be assignable to the return type
+ // expectation from the downwards inference context.
+ if (statement.expression != null) {
+ if (!inferrer.isAssignable(_returnContext, type)) {
+ type = inferrer.computeGreatestClosure(_returnContext);
+ }
+ }
+ if (inferredType == null) {
+ inferredType = type;
+ } else {
+ inferredType = inferrer.typeSchemaEnvironment.getStandardUpperBound(
+ inferredType, type, inferrer.library.library);
+ }
+ }
+ } else if (hasImplicitReturn) {
+ // No explicit returns we have an implicit `return null`.
+ inferredType = inferrer.typeSchemaEnvironment.nullType;
+ } else {
+ // No explicit return and the function doesn't complete normally; that is,
+ // it throws.
+ if (inferrer.isNonNullableByDefault) {
+ inferredType = new NeverType(inferrer.library.nonNullable);
+ } else {
+ inferredType = inferrer.typeSchemaEnvironment.nullType;
+ }
+ }
+
+ if (!inferrer.typeSchemaEnvironment.isSubtypeOf(
+ inferredType, _returnContext, SubtypeCheckMode.withNullabilities)) {
+ // If the inferred return type isn't a subtype of the context, we use the
+ // context.
+ inferredType = inferrer.computeGreatestClosure2(_declaredReturnType);
+ }
+
+ for (int i = 0; i < _returnStatements.length; ++i) {
+ _checkValidReturn(inferrer, inferredType, _returnStatements[i],
+ _returnExpressionTypes[i]);
+ }
+
+ return _inferredReturnType =
+ demoteTypeInLibrary(inferredType, inferrer.library.library);
+ }
+
+ @override
+ StatementInferenceResult handleImplicitReturn(
+ TypeInferrerImpl inferrer,
+ Statement body,
+ StatementInferenceResult inferenceResult,
+ int fileOffset) {
+ DartType returnType;
+ if (_needToInferReturnType) {
+ assert(_inferredReturnType != null,
+ "Return type has not yet been inferred.");
+ returnType = _inferredReturnType;
+ } else {
+ returnType = _declaredReturnType;
+ }
+ if (inferrer.library.isNonNullableByDefault &&
+ (containsInvalidType(returnType) ||
+ returnType.isPotentiallyNonNullable) &&
+ inferrer.flowAnalysis.isReachable) {
+ Statement resultStatement =
+ inferenceResult.hasChanged ? inferenceResult.statement : body;
+ // Create a synthetic return statement with the error.
+ Statement returnStatement = new ReturnStatement(inferrer.helper
+ .wrapInProblem(
+ new NullLiteral()..fileOffset = fileOffset,
+ templateImplicitReturnNull.withArguments(
+ returnType, inferrer.library.isNonNullableByDefault),
+ fileOffset,
+ noLength))
+ ..fileOffset = fileOffset;
+ if (resultStatement is Block) {
+ resultStatement.statements.add(returnStatement);
+ } else {
+ resultStatement =
+ new Block(<Statement>[resultStatement, returnStatement])
+ ..fileOffset = fileOffset;
+ }
+ return new StatementInferenceResult.single(resultStatement);
+ }
+ return inferenceResult;
+ }
+}
+
+/// Keeps track of information about the innermost function or closure being
+/// inferred.
+class _AsyncClosureContext implements ClosureContext {
+ bool get isAsync => true;
+
+ /// The typing expectation for the subexpression of a `return` or `yield`
+ /// statement inside the function.
+ ///
+ /// For non-generator async functions, this will be a "FutureOr" type (since
+ /// it is permissible for such a function to return either a direct value or
+ /// a future).
+ ///
+ /// For generator functions containing a `yield*` statement, the expected type
+ /// for the subexpression of the `yield*` statement is the result of wrapping
+ /// this typing expectation in `Stream` or `Iterator`, as appropriate.
+ final DartType _returnContext;
+
+ @override
+ DartType get returnContext => _returnContext;
+
+ @override
+ DartType get yieldContext => const UnknownType();
+
+ final DartType _declaredReturnType;
+
+ final bool _needToInferReturnType;
+
+ DartType _inferredReturnType;
+
+ /// Whether the function is an arrow function.
+ bool _isArrow;
+
+ /// A list of return statements in functions whose return type is being
+ /// inferred.
+ ///
+ /// The returns are checked for validity after the return type is inferred.
+ List<ReturnStatement> _returnStatements;
+
+ /// A list of return expression types in functions whose return type is
+ /// being inferred.
+ List<DartType> _returnExpressionTypes;
+
+ _AsyncClosureContext(this._returnContext, this._declaredReturnType,
+ this._needToInferReturnType) {
+ if (_needToInferReturnType) {
+ _returnStatements = [];
+ _returnExpressionTypes = [];
+ }
+ }
+
+ void _checkValidReturn(TypeInferrerImpl inferrer, DartType returnType,
+ ReturnStatement statement, DartType expressionType) {
+ // The rules for valid returns for async functions with [returnType] `T` and
+ // a return expression with static [expressionType] `S`.
+ DartType flattenedReturnType =
+ inferrer.typeSchemaEnvironment.unfutureType(returnType);
+ if (statement.expression == null) {
+ // `return;` is a valid return if flatten(T) is void, dynamic, or Null.
+ if (flattenedReturnType is VoidType ||
+ flattenedReturnType is DynamicType ||
+ flattenedReturnType == inferrer.coreTypes.nullType) {
+ // Valid return;
+ } else {
+ statement.expression = inferrer.helper.wrapInProblem(
+ new NullLiteral()..fileOffset = statement.fileOffset,
+ messageReturnWithoutExpression,
+ statement.fileOffset,
+ noLength)
+ ..parent = statement;
+ }
+ } else {
+ DartType flattenedExpressionType =
+ inferrer.typeSchemaEnvironment.unfutureType(expressionType);
+
+ void ensureAssignability() {
+ DartType wrappedType = inferrer.typeSchemaEnvironment
+ .futureType(flattenedExpressionType, Nullability.nonNullable);
+ Expression expression = inferrer.ensureAssignable(
+ computeAssignableType(inferrer, _returnContext, wrappedType),
+ wrappedType,
+ statement.expression,
+ fileOffset: statement.fileOffset,
+ isVoidAllowed: true,
+ runtimeCheckedType: _returnContext);
+ statement.expression = expression..parent = statement;
+ }
+
+ if (_isArrow && flattenedReturnType is VoidType) {
+ // Arrow functions are valid if: flatten(T) is void or return exp; is
+ // valid for a block-bodied function.
+ ensureAssignability();
+ } else if (returnType is VoidType &&
+ flattenedExpressionType is! VoidType &&
+ flattenedExpressionType is! DynamicType &&
+ flattenedExpressionType != inferrer.coreTypes.nullType) {
+ // Invalid if T is void and flatten(S) is not void, dynamic, or Null.
+ statement.expression = inferrer.helper.wrapInProblem(
+ statement.expression,
+ messageReturnFromVoidFunction,
+ statement.expression.fileOffset,
+ noLength)
+ ..parent = statement;
+ } else if (flattenedExpressionType is VoidType &&
+ flattenedReturnType is! VoidType &&
+ flattenedReturnType is! DynamicType &&
+ flattenedReturnType != inferrer.coreTypes.nullType) {
+ // Invalid if flatten(S) is void and flatten(T) is not void, dynamic,
+ // or Null.
+ statement.expression = inferrer.helper.wrapInProblem(
+ statement.expression,
+ messageVoidExpression,
+ statement.expression.fileOffset,
+ noLength)
+ ..parent = statement;
+ } else {
+ // The caller will check that the return expression is assignable to the
+ // return type.
+ ensureAssignability();
+ }
+ }
+ }
+
+ /// Updates the inferred return type based on the presence of a return
+ /// statement returning the given [type].
+ @override
+ void handleReturn(TypeInferrerImpl inferrer, ReturnStatement statement,
+ DartType type, bool isArrow) {
+ // The first return we see tells us if we have an arrow function.
+ if (this._isArrow == null) {
+ this._isArrow = isArrow;
+ } else {
+ assert(this._isArrow == isArrow);
+ }
+
+ if (_needToInferReturnType) {
+ // Add the return to a list to be checked for validity after we've
+ // inferred the return type.
+ _returnStatements.add(statement);
+ _returnExpressionTypes.add(type);
+ } else {
+ _checkValidReturn(inferrer, _declaredReturnType, statement, type);
+ }
+ }
+
+ @override
+ void handleYield(TypeInferrerImpl inferrer, YieldStatement node,
+ ExpressionInferenceResult expressionResult) {
+ node.expression = expressionResult.expression..parent = node;
+ }
+
+ DartType computeAssignableType(TypeInferrerImpl inferrer,
+ DartType contextType, DartType expressionType) {
+ contextType = inferrer.computeGreatestClosure(contextType);
+
+ DartType initialContextType = contextType;
+ if (!inferrer.isAssignable(initialContextType, expressionType)) {
+ // If the body of the function is async, the expected return type has the
+ // shape FutureOr<T>. We check both branches for FutureOr here: both T
+ // and Future<T>.
+ DartType unfuturedExpectedType =
+ inferrer.typeSchemaEnvironment.unfutureType(contextType);
+ DartType futuredExpectedType = inferrer.wrapFutureType(
+ unfuturedExpectedType, inferrer.library.nonNullable);
+ if (inferrer.isAssignable(unfuturedExpectedType, expressionType)) {
+ contextType = unfuturedExpectedType;
+ } else if (inferrer.isAssignable(futuredExpectedType, expressionType)) {
+ contextType = futuredExpectedType;
+ }
+ }
+ return contextType;
+ }
+
+ @override
+ DartType inferReturnType(TypeInferrerImpl inferrer,
+ {bool hasImplicitReturn}) {
+ assert(_needToInferReturnType);
+ assert(hasImplicitReturn != null);
+ DartType inferredType;
+ if (_returnStatements.isNotEmpty) {
+ // Use the types seen from the explicit return statements.
+ for (int i = 0; i < _returnStatements.length; i++) {
+ ReturnStatement statement = _returnStatements[i];
+ DartType type = _returnExpressionTypes[i];
+
+ // The return expression has to be assignable to the return type
+ // expectation from the downwards inference context.
+ if (statement.expression != null) {
+ if (!inferrer.isAssignable(
+ computeAssignableType(inferrer, _returnContext, type), type)) {
+ // Not assignable, use the expectation.
+ type = inferrer.computeGreatestClosure(_returnContext);
+ }
+ }
+ DartType unwrappedType =
+ inferrer.typeSchemaEnvironment.unfutureType(type);
+ if (inferredType == null) {
+ inferredType = unwrappedType;
+ } else {
+ inferredType = inferrer.typeSchemaEnvironment.getStandardUpperBound(
+ inferredType, unwrappedType, inferrer.library.library);
+ }
+ }
+ } else if (hasImplicitReturn) {
+ // No explicit returns we have an implicit `return null`.
+ inferredType = inferrer.typeSchemaEnvironment.nullType;
+ } else {
+ // No explicit return and the function doesn't complete normally; that is,
+ // it throws.
+ if (inferrer.isNonNullableByDefault) {
+ inferredType = new NeverType(inferrer.library.nonNullable);
+ } else {
+ inferredType = inferrer.typeSchemaEnvironment.nullType;
+ }
+ }
+
+ inferredType =
+ inferrer.wrapFutureType(inferredType, inferrer.library.nonNullable);
+
+ if (!inferrer.typeSchemaEnvironment.isSubtypeOf(
+ inferredType, _returnContext, SubtypeCheckMode.withNullabilities)) {
+ // If the inferred return type isn't a subtype of the context, we use the
+ // context.
+ inferredType = inferrer.computeGreatestClosure2(_declaredReturnType);
+ }
+
+ for (int i = 0; i < _returnStatements.length; ++i) {
+ _checkValidReturn(inferrer, inferredType, _returnStatements[i],
+ _returnExpressionTypes[i]);
+ }
+
+ return _inferredReturnType =
+ demoteTypeInLibrary(inferredType, inferrer.library.library);
+ }
+
+ @override
+ StatementInferenceResult handleImplicitReturn(
+ TypeInferrerImpl inferrer,
+ Statement body,
+ StatementInferenceResult inferenceResult,
+ int fileOffset) {
+ DartType returnType;
+ if (_needToInferReturnType) {
+ assert(_inferredReturnType != null,
+ "Return type has not yet been inferred.");
+ returnType = _inferredReturnType;
+ } else {
+ returnType = _declaredReturnType;
+ }
+ returnType = inferrer.typeSchemaEnvironment.unfutureType(returnType);
+ if (inferrer.library.isNonNullableByDefault &&
+ (containsInvalidType(returnType) ||
+ returnType.isPotentiallyNonNullable) &&
+ inferrer.flowAnalysis.isReachable) {
+ Statement resultStatement =
+ inferenceResult.hasChanged ? inferenceResult.statement : body;
+ // Create a synthetic return statement with the error.
+ Statement returnStatement = new ReturnStatement(inferrer.helper
+ .wrapInProblem(
+ new NullLiteral()..fileOffset = fileOffset,
+ templateImplicitReturnNull.withArguments(
+ returnType, inferrer.library.isNonNullableByDefault),
+ fileOffset,
+ noLength))
+ ..fileOffset = fileOffset;
+ if (resultStatement is Block) {
+ resultStatement.statements.add(returnStatement);
+ } else {
+ resultStatement =
+ new Block(<Statement>[resultStatement, returnStatement])
+ ..fileOffset = fileOffset;
+ }
+ return new StatementInferenceResult.single(resultStatement);
+ }
+ return inferenceResult;
+ }
+}
+
+/// Keeps track of information about the innermost function or closure being
+/// inferred.
+class _SyncStarClosureContext implements ClosureContext {
+ bool get isAsync => false;
+
+ /// The typing expectation for the subexpression of a `return` or `yield`
+ /// statement inside the function.
+ ///
+ /// For non-generator async functions, this will be a "FutureOr" type (since
+ /// it is permissible for such a function to return either a direct value or
+ /// a future).
+ ///
+ /// For generator functions containing a `yield*` statement, the expected type
+ /// for the subexpression of the `yield*` statement is the result of wrapping
+ /// this typing expectation in `Stream` or `Iterator`, as appropriate.
+ final DartType _yieldElementContext;
+
+ @override
+ DartType get returnContext => const UnknownType();
+
+ @override
+ DartType get yieldContext => _yieldElementContext;
+
+ final DartType _declaredReturnType;
+
+ final bool _needToInferReturnType;
+
+ /// A list of return expression types in functions whose return type is
+ /// being inferred.
+ List<DartType> _yieldElementTypes;
+
+ _SyncStarClosureContext(this._yieldElementContext, this._declaredReturnType,
+ this._needToInferReturnType) {
+ if (_needToInferReturnType) {
+ _yieldElementTypes = [];
+ }
+ }
+
+ /// Updates the inferred return type based on the presence of a return
+ /// statement returning the given [type].
+ @override
+ void handleReturn(TypeInferrerImpl inferrer, ReturnStatement statement,
+ DartType type, bool isArrow) {}
+
+ @override
+ void handleYield(TypeInferrerImpl inferrer, YieldStatement node,
+ ExpressionInferenceResult expressionResult) {
+ DartType expectedType = node.isYieldStar
+ ? inferrer.wrapType(_yieldElementContext,
+ inferrer.coreTypes.iterableClass, inferrer.library.nonNullable)
+ : _yieldElementContext;
+ Expression expression = inferrer.ensureAssignableResult(
+ expectedType, expressionResult,
+ fileOffset: node.fileOffset);
+ node.expression = expression..parent = node;
+ DartType type = expressionResult.inferredType;
+ if (!identical(expressionResult.expression, expression)) {
+ type = inferrer.computeGreatestClosure(expectedType);
+ }
+ if (_needToInferReturnType) {
+ DartType elementType = type;
+ if (node.isYieldStar) {
+ elementType = inferrer.getDerivedTypeArgumentOf(
+ type, inferrer.coreTypes.iterableClass) ??
+ elementType;
+ }
+ _yieldElementTypes.add(elementType);
+ }
+ }
+
+ @override
+ DartType inferReturnType(TypeInferrerImpl inferrer,
+ {bool hasImplicitReturn}) {
+ assert(_needToInferReturnType);
+ assert(hasImplicitReturn != null);
+ DartType inferredElementType;
+ if (_yieldElementTypes.isNotEmpty) {
+ // Use the types seen from the explicit return statements.
+ for (int i = 0; i < _yieldElementTypes.length; i++) {
+ DartType type = _yieldElementTypes[i];
+ if (inferredElementType == null) {
+ inferredElementType = type;
+ } else {
+ inferredElementType = inferrer.typeSchemaEnvironment
+ .getStandardUpperBound(
+ inferredElementType, type, inferrer.library.library);
+ }
+ }
+ } else if (hasImplicitReturn) {
+ // No explicit returns we have an implicit `return null`.
+ inferredElementType = inferrer.typeSchemaEnvironment.nullType;
+ } else {
+ // No explicit return and the function doesn't complete normally; that is,
+ // it throws.
+ if (inferrer.isNonNullableByDefault) {
+ inferredElementType = new NeverType(inferrer.library.nonNullable);
+ } else {
+ inferredElementType = inferrer.typeSchemaEnvironment.nullType;
+ }
+ }
+
+ DartType inferredType = inferrer.wrapType(inferredElementType,
+ inferrer.coreTypes.iterableClass, inferrer.library.nonNullable);
+
+ if (!inferrer.typeSchemaEnvironment.isSubtypeOf(inferredType,
+ _yieldElementContext, SubtypeCheckMode.withNullabilities)) {
+ // If the inferred return type isn't a subtype of the context, we use the
+ // context.
+ inferredType = inferrer.computeGreatestClosure2(_declaredReturnType);
+ }
+
+ return demoteTypeInLibrary(inferredType, inferrer.library.library);
+ }
+
+ @override
+ StatementInferenceResult handleImplicitReturn(
+ TypeInferrerImpl inferrer,
+ Statement body,
+ StatementInferenceResult inferenceResult,
+ int fileOffset) {
+ // There is no implicit return.
+ return inferenceResult;
+ }
+}
+
+/// Keeps track of information about the innermost function or closure being
+/// inferred.
+class _AsyncStarClosureContext implements ClosureContext {
+ bool get isAsync => true;
+
+ /// The typing expectation for the subexpression of a `return` or `yield`
+ /// statement inside the function.
+ ///
+ /// For non-generator async functions, this will be a "FutureOr" type (since
+ /// it is permissible for such a function to return either a direct value or
+ /// a future).
+ ///
+ /// For generator functions containing a `yield*` statement, the expected type
+ /// for the subexpression of the `yield*` statement is the result of wrapping
+ /// this typing expectation in `Stream` or `Iterator`, as appropriate.
+ final DartType _yieldElementContext;
+
+ @override
+ DartType get returnContext => const UnknownType();
+
+ @override
+ DartType get yieldContext => _yieldElementContext;
+
+ final DartType _declaredReturnType;
+
+ final bool _needToInferReturnType;
+
+ /// A list of return expression types in functions whose return type is
+ /// being inferred.
+ List<DartType> _yieldElementTypes;
+
+ _AsyncStarClosureContext(this._yieldElementContext, this._declaredReturnType,
+ this._needToInferReturnType) {
+ if (_needToInferReturnType) {
+ _yieldElementTypes = [];
+ }
+ }
+
+ /// Updates the inferred return type based on the presence of a return
+ /// statement returning the given [type].
+ @override
+ void handleReturn(TypeInferrerImpl inferrer, ReturnStatement statement,
+ DartType type, bool isArrow) {}
+
+ @override
+ void handleYield(TypeInferrerImpl inferrer, YieldStatement node,
+ ExpressionInferenceResult expressionResult) {
+ DartType expectedType = node.isYieldStar
+ ? inferrer.wrapType(_yieldElementContext,
+ inferrer.coreTypes.streamClass, inferrer.library.nonNullable)
+ : _yieldElementContext;
+
+ Expression expression = inferrer.ensureAssignableResult(
+ expectedType, expressionResult,
+ fileOffset: node.fileOffset);
+ node.expression = expression..parent = node;
+ DartType type = expressionResult.inferredType;
+ if (!identical(expressionResult.expression, expression)) {
+ type = inferrer.computeGreatestClosure(expectedType);
+ }
+ if (_needToInferReturnType) {
+ DartType elementType = type;
+ if (node.isYieldStar) {
+ elementType = inferrer.getDerivedTypeArgumentOf(
+ type, inferrer.coreTypes.streamClass) ??
+ type;
+ }
+ _yieldElementTypes.add(elementType);
+ }
+ }
+
+ @override
+ DartType inferReturnType(TypeInferrerImpl inferrer,
+ {bool hasImplicitReturn}) {
+ assert(_needToInferReturnType);
+ assert(hasImplicitReturn != null);
+ DartType inferredElementType;
+ if (_yieldElementTypes.isNotEmpty) {
+ // Use the types seen from the explicit return statements.
+ for (DartType elementType in _yieldElementTypes) {
+ if (inferredElementType == null) {
+ inferredElementType = elementType;
+ } else {
+ inferredElementType = inferrer.typeSchemaEnvironment
+ .getStandardUpperBound(
+ inferredElementType, elementType, inferrer.library.library);
+ }
+ }
+ } else if (hasImplicitReturn) {
+ // No explicit returns we have an implicit `return null`.
+ inferredElementType = inferrer.typeSchemaEnvironment.nullType;
+ } else {
+ // No explicit return and the function doesn't complete normally; that is,
+ // it throws.
+ if (inferrer.isNonNullableByDefault) {
+ inferredElementType = new NeverType(inferrer.library.nonNullable);
+ } else {
+ inferredElementType = inferrer.typeSchemaEnvironment.nullType;
+ }
+ }
+
+ DartType inferredType = inferrer.wrapType(inferredElementType,
+ inferrer.coreTypes.streamClass, inferrer.library.nonNullable);
+
+ if (!inferrer.typeSchemaEnvironment.isSubtypeOf(inferredType,
+ _yieldElementContext, SubtypeCheckMode.withNullabilities)) {
+ // If the inferred return type isn't a subtype of the context, we use the
+ // context.
+ inferredType = inferrer.computeGreatestClosure2(_declaredReturnType);
+ }
+
+ return demoteTypeInLibrary(inferredType, inferrer.library.library);
+ }
+
+ @override
+ StatementInferenceResult handleImplicitReturn(
+ TypeInferrerImpl inferrer,
+ Statement body,
+ StatementInferenceResult inferenceResult,
+ int fileOffset) {
+ // There is no implicit return.
+ return inferenceResult;
+ }
+}
diff --git a/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart b/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
index e814390..59fa0b1 100644
--- a/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
+++ b/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
@@ -78,6 +78,8 @@
TypeVariableEliminator,
TypeSchemaEnvironment;
+part 'closure_context.dart';
+
/// Given a [FunctionNode], gets the named parameter identified by [name], or
/// `null` if there is no parameter with the given name.
VariableDeclaration getNamedFormal(FunctionNode function, String name) {
@@ -104,361 +106,6 @@
identical(name, '%');
}
-/// Keeps track of information about the innermost function or closure being
-/// inferred.
-class ClosureContext {
- final bool isAsync;
-
- final bool isGenerator;
-
- /// The typing expectation for the subexpression of a `return` or `yield`
- /// statement inside the function.
- ///
- /// For non-generator async functions, this will be a "FutureOr" type (since
- /// it is permissible for such a function to return either a direct value or
- /// a future).
- ///
- /// For generator functions containing a `yield*` statement, the expected type
- /// for the subexpression of the `yield*` statement is the result of wrapping
- /// this typing expectation in `Stream` or `Iterator`, as appropriate.
- final DartType returnOrYieldContext;
-
- final DartType declaredReturnType;
-
- final bool _needToInferReturnType;
-
- DartType _inferredReturnType;
-
- /// The type that actually appeared as the subexpression of `return` or
- /// `yield` statements inside the function.
- ///
- /// For non-generator async functions, this is the "unwrapped" type (e.g. if
- /// the function is expected to return `Future<int>`, this is `int`).
- ///
- /// For generator functions containing a `yield*` statement, the type that
- /// appeared as the subexpression of the `yield*` statement was the result of
- /// wrapping this type in `Stream` or `Iterator`, as appropriate.
- DartType _inferredUnwrappedReturnOrYieldType;
-
- /// Whether the function is an arrow function.
- bool isArrow;
-
- /// A list of return statements in functions whose return type is being
- /// inferred.
- ///
- /// The returns are checked for validity after the return type is inferred.
- List<ReturnStatement> returnStatements;
-
- /// A list of return expression types in functions whose return type is
- /// being inferred.
- List<DartType> returnExpressionTypes;
-
- factory ClosureContext(TypeInferrerImpl inferrer, AsyncMarker asyncMarker,
- DartType returnContext, bool needToInferReturnType) {
- assert(returnContext != null);
- DartType declaredReturnType =
- inferrer.computeGreatestClosure(returnContext);
- bool isAsync = asyncMarker == AsyncMarker.Async ||
- asyncMarker == AsyncMarker.AsyncStar;
- bool isGenerator = asyncMarker == AsyncMarker.SyncStar ||
- asyncMarker == AsyncMarker.AsyncStar;
- if (isGenerator) {
- if (isAsync) {
- returnContext = inferrer.getTypeArgumentOf(
- returnContext, inferrer.coreTypes.streamClass);
- } else {
- returnContext = inferrer.getTypeArgumentOf(
- returnContext, inferrer.coreTypes.iterableClass);
- }
- } else if (isAsync) {
- returnContext = inferrer.wrapFutureOrType(
- inferrer.typeSchemaEnvironment.unfutureType(returnContext));
- }
- return new ClosureContext._(isAsync, isGenerator, returnContext,
- declaredReturnType, needToInferReturnType);
- }
-
- ClosureContext._(this.isAsync, this.isGenerator, this.returnOrYieldContext,
- this.declaredReturnType, this._needToInferReturnType) {
- if (_needToInferReturnType) {
- returnStatements = [];
- returnExpressionTypes = [];
- }
- }
-
- bool checkValidReturn(TypeInferrerImpl inferrer, DartType returnType,
- ReturnStatement statement, DartType expressionType) {
- // The rules for valid returns for functions with return type T and possibly
- // a return expression with static type S.
- DartType flattenedReturnType = isAsync
- ? inferrer.typeSchemaEnvironment.unfutureType(returnType)
- : returnType;
- if (statement.expression == null) {
- // Sync: return; is a valid return if T is void, dynamic, or Null.
- // Async: return; is a valid return if flatten(T) is void, dynamic, or
- // Null.
- if (flattenedReturnType is VoidType ||
- flattenedReturnType is DynamicType ||
- flattenedReturnType == inferrer.coreTypes.nullType) {
- return true;
- }
- statement.expression = inferrer.helper.wrapInProblem(
- new NullLiteral()..fileOffset = statement.fileOffset,
- messageReturnWithoutExpression,
- statement.fileOffset,
- noLength)
- ..parent = statement;
- return false;
- }
-
- // Arrow functions are valid if:
- // Sync: T is void or return exp; is a valid for a block-bodied function.
- // Async: flatten(T) is void or return exp; is valid for a block-bodied
- // function.
- if (isArrow && flattenedReturnType is VoidType) return true;
-
- // Sync: invalid if T is void and S is not void, dynamic, or Null
- // Async: invalid if T is void and flatten(S) is not void, dynamic, or Null.
- DartType flattenedExpressionType = isAsync
- ? inferrer.typeSchemaEnvironment.unfutureType(expressionType)
- : expressionType;
- if (returnType is VoidType &&
- flattenedExpressionType is! VoidType &&
- flattenedExpressionType is! DynamicType &&
- flattenedExpressionType != inferrer.coreTypes.nullType) {
- statement.expression = inferrer.helper.wrapInProblem(
- statement.expression,
- messageReturnFromVoidFunction,
- statement.expression.fileOffset,
- noLength)
- ..parent = statement;
- return false;
- }
-
- // Sync: invalid if S is void and T is not void, dynamic, or Null.
- // Async: invalid if flatten(S) is void and flatten(T) is not void, dynamic,
- // or Null.
- if (flattenedExpressionType is VoidType &&
- flattenedReturnType is! VoidType &&
- flattenedReturnType is! DynamicType &&
- flattenedReturnType != inferrer.coreTypes.nullType) {
- statement.expression = inferrer.helper.wrapInProblem(statement.expression,
- messageVoidExpression, statement.expression.fileOffset, noLength)
- ..parent = statement;
- return false;
- }
-
- // The caller will check that the return expression is assignable to the
- // return type.
- return true;
- }
-
- void _updateInferredUnwrappedReturnOrYieldType(
- TypeInferrerImpl inferrer, DartType unwrappedType) {
- if (_inferredUnwrappedReturnOrYieldType == null) {
- _inferredUnwrappedReturnOrYieldType = unwrappedType;
- } else {
- _inferredUnwrappedReturnOrYieldType = inferrer.typeSchemaEnvironment
- .getStandardUpperBound(_inferredUnwrappedReturnOrYieldType,
- unwrappedType, inferrer.library.library);
- }
- }
-
- /// Updates the inferred return type based on the presence of a return
- /// statement returning the given [type].
- void handleReturn(TypeInferrerImpl inferrer, ReturnStatement statement,
- DartType type, bool isArrow) {
- if (isGenerator) return;
- // The first return we see tells us if we have an arrow function.
- if (this.isArrow == null) {
- this.isArrow = isArrow;
- } else {
- assert(this.isArrow == isArrow);
- }
-
- if (_needToInferReturnType) {
- // Add the return to a list to be checked for validity after we've
- // inferred the return type.
- returnStatements.add(statement);
- returnExpressionTypes.add(type);
-
- // The return expression has to be assignable to the return type
- // expectation from the downwards inference context.
- if (statement.expression != null) {
- Expression expression = inferrer.ensureAssignable(
- returnOrYieldContext, type, statement.expression,
- fileOffset: statement.fileOffset,
- isReturnFromAsync: isAsync,
- isVoidAllowed: true);
- if (!identical(statement.expression, expression)) {
- statement.expression = expression..parent = statement;
- // Not assignable, use the expectation.
- type = inferrer.computeGreatestClosure(returnOrYieldContext);
- }
- }
- DartType unwrappedType = type;
- if (isAsync) {
- unwrappedType = inferrer.typeSchemaEnvironment.unfutureType(type);
- }
- _updateInferredUnwrappedReturnOrYieldType(inferrer, unwrappedType);
- } else if (checkValidReturn(
- inferrer, declaredReturnType, statement, type) &&
- statement.expression != null) {
- // If we are not inferring a type we can immediately check that the return
- // is valid.
- DartType wrappedType = type;
- if (isAsync) {
- wrappedType = inferrer.typeSchemaEnvironment.futureType(
- inferrer.typeSchemaEnvironment.unfutureType(type),
- Nullability.nonNullable);
- }
- Expression expression = inferrer.ensureAssignable(
- returnOrYieldContext, wrappedType, statement.expression,
- fileOffset: statement.fileOffset,
- isReturnFromAsync: isAsync,
- isVoidAllowed: true);
- statement.expression = expression..parent = statement;
- }
- }
-
- void handleYield(TypeInferrerImpl inferrer, YieldStatement node,
- ExpressionInferenceResult expressionResult) {
- if (!isGenerator) {
- node.expression = expressionResult.expression..parent = node;
- return;
- }
- DartType expectedType = node.isYieldStar
- ? _wrapAsyncOrGenerator(
- inferrer, returnOrYieldContext, inferrer.library.nonNullable)
- : returnOrYieldContext;
- Expression expression = inferrer.ensureAssignableResult(
- expectedType, expressionResult,
- fileOffset: node.fileOffset, isReturnFromAsync: isAsync);
- node.expression = expression..parent = node;
- DartType type = expressionResult.inferredType;
- if (!identical(expressionResult.expression, expression)) {
- type = inferrer.computeGreatestClosure(expectedType);
- }
- if (_needToInferReturnType) {
- DartType unwrappedType = type;
- if (node.isYieldStar) {
- unwrappedType = inferrer.getDerivedTypeArgumentOf(
- type,
- isAsync
- ? inferrer.coreTypes.streamClass
- : inferrer.coreTypes.iterableClass) ??
- type;
- }
- _updateInferredUnwrappedReturnOrYieldType(inferrer, unwrappedType);
- }
- }
-
- DartType inferReturnType(TypeInferrerImpl inferrer,
- {bool hasImplicitReturn}) {
- assert(_needToInferReturnType);
- assert(hasImplicitReturn != null);
- DartType inferredType;
- if (_inferredUnwrappedReturnOrYieldType != null) {
- // Use the types seen from the explicit return statements.
- inferredType = _inferredUnwrappedReturnOrYieldType;
- } else if (hasImplicitReturn) {
- // No explicit returns we have an implicit `return null`.
- inferredType = inferrer.typeSchemaEnvironment.nullType;
- } else {
- // No explicit return and the function doesn't complete normally; that is,
- // it throws.
- if (inferrer.isNonNullableByDefault) {
- inferredType = new NeverType(inferrer.library.nonNullable);
- } else {
- inferredType = inferrer.typeSchemaEnvironment.nullType;
- }
- }
-
- inferredType = _wrapAsyncOrGenerator(
- inferrer, inferredType, inferrer.library.nonNullable);
-
- if (!inferrer.typeSchemaEnvironment.isSubtypeOf(inferredType,
- returnOrYieldContext, SubtypeCheckMode.withNullabilities)) {
- // If the inferred return type isn't a subtype of the context, we use the
- // context.
- inferredType = inferrer.computeGreatestClosure2(declaredReturnType);
- }
-
- for (int i = 0; i < returnStatements.length; ++i) {
- checkValidReturn(inferrer, inferredType, returnStatements[i],
- returnExpressionTypes[i]);
- }
-
- return _inferredReturnType =
- demoteTypeInLibrary(inferredType, inferrer.library.library);
- }
-
- StatementInferenceResult handleImplicitReturn(
- TypeInferrerImpl inferrer,
- Statement body,
- StatementInferenceResult inferenceResult,
- int fileOffset) {
- if (isGenerator) {
- // There is no implicit return.
- return inferenceResult;
- }
-
- DartType returnType;
- if (_needToInferReturnType) {
- assert(_inferredReturnType != null,
- "Return type has not yet been inferred.");
- returnType = _inferredReturnType;
- } else {
- returnType = declaredReturnType;
- }
- if (isAsync) {
- returnType = inferrer.typeSchemaEnvironment.unfutureType(returnType);
- }
- if (inferrer.library.isNonNullableByDefault &&
- (containsInvalidType(returnType) ||
- returnType.isPotentiallyNonNullable) &&
- inferrer.flowAnalysis.isReachable) {
- Statement resultStatement =
- inferenceResult.hasChanged ? inferenceResult.statement : body;
- // Create a synthetic return statement with the error.
- Statement returnStatement = new ReturnStatement(inferrer.helper
- .wrapInProblem(
- new NullLiteral()..fileOffset = fileOffset,
- templateImplicitReturnNull.withArguments(
- returnType, inferrer.library.isNonNullableByDefault),
- fileOffset,
- noLength))
- ..fileOffset = fileOffset;
- if (resultStatement is Block) {
- resultStatement.statements.add(returnStatement);
- } else {
- resultStatement =
- new Block(<Statement>[resultStatement, returnStatement])
- ..fileOffset = fileOffset;
- }
- return new StatementInferenceResult.single(resultStatement);
- }
- return inferenceResult;
- }
-
- DartType _wrapAsyncOrGenerator(
- TypeInferrerImpl inferrer, DartType type, Nullability nullability) {
- if (isGenerator) {
- if (isAsync) {
- return inferrer.wrapType(
- type, inferrer.coreTypes.streamClass, nullability);
- } else {
- return inferrer.wrapType(
- type, inferrer.coreTypes.iterableClass, nullability);
- }
- } else if (isAsync) {
- return inferrer.wrapFutureType(type, nullability);
- } else {
- return type;
- }
- }
-}
-
/// Enum denoting the kinds of contravariance check that might need to be
/// inserted for a method call.
enum MethodContravarianceCheckKind {
@@ -731,13 +378,11 @@
DartType expectedType, ExpressionInferenceResult result,
{int fileOffset,
bool isVoidAllowed: false,
- bool isReturnFromAsync: false,
Template<Message Function(DartType, DartType, bool)> errorTemplate}) {
return ensureAssignable(
expectedType, result.inferredType, result.expression,
fileOffset: fileOffset,
isVoidAllowed: isVoidAllowed,
- isReturnFromAsync: isReturnFromAsync,
errorTemplate: errorTemplate);
}
@@ -746,10 +391,16 @@
/// Checks whether [expressionType] can be assigned to the greatest closure of
/// [contextType], and inserts an implicit downcast, inserts a tear-off, or
/// reports an error if appropriate.
+ ///
+ /// If [runtimeCheckedType] is provided, this is used for the implicit cast,
+ /// otherwise [contextType] is used. This is used for return from async
+ /// where the returned expression is wrapped in a `Future`, if necessary,
+ /// before returned and therefore shouldn't be checked to be a `Future`
+ /// directly.
Expression ensureAssignable(
DartType contextType, DartType expressionType, Expression expression,
{int fileOffset,
- bool isReturnFromAsync: false,
+ DartType runtimeCheckedType,
bool isVoidAllowed: false,
Template<Message Function(DartType, DartType, bool)> errorTemplate}) {
assert(contextType != null);
@@ -762,22 +413,7 @@
fileOffset ??= expression.fileOffset;
contextType = computeGreatestClosure(contextType);
- DartType initialContextType = contextType;
- if (isReturnFromAsync &&
- !isAssignable(initialContextType, expressionType)) {
- // If the body of the function is async, the expected return type has the
- // shape FutureOr<T>. We check both branches for FutureOr here: both T
- // and Future<T>.
- DartType unfuturedExpectedType =
- typeSchemaEnvironment.unfutureType(contextType);
- DartType futuredExpectedType =
- wrapFutureType(unfuturedExpectedType, library.nonNullable);
- if (isAssignable(unfuturedExpectedType, expressionType)) {
- contextType = unfuturedExpectedType;
- } else if (isAssignable(futuredExpectedType, expressionType)) {
- contextType = futuredExpectedType;
- }
- }
+ DartType initialContextType = runtimeCheckedType ?? contextType;
Template<Message Function(DartType, DartType, bool)>
preciseTypeErrorTemplate = _getPreciseTypeErrorTemplate(expression);
diff --git a/pkg/front_end/pubspec.yaml b/pkg/front_end/pubspec.yaml
index dd1b0c5..73e86ee 100644
--- a/pkg/front_end/pubspec.yaml
+++ b/pkg/front_end/pubspec.yaml
@@ -1,10 +1,10 @@
-name: front_end
# Currently, front_end API is not stable and users should not
# depend on semver semantics when depending on this package.
-version: 0.1.29
-author: Dart Team <misc@dartlang.org>
-description: Front end for compilation of Dart code.
-homepage: https://github.com/dart-lang/sdk/tree/master/pkg/front_end
+name: front_end
+
+# This package is not intended for consumption on pub.dev. DO NOT publish.
+publish_to: none
+
environment:
sdk: '>=2.2.2 <3.0.0'
dependencies:
diff --git a/pkg/front_end/test/spell_checking_list_common.txt b/pkg/front_end/test/spell_checking_list_common.txt
index 2e12861..4e0e201 100644
--- a/pkg/front_end/test/spell_checking_list_common.txt
+++ b/pkg/front_end/test/spell_checking_list_common.txt
@@ -686,6 +686,7 @@
creates
creating
creation
+creations
crossed
curly
current
@@ -1077,6 +1078,7 @@
experiments
expired
explanation
+explain
explicit
explicitly
exponent
@@ -2901,6 +2903,7 @@
targeting
targets
task
+tasks
tear
tearing
technically
diff --git a/pkg/front_end/test/spell_checking_list_tests.txt b/pkg/front_end/test/spell_checking_list_tests.txt
index 1df02b2..8af47b7 100644
--- a/pkg/front_end/test/spell_checking_list_tests.txt
+++ b/pkg/front_end/test/spell_checking_list_tests.txt
@@ -21,6 +21,8 @@
affected
albeit
allocations
+allowlist
+allowlisting
alt
amortized
analyses
@@ -54,7 +56,6 @@
besides
beta
bigger
-denylist
blah
blindly
blocked
@@ -131,6 +132,7 @@
decrements
def
deleting
+denylist
depended
depfile
desc
@@ -233,6 +235,7 @@
forces
foreign
forrest
+foundation
fox
fulfill
func
@@ -472,6 +475,7 @@
runtimes
say
scans
+scheduler
screen
scripts
sdkroot
@@ -479,6 +483,7 @@
secondary
segment
sensitive
+services
severe
sheets
shipped
@@ -577,8 +582,6 @@
walt
warmup
wherever
-allowlist
-allowlisting
wins
workflow
worlds
diff --git a/pkg/front_end/testcases/general/future_return.dart b/pkg/front_end/testcases/general/future_return.dart
new file mode 100644
index 0000000..aae7105
--- /dev/null
+++ b/pkg/front_end/testcases/general/future_return.dart
@@ -0,0 +1,51 @@
+// 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';
+
+class Class {}
+
+dynamic returnDynamic() => new Class();
+
+Class returnClass() async => new Class();
+
+Future<Class> returnFutureClass() async => new Class();
+
+FutureOr<Class> returnFutureOrClass() async => new Class();
+
+Class returnClassFromDynamic() async => returnDynamic();
+
+Future<Class> returnFutureClassDynamic() async => returnDynamic();
+
+FutureOr<Class> returnFutureOrClassDynamic() async => returnDynamic();
+
+Class returnClassFromFutureClass() async => returnFutureClass();
+
+Future<Class> returnFutureClassFromFutureClass() async => returnFutureClass();
+
+FutureOr<Class> returnFutureOrClassFromFutureClass() async =>
+ returnFutureClass();
+
+Class returnClassFromFutureOrClass() async => returnFutureOrClass();
+
+Future<Class> returnFutureClassFromFutureOrClass() async =>
+ returnFutureOrClass();
+
+FutureOr<Class> returnFutureOrClassFromFutureOrClass() async =>
+ returnFutureOrClass();
+
+main() async {
+ await returnClass();
+ await returnFutureClass();
+ await returnFutureOrClass();
+ await returnClassFromDynamic();
+ await returnFutureClassDynamic();
+ await returnFutureOrClassDynamic();
+ await returnClassFromFutureClass();
+ await returnFutureClassFromFutureClass();
+ await returnFutureOrClassFromFutureClass();
+ await returnClassFromFutureOrClass();
+ await returnFutureClassFromFutureOrClass();
+ await returnFutureOrClassFromFutureOrClass();
+}
diff --git a/pkg/front_end/testcases/general/future_return.dart.outline.expect b/pkg/front_end/testcases/general/future_return.dart.outline.expect
new file mode 100644
index 0000000..7fed171
--- /dev/null
+++ b/pkg/front_end/testcases/general/future_return.dart.outline.expect
@@ -0,0 +1,49 @@
+library;
+import self as self;
+import "dart:core" as core;
+import "dart:async" as asy;
+
+import "dart:async";
+
+class Class extends core::Object {
+ synthetic constructor •() → self::Class*
+ ;
+ abstract member-signature get _identityHashCode() → core::int*;
+ abstract member-signature method _instanceOf(dynamic instantiatorTypeArguments, dynamic functionTypeArguments, dynamic type) → core::bool*;
+ abstract member-signature method _simpleInstanceOf(dynamic type) → core::bool*;
+ abstract member-signature method _simpleInstanceOfTrue(dynamic type) → core::bool*;
+ abstract member-signature method _simpleInstanceOfFalse(dynamic type) → core::bool*;
+ abstract member-signature operator ==(dynamic other) → core::bool*;
+ abstract member-signature get hashCode() → core::int*;
+ abstract member-signature method toString() → core::String*;
+ abstract member-signature method noSuchMethod(core::Invocation* invocation) → dynamic;
+ abstract member-signature get runtimeType() → core::Type*;
+}
+static method returnDynamic() → dynamic
+ ;
+static method returnClass() → self::Class*
+ ;
+static method returnFutureClass() → asy::Future<self::Class*>*
+ ;
+static method returnFutureOrClass() → FutureOr<self::Class*>*
+ ;
+static method returnClassFromDynamic() → self::Class*
+ ;
+static method returnFutureClassDynamic() → asy::Future<self::Class*>*
+ ;
+static method returnFutureOrClassDynamic() → FutureOr<self::Class*>*
+ ;
+static method returnClassFromFutureClass() → self::Class*
+ ;
+static method returnFutureClassFromFutureClass() → asy::Future<self::Class*>*
+ ;
+static method returnFutureOrClassFromFutureClass() → FutureOr<self::Class*>*
+ ;
+static method returnClassFromFutureOrClass() → self::Class*
+ ;
+static method returnFutureClassFromFutureOrClass() → asy::Future<self::Class*>*
+ ;
+static method returnFutureOrClassFromFutureOrClass() → FutureOr<self::Class*>*
+ ;
+static method main() → dynamic
+ ;
diff --git a/pkg/front_end/testcases/general/future_return.dart.strong.expect b/pkg/front_end/testcases/general/future_return.dart.strong.expect
new file mode 100644
index 0000000..7cec040
--- /dev/null
+++ b/pkg/front_end/testcases/general/future_return.dart.strong.expect
@@ -0,0 +1,81 @@
+library;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/general/future_return.dart:11:7: Error: Functions marked 'async' must have a return type assignable to 'Future'.
+// Class returnClass() async => new Class();
+// ^^^^^^^^^^^
+//
+// pkg/front_end/testcases/general/future_return.dart:17:7: Error: Functions marked 'async' must have a return type assignable to 'Future'.
+// Class returnClassFromDynamic() async => returnDynamic();
+// ^^^^^^^^^^^^^^^^^^^^^^
+//
+// pkg/front_end/testcases/general/future_return.dart:23:7: Error: Functions marked 'async' must have a return type assignable to 'Future'.
+// Class returnClassFromFutureClass() async => returnFutureClass();
+// ^^^^^^^^^^^^^^^^^^^^^^^^^^
+//
+// pkg/front_end/testcases/general/future_return.dart:30:7: Error: Functions marked 'async' must have a return type assignable to 'Future'.
+// Class returnClassFromFutureOrClass() async => returnFutureOrClass();
+// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+//
+import self as self;
+import "dart:core" as core;
+import "dart:async" as asy;
+
+import "dart:async";
+
+class Class extends core::Object {
+ synthetic constructor •() → self::Class*
+ : super core::Object::•()
+ ;
+ abstract member-signature get _identityHashCode() → core::int*;
+ abstract member-signature method _instanceOf(dynamic instantiatorTypeArguments, dynamic functionTypeArguments, dynamic type) → core::bool*;
+ abstract member-signature method _simpleInstanceOf(dynamic type) → core::bool*;
+ abstract member-signature method _simpleInstanceOfTrue(dynamic type) → core::bool*;
+ abstract member-signature method _simpleInstanceOfFalse(dynamic type) → core::bool*;
+ abstract member-signature operator ==(dynamic other) → core::bool*;
+ abstract member-signature get hashCode() → core::int*;
+ abstract member-signature method toString() → core::String*;
+ abstract member-signature method noSuchMethod(core::Invocation* invocation) → dynamic;
+ abstract member-signature get runtimeType() → core::Type*;
+}
+static method returnDynamic() → dynamic
+ return new self::Class::•();
+static method returnClass() → self::Class* async
+ return new self::Class::•();
+static method returnFutureClass() → asy::Future<self::Class*>* async
+ return new self::Class::•();
+static method returnFutureOrClass() → FutureOr<self::Class*>* async
+ return new self::Class::•();
+static method returnClassFromDynamic() → self::Class* async
+ return self::returnDynamic() as{TypeError} FutureOr<self::Class*>*;
+static method returnFutureClassDynamic() → asy::Future<self::Class*>* async
+ return self::returnDynamic() as{TypeError} FutureOr<self::Class*>*;
+static method returnFutureOrClassDynamic() → FutureOr<self::Class*>* async
+ return self::returnDynamic() as{TypeError} FutureOr<self::Class*>*;
+static method returnClassFromFutureClass() → self::Class* async
+ return self::returnFutureClass();
+static method returnFutureClassFromFutureClass() → asy::Future<self::Class*>* async
+ return self::returnFutureClass();
+static method returnFutureOrClassFromFutureClass() → FutureOr<self::Class*>* async
+ return self::returnFutureClass();
+static method returnClassFromFutureOrClass() → self::Class* async
+ return self::returnFutureOrClass();
+static method returnFutureClassFromFutureOrClass() → asy::Future<self::Class*>* async
+ return self::returnFutureOrClass();
+static method returnFutureOrClassFromFutureOrClass() → FutureOr<self::Class*>* async
+ return self::returnFutureOrClass();
+static method main() → dynamic async {
+ await self::returnClass();
+ await self::returnFutureClass();
+ await self::returnFutureOrClass();
+ await self::returnClassFromDynamic();
+ await self::returnFutureClassDynamic();
+ await self::returnFutureOrClassDynamic();
+ await self::returnClassFromFutureClass();
+ await self::returnFutureClassFromFutureClass();
+ await self::returnFutureOrClassFromFutureClass();
+ await self::returnClassFromFutureOrClass();
+ await self::returnFutureClassFromFutureOrClass();
+ await self::returnFutureOrClassFromFutureOrClass();
+}
diff --git a/pkg/front_end/testcases/general/future_return.dart.strong.transformed.expect b/pkg/front_end/testcases/general/future_return.dart.strong.transformed.expect
new file mode 100644
index 0000000..0bb1231
--- /dev/null
+++ b/pkg/front_end/testcases/general/future_return.dart.strong.transformed.expect
@@ -0,0 +1,418 @@
+library;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/general/future_return.dart:11:7: Error: Functions marked 'async' must have a return type assignable to 'Future'.
+// Class returnClass() async => new Class();
+// ^^^^^^^^^^^
+//
+// pkg/front_end/testcases/general/future_return.dart:17:7: Error: Functions marked 'async' must have a return type assignable to 'Future'.
+// Class returnClassFromDynamic() async => returnDynamic();
+// ^^^^^^^^^^^^^^^^^^^^^^
+//
+// pkg/front_end/testcases/general/future_return.dart:23:7: Error: Functions marked 'async' must have a return type assignable to 'Future'.
+// Class returnClassFromFutureClass() async => returnFutureClass();
+// ^^^^^^^^^^^^^^^^^^^^^^^^^^
+//
+// pkg/front_end/testcases/general/future_return.dart:30:7: Error: Functions marked 'async' must have a return type assignable to 'Future'.
+// Class returnClassFromFutureOrClass() async => returnFutureOrClass();
+// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+//
+import self as self;
+import "dart:core" as core;
+import "dart:async" as asy;
+import "dart:_internal" as _in;
+
+import "dart:async";
+
+class Class extends core::Object {
+ synthetic constructor •() → self::Class*
+ : super core::Object::•()
+ ;
+ abstract member-signature get _identityHashCode() → core::int*;
+ abstract member-signature method _instanceOf(dynamic instantiatorTypeArguments, dynamic functionTypeArguments, dynamic type) → core::bool*;
+ abstract member-signature method _simpleInstanceOf(dynamic type) → core::bool*;
+ abstract member-signature method _simpleInstanceOfTrue(dynamic type) → core::bool*;
+ abstract member-signature method _simpleInstanceOfFalse(dynamic type) → core::bool*;
+ abstract member-signature operator ==(dynamic other) → core::bool*;
+ abstract member-signature get hashCode() → core::int*;
+ abstract member-signature method toString() → core::String*;
+ abstract member-signature method noSuchMethod(core::Invocation* invocation) → dynamic;
+ abstract member-signature get runtimeType() → core::Type*;
+}
+static method returnDynamic() → dynamic
+ return new self::Class::•();
+static method returnClass() → self::Class* /* originally async */ {
+ final asy::_AsyncAwaitCompleter<dynamic>* :async_completer = new asy::_AsyncAwaitCompleter::•<dynamic>();
+ FutureOr<dynamic>* :return_value;
+ dynamic :async_stack_trace;
+ (dynamic) →* dynamic :async_op_then;
+ (core::Object*, core::StackTrace*) →* dynamic :async_op_error;
+ core::int* :await_jump_var = 0;
+ dynamic :await_ctx_var;
+ function :async_op([dynamic :result, dynamic :exception, dynamic :stack_trace]) → dynamic yielding
+ try {
+ #L1:
+ {
+ :return_value = new self::Class::•();
+ break #L1;
+ }
+ asy::_completeOnAsyncReturn(:async_completer, :return_value);
+ return;
+ }
+ on dynamic catch(dynamic exception, core::StackTrace* stack_trace) {
+ :async_completer.{asy::Completer::completeError}(exception, stack_trace);
+ }
+ :async_stack_trace = asy::_asyncStackTraceHelper(:async_op);
+ :async_op_then = asy::_asyncThenWrapperHelper(:async_op);
+ :async_op_error = asy::_asyncErrorWrapperHelper(:async_op);
+ :async_completer.{asy::_AsyncAwaitCompleter::start}(:async_op);
+ return :async_completer.{asy::Completer::future};
+}
+static method returnFutureClass() → asy::Future<self::Class*>* /* originally async */ {
+ final asy::_AsyncAwaitCompleter<self::Class*>* :async_completer = new asy::_AsyncAwaitCompleter::•<self::Class*>();
+ FutureOr<self::Class*>* :return_value;
+ dynamic :async_stack_trace;
+ (dynamic) →* dynamic :async_op_then;
+ (core::Object*, core::StackTrace*) →* dynamic :async_op_error;
+ core::int* :await_jump_var = 0;
+ dynamic :await_ctx_var;
+ function :async_op([dynamic :result, dynamic :exception, dynamic :stack_trace]) → dynamic yielding
+ try {
+ #L2:
+ {
+ :return_value = new self::Class::•();
+ break #L2;
+ }
+ asy::_completeOnAsyncReturn(:async_completer, :return_value);
+ return;
+ }
+ on dynamic catch(dynamic exception, core::StackTrace* stack_trace) {
+ :async_completer.{asy::Completer::completeError}(exception, stack_trace);
+ }
+ :async_stack_trace = asy::_asyncStackTraceHelper(:async_op);
+ :async_op_then = asy::_asyncThenWrapperHelper(:async_op);
+ :async_op_error = asy::_asyncErrorWrapperHelper(:async_op);
+ :async_completer.{asy::_AsyncAwaitCompleter::start}(:async_op);
+ return :async_completer.{asy::Completer::future};
+}
+static method returnFutureOrClass() → FutureOr<self::Class*>* /* originally async */ {
+ final asy::_AsyncAwaitCompleter<self::Class*>* :async_completer = new asy::_AsyncAwaitCompleter::•<self::Class*>();
+ FutureOr<self::Class*>* :return_value;
+ dynamic :async_stack_trace;
+ (dynamic) →* dynamic :async_op_then;
+ (core::Object*, core::StackTrace*) →* dynamic :async_op_error;
+ core::int* :await_jump_var = 0;
+ dynamic :await_ctx_var;
+ function :async_op([dynamic :result, dynamic :exception, dynamic :stack_trace]) → dynamic yielding
+ try {
+ #L3:
+ {
+ :return_value = new self::Class::•();
+ break #L3;
+ }
+ asy::_completeOnAsyncReturn(:async_completer, :return_value);
+ return;
+ }
+ on dynamic catch(dynamic exception, core::StackTrace* stack_trace) {
+ :async_completer.{asy::Completer::completeError}(exception, stack_trace);
+ }
+ :async_stack_trace = asy::_asyncStackTraceHelper(:async_op);
+ :async_op_then = asy::_asyncThenWrapperHelper(:async_op);
+ :async_op_error = asy::_asyncErrorWrapperHelper(:async_op);
+ :async_completer.{asy::_AsyncAwaitCompleter::start}(:async_op);
+ return :async_completer.{asy::Completer::future};
+}
+static method returnClassFromDynamic() → self::Class* /* originally async */ {
+ final asy::_AsyncAwaitCompleter<dynamic>* :async_completer = new asy::_AsyncAwaitCompleter::•<dynamic>();
+ FutureOr<dynamic>* :return_value;
+ dynamic :async_stack_trace;
+ (dynamic) →* dynamic :async_op_then;
+ (core::Object*, core::StackTrace*) →* dynamic :async_op_error;
+ core::int* :await_jump_var = 0;
+ dynamic :await_ctx_var;
+ function :async_op([dynamic :result, dynamic :exception, dynamic :stack_trace]) → dynamic yielding
+ try {
+ #L4:
+ {
+ :return_value = self::returnDynamic() as{TypeError} FutureOr<self::Class*>*;
+ break #L4;
+ }
+ asy::_completeOnAsyncReturn(:async_completer, :return_value);
+ return;
+ }
+ on dynamic catch(dynamic exception, core::StackTrace* stack_trace) {
+ :async_completer.{asy::Completer::completeError}(exception, stack_trace);
+ }
+ :async_stack_trace = asy::_asyncStackTraceHelper(:async_op);
+ :async_op_then = asy::_asyncThenWrapperHelper(:async_op);
+ :async_op_error = asy::_asyncErrorWrapperHelper(:async_op);
+ :async_completer.{asy::_AsyncAwaitCompleter::start}(:async_op);
+ return :async_completer.{asy::Completer::future};
+}
+static method returnFutureClassDynamic() → asy::Future<self::Class*>* /* originally async */ {
+ final asy::_AsyncAwaitCompleter<self::Class*>* :async_completer = new asy::_AsyncAwaitCompleter::•<self::Class*>();
+ FutureOr<self::Class*>* :return_value;
+ dynamic :async_stack_trace;
+ (dynamic) →* dynamic :async_op_then;
+ (core::Object*, core::StackTrace*) →* dynamic :async_op_error;
+ core::int* :await_jump_var = 0;
+ dynamic :await_ctx_var;
+ function :async_op([dynamic :result, dynamic :exception, dynamic :stack_trace]) → dynamic yielding
+ try {
+ #L5:
+ {
+ :return_value = self::returnDynamic() as{TypeError} FutureOr<self::Class*>*;
+ break #L5;
+ }
+ asy::_completeOnAsyncReturn(:async_completer, :return_value);
+ return;
+ }
+ on dynamic catch(dynamic exception, core::StackTrace* stack_trace) {
+ :async_completer.{asy::Completer::completeError}(exception, stack_trace);
+ }
+ :async_stack_trace = asy::_asyncStackTraceHelper(:async_op);
+ :async_op_then = asy::_asyncThenWrapperHelper(:async_op);
+ :async_op_error = asy::_asyncErrorWrapperHelper(:async_op);
+ :async_completer.{asy::_AsyncAwaitCompleter::start}(:async_op);
+ return :async_completer.{asy::Completer::future};
+}
+static method returnFutureOrClassDynamic() → FutureOr<self::Class*>* /* originally async */ {
+ final asy::_AsyncAwaitCompleter<self::Class*>* :async_completer = new asy::_AsyncAwaitCompleter::•<self::Class*>();
+ FutureOr<self::Class*>* :return_value;
+ dynamic :async_stack_trace;
+ (dynamic) →* dynamic :async_op_then;
+ (core::Object*, core::StackTrace*) →* dynamic :async_op_error;
+ core::int* :await_jump_var = 0;
+ dynamic :await_ctx_var;
+ function :async_op([dynamic :result, dynamic :exception, dynamic :stack_trace]) → dynamic yielding
+ try {
+ #L6:
+ {
+ :return_value = self::returnDynamic() as{TypeError} FutureOr<self::Class*>*;
+ break #L6;
+ }
+ asy::_completeOnAsyncReturn(:async_completer, :return_value);
+ return;
+ }
+ on dynamic catch(dynamic exception, core::StackTrace* stack_trace) {
+ :async_completer.{asy::Completer::completeError}(exception, stack_trace);
+ }
+ :async_stack_trace = asy::_asyncStackTraceHelper(:async_op);
+ :async_op_then = asy::_asyncThenWrapperHelper(:async_op);
+ :async_op_error = asy::_asyncErrorWrapperHelper(:async_op);
+ :async_completer.{asy::_AsyncAwaitCompleter::start}(:async_op);
+ return :async_completer.{asy::Completer::future};
+}
+static method returnClassFromFutureClass() → self::Class* /* originally async */ {
+ final asy::_AsyncAwaitCompleter<dynamic>* :async_completer = new asy::_AsyncAwaitCompleter::•<dynamic>();
+ FutureOr<dynamic>* :return_value;
+ dynamic :async_stack_trace;
+ (dynamic) →* dynamic :async_op_then;
+ (core::Object*, core::StackTrace*) →* dynamic :async_op_error;
+ core::int* :await_jump_var = 0;
+ dynamic :await_ctx_var;
+ function :async_op([dynamic :result, dynamic :exception, dynamic :stack_trace]) → dynamic yielding
+ try {
+ #L7:
+ {
+ :return_value = self::returnFutureClass();
+ break #L7;
+ }
+ asy::_completeOnAsyncReturn(:async_completer, :return_value);
+ return;
+ }
+ on dynamic catch(dynamic exception, core::StackTrace* stack_trace) {
+ :async_completer.{asy::Completer::completeError}(exception, stack_trace);
+ }
+ :async_stack_trace = asy::_asyncStackTraceHelper(:async_op);
+ :async_op_then = asy::_asyncThenWrapperHelper(:async_op);
+ :async_op_error = asy::_asyncErrorWrapperHelper(:async_op);
+ :async_completer.{asy::_AsyncAwaitCompleter::start}(:async_op);
+ return :async_completer.{asy::Completer::future};
+}
+static method returnFutureClassFromFutureClass() → asy::Future<self::Class*>* /* originally async */ {
+ final asy::_AsyncAwaitCompleter<self::Class*>* :async_completer = new asy::_AsyncAwaitCompleter::•<self::Class*>();
+ FutureOr<self::Class*>* :return_value;
+ dynamic :async_stack_trace;
+ (dynamic) →* dynamic :async_op_then;
+ (core::Object*, core::StackTrace*) →* dynamic :async_op_error;
+ core::int* :await_jump_var = 0;
+ dynamic :await_ctx_var;
+ function :async_op([dynamic :result, dynamic :exception, dynamic :stack_trace]) → dynamic yielding
+ try {
+ #L8:
+ {
+ :return_value = self::returnFutureClass();
+ break #L8;
+ }
+ asy::_completeOnAsyncReturn(:async_completer, :return_value);
+ return;
+ }
+ on dynamic catch(dynamic exception, core::StackTrace* stack_trace) {
+ :async_completer.{asy::Completer::completeError}(exception, stack_trace);
+ }
+ :async_stack_trace = asy::_asyncStackTraceHelper(:async_op);
+ :async_op_then = asy::_asyncThenWrapperHelper(:async_op);
+ :async_op_error = asy::_asyncErrorWrapperHelper(:async_op);
+ :async_completer.{asy::_AsyncAwaitCompleter::start}(:async_op);
+ return :async_completer.{asy::Completer::future};
+}
+static method returnFutureOrClassFromFutureClass() → FutureOr<self::Class*>* /* originally async */ {
+ final asy::_AsyncAwaitCompleter<self::Class*>* :async_completer = new asy::_AsyncAwaitCompleter::•<self::Class*>();
+ FutureOr<self::Class*>* :return_value;
+ dynamic :async_stack_trace;
+ (dynamic) →* dynamic :async_op_then;
+ (core::Object*, core::StackTrace*) →* dynamic :async_op_error;
+ core::int* :await_jump_var = 0;
+ dynamic :await_ctx_var;
+ function :async_op([dynamic :result, dynamic :exception, dynamic :stack_trace]) → dynamic yielding
+ try {
+ #L9:
+ {
+ :return_value = self::returnFutureClass();
+ break #L9;
+ }
+ asy::_completeOnAsyncReturn(:async_completer, :return_value);
+ return;
+ }
+ on dynamic catch(dynamic exception, core::StackTrace* stack_trace) {
+ :async_completer.{asy::Completer::completeError}(exception, stack_trace);
+ }
+ :async_stack_trace = asy::_asyncStackTraceHelper(:async_op);
+ :async_op_then = asy::_asyncThenWrapperHelper(:async_op);
+ :async_op_error = asy::_asyncErrorWrapperHelper(:async_op);
+ :async_completer.{asy::_AsyncAwaitCompleter::start}(:async_op);
+ return :async_completer.{asy::Completer::future};
+}
+static method returnClassFromFutureOrClass() → self::Class* /* originally async */ {
+ final asy::_AsyncAwaitCompleter<dynamic>* :async_completer = new asy::_AsyncAwaitCompleter::•<dynamic>();
+ FutureOr<dynamic>* :return_value;
+ dynamic :async_stack_trace;
+ (dynamic) →* dynamic :async_op_then;
+ (core::Object*, core::StackTrace*) →* dynamic :async_op_error;
+ core::int* :await_jump_var = 0;
+ dynamic :await_ctx_var;
+ function :async_op([dynamic :result, dynamic :exception, dynamic :stack_trace]) → dynamic yielding
+ try {
+ #L10:
+ {
+ :return_value = self::returnFutureOrClass();
+ break #L10;
+ }
+ asy::_completeOnAsyncReturn(:async_completer, :return_value);
+ return;
+ }
+ on dynamic catch(dynamic exception, core::StackTrace* stack_trace) {
+ :async_completer.{asy::Completer::completeError}(exception, stack_trace);
+ }
+ :async_stack_trace = asy::_asyncStackTraceHelper(:async_op);
+ :async_op_then = asy::_asyncThenWrapperHelper(:async_op);
+ :async_op_error = asy::_asyncErrorWrapperHelper(:async_op);
+ :async_completer.{asy::_AsyncAwaitCompleter::start}(:async_op);
+ return :async_completer.{asy::Completer::future};
+}
+static method returnFutureClassFromFutureOrClass() → asy::Future<self::Class*>* /* originally async */ {
+ final asy::_AsyncAwaitCompleter<self::Class*>* :async_completer = new asy::_AsyncAwaitCompleter::•<self::Class*>();
+ FutureOr<self::Class*>* :return_value;
+ dynamic :async_stack_trace;
+ (dynamic) →* dynamic :async_op_then;
+ (core::Object*, core::StackTrace*) →* dynamic :async_op_error;
+ core::int* :await_jump_var = 0;
+ dynamic :await_ctx_var;
+ function :async_op([dynamic :result, dynamic :exception, dynamic :stack_trace]) → dynamic yielding
+ try {
+ #L11:
+ {
+ :return_value = self::returnFutureOrClass();
+ break #L11;
+ }
+ asy::_completeOnAsyncReturn(:async_completer, :return_value);
+ return;
+ }
+ on dynamic catch(dynamic exception, core::StackTrace* stack_trace) {
+ :async_completer.{asy::Completer::completeError}(exception, stack_trace);
+ }
+ :async_stack_trace = asy::_asyncStackTraceHelper(:async_op);
+ :async_op_then = asy::_asyncThenWrapperHelper(:async_op);
+ :async_op_error = asy::_asyncErrorWrapperHelper(:async_op);
+ :async_completer.{asy::_AsyncAwaitCompleter::start}(:async_op);
+ return :async_completer.{asy::Completer::future};
+}
+static method returnFutureOrClassFromFutureOrClass() → FutureOr<self::Class*>* /* originally async */ {
+ final asy::_AsyncAwaitCompleter<self::Class*>* :async_completer = new asy::_AsyncAwaitCompleter::•<self::Class*>();
+ FutureOr<self::Class*>* :return_value;
+ dynamic :async_stack_trace;
+ (dynamic) →* dynamic :async_op_then;
+ (core::Object*, core::StackTrace*) →* dynamic :async_op_error;
+ core::int* :await_jump_var = 0;
+ dynamic :await_ctx_var;
+ function :async_op([dynamic :result, dynamic :exception, dynamic :stack_trace]) → dynamic yielding
+ try {
+ #L12:
+ {
+ :return_value = self::returnFutureOrClass();
+ break #L12;
+ }
+ asy::_completeOnAsyncReturn(:async_completer, :return_value);
+ return;
+ }
+ on dynamic catch(dynamic exception, core::StackTrace* stack_trace) {
+ :async_completer.{asy::Completer::completeError}(exception, stack_trace);
+ }
+ :async_stack_trace = asy::_asyncStackTraceHelper(:async_op);
+ :async_op_then = asy::_asyncThenWrapperHelper(:async_op);
+ :async_op_error = asy::_asyncErrorWrapperHelper(:async_op);
+ :async_completer.{asy::_AsyncAwaitCompleter::start}(:async_op);
+ return :async_completer.{asy::Completer::future};
+}
+static method main() → dynamic /* originally async */ {
+ final asy::_AsyncAwaitCompleter<dynamic>* :async_completer = new asy::_AsyncAwaitCompleter::•<dynamic>();
+ FutureOr<dynamic>* :return_value;
+ dynamic :async_stack_trace;
+ (dynamic) →* dynamic :async_op_then;
+ (core::Object*, core::StackTrace*) →* dynamic :async_op_error;
+ core::int* :await_jump_var = 0;
+ dynamic :await_ctx_var;
+ dynamic :saved_try_context_var0;
+ function :async_op([dynamic :result, dynamic :exception, dynamic :stack_trace]) → dynamic yielding
+ try {
+ #L13:
+ {
+ [yield] let dynamic #t1 = asy::_awaitHelper(self::returnClass(), :async_op_then, :async_op_error, :async_op) in null;
+ _in::unsafeCast<self::Class*>(:result);
+ [yield] let dynamic #t2 = asy::_awaitHelper(self::returnFutureClass(), :async_op_then, :async_op_error, :async_op) in null;
+ _in::unsafeCast<self::Class*>(:result);
+ [yield] let dynamic #t3 = asy::_awaitHelper(self::returnFutureOrClass(), :async_op_then, :async_op_error, :async_op) in null;
+ _in::unsafeCast<self::Class*>(:result);
+ [yield] let dynamic #t4 = asy::_awaitHelper(self::returnClassFromDynamic(), :async_op_then, :async_op_error, :async_op) in null;
+ _in::unsafeCast<self::Class*>(:result);
+ [yield] let dynamic #t5 = asy::_awaitHelper(self::returnFutureClassDynamic(), :async_op_then, :async_op_error, :async_op) in null;
+ _in::unsafeCast<self::Class*>(:result);
+ [yield] let dynamic #t6 = asy::_awaitHelper(self::returnFutureOrClassDynamic(), :async_op_then, :async_op_error, :async_op) in null;
+ _in::unsafeCast<self::Class*>(:result);
+ [yield] let dynamic #t7 = asy::_awaitHelper(self::returnClassFromFutureClass(), :async_op_then, :async_op_error, :async_op) in null;
+ _in::unsafeCast<self::Class*>(:result);
+ [yield] let dynamic #t8 = asy::_awaitHelper(self::returnFutureClassFromFutureClass(), :async_op_then, :async_op_error, :async_op) in null;
+ _in::unsafeCast<self::Class*>(:result);
+ [yield] let dynamic #t9 = asy::_awaitHelper(self::returnFutureOrClassFromFutureClass(), :async_op_then, :async_op_error, :async_op) in null;
+ _in::unsafeCast<self::Class*>(:result);
+ [yield] let dynamic #t10 = asy::_awaitHelper(self::returnClassFromFutureOrClass(), :async_op_then, :async_op_error, :async_op) in null;
+ _in::unsafeCast<self::Class*>(:result);
+ [yield] let dynamic #t11 = asy::_awaitHelper(self::returnFutureClassFromFutureOrClass(), :async_op_then, :async_op_error, :async_op) in null;
+ _in::unsafeCast<self::Class*>(:result);
+ [yield] let dynamic #t12 = asy::_awaitHelper(self::returnFutureOrClassFromFutureOrClass(), :async_op_then, :async_op_error, :async_op) in null;
+ _in::unsafeCast<self::Class*>(:result);
+ }
+ asy::_completeOnAsyncReturn(:async_completer, :return_value);
+ return;
+ }
+ on dynamic catch(dynamic exception, core::StackTrace* stack_trace) {
+ :async_completer.{asy::Completer::completeError}(exception, stack_trace);
+ }
+ :async_stack_trace = asy::_asyncStackTraceHelper(:async_op);
+ :async_op_then = asy::_asyncThenWrapperHelper(:async_op);
+ :async_op_error = asy::_asyncErrorWrapperHelper(:async_op);
+ :async_completer.{asy::_AsyncAwaitCompleter::start}(:async_op);
+ return :async_completer.{asy::Completer::future};
+}
diff --git a/pkg/front_end/testcases/general/future_return.dart.textual_outline.expect b/pkg/front_end/testcases/general/future_return.dart.textual_outline.expect
new file mode 100644
index 0000000..75767f5
--- /dev/null
+++ b/pkg/front_end/testcases/general/future_return.dart.textual_outline.expect
@@ -0,0 +1,21 @@
+import 'dart:async';
+
+class Class {}
+
+dynamic returnDynamic() => new Class();
+Class returnClass() async => new Class();
+Future<Class> returnFutureClass() async => new Class();
+FutureOr<Class> returnFutureOrClass() async => new Class();
+Class returnClassFromDynamic() async => returnDynamic();
+Future<Class> returnFutureClassDynamic() async => returnDynamic();
+FutureOr<Class> returnFutureOrClassDynamic() async => returnDynamic();
+Class returnClassFromFutureClass() async => returnFutureClass();
+Future<Class> returnFutureClassFromFutureClass() async => returnFutureClass();
+FutureOr<Class> returnFutureOrClassFromFutureClass() async =>
+ returnFutureClass();
+Class returnClassFromFutureOrClass() async => returnFutureOrClass();
+Future<Class> returnFutureClassFromFutureOrClass() async =>
+ returnFutureOrClass();
+FutureOr<Class> returnFutureOrClassFromFutureOrClass() async =>
+ returnFutureOrClass();
+main() async {}
diff --git a/pkg/front_end/testcases/general/future_return.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/general/future_return.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..0e7261f
--- /dev/null
+++ b/pkg/front_end/testcases/general/future_return.dart.textual_outline_modelled.expect
@@ -0,0 +1,22 @@
+import 'dart:async';
+
+Class returnClass() async => new Class();
+Class returnClassFromDynamic() async => returnDynamic();
+Class returnClassFromFutureClass() async => returnFutureClass();
+Class returnClassFromFutureOrClass() async => returnFutureOrClass();
+Future<Class> returnFutureClass() async => new Class();
+Future<Class> returnFutureClassDynamic() async => returnDynamic();
+Future<Class> returnFutureClassFromFutureClass() async => returnFutureClass();
+Future<Class> returnFutureClassFromFutureOrClass() async =>
+ returnFutureOrClass();
+FutureOr<Class> returnFutureOrClass() async => new Class();
+FutureOr<Class> returnFutureOrClassDynamic() async => returnDynamic();
+FutureOr<Class> returnFutureOrClassFromFutureClass() async =>
+ returnFutureClass();
+FutureOr<Class> returnFutureOrClassFromFutureOrClass() async =>
+ returnFutureOrClass();
+
+class Class {}
+
+dynamic returnDynamic() => new Class();
+main() async {}
diff --git a/pkg/front_end/testcases/general/stream_future.dart b/pkg/front_end/testcases/general/stream_future.dart
new file mode 100644
index 0000000..8904ea0
--- /dev/null
+++ b/pkg/front_end/testcases/general/stream_future.dart
@@ -0,0 +1,31 @@
+// 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';
+
+class Class {}
+
+dynamic returnDynamic() => new Class();
+
+Class returnClass() => new Class();
+
+Future<dynamic> returnFutureDynamic() async => new Class();
+
+Future<Class> returnFutureClass() async => new Class();
+
+Stream<FutureOr<Class>> error() async* {
+ yield returnFutureDynamic();
+}
+
+Stream<FutureOr<Class>> stream() async* {
+ yield returnDynamic();
+ yield returnClass();
+ yield returnFutureClass();
+}
+
+main() async {
+ await for (FutureOr<Class> cls in stream()) {
+ print(cls);
+ }
+}
diff --git a/pkg/front_end/testcases/general/stream_future.dart.outline.expect b/pkg/front_end/testcases/general/stream_future.dart.outline.expect
new file mode 100644
index 0000000..7554d93
--- /dev/null
+++ b/pkg/front_end/testcases/general/stream_future.dart.outline.expect
@@ -0,0 +1,35 @@
+library;
+import self as self;
+import "dart:core" as core;
+import "dart:async" as asy;
+
+import "dart:async";
+
+class Class extends core::Object {
+ synthetic constructor •() → self::Class*
+ ;
+ abstract member-signature get _identityHashCode() → core::int*;
+ abstract member-signature method _instanceOf(dynamic instantiatorTypeArguments, dynamic functionTypeArguments, dynamic type) → core::bool*;
+ abstract member-signature method _simpleInstanceOf(dynamic type) → core::bool*;
+ abstract member-signature method _simpleInstanceOfTrue(dynamic type) → core::bool*;
+ abstract member-signature method _simpleInstanceOfFalse(dynamic type) → core::bool*;
+ abstract member-signature operator ==(dynamic other) → core::bool*;
+ abstract member-signature get hashCode() → core::int*;
+ abstract member-signature method toString() → core::String*;
+ abstract member-signature method noSuchMethod(core::Invocation* invocation) → dynamic;
+ abstract member-signature get runtimeType() → core::Type*;
+}
+static method returnDynamic() → dynamic
+ ;
+static method returnClass() → self::Class*
+ ;
+static method returnFutureDynamic() → asy::Future<dynamic>*
+ ;
+static method returnFutureClass() → asy::Future<self::Class*>*
+ ;
+static method error() → asy::Stream<FutureOr<self::Class*>*>*
+ ;
+static method stream() → asy::Stream<FutureOr<self::Class*>*>*
+ ;
+static method main() → dynamic
+ ;
diff --git a/pkg/front_end/testcases/general/stream_future.dart.strong.expect b/pkg/front_end/testcases/general/stream_future.dart.strong.expect
new file mode 100644
index 0000000..34be18d
--- /dev/null
+++ b/pkg/front_end/testcases/general/stream_future.dart.strong.expect
@@ -0,0 +1,56 @@
+library;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/general/stream_future.dart:18:9: Error: A value of type 'Future<dynamic>' can't be assigned to a variable of type 'FutureOr<Class>'.
+// - 'Future' is from 'dart:async'.
+// - 'Class' is from 'pkg/front_end/testcases/general/stream_future.dart'.
+// yield returnFutureDynamic();
+// ^
+//
+import self as self;
+import "dart:core" as core;
+import "dart:async" as asy;
+
+import "dart:async";
+
+class Class extends core::Object {
+ synthetic constructor •() → self::Class*
+ : super core::Object::•()
+ ;
+ abstract member-signature get _identityHashCode() → core::int*;
+ abstract member-signature method _instanceOf(dynamic instantiatorTypeArguments, dynamic functionTypeArguments, dynamic type) → core::bool*;
+ abstract member-signature method _simpleInstanceOf(dynamic type) → core::bool*;
+ abstract member-signature method _simpleInstanceOfTrue(dynamic type) → core::bool*;
+ abstract member-signature method _simpleInstanceOfFalse(dynamic type) → core::bool*;
+ abstract member-signature operator ==(dynamic other) → core::bool*;
+ abstract member-signature get hashCode() → core::int*;
+ abstract member-signature method toString() → core::String*;
+ abstract member-signature method noSuchMethod(core::Invocation* invocation) → dynamic;
+ abstract member-signature get runtimeType() → core::Type*;
+}
+static method returnDynamic() → dynamic
+ return new self::Class::•();
+static method returnClass() → self::Class*
+ return new self::Class::•();
+static method returnFutureDynamic() → asy::Future<dynamic>* async
+ return new self::Class::•();
+static method returnFutureClass() → asy::Future<self::Class*>* async
+ return new self::Class::•();
+static method error() → asy::Stream<FutureOr<self::Class*>*>* async* {
+ yield let final<BottomType> #t1 = invalid-expression "pkg/front_end/testcases/general/stream_future.dart:18:9: Error: A value of type 'Future<dynamic>' can't be assigned to a variable of type 'FutureOr<Class>'.
+ - 'Future' is from 'dart:async'.
+ - 'Class' is from 'pkg/front_end/testcases/general/stream_future.dart'.
+ yield returnFutureDynamic();
+ ^" in self::returnFutureDynamic() as{TypeError} FutureOr<self::Class*>*;
+}
+static method stream() → asy::Stream<FutureOr<self::Class*>*>* async* {
+ yield self::returnDynamic() as{TypeError,ForDynamic} FutureOr<self::Class*>*;
+ yield self::returnClass();
+ yield self::returnFutureClass();
+}
+static method main() → dynamic async {
+ await for (FutureOr<self::Class*>* cls in self::stream()) {
+ core::print(cls);
+ }
+}
diff --git a/pkg/front_end/testcases/general/stream_future.dart.strong.transformed.expect b/pkg/front_end/testcases/general/stream_future.dart.strong.transformed.expect
new file mode 100644
index 0000000..54b455b
--- /dev/null
+++ b/pkg/front_end/testcases/general/stream_future.dart.strong.transformed.expect
@@ -0,0 +1,225 @@
+library;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/general/stream_future.dart:18:9: Error: A value of type 'Future<dynamic>' can't be assigned to a variable of type 'FutureOr<Class>'.
+// - 'Future' is from 'dart:async'.
+// - 'Class' is from 'pkg/front_end/testcases/general/stream_future.dart'.
+// yield returnFutureDynamic();
+// ^
+//
+import self as self;
+import "dart:core" as core;
+import "dart:async" as asy;
+import "dart:_internal" as _in;
+
+import "dart:async";
+
+class Class extends core::Object {
+ synthetic constructor •() → self::Class*
+ : super core::Object::•()
+ ;
+ abstract member-signature get _identityHashCode() → core::int*;
+ abstract member-signature method _instanceOf(dynamic instantiatorTypeArguments, dynamic functionTypeArguments, dynamic type) → core::bool*;
+ abstract member-signature method _simpleInstanceOf(dynamic type) → core::bool*;
+ abstract member-signature method _simpleInstanceOfTrue(dynamic type) → core::bool*;
+ abstract member-signature method _simpleInstanceOfFalse(dynamic type) → core::bool*;
+ abstract member-signature operator ==(dynamic other) → core::bool*;
+ abstract member-signature get hashCode() → core::int*;
+ abstract member-signature method toString() → core::String*;
+ abstract member-signature method noSuchMethod(core::Invocation* invocation) → dynamic;
+ abstract member-signature get runtimeType() → core::Type*;
+}
+static method returnDynamic() → dynamic
+ return new self::Class::•();
+static method returnClass() → self::Class*
+ return new self::Class::•();
+static method returnFutureDynamic() → asy::Future<dynamic>* /* originally async */ {
+ final asy::_AsyncAwaitCompleter<dynamic>* :async_completer = new asy::_AsyncAwaitCompleter::•<dynamic>();
+ FutureOr<dynamic>* :return_value;
+ dynamic :async_stack_trace;
+ (dynamic) →* dynamic :async_op_then;
+ (core::Object*, core::StackTrace*) →* dynamic :async_op_error;
+ core::int* :await_jump_var = 0;
+ dynamic :await_ctx_var;
+ function :async_op([dynamic :result, dynamic :exception, dynamic :stack_trace]) → dynamic yielding
+ try {
+ #L1:
+ {
+ :return_value = new self::Class::•();
+ break #L1;
+ }
+ asy::_completeOnAsyncReturn(:async_completer, :return_value);
+ return;
+ }
+ on dynamic catch(dynamic exception, core::StackTrace* stack_trace) {
+ :async_completer.{asy::Completer::completeError}(exception, stack_trace);
+ }
+ :async_stack_trace = asy::_asyncStackTraceHelper(:async_op);
+ :async_op_then = asy::_asyncThenWrapperHelper(:async_op);
+ :async_op_error = asy::_asyncErrorWrapperHelper(:async_op);
+ :async_completer.{asy::_AsyncAwaitCompleter::start}(:async_op);
+ return :async_completer.{asy::Completer::future};
+}
+static method returnFutureClass() → asy::Future<self::Class*>* /* originally async */ {
+ final asy::_AsyncAwaitCompleter<self::Class*>* :async_completer = new asy::_AsyncAwaitCompleter::•<self::Class*>();
+ FutureOr<self::Class*>* :return_value;
+ dynamic :async_stack_trace;
+ (dynamic) →* dynamic :async_op_then;
+ (core::Object*, core::StackTrace*) →* dynamic :async_op_error;
+ core::int* :await_jump_var = 0;
+ dynamic :await_ctx_var;
+ function :async_op([dynamic :result, dynamic :exception, dynamic :stack_trace]) → dynamic yielding
+ try {
+ #L2:
+ {
+ :return_value = new self::Class::•();
+ break #L2;
+ }
+ asy::_completeOnAsyncReturn(:async_completer, :return_value);
+ return;
+ }
+ on dynamic catch(dynamic exception, core::StackTrace* stack_trace) {
+ :async_completer.{asy::Completer::completeError}(exception, stack_trace);
+ }
+ :async_stack_trace = asy::_asyncStackTraceHelper(:async_op);
+ :async_op_then = asy::_asyncThenWrapperHelper(:async_op);
+ :async_op_error = asy::_asyncErrorWrapperHelper(:async_op);
+ :async_completer.{asy::_AsyncAwaitCompleter::start}(:async_op);
+ return :async_completer.{asy::Completer::future};
+}
+static method error() → asy::Stream<FutureOr<self::Class*>*>* /* originally async* */ {
+ asy::_AsyncStarStreamController<FutureOr<self::Class*>*>* :controller;
+ dynamic :controller_stream;
+ dynamic :async_stack_trace;
+ (dynamic) →* dynamic :async_op_then;
+ (core::Object*, core::StackTrace*) →* dynamic :async_op_error;
+ core::int* :await_jump_var = 0;
+ dynamic :await_ctx_var;
+ dynamic :saved_try_context_var0;
+ dynamic :saved_try_context_var1;
+ function :async_op([dynamic :result, dynamic :exception, dynamic :stack_trace]) → dynamic yielding
+ try
+ try {
+ #L3:
+ {
+ if(:controller.{asy::_AsyncStarStreamController::add}(let final<BottomType> #t1 = invalid-expression "pkg/front_end/testcases/general/stream_future.dart:18:9: Error: A value of type 'Future<dynamic>' can't be assigned to a variable of type 'FutureOr<Class>'.
+ - 'Future' is from 'dart:async'.
+ - 'Class' is from 'pkg/front_end/testcases/general/stream_future.dart'.
+ yield returnFutureDynamic();
+ ^" in self::returnFutureDynamic() as{TypeError} FutureOr<self::Class*>*))
+ return null;
+ else
+ [yield] null;
+ }
+ return;
+ }
+ on dynamic catch(dynamic exception, core::StackTrace* stack_trace) {
+ :controller.{asy::_AsyncStarStreamController::addError}(exception, stack_trace);
+ }
+ finally {
+ :controller.{asy::_AsyncStarStreamController::close}();
+ }
+ :async_stack_trace = asy::_asyncStackTraceHelper(:async_op);
+ :async_op_then = asy::_asyncThenWrapperHelper(:async_op);
+ :async_op_error = asy::_asyncErrorWrapperHelper(:async_op);
+ :controller = new asy::_AsyncStarStreamController::•<FutureOr<self::Class*>*>(:async_op);
+ :controller_stream = :controller.{asy::_AsyncStarStreamController::stream};
+ return :controller_stream;
+}
+static method stream() → asy::Stream<FutureOr<self::Class*>*>* /* originally async* */ {
+ asy::_AsyncStarStreamController<FutureOr<self::Class*>*>* :controller;
+ dynamic :controller_stream;
+ dynamic :async_stack_trace;
+ (dynamic) →* dynamic :async_op_then;
+ (core::Object*, core::StackTrace*) →* dynamic :async_op_error;
+ core::int* :await_jump_var = 0;
+ dynamic :await_ctx_var;
+ dynamic :saved_try_context_var0;
+ dynamic :saved_try_context_var1;
+ function :async_op([dynamic :result, dynamic :exception, dynamic :stack_trace]) → dynamic yielding
+ try
+ try {
+ #L4:
+ {
+ if(:controller.{asy::_AsyncStarStreamController::add}(self::returnDynamic() as{TypeError,ForDynamic} FutureOr<self::Class*>*))
+ return null;
+ else
+ [yield] null;
+ if(:controller.{asy::_AsyncStarStreamController::add}(self::returnClass()))
+ return null;
+ else
+ [yield] null;
+ if(:controller.{asy::_AsyncStarStreamController::add}(self::returnFutureClass()))
+ return null;
+ else
+ [yield] null;
+ }
+ return;
+ }
+ on dynamic catch(dynamic exception, core::StackTrace* stack_trace) {
+ :controller.{asy::_AsyncStarStreamController::addError}(exception, stack_trace);
+ }
+ finally {
+ :controller.{asy::_AsyncStarStreamController::close}();
+ }
+ :async_stack_trace = asy::_asyncStackTraceHelper(:async_op);
+ :async_op_then = asy::_asyncThenWrapperHelper(:async_op);
+ :async_op_error = asy::_asyncErrorWrapperHelper(:async_op);
+ :controller = new asy::_AsyncStarStreamController::•<FutureOr<self::Class*>*>(:async_op);
+ :controller_stream = :controller.{asy::_AsyncStarStreamController::stream};
+ return :controller_stream;
+}
+static method main() → dynamic /* originally async */ {
+ final asy::_AsyncAwaitCompleter<dynamic>* :async_completer = new asy::_AsyncAwaitCompleter::•<dynamic>();
+ FutureOr<dynamic>* :return_value;
+ dynamic :async_stack_trace;
+ (dynamic) →* dynamic :async_op_then;
+ (core::Object*, core::StackTrace*) →* dynamic :async_op_error;
+ core::int* :await_jump_var = 0;
+ dynamic :await_ctx_var;
+ dynamic :saved_try_context_var0;
+ dynamic :saved_try_context_var1;
+ dynamic :exception0;
+ dynamic :stack_trace0;
+ function :async_op([dynamic :result, dynamic :exception, dynamic :stack_trace]) → dynamic yielding
+ try {
+ #L5:
+ {
+ {
+ asy::Stream<FutureOr<self::Class*>*>* :stream = self::stream();
+ asy::_asyncStarListenHelper(:stream, :async_op);
+ asy::_StreamIterator<FutureOr<self::Class*>*>* :for-iterator = new asy::_StreamIterator::•<FutureOr<self::Class*>*>(:stream);
+ try
+ #L6:
+ while (true) {
+ dynamic #t2 = asy::_asyncStarMoveNextHelper(:stream);
+ [yield] let dynamic #t3 = asy::_awaitHelper(:for-iterator.{asy::_StreamIterator::moveNext}(), :async_op_then, :async_op_error, :async_op) in null;
+ if(_in::unsafeCast<core::bool>(:result)) {
+ FutureOr<self::Class*>* cls = :for-iterator.{asy::_StreamIterator::current};
+ {
+ core::print(cls);
+ }
+ }
+ else
+ break #L6;
+ }
+ finally
+ if(!:for-iterator.{asy::_StreamIterator::_subscription}.{core::Object::==}(null)) {
+ [yield] let dynamic #t4 = asy::_awaitHelper(:for-iterator.{asy::_StreamIterator::cancel}(), :async_op_then, :async_op_error, :async_op) in null;
+ :result;
+ }
+ }
+ }
+ asy::_completeOnAsyncReturn(:async_completer, :return_value);
+ return;
+ }
+ on dynamic catch(dynamic exception, core::StackTrace* stack_trace) {
+ :async_completer.{asy::Completer::completeError}(exception, stack_trace);
+ }
+ :async_stack_trace = asy::_asyncStackTraceHelper(:async_op);
+ :async_op_then = asy::_asyncThenWrapperHelper(:async_op);
+ :async_op_error = asy::_asyncErrorWrapperHelper(:async_op);
+ :async_completer.{asy::_AsyncAwaitCompleter::start}(:async_op);
+ return :async_completer.{asy::Completer::future};
+}
diff --git a/pkg/front_end/testcases/general/stream_future.dart.textual_outline.expect b/pkg/front_end/testcases/general/stream_future.dart.textual_outline.expect
new file mode 100644
index 0000000..b720b02
--- /dev/null
+++ b/pkg/front_end/testcases/general/stream_future.dart.textual_outline.expect
@@ -0,0 +1,11 @@
+import 'dart:async';
+
+class Class {}
+
+dynamic returnDynamic() => new Class();
+Class returnClass() => new Class();
+Future<dynamic> returnFutureDynamic() async => new Class();
+Future<Class> returnFutureClass() async => new Class();
+Stream<FutureOr<Class>> error() async* {}
+Stream<FutureOr<Class>> stream() async* {}
+main() async {}
diff --git a/pkg/front_end/testcases/general/stream_future.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/general/stream_future.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..38f650e
--- /dev/null
+++ b/pkg/front_end/testcases/general/stream_future.dart.textual_outline_modelled.expect
@@ -0,0 +1,12 @@
+import 'dart:async';
+
+Class returnClass() => new Class();
+Future<Class> returnFutureClass() async => new Class();
+Future<dynamic> returnFutureDynamic() async => new Class();
+Stream<FutureOr<Class>> error() async* {}
+Stream<FutureOr<Class>> stream() async* {}
+
+class Class {}
+
+dynamic returnDynamic() => new Class();
+main() async {}
diff --git a/pkg/front_end/testcases/inference/future_then_conditional.dart b/pkg/front_end/testcases/inference/future_then_conditional.dart
index b9c870e..3a00196 100644
--- a/pkg/front_end/testcases/inference/future_then_conditional.dart
+++ b/pkg/front_end/testcases/inference/future_then_conditional.dart
@@ -20,7 +20,7 @@
/*@ returnType=Future<int*>* */ (/*@ type=bool* */ x) async =>
x ? 2 : await new Future<int>.value(3));
Future<int> t2 = f. /*@ typeArgs=int* */ /*@target=MyFuture.then*/ then(
- /*@ returnType=Future<int*>* */ (/*@ type=bool* */ x) async {
+ /*@returnType=FutureOr<int*>**/ (/*@ type=bool* */ x) async {
return /*info:DOWN_CAST_COMPOSITE*/ await x ? 2 : new Future<int>.value(3);
});
Future<int> t5 = f. /*@ typeArgs=int* */ /*@target=MyFuture.then*/ then(
diff --git a/pkg/front_end/testcases/inference/future_then_conditional.dart.strong.expect b/pkg/front_end/testcases/inference/future_then_conditional.dart.strong.expect
index 390633b..2210fc5 100644
--- a/pkg/front_end/testcases/inference/future_then_conditional.dart.strong.expect
+++ b/pkg/front_end/testcases/inference/future_then_conditional.dart.strong.expect
@@ -35,7 +35,7 @@
static method test() → void {
self::MyFuture<core::bool*>* f;
asy::Future<core::int*>* t1 = f.{self::MyFuture::then}<core::int*>((core::bool* x) → asy::Future<core::int*>* async => x ?{core::int*} 2 : await asy::Future::value<core::int*>(3));
- asy::Future<core::int*>* t2 = f.{self::MyFuture::then}<core::int*>((core::bool* x) → asy::Future<core::int*>* async {
+ asy::Future<core::int*>* t2 = f.{self::MyFuture::then}<core::int*>((core::bool* x) → FutureOr<core::int*>* async {
return (await x ?{core::Object*} 2 : asy::Future::value<core::int*>(3)) as{TypeError} FutureOr<core::int*>*;
});
asy::Future<core::int*>* t5 = f.{self::MyFuture::then}<core::int*>((core::bool* x) → FutureOr<core::int*>* => (x ?{core::Object*} 2 : asy::Future::value<core::int*>(3)) as{TypeError} FutureOr<core::int*>*);
diff --git a/pkg/front_end/testcases/inference/future_then_conditional.dart.strong.transformed.expect b/pkg/front_end/testcases/inference/future_then_conditional.dart.strong.transformed.expect
index d466ab6..2c09036 100644
--- a/pkg/front_end/testcases/inference/future_then_conditional.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/inference/future_then_conditional.dart.strong.transformed.expect
@@ -71,7 +71,7 @@
:async_completer.{asy::_AsyncAwaitCompleter::start}(:async_op);
return :async_completer.{asy::Completer::future};
});
- asy::Future<core::int*>* t2 = f.{self::MyFuture::then}<core::int*>((core::bool* x) → asy::Future<core::int*>* /* originally async */ {
+ asy::Future<core::int*>* t2 = f.{self::MyFuture::then}<core::int*>((core::bool* x) → FutureOr<core::int*>* /* originally async */ {
final asy::_AsyncAwaitCompleter<core::int*>* :async_completer = new asy::_AsyncAwaitCompleter::•<core::int*>();
FutureOr<core::int*>* :return_value;
dynamic :async_stack_trace;
diff --git a/pkg/front_end/testcases/inference/future_then_conditional_2.dart b/pkg/front_end/testcases/inference/future_then_conditional_2.dart
index e492066..3738cae 100644
--- a/pkg/front_end/testcases/inference/future_then_conditional_2.dart
+++ b/pkg/front_end/testcases/inference/future_then_conditional_2.dart
@@ -20,7 +20,7 @@
/*@ returnType=Future<int*>* */ (/*@ type=bool* */ x) async =>
x ? 2 : await new MyFuture<int>.value(3));
Future<int> t2 = f. /*@ typeArgs=int* */ /*@target=MyFuture.then*/ then(
- /*@ returnType=Future<int*>* */ (/*@ type=bool* */ x) async {
+ /*@returnType=FutureOr<int*>**/ (/*@ type=bool* */ x) async {
return /*info:DOWN_CAST_COMPOSITE*/ await x
? 2
: new MyFuture<int>.value(3);
diff --git a/pkg/front_end/testcases/inference/future_then_conditional_2.dart.strong.expect b/pkg/front_end/testcases/inference/future_then_conditional_2.dart.strong.expect
index f52ac69..086050d 100644
--- a/pkg/front_end/testcases/inference/future_then_conditional_2.dart.strong.expect
+++ b/pkg/front_end/testcases/inference/future_then_conditional_2.dart.strong.expect
@@ -35,7 +35,7 @@
static method test() → void {
self::MyFuture<core::bool*>* f;
asy::Future<core::int*>* t1 = f.{self::MyFuture::then}<core::int*>((core::bool* x) → asy::Future<core::int*>* async => x ?{core::int*} 2 : await new self::MyFuture::value<core::int*>(3));
- asy::Future<core::int*>* t2 = f.{self::MyFuture::then}<core::int*>((core::bool* x) → asy::Future<core::int*>* async {
+ asy::Future<core::int*>* t2 = f.{self::MyFuture::then}<core::int*>((core::bool* x) → FutureOr<core::int*>* async {
return (await x ?{core::Object*} 2 : new self::MyFuture::value<core::int*>(3)) as{TypeError} FutureOr<core::int*>*;
});
asy::Future<core::int*>* t5 = f.{self::MyFuture::then}<core::int*>((core::bool* x) → FutureOr<core::int*>* => (x ?{core::Object*} 2 : new self::MyFuture::value<core::int*>(3)) as{TypeError} FutureOr<core::int*>*);
diff --git a/pkg/front_end/testcases/inference/future_then_conditional_2.dart.strong.transformed.expect b/pkg/front_end/testcases/inference/future_then_conditional_2.dart.strong.transformed.expect
index 0738d0a1..72ca616 100644
--- a/pkg/front_end/testcases/inference/future_then_conditional_2.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/inference/future_then_conditional_2.dart.strong.transformed.expect
@@ -71,7 +71,7 @@
:async_completer.{asy::_AsyncAwaitCompleter::start}(:async_op);
return :async_completer.{asy::Completer::future};
});
- asy::Future<core::int*>* t2 = f.{self::MyFuture::then}<core::int*>((core::bool* x) → asy::Future<core::int*>* /* originally async */ {
+ asy::Future<core::int*>* t2 = f.{self::MyFuture::then}<core::int*>((core::bool* x) → FutureOr<core::int*>* /* originally async */ {
final asy::_AsyncAwaitCompleter<core::int*>* :async_completer = new asy::_AsyncAwaitCompleter::•<core::int*>();
FutureOr<core::int*>* :return_value;
dynamic :async_stack_trace;
diff --git a/pkg/front_end/testcases/inference/future_then_conditional_3.dart b/pkg/front_end/testcases/inference/future_then_conditional_3.dart
index bb97825..f6a18b5 100644
--- a/pkg/front_end/testcases/inference/future_then_conditional_3.dart
+++ b/pkg/front_end/testcases/inference/future_then_conditional_3.dart
@@ -20,7 +20,7 @@
/*@ returnType=Future<int*>* */ (/*@ type=bool* */ x) async =>
x ? 2 : await new Future<int>.value(3));
MyFuture<int> t2 = f. /*@ typeArgs=int* */ /*@target=MyFuture.then*/ then(
- /*@ returnType=Future<int*>* */ (/*@ type=bool* */ x) async {
+ /*@returnType=FutureOr<int*>**/ (/*@ type=bool* */ x) async {
return /*info:DOWN_CAST_COMPOSITE*/ await x ? 2 : new Future<int>.value(3);
});
MyFuture<int> t5 = f. /*@ typeArgs=int* */ /*@target=MyFuture.then*/ then(
diff --git a/pkg/front_end/testcases/inference/future_then_conditional_3.dart.strong.expect b/pkg/front_end/testcases/inference/future_then_conditional_3.dart.strong.expect
index 9b83c6d..a86e98a 100644
--- a/pkg/front_end/testcases/inference/future_then_conditional_3.dart.strong.expect
+++ b/pkg/front_end/testcases/inference/future_then_conditional_3.dart.strong.expect
@@ -35,7 +35,7 @@
static method test() → void {
self::MyFuture<core::bool*>* f;
self::MyFuture<core::int*>* t1 = f.{self::MyFuture::then}<core::int*>((core::bool* x) → asy::Future<core::int*>* async => x ?{core::int*} 2 : await asy::Future::value<core::int*>(3));
- self::MyFuture<core::int*>* t2 = f.{self::MyFuture::then}<core::int*>((core::bool* x) → asy::Future<core::int*>* async {
+ self::MyFuture<core::int*>* t2 = f.{self::MyFuture::then}<core::int*>((core::bool* x) → FutureOr<core::int*>* async {
return (await x ?{core::Object*} 2 : asy::Future::value<core::int*>(3)) as{TypeError} FutureOr<core::int*>*;
});
self::MyFuture<core::int*>* t5 = f.{self::MyFuture::then}<core::int*>((core::bool* x) → FutureOr<core::int*>* => (x ?{core::Object*} 2 : asy::Future::value<core::int*>(3)) as{TypeError} FutureOr<core::int*>*);
diff --git a/pkg/front_end/testcases/inference/future_then_conditional_3.dart.strong.transformed.expect b/pkg/front_end/testcases/inference/future_then_conditional_3.dart.strong.transformed.expect
index 12e2069..614bb632d 100644
--- a/pkg/front_end/testcases/inference/future_then_conditional_3.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/inference/future_then_conditional_3.dart.strong.transformed.expect
@@ -71,7 +71,7 @@
:async_completer.{asy::_AsyncAwaitCompleter::start}(:async_op);
return :async_completer.{asy::Completer::future};
});
- self::MyFuture<core::int*>* t2 = f.{self::MyFuture::then}<core::int*>((core::bool* x) → asy::Future<core::int*>* /* originally async */ {
+ self::MyFuture<core::int*>* t2 = f.{self::MyFuture::then}<core::int*>((core::bool* x) → FutureOr<core::int*>* /* originally async */ {
final asy::_AsyncAwaitCompleter<core::int*>* :async_completer = new asy::_AsyncAwaitCompleter::•<core::int*>();
FutureOr<core::int*>* :return_value;
dynamic :async_stack_trace;
diff --git a/pkg/front_end/testcases/inference/future_then_conditional_4.dart b/pkg/front_end/testcases/inference/future_then_conditional_4.dart
index 900063b..19fafd4 100644
--- a/pkg/front_end/testcases/inference/future_then_conditional_4.dart
+++ b/pkg/front_end/testcases/inference/future_then_conditional_4.dart
@@ -20,7 +20,7 @@
/*@ returnType=Future<int*>* */ (/*@ type=bool* */ x) async =>
x ? 2 : await new MyFuture<int>.value(3));
MyFuture<int> t2 = f. /*@ typeArgs=int* */ /*@target=MyFuture.then*/ then(
- /*@ returnType=Future<int*>* */ (/*@ type=bool* */ x) async {
+ /*@returnType=FutureOr<int*>**/ (/*@ type=bool* */ x) async {
return /*info:DOWN_CAST_COMPOSITE*/ await x
? 2
: new MyFuture<int>.value(3);
diff --git a/pkg/front_end/testcases/inference/future_then_conditional_4.dart.strong.expect b/pkg/front_end/testcases/inference/future_then_conditional_4.dart.strong.expect
index bf3ab6b..1f5026e 100644
--- a/pkg/front_end/testcases/inference/future_then_conditional_4.dart.strong.expect
+++ b/pkg/front_end/testcases/inference/future_then_conditional_4.dart.strong.expect
@@ -35,7 +35,7 @@
static method test() → void {
self::MyFuture<core::bool*>* f;
self::MyFuture<core::int*>* t1 = f.{self::MyFuture::then}<core::int*>((core::bool* x) → asy::Future<core::int*>* async => x ?{core::int*} 2 : await new self::MyFuture::value<core::int*>(3));
- self::MyFuture<core::int*>* t2 = f.{self::MyFuture::then}<core::int*>((core::bool* x) → asy::Future<core::int*>* async {
+ self::MyFuture<core::int*>* t2 = f.{self::MyFuture::then}<core::int*>((core::bool* x) → FutureOr<core::int*>* async {
return (await x ?{core::Object*} 2 : new self::MyFuture::value<core::int*>(3)) as{TypeError} FutureOr<core::int*>*;
});
self::MyFuture<core::int*>* t5 = f.{self::MyFuture::then}<core::int*>((core::bool* x) → FutureOr<core::int*>* => (x ?{core::Object*} 2 : new self::MyFuture::value<core::int*>(3)) as{TypeError} FutureOr<core::int*>*);
diff --git a/pkg/front_end/testcases/inference/future_then_conditional_4.dart.strong.transformed.expect b/pkg/front_end/testcases/inference/future_then_conditional_4.dart.strong.transformed.expect
index 8142e71..6ea94f5 100644
--- a/pkg/front_end/testcases/inference/future_then_conditional_4.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/inference/future_then_conditional_4.dart.strong.transformed.expect
@@ -71,7 +71,7 @@
:async_completer.{asy::_AsyncAwaitCompleter::start}(:async_op);
return :async_completer.{asy::Completer::future};
});
- self::MyFuture<core::int*>* t2 = f.{self::MyFuture::then}<core::int*>((core::bool* x) → asy::Future<core::int*>* /* originally async */ {
+ self::MyFuture<core::int*>* t2 = f.{self::MyFuture::then}<core::int*>((core::bool* x) → FutureOr<core::int*>* /* originally async */ {
final asy::_AsyncAwaitCompleter<core::int*>* :async_completer = new asy::_AsyncAwaitCompleter::•<core::int*>();
FutureOr<core::int*>* :return_value;
dynamic :async_stack_trace;
diff --git a/pkg/front_end/testcases/inference/future_then_conditional_5.dart b/pkg/front_end/testcases/inference/future_then_conditional_5.dart
index de773b9..e44607b 100644
--- a/pkg/front_end/testcases/inference/future_then_conditional_5.dart
+++ b/pkg/front_end/testcases/inference/future_then_conditional_5.dart
@@ -20,7 +20,7 @@
/*@ returnType=Future<int*>* */ (/*@ type=bool* */ x) async =>
x ? 2 : await new MyFuture<int>.value(3));
Future<int> t2 = f. /*@ typeArgs=int* */ /*@target=Future.then*/ then(
- /*@ returnType=Future<int*>* */ (/*@ type=bool* */ x) async {
+ /*@returnType=FutureOr<int*>*/ (/*@ type=bool* */ x) async {
return /*info:DOWN_CAST_COMPOSITE*/ await x
? 2
: new MyFuture<int>.value(3);
diff --git a/pkg/front_end/testcases/inference/future_then_conditional_5.dart.strong.expect b/pkg/front_end/testcases/inference/future_then_conditional_5.dart.strong.expect
index 8bb9c69..778a252 100644
--- a/pkg/front_end/testcases/inference/future_then_conditional_5.dart.strong.expect
+++ b/pkg/front_end/testcases/inference/future_then_conditional_5.dart.strong.expect
@@ -35,7 +35,7 @@
static method test() → void {
asy::Future<core::bool*>* f;
asy::Future<core::int*>* t1 = f.{asy::Future::then}<core::int*>((core::bool* x) → asy::Future<core::int*>* async => x ?{core::int*} 2 : await new self::MyFuture::value<core::int*>(3));
- asy::Future<core::int*>* t2 = f.{asy::Future::then}<core::int*>((core::bool* x) → asy::Future<core::int*>* async {
+ asy::Future<core::int*>* t2 = f.{asy::Future::then}<core::int*>((core::bool* x) → FutureOr<core::int*> async {
return (await x ?{core::Object*} 2 : new self::MyFuture::value<core::int*>(3)) as{TypeError} FutureOr<core::int*>*;
});
asy::Future<core::int*>* t5 = f.{asy::Future::then}<core::int*>((core::bool* x) → FutureOr<core::int*> => (x ?{core::Object*} 2 : new self::MyFuture::value<core::int*>(3)) as{TypeError} FutureOr<core::int*>);
diff --git a/pkg/front_end/testcases/inference/future_then_conditional_5.dart.strong.transformed.expect b/pkg/front_end/testcases/inference/future_then_conditional_5.dart.strong.transformed.expect
index b3e2352..4885d08 100644
--- a/pkg/front_end/testcases/inference/future_then_conditional_5.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/inference/future_then_conditional_5.dart.strong.transformed.expect
@@ -71,7 +71,7 @@
:async_completer.{asy::_AsyncAwaitCompleter::start}(:async_op);
return :async_completer.{asy::Completer::future};
});
- asy::Future<core::int*>* t2 = f.{asy::Future::then}<core::int*>((core::bool* x) → asy::Future<core::int*>* /* originally async */ {
+ asy::Future<core::int*>* t2 = f.{asy::Future::then}<core::int*>((core::bool* x) → FutureOr<core::int*> /* originally async */ {
final asy::_AsyncAwaitCompleter<core::int*>* :async_completer = new asy::_AsyncAwaitCompleter::•<core::int*>();
FutureOr<core::int*>* :return_value;
dynamic :async_stack_trace;
diff --git a/pkg/front_end/testcases/inference/future_then_conditional_6.dart b/pkg/front_end/testcases/inference/future_then_conditional_6.dart
index 9ee9c32..4a0cc0e 100644
--- a/pkg/front_end/testcases/inference/future_then_conditional_6.dart
+++ b/pkg/front_end/testcases/inference/future_then_conditional_6.dart
@@ -20,7 +20,7 @@
/*@ returnType=Future<int*>* */ (/*@ type=bool* */ x) async =>
x ? 2 : await new Future<int>.value(3));
Future<int> t2 = f. /*@ typeArgs=int* */ /*@target=Future.then*/ then(
- /*@ returnType=Future<int*>* */ (/*@ type=bool* */ x) async {
+ /*@returnType=FutureOr<int*>*/ (/*@ type=bool* */ x) async {
return /*info:DOWN_CAST_COMPOSITE*/ await x ? 2 : new Future<int>.value(3);
});
Future<int> t5 = f. /*@ typeArgs=int* */ /*@target=Future.then*/ then(
diff --git a/pkg/front_end/testcases/inference/future_then_conditional_6.dart.strong.expect b/pkg/front_end/testcases/inference/future_then_conditional_6.dart.strong.expect
index 7ff41c8..a620834 100644
--- a/pkg/front_end/testcases/inference/future_then_conditional_6.dart.strong.expect
+++ b/pkg/front_end/testcases/inference/future_then_conditional_6.dart.strong.expect
@@ -35,7 +35,7 @@
static method test() → void {
asy::Future<core::bool*>* f;
asy::Future<core::int*>* t1 = f.{asy::Future::then}<core::int*>((core::bool* x) → asy::Future<core::int*>* async => x ?{core::int*} 2 : await asy::Future::value<core::int*>(3));
- asy::Future<core::int*>* t2 = f.{asy::Future::then}<core::int*>((core::bool* x) → asy::Future<core::int*>* async {
+ asy::Future<core::int*>* t2 = f.{asy::Future::then}<core::int*>((core::bool* x) → FutureOr<core::int*> async {
return (await x ?{core::Object*} 2 : asy::Future::value<core::int*>(3)) as{TypeError} FutureOr<core::int*>*;
});
asy::Future<core::int*>* t5 = f.{asy::Future::then}<core::int*>((core::bool* x) → FutureOr<core::int*> => (x ?{core::Object*} 2 : asy::Future::value<core::int*>(3)) as{TypeError} FutureOr<core::int*>);
diff --git a/pkg/front_end/testcases/inference/future_then_conditional_6.dart.strong.transformed.expect b/pkg/front_end/testcases/inference/future_then_conditional_6.dart.strong.transformed.expect
index f5032974..59dd08b 100644
--- a/pkg/front_end/testcases/inference/future_then_conditional_6.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/inference/future_then_conditional_6.dart.strong.transformed.expect
@@ -71,7 +71,7 @@
:async_completer.{asy::_AsyncAwaitCompleter::start}(:async_op);
return :async_completer.{asy::Completer::future};
});
- asy::Future<core::int*>* t2 = f.{asy::Future::then}<core::int*>((core::bool* x) → asy::Future<core::int*>* /* originally async */ {
+ asy::Future<core::int*>* t2 = f.{asy::Future::then}<core::int*>((core::bool* x) → FutureOr<core::int*> /* originally async */ {
final asy::_AsyncAwaitCompleter<core::int*>* :async_completer = new asy::_AsyncAwaitCompleter::•<core::int*>();
FutureOr<core::int*>* :return_value;
dynamic :async_stack_trace;
diff --git a/pkg/front_end/testcases/inference/future_then_ifNull.dart b/pkg/front_end/testcases/inference/future_then_ifNull.dart
index 06b9f65..013774e 100644
--- a/pkg/front_end/testcases/inference/future_then_ifNull.dart
+++ b/pkg/front_end/testcases/inference/future_then_ifNull.dart
@@ -20,7 +20,7 @@
/*@ returnType=Future<int*>* */ (/*@ type=int* */ x) async =>
x /*@target=num.==*/ ?? await new Future<int>.value(3));
Future<int> t2 = f. /*@ typeArgs=int* */ /*@target=MyFuture.then*/ then(
- /*@ returnType=Future<int*>* */ (/*@ type=int* */ x) async {
+ /*@returnType=FutureOr<int*>**/ (/*@ type=int* */ x) async {
return /*info:DOWN_CAST_COMPOSITE*/ await x /*@target=num.==*/ ??
new Future<int>.value(3);
});
diff --git a/pkg/front_end/testcases/inference/future_then_ifNull.dart.strong.expect b/pkg/front_end/testcases/inference/future_then_ifNull.dart.strong.expect
index dd021c0..602b1db 100644
--- a/pkg/front_end/testcases/inference/future_then_ifNull.dart.strong.expect
+++ b/pkg/front_end/testcases/inference/future_then_ifNull.dart.strong.expect
@@ -35,7 +35,7 @@
static method test() → void {
self::MyFuture<core::int*>* f;
asy::Future<core::int*>* t1 = f.{self::MyFuture::then}<core::int*>((core::int* x) → asy::Future<core::int*>* async => let final core::int* #t1 = x in #t1.{core::num::==}(null) ?{core::int*} await asy::Future::value<core::int*>(3) : #t1);
- asy::Future<core::int*>* t2 = f.{self::MyFuture::then}<core::int*>((core::int* x) → asy::Future<core::int*>* async {
+ asy::Future<core::int*>* t2 = f.{self::MyFuture::then}<core::int*>((core::int* x) → FutureOr<core::int*>* async {
return (let final core::int* #t2 = await x in #t2.{core::num::==}(null) ?{core::Object*} asy::Future::value<core::int*>(3) : #t2) as{TypeError} FutureOr<core::int*>*;
});
asy::Future<core::int*>* t5 = f.{self::MyFuture::then}<core::int*>((core::int* x) → FutureOr<core::int*>* => (let final core::int* #t3 = x in #t3.{core::num::==}(null) ?{core::Object*} asy::Future::value<core::int*>(3) : #t3) as{TypeError} FutureOr<core::int*>*);
diff --git a/pkg/front_end/testcases/inference/future_then_ifNull.dart.strong.transformed.expect b/pkg/front_end/testcases/inference/future_then_ifNull.dart.strong.transformed.expect
index 1332feb..e254364 100644
--- a/pkg/front_end/testcases/inference/future_then_ifNull.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/inference/future_then_ifNull.dart.strong.transformed.expect
@@ -72,7 +72,7 @@
:async_completer.{asy::_AsyncAwaitCompleter::start}(:async_op);
return :async_completer.{asy::Completer::future};
});
- asy::Future<core::int*>* t2 = f.{self::MyFuture::then}<core::int*>((core::int* x) → asy::Future<core::int*>* /* originally async */ {
+ asy::Future<core::int*>* t2 = f.{self::MyFuture::then}<core::int*>((core::int* x) → FutureOr<core::int*>* /* originally async */ {
final asy::_AsyncAwaitCompleter<core::int*>* :async_completer = new asy::_AsyncAwaitCompleter::•<core::int*>();
FutureOr<core::int*>* :return_value;
dynamic :async_stack_trace;
diff --git a/pkg/front_end/testcases/late_lowering/issue42407.dart b/pkg/front_end/testcases/late_lowering/issue42407.dart
new file mode 100644
index 0000000..97ac374
--- /dev/null
+++ b/pkg/front_end/testcases/late_lowering/issue42407.dart
@@ -0,0 +1,38 @@
+// 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 A<T> {
+ late T x;
+}
+
+class B<T> {
+ T? _y;
+ T? get y => _y;
+ set y(T? val) {
+ _y = val;
+ }
+}
+
+main() {
+ A<num> a = new A<int>();
+ expect(42, a.x = 42);
+ throws(() => a.x = 0.5);
+
+ B<num> b = new B<int>();
+ expect(42, b.y = 42);
+ throws(() => b.y = 0.5);
+}
+
+expect(expected, actual) {
+ if (expected != actual) throw "Expected $expected, actual $actual";
+}
+
+throws(void Function() f) {
+ try {
+ f();
+ } catch (_) {
+ return;
+ }
+ throw "Expected exception";
+}
diff --git a/pkg/front_end/testcases/late_lowering/issue42407.dart.outline.expect b/pkg/front_end/testcases/late_lowering/issue42407.dart.outline.expect
new file mode 100644
index 0000000..9abba16
--- /dev/null
+++ b/pkg/front_end/testcases/late_lowering/issue42407.dart.outline.expect
@@ -0,0 +1,27 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class A<T extends core::Object? = dynamic> extends core::Object {
+ generic-covariant-impl field self::A::T? _#A#x;
+ field core::bool _#A#x#isSet;
+ synthetic constructor •() → self::A<self::A::T%>
+ ;
+ get x() → self::A::T%;
+ set x(generic-covariant-impl self::A::T% #t1) → void;
+}
+class B<T extends core::Object? = dynamic> extends core::Object {
+ generic-covariant-impl field self::B::T? _y;
+ synthetic constructor •() → self::B<self::B::T%>
+ ;
+ get y() → self::B::T?
+ ;
+ set y(generic-covariant-impl self::B::T? val) → void
+ ;
+}
+static method main() → dynamic
+ ;
+static method expect(dynamic expected, dynamic actual) → dynamic
+ ;
+static method throws(() → void f) → dynamic
+ ;
diff --git a/pkg/front_end/testcases/late_lowering/issue42407.dart.strong.expect b/pkg/front_end/testcases/late_lowering/issue42407.dart.strong.expect
new file mode 100644
index 0000000..59101b9
--- /dev/null
+++ b/pkg/front_end/testcases/late_lowering/issue42407.dart.strong.expect
@@ -0,0 +1,50 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "dart:_internal" as _in;
+
+class A<T extends core::Object? = dynamic> extends core::Object {
+ generic-covariant-impl field self::A::T? _#A#x = null;
+ field core::bool _#A#x#isSet = false;
+ synthetic constructor •() → self::A<self::A::T%>
+ : super core::Object::•()
+ ;
+ get x() → self::A::T%
+ return this.{self::A::_#A#x#isSet} ?{self::A::T%} let final self::A::T? #t1 = this.{self::A::_#A#x} in #t1{self::A::T%} : throw new _in::LateInitializationErrorImpl::•("Field 'x' has not been initialized.");
+ set x(generic-covariant-impl self::A::T% #t2) → void {
+ this.{self::A::_#A#x#isSet} = true;
+ this.{self::A::_#A#x} = #t2;
+ }
+}
+class B<T extends core::Object? = dynamic> extends core::Object {
+ generic-covariant-impl field self::B::T? _y = null;
+ synthetic constructor •() → self::B<self::B::T%>
+ : super core::Object::•()
+ ;
+ get y() → self::B::T?
+ return this.{self::B::_y};
+ set y(generic-covariant-impl self::B::T? val) → void {
+ this.{self::B::_y} = val;
+ }
+}
+static method main() → dynamic {
+ self::A<core::num> a = new self::A::•<core::int>();
+ self::expect(42, a.{self::A::x} = 42);
+ self::throws(() → core::double => a.{self::A::x} = 0.5);
+ self::B<core::num> b = new self::B::•<core::int>();
+ self::expect(42, b.{self::B::y} = 42);
+ self::throws(() → core::double => b.{self::B::y} = 0.5);
+}
+static method expect(dynamic expected, dynamic actual) → dynamic {
+ if(!expected.{core::Object::==}(actual))
+ throw "Expected ${expected}, actual ${actual}";
+}
+static method throws(() → void f) → dynamic {
+ try {
+ f.call();
+ }
+ on core::Object catch(final core::Object _) {
+ return;
+ }
+ throw "Expected exception";
+}
diff --git a/pkg/front_end/testcases/late_lowering/issue42407.dart.strong.transformed.expect b/pkg/front_end/testcases/late_lowering/issue42407.dart.strong.transformed.expect
new file mode 100644
index 0000000..59101b9
--- /dev/null
+++ b/pkg/front_end/testcases/late_lowering/issue42407.dart.strong.transformed.expect
@@ -0,0 +1,50 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "dart:_internal" as _in;
+
+class A<T extends core::Object? = dynamic> extends core::Object {
+ generic-covariant-impl field self::A::T? _#A#x = null;
+ field core::bool _#A#x#isSet = false;
+ synthetic constructor •() → self::A<self::A::T%>
+ : super core::Object::•()
+ ;
+ get x() → self::A::T%
+ return this.{self::A::_#A#x#isSet} ?{self::A::T%} let final self::A::T? #t1 = this.{self::A::_#A#x} in #t1{self::A::T%} : throw new _in::LateInitializationErrorImpl::•("Field 'x' has not been initialized.");
+ set x(generic-covariant-impl self::A::T% #t2) → void {
+ this.{self::A::_#A#x#isSet} = true;
+ this.{self::A::_#A#x} = #t2;
+ }
+}
+class B<T extends core::Object? = dynamic> extends core::Object {
+ generic-covariant-impl field self::B::T? _y = null;
+ synthetic constructor •() → self::B<self::B::T%>
+ : super core::Object::•()
+ ;
+ get y() → self::B::T?
+ return this.{self::B::_y};
+ set y(generic-covariant-impl self::B::T? val) → void {
+ this.{self::B::_y} = val;
+ }
+}
+static method main() → dynamic {
+ self::A<core::num> a = new self::A::•<core::int>();
+ self::expect(42, a.{self::A::x} = 42);
+ self::throws(() → core::double => a.{self::A::x} = 0.5);
+ self::B<core::num> b = new self::B::•<core::int>();
+ self::expect(42, b.{self::B::y} = 42);
+ self::throws(() → core::double => b.{self::B::y} = 0.5);
+}
+static method expect(dynamic expected, dynamic actual) → dynamic {
+ if(!expected.{core::Object::==}(actual))
+ throw "Expected ${expected}, actual ${actual}";
+}
+static method throws(() → void f) → dynamic {
+ try {
+ f.call();
+ }
+ on core::Object catch(final core::Object _) {
+ return;
+ }
+ throw "Expected exception";
+}
diff --git a/pkg/front_end/testcases/late_lowering/issue42407.dart.textual_outline.expect b/pkg/front_end/testcases/late_lowering/issue42407.dart.textual_outline.expect
new file mode 100644
index 0000000..d50d6fd
--- /dev/null
+++ b/pkg/front_end/testcases/late_lowering/issue42407.dart.textual_outline.expect
@@ -0,0 +1,12 @@
+class A<T> {
+ late T ;
+ x;
+}
+class B<T> {
+ T? _y;
+ T? get y => _y;
+ set y(T? val) { }
+}
+main() { }
+expect(expected, actual) { }
+throws(void Function() f) { }
diff --git a/pkg/front_end/testcases/late_lowering/issue42407.dart.weak.expect b/pkg/front_end/testcases/late_lowering/issue42407.dart.weak.expect
new file mode 100644
index 0000000..59101b9
--- /dev/null
+++ b/pkg/front_end/testcases/late_lowering/issue42407.dart.weak.expect
@@ -0,0 +1,50 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "dart:_internal" as _in;
+
+class A<T extends core::Object? = dynamic> extends core::Object {
+ generic-covariant-impl field self::A::T? _#A#x = null;
+ field core::bool _#A#x#isSet = false;
+ synthetic constructor •() → self::A<self::A::T%>
+ : super core::Object::•()
+ ;
+ get x() → self::A::T%
+ return this.{self::A::_#A#x#isSet} ?{self::A::T%} let final self::A::T? #t1 = this.{self::A::_#A#x} in #t1{self::A::T%} : throw new _in::LateInitializationErrorImpl::•("Field 'x' has not been initialized.");
+ set x(generic-covariant-impl self::A::T% #t2) → void {
+ this.{self::A::_#A#x#isSet} = true;
+ this.{self::A::_#A#x} = #t2;
+ }
+}
+class B<T extends core::Object? = dynamic> extends core::Object {
+ generic-covariant-impl field self::B::T? _y = null;
+ synthetic constructor •() → self::B<self::B::T%>
+ : super core::Object::•()
+ ;
+ get y() → self::B::T?
+ return this.{self::B::_y};
+ set y(generic-covariant-impl self::B::T? val) → void {
+ this.{self::B::_y} = val;
+ }
+}
+static method main() → dynamic {
+ self::A<core::num> a = new self::A::•<core::int>();
+ self::expect(42, a.{self::A::x} = 42);
+ self::throws(() → core::double => a.{self::A::x} = 0.5);
+ self::B<core::num> b = new self::B::•<core::int>();
+ self::expect(42, b.{self::B::y} = 42);
+ self::throws(() → core::double => b.{self::B::y} = 0.5);
+}
+static method expect(dynamic expected, dynamic actual) → dynamic {
+ if(!expected.{core::Object::==}(actual))
+ throw "Expected ${expected}, actual ${actual}";
+}
+static method throws(() → void f) → dynamic {
+ try {
+ f.call();
+ }
+ on core::Object catch(final core::Object _) {
+ return;
+ }
+ throw "Expected exception";
+}
diff --git a/pkg/front_end/testcases/late_lowering/issue42407.dart.weak.transformed.expect b/pkg/front_end/testcases/late_lowering/issue42407.dart.weak.transformed.expect
new file mode 100644
index 0000000..59101b9
--- /dev/null
+++ b/pkg/front_end/testcases/late_lowering/issue42407.dart.weak.transformed.expect
@@ -0,0 +1,50 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "dart:_internal" as _in;
+
+class A<T extends core::Object? = dynamic> extends core::Object {
+ generic-covariant-impl field self::A::T? _#A#x = null;
+ field core::bool _#A#x#isSet = false;
+ synthetic constructor •() → self::A<self::A::T%>
+ : super core::Object::•()
+ ;
+ get x() → self::A::T%
+ return this.{self::A::_#A#x#isSet} ?{self::A::T%} let final self::A::T? #t1 = this.{self::A::_#A#x} in #t1{self::A::T%} : throw new _in::LateInitializationErrorImpl::•("Field 'x' has not been initialized.");
+ set x(generic-covariant-impl self::A::T% #t2) → void {
+ this.{self::A::_#A#x#isSet} = true;
+ this.{self::A::_#A#x} = #t2;
+ }
+}
+class B<T extends core::Object? = dynamic> extends core::Object {
+ generic-covariant-impl field self::B::T? _y = null;
+ synthetic constructor •() → self::B<self::B::T%>
+ : super core::Object::•()
+ ;
+ get y() → self::B::T?
+ return this.{self::B::_y};
+ set y(generic-covariant-impl self::B::T? val) → void {
+ this.{self::B::_y} = val;
+ }
+}
+static method main() → dynamic {
+ self::A<core::num> a = new self::A::•<core::int>();
+ self::expect(42, a.{self::A::x} = 42);
+ self::throws(() → core::double => a.{self::A::x} = 0.5);
+ self::B<core::num> b = new self::B::•<core::int>();
+ self::expect(42, b.{self::B::y} = 42);
+ self::throws(() → core::double => b.{self::B::y} = 0.5);
+}
+static method expect(dynamic expected, dynamic actual) → dynamic {
+ if(!expected.{core::Object::==}(actual))
+ throw "Expected ${expected}, actual ${actual}";
+}
+static method throws(() → void f) → dynamic {
+ try {
+ f.call();
+ }
+ on core::Object catch(final core::Object _) {
+ return;
+ }
+ throw "Expected exception";
+}
diff --git a/pkg/front_end/testcases/late_lowering/late_field_with_initializer.dart.outline.expect b/pkg/front_end/testcases/late_lowering/late_field_with_initializer.dart.outline.expect
index e0a070f..3df6d7f 100644
--- a/pkg/front_end/testcases/late_lowering/late_field_with_initializer.dart.outline.expect
+++ b/pkg/front_end/testcases/late_lowering/late_field_with_initializer.dart.outline.expect
@@ -22,9 +22,9 @@
get lateInstanceField() → core::int;
set lateInstanceField(core::int #t3) → void;
get lateGenericField1() → self::Class::T%;
- set lateGenericField1(self::Class::T% #t4) → void;
+ set lateGenericField1(generic-covariant-impl self::Class::T% #t4) → void;
get lateGenericField2() → self::Class::T%;
- set lateGenericField2(self::Class::T% #t5) → void;
+ set lateGenericField2(generic-covariant-impl self::Class::T% #t5) → void;
method instanceMethod(generic-covariant-impl self::Class::T% value) → dynamic
;
}
diff --git a/pkg/front_end/testcases/late_lowering/late_field_with_initializer.dart.strong.expect b/pkg/front_end/testcases/late_lowering/late_field_with_initializer.dart.strong.expect
index 18cb216..4030034 100644
--- a/pkg/front_end/testcases/late_lowering/late_field_with_initializer.dart.strong.expect
+++ b/pkg/front_end/testcases/late_lowering/late_field_with_initializer.dart.strong.expect
@@ -38,7 +38,7 @@
}
return let final self::Class::T? #t7 = this.{self::Class::_#Class#lateGenericField1} in #t7{self::Class::T%};
}
- set lateGenericField1(self::Class::T% #t8) → void {
+ set lateGenericField1(generic-covariant-impl self::Class::T% #t8) → void {
this.{self::Class::_#Class#lateGenericField1#isSet} = true;
this.{self::Class::_#Class#lateGenericField1} = #t8;
}
@@ -49,7 +49,7 @@
}
return let final self::Class::T? #t9 = this.{self::Class::_#Class#lateGenericField2} in #t9{self::Class::T%};
}
- set lateGenericField2(self::Class::T% #t10) → void {
+ set lateGenericField2(generic-covariant-impl self::Class::T% #t10) → void {
this.{self::Class::_#Class#lateGenericField2#isSet} = true;
this.{self::Class::_#Class#lateGenericField2} = #t10;
}
diff --git a/pkg/front_end/testcases/late_lowering/late_field_with_initializer.dart.strong.transformed.expect b/pkg/front_end/testcases/late_lowering/late_field_with_initializer.dart.strong.transformed.expect
index 18cb216..4030034 100644
--- a/pkg/front_end/testcases/late_lowering/late_field_with_initializer.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/late_lowering/late_field_with_initializer.dart.strong.transformed.expect
@@ -38,7 +38,7 @@
}
return let final self::Class::T? #t7 = this.{self::Class::_#Class#lateGenericField1} in #t7{self::Class::T%};
}
- set lateGenericField1(self::Class::T% #t8) → void {
+ set lateGenericField1(generic-covariant-impl self::Class::T% #t8) → void {
this.{self::Class::_#Class#lateGenericField1#isSet} = true;
this.{self::Class::_#Class#lateGenericField1} = #t8;
}
@@ -49,7 +49,7 @@
}
return let final self::Class::T? #t9 = this.{self::Class::_#Class#lateGenericField2} in #t9{self::Class::T%};
}
- set lateGenericField2(self::Class::T% #t10) → void {
+ set lateGenericField2(generic-covariant-impl self::Class::T% #t10) → void {
this.{self::Class::_#Class#lateGenericField2#isSet} = true;
this.{self::Class::_#Class#lateGenericField2} = #t10;
}
diff --git a/pkg/front_end/testcases/late_lowering/late_field_with_initializer.dart.weak.expect b/pkg/front_end/testcases/late_lowering/late_field_with_initializer.dart.weak.expect
index 18cb216..4030034 100644
--- a/pkg/front_end/testcases/late_lowering/late_field_with_initializer.dart.weak.expect
+++ b/pkg/front_end/testcases/late_lowering/late_field_with_initializer.dart.weak.expect
@@ -38,7 +38,7 @@
}
return let final self::Class::T? #t7 = this.{self::Class::_#Class#lateGenericField1} in #t7{self::Class::T%};
}
- set lateGenericField1(self::Class::T% #t8) → void {
+ set lateGenericField1(generic-covariant-impl self::Class::T% #t8) → void {
this.{self::Class::_#Class#lateGenericField1#isSet} = true;
this.{self::Class::_#Class#lateGenericField1} = #t8;
}
@@ -49,7 +49,7 @@
}
return let final self::Class::T? #t9 = this.{self::Class::_#Class#lateGenericField2} in #t9{self::Class::T%};
}
- set lateGenericField2(self::Class::T% #t10) → void {
+ set lateGenericField2(generic-covariant-impl self::Class::T% #t10) → void {
this.{self::Class::_#Class#lateGenericField2#isSet} = true;
this.{self::Class::_#Class#lateGenericField2} = #t10;
}
diff --git a/pkg/front_end/testcases/late_lowering/late_field_with_initializer.dart.weak.transformed.expect b/pkg/front_end/testcases/late_lowering/late_field_with_initializer.dart.weak.transformed.expect
index 18cb216..4030034 100644
--- a/pkg/front_end/testcases/late_lowering/late_field_with_initializer.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/late_lowering/late_field_with_initializer.dart.weak.transformed.expect
@@ -38,7 +38,7 @@
}
return let final self::Class::T? #t7 = this.{self::Class::_#Class#lateGenericField1} in #t7{self::Class::T%};
}
- set lateGenericField1(self::Class::T% #t8) → void {
+ set lateGenericField1(generic-covariant-impl self::Class::T% #t8) → void {
this.{self::Class::_#Class#lateGenericField1#isSet} = true;
this.{self::Class::_#Class#lateGenericField1} = #t8;
}
@@ -49,7 +49,7 @@
}
return let final self::Class::T? #t9 = this.{self::Class::_#Class#lateGenericField2} in #t9{self::Class::T%};
}
- set lateGenericField2(self::Class::T% #t10) → void {
+ set lateGenericField2(generic-covariant-impl self::Class::T% #t10) → void {
this.{self::Class::_#Class#lateGenericField2#isSet} = true;
this.{self::Class::_#Class#lateGenericField2} = #t10;
}
diff --git a/pkg/front_end/testcases/late_lowering/late_field_without_initializer.dart.outline.expect b/pkg/front_end/testcases/late_lowering/late_field_without_initializer.dart.outline.expect
index 10a2e03..4eeb05b 100644
--- a/pkg/front_end/testcases/late_lowering/late_field_without_initializer.dart.outline.expect
+++ b/pkg/front_end/testcases/late_lowering/late_field_without_initializer.dart.outline.expect
@@ -19,7 +19,7 @@
get lateInstanceField() → core::int;
set lateInstanceField(core::int #t3) → void;
get lateGenericInstanceField() → self::Class::T%;
- set lateGenericInstanceField(self::Class::T% #t4) → void;
+ set lateGenericInstanceField(generic-covariant-impl self::Class::T% #t4) → void;
method instanceMethod(generic-covariant-impl self::Class::T% value) → dynamic
;
}
diff --git a/pkg/front_end/testcases/late_lowering/late_field_without_initializer.dart.strong.expect b/pkg/front_end/testcases/late_lowering/late_field_without_initializer.dart.strong.expect
index 4d78e46..28513d18 100644
--- a/pkg/front_end/testcases/late_lowering/late_field_without_initializer.dart.strong.expect
+++ b/pkg/front_end/testcases/late_lowering/late_field_without_initializer.dart.strong.expect
@@ -31,7 +31,7 @@
this.{self::Class::_#Class#lateInstanceField} = #t6;
get lateGenericInstanceField() → self::Class::T%
return this.{self::Class::_#Class#lateGenericInstanceField#isSet} ?{self::Class::T%} let final self::Class::T? #t7 = this.{self::Class::_#Class#lateGenericInstanceField} in #t7{self::Class::T%} : throw new _in::LateInitializationErrorImpl::•("Field 'lateGenericInstanceField' has not been initialized.");
- set lateGenericInstanceField(self::Class::T% #t8) → void {
+ set lateGenericInstanceField(generic-covariant-impl self::Class::T% #t8) → void {
this.{self::Class::_#Class#lateGenericInstanceField#isSet} = true;
this.{self::Class::_#Class#lateGenericInstanceField} = #t8;
}
diff --git a/pkg/front_end/testcases/late_lowering/late_field_without_initializer.dart.strong.transformed.expect b/pkg/front_end/testcases/late_lowering/late_field_without_initializer.dart.strong.transformed.expect
index 4d78e46..28513d18 100644
--- a/pkg/front_end/testcases/late_lowering/late_field_without_initializer.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/late_lowering/late_field_without_initializer.dart.strong.transformed.expect
@@ -31,7 +31,7 @@
this.{self::Class::_#Class#lateInstanceField} = #t6;
get lateGenericInstanceField() → self::Class::T%
return this.{self::Class::_#Class#lateGenericInstanceField#isSet} ?{self::Class::T%} let final self::Class::T? #t7 = this.{self::Class::_#Class#lateGenericInstanceField} in #t7{self::Class::T%} : throw new _in::LateInitializationErrorImpl::•("Field 'lateGenericInstanceField' has not been initialized.");
- set lateGenericInstanceField(self::Class::T% #t8) → void {
+ set lateGenericInstanceField(generic-covariant-impl self::Class::T% #t8) → void {
this.{self::Class::_#Class#lateGenericInstanceField#isSet} = true;
this.{self::Class::_#Class#lateGenericInstanceField} = #t8;
}
diff --git a/pkg/front_end/testcases/late_lowering/late_field_without_initializer.dart.weak.expect b/pkg/front_end/testcases/late_lowering/late_field_without_initializer.dart.weak.expect
index 4d78e46..28513d18 100644
--- a/pkg/front_end/testcases/late_lowering/late_field_without_initializer.dart.weak.expect
+++ b/pkg/front_end/testcases/late_lowering/late_field_without_initializer.dart.weak.expect
@@ -31,7 +31,7 @@
this.{self::Class::_#Class#lateInstanceField} = #t6;
get lateGenericInstanceField() → self::Class::T%
return this.{self::Class::_#Class#lateGenericInstanceField#isSet} ?{self::Class::T%} let final self::Class::T? #t7 = this.{self::Class::_#Class#lateGenericInstanceField} in #t7{self::Class::T%} : throw new _in::LateInitializationErrorImpl::•("Field 'lateGenericInstanceField' has not been initialized.");
- set lateGenericInstanceField(self::Class::T% #t8) → void {
+ set lateGenericInstanceField(generic-covariant-impl self::Class::T% #t8) → void {
this.{self::Class::_#Class#lateGenericInstanceField#isSet} = true;
this.{self::Class::_#Class#lateGenericInstanceField} = #t8;
}
diff --git a/pkg/front_end/testcases/late_lowering/late_field_without_initializer.dart.weak.transformed.expect b/pkg/front_end/testcases/late_lowering/late_field_without_initializer.dart.weak.transformed.expect
index 4d78e46..28513d18 100644
--- a/pkg/front_end/testcases/late_lowering/late_field_without_initializer.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/late_lowering/late_field_without_initializer.dart.weak.transformed.expect
@@ -31,7 +31,7 @@
this.{self::Class::_#Class#lateInstanceField} = #t6;
get lateGenericInstanceField() → self::Class::T%
return this.{self::Class::_#Class#lateGenericInstanceField#isSet} ?{self::Class::T%} let final self::Class::T? #t7 = this.{self::Class::_#Class#lateGenericInstanceField} in #t7{self::Class::T%} : throw new _in::LateInitializationErrorImpl::•("Field 'lateGenericInstanceField' has not been initialized.");
- set lateGenericInstanceField(self::Class::T% #t8) → void {
+ set lateGenericInstanceField(generic-covariant-impl self::Class::T% #t8) → void {
this.{self::Class::_#Class#lateGenericInstanceField#isSet} = true;
this.{self::Class::_#Class#lateGenericInstanceField} = #t8;
}
diff --git a/pkg/front_end/testcases/late_lowering/late_future_or.dart.outline.expect b/pkg/front_end/testcases/late_lowering/late_future_or.dart.outline.expect
index ac02afe..14dcaea 100644
--- a/pkg/front_end/testcases/late_lowering/late_future_or.dart.outline.expect
+++ b/pkg/front_end/testcases/late_lowering/late_future_or.dart.outline.expect
@@ -22,11 +22,11 @@
get field2() → FutureOr<dynamic>?;
set field2(FutureOr<dynamic>? #t2) → void;
get field3() → FutureOr<self::C::T%>;
- set field3(FutureOr<self::C::T%>#t3) → void;
+ set field3(generic-covariant-impl FutureOr<self::C::T%>#t3) → void;
get field4() → FutureOr<self::C::T?>;
- set field4(FutureOr<self::C::T?>#t4) → void;
+ set field4(generic-covariant-impl FutureOr<self::C::T?>#t4) → void;
get field5() → FutureOr<self::C::T?>?;
- set field5(FutureOr<self::C::T?>? #t5) → void;
+ set field5(generic-covariant-impl FutureOr<self::C::T?>? #t5) → void;
method method() → dynamic
;
}
diff --git a/pkg/front_end/testcases/late_lowering/late_future_or.dart.strong.expect b/pkg/front_end/testcases/late_lowering/late_future_or.dart.strong.expect
index c8d8ea6..6a5587c 100644
--- a/pkg/front_end/testcases/late_lowering/late_future_or.dart.strong.expect
+++ b/pkg/front_end/testcases/late_lowering/late_future_or.dart.strong.expect
@@ -33,19 +33,19 @@
}
get field3() → FutureOr<self::C::T%>
return this.{self::C::_#C#field3#isSet} ?{FutureOr<self::C::T%>} let final FutureOr<self::C::T%>? #t3 = this.{self::C::_#C#field3} in #t3{FutureOr<self::C::T%>} : throw new _in::LateInitializationErrorImpl::•("Field 'field3' has not been initialized.");
- set field3(FutureOr<self::C::T%>#t4) → void {
+ set field3(generic-covariant-impl FutureOr<self::C::T%>#t4) → void {
this.{self::C::_#C#field3#isSet} = true;
this.{self::C::_#C#field3} = #t4;
}
get field4() → FutureOr<self::C::T?>
return this.{self::C::_#C#field4#isSet} ?{FutureOr<self::C::T?>} this.{self::C::_#C#field4} : throw new _in::LateInitializationErrorImpl::•("Field 'field4' has not been initialized.");
- set field4(FutureOr<self::C::T?>#t5) → void {
+ set field4(generic-covariant-impl FutureOr<self::C::T?>#t5) → void {
this.{self::C::_#C#field4#isSet} = true;
this.{self::C::_#C#field4} = #t5;
}
get field5() → FutureOr<self::C::T?>?
return this.{self::C::_#C#field5#isSet} ?{FutureOr<self::C::T?>?} this.{self::C::_#C#field5} : throw new _in::LateInitializationErrorImpl::•("Field 'field5' has not been initialized.");
- set field5(FutureOr<self::C::T?>? #t6) → void {
+ set field5(generic-covariant-impl FutureOr<self::C::T?>? #t6) → void {
this.{self::C::_#C#field5#isSet} = true;
this.{self::C::_#C#field5} = #t6;
}
diff --git a/pkg/front_end/testcases/late_lowering/late_future_or.dart.strong.transformed.expect b/pkg/front_end/testcases/late_lowering/late_future_or.dart.strong.transformed.expect
index c8d8ea6..6a5587c 100644
--- a/pkg/front_end/testcases/late_lowering/late_future_or.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/late_lowering/late_future_or.dart.strong.transformed.expect
@@ -33,19 +33,19 @@
}
get field3() → FutureOr<self::C::T%>
return this.{self::C::_#C#field3#isSet} ?{FutureOr<self::C::T%>} let final FutureOr<self::C::T%>? #t3 = this.{self::C::_#C#field3} in #t3{FutureOr<self::C::T%>} : throw new _in::LateInitializationErrorImpl::•("Field 'field3' has not been initialized.");
- set field3(FutureOr<self::C::T%>#t4) → void {
+ set field3(generic-covariant-impl FutureOr<self::C::T%>#t4) → void {
this.{self::C::_#C#field3#isSet} = true;
this.{self::C::_#C#field3} = #t4;
}
get field4() → FutureOr<self::C::T?>
return this.{self::C::_#C#field4#isSet} ?{FutureOr<self::C::T?>} this.{self::C::_#C#field4} : throw new _in::LateInitializationErrorImpl::•("Field 'field4' has not been initialized.");
- set field4(FutureOr<self::C::T?>#t5) → void {
+ set field4(generic-covariant-impl FutureOr<self::C::T?>#t5) → void {
this.{self::C::_#C#field4#isSet} = true;
this.{self::C::_#C#field4} = #t5;
}
get field5() → FutureOr<self::C::T?>?
return this.{self::C::_#C#field5#isSet} ?{FutureOr<self::C::T?>?} this.{self::C::_#C#field5} : throw new _in::LateInitializationErrorImpl::•("Field 'field5' has not been initialized.");
- set field5(FutureOr<self::C::T?>? #t6) → void {
+ set field5(generic-covariant-impl FutureOr<self::C::T?>? #t6) → void {
this.{self::C::_#C#field5#isSet} = true;
this.{self::C::_#C#field5} = #t6;
}
diff --git a/pkg/front_end/testcases/late_lowering/late_future_or.dart.weak.expect b/pkg/front_end/testcases/late_lowering/late_future_or.dart.weak.expect
index c8d8ea6..6a5587c 100644
--- a/pkg/front_end/testcases/late_lowering/late_future_or.dart.weak.expect
+++ b/pkg/front_end/testcases/late_lowering/late_future_or.dart.weak.expect
@@ -33,19 +33,19 @@
}
get field3() → FutureOr<self::C::T%>
return this.{self::C::_#C#field3#isSet} ?{FutureOr<self::C::T%>} let final FutureOr<self::C::T%>? #t3 = this.{self::C::_#C#field3} in #t3{FutureOr<self::C::T%>} : throw new _in::LateInitializationErrorImpl::•("Field 'field3' has not been initialized.");
- set field3(FutureOr<self::C::T%>#t4) → void {
+ set field3(generic-covariant-impl FutureOr<self::C::T%>#t4) → void {
this.{self::C::_#C#field3#isSet} = true;
this.{self::C::_#C#field3} = #t4;
}
get field4() → FutureOr<self::C::T?>
return this.{self::C::_#C#field4#isSet} ?{FutureOr<self::C::T?>} this.{self::C::_#C#field4} : throw new _in::LateInitializationErrorImpl::•("Field 'field4' has not been initialized.");
- set field4(FutureOr<self::C::T?>#t5) → void {
+ set field4(generic-covariant-impl FutureOr<self::C::T?>#t5) → void {
this.{self::C::_#C#field4#isSet} = true;
this.{self::C::_#C#field4} = #t5;
}
get field5() → FutureOr<self::C::T?>?
return this.{self::C::_#C#field5#isSet} ?{FutureOr<self::C::T?>?} this.{self::C::_#C#field5} : throw new _in::LateInitializationErrorImpl::•("Field 'field5' has not been initialized.");
- set field5(FutureOr<self::C::T?>? #t6) → void {
+ set field5(generic-covariant-impl FutureOr<self::C::T?>? #t6) → void {
this.{self::C::_#C#field5#isSet} = true;
this.{self::C::_#C#field5} = #t6;
}
diff --git a/pkg/front_end/testcases/late_lowering/late_future_or.dart.weak.transformed.expect b/pkg/front_end/testcases/late_lowering/late_future_or.dart.weak.transformed.expect
index c8d8ea6..6a5587c 100644
--- a/pkg/front_end/testcases/late_lowering/late_future_or.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/late_lowering/late_future_or.dart.weak.transformed.expect
@@ -33,19 +33,19 @@
}
get field3() → FutureOr<self::C::T%>
return this.{self::C::_#C#field3#isSet} ?{FutureOr<self::C::T%>} let final FutureOr<self::C::T%>? #t3 = this.{self::C::_#C#field3} in #t3{FutureOr<self::C::T%>} : throw new _in::LateInitializationErrorImpl::•("Field 'field3' has not been initialized.");
- set field3(FutureOr<self::C::T%>#t4) → void {
+ set field3(generic-covariant-impl FutureOr<self::C::T%>#t4) → void {
this.{self::C::_#C#field3#isSet} = true;
this.{self::C::_#C#field3} = #t4;
}
get field4() → FutureOr<self::C::T?>
return this.{self::C::_#C#field4#isSet} ?{FutureOr<self::C::T?>} this.{self::C::_#C#field4} : throw new _in::LateInitializationErrorImpl::•("Field 'field4' has not been initialized.");
- set field4(FutureOr<self::C::T?>#t5) → void {
+ set field4(generic-covariant-impl FutureOr<self::C::T?>#t5) → void {
this.{self::C::_#C#field4#isSet} = true;
this.{self::C::_#C#field4} = #t5;
}
get field5() → FutureOr<self::C::T?>?
return this.{self::C::_#C#field5#isSet} ?{FutureOr<self::C::T?>?} this.{self::C::_#C#field5} : throw new _in::LateInitializationErrorImpl::•("Field 'field5' has not been initialized.");
- set field5(FutureOr<self::C::T?>? #t6) → void {
+ set field5(generic-covariant-impl FutureOr<self::C::T?>? #t6) → void {
this.{self::C::_#C#field5#isSet} = true;
this.{self::C::_#C#field5} = #t6;
}
diff --git a/pkg/front_end/testcases/late_lowering/late_nullable_field_with_initializer.dart.outline.expect b/pkg/front_end/testcases/late_lowering/late_nullable_field_with_initializer.dart.outline.expect
index f377c7e..995658d 100644
--- a/pkg/front_end/testcases/late_lowering/late_nullable_field_with_initializer.dart.outline.expect
+++ b/pkg/front_end/testcases/late_lowering/late_nullable_field_with_initializer.dart.outline.expect
@@ -31,7 +31,7 @@
method lateGenericInstanceFieldInit() → self::Class::T?
;
get lateGenericInstanceField() → self::Class::T?;
- set lateGenericInstanceField(self::Class::T? #t4) → void;
+ set lateGenericInstanceField(generic-covariant-impl self::Class::T? #t4) → void;
method instanceMethod(generic-covariant-impl self::Class::T? value) → dynamic
;
}
diff --git a/pkg/front_end/testcases/late_lowering/late_nullable_field_with_initializer.dart.strong.expect b/pkg/front_end/testcases/late_lowering/late_nullable_field_with_initializer.dart.strong.expect
index e44eb03..36b9cb9 100644
--- a/pkg/front_end/testcases/late_lowering/late_nullable_field_with_initializer.dart.strong.expect
+++ b/pkg/front_end/testcases/late_lowering/late_nullable_field_with_initializer.dart.strong.expect
@@ -68,7 +68,7 @@
}
return this.{self::Class::_#Class#lateGenericInstanceField};
}
- set lateGenericInstanceField(self::Class::T? #t4) → void {
+ set lateGenericInstanceField(generic-covariant-impl self::Class::T? #t4) → void {
this.{self::Class::_#Class#lateGenericInstanceField#isSet} = true;
this.{self::Class::_#Class#lateGenericInstanceField} = #t4;
}
diff --git a/pkg/front_end/testcases/late_lowering/late_nullable_field_with_initializer.dart.strong.transformed.expect b/pkg/front_end/testcases/late_lowering/late_nullable_field_with_initializer.dart.strong.transformed.expect
index e44eb03..36b9cb9 100644
--- a/pkg/front_end/testcases/late_lowering/late_nullable_field_with_initializer.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/late_lowering/late_nullable_field_with_initializer.dart.strong.transformed.expect
@@ -68,7 +68,7 @@
}
return this.{self::Class::_#Class#lateGenericInstanceField};
}
- set lateGenericInstanceField(self::Class::T? #t4) → void {
+ set lateGenericInstanceField(generic-covariant-impl self::Class::T? #t4) → void {
this.{self::Class::_#Class#lateGenericInstanceField#isSet} = true;
this.{self::Class::_#Class#lateGenericInstanceField} = #t4;
}
diff --git a/pkg/front_end/testcases/late_lowering/late_nullable_field_with_initializer.dart.weak.expect b/pkg/front_end/testcases/late_lowering/late_nullable_field_with_initializer.dart.weak.expect
index e44eb03..36b9cb9 100644
--- a/pkg/front_end/testcases/late_lowering/late_nullable_field_with_initializer.dart.weak.expect
+++ b/pkg/front_end/testcases/late_lowering/late_nullable_field_with_initializer.dart.weak.expect
@@ -68,7 +68,7 @@
}
return this.{self::Class::_#Class#lateGenericInstanceField};
}
- set lateGenericInstanceField(self::Class::T? #t4) → void {
+ set lateGenericInstanceField(generic-covariant-impl self::Class::T? #t4) → void {
this.{self::Class::_#Class#lateGenericInstanceField#isSet} = true;
this.{self::Class::_#Class#lateGenericInstanceField} = #t4;
}
diff --git a/pkg/front_end/testcases/late_lowering/late_nullable_field_with_initializer.dart.weak.transformed.expect b/pkg/front_end/testcases/late_lowering/late_nullable_field_with_initializer.dart.weak.transformed.expect
index e44eb03..36b9cb9 100644
--- a/pkg/front_end/testcases/late_lowering/late_nullable_field_with_initializer.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/late_lowering/late_nullable_field_with_initializer.dart.weak.transformed.expect
@@ -68,7 +68,7 @@
}
return this.{self::Class::_#Class#lateGenericInstanceField};
}
- set lateGenericInstanceField(self::Class::T? #t4) → void {
+ set lateGenericInstanceField(generic-covariant-impl self::Class::T? #t4) → void {
this.{self::Class::_#Class#lateGenericInstanceField#isSet} = true;
this.{self::Class::_#Class#lateGenericInstanceField} = #t4;
}
diff --git a/pkg/front_end/testcases/late_lowering/late_nullable_field_without_initializer.dart.outline.expect b/pkg/front_end/testcases/late_lowering/late_nullable_field_without_initializer.dart.outline.expect
index b5c1e15..b36f1a2 100644
--- a/pkg/front_end/testcases/late_lowering/late_nullable_field_without_initializer.dart.outline.expect
+++ b/pkg/front_end/testcases/late_lowering/late_nullable_field_without_initializer.dart.outline.expect
@@ -22,7 +22,7 @@
get lateInstanceField() → core::int?;
set lateInstanceField(core::int? #t3) → void;
get lateGenericInstanceField() → self::Class::T?;
- set lateGenericInstanceField(self::Class::T? #t4) → void;
+ set lateGenericInstanceField(generic-covariant-impl self::Class::T? #t4) → void;
method instanceMethod(generic-covariant-impl self::Class::T? value) → dynamic
;
}
diff --git a/pkg/front_end/testcases/late_lowering/late_nullable_field_without_initializer.dart.strong.expect b/pkg/front_end/testcases/late_lowering/late_nullable_field_without_initializer.dart.strong.expect
index f0bc8b7..fe6ab5e 100644
--- a/pkg/front_end/testcases/late_lowering/late_nullable_field_without_initializer.dart.strong.expect
+++ b/pkg/front_end/testcases/late_lowering/late_nullable_field_without_initializer.dart.strong.expect
@@ -40,7 +40,7 @@
}
get lateGenericInstanceField() → self::Class::T?
return this.{self::Class::_#Class#lateGenericInstanceField#isSet} ?{self::Class::T?} this.{self::Class::_#Class#lateGenericInstanceField} : throw new _in::LateInitializationErrorImpl::•("Field 'lateGenericInstanceField' has not been initialized.");
- set lateGenericInstanceField(self::Class::T? #t4) → void {
+ set lateGenericInstanceField(generic-covariant-impl self::Class::T? #t4) → void {
this.{self::Class::_#Class#lateGenericInstanceField#isSet} = true;
this.{self::Class::_#Class#lateGenericInstanceField} = #t4;
}
diff --git a/pkg/front_end/testcases/late_lowering/late_nullable_field_without_initializer.dart.strong.transformed.expect b/pkg/front_end/testcases/late_lowering/late_nullable_field_without_initializer.dart.strong.transformed.expect
index f0bc8b7..fe6ab5e 100644
--- a/pkg/front_end/testcases/late_lowering/late_nullable_field_without_initializer.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/late_lowering/late_nullable_field_without_initializer.dart.strong.transformed.expect
@@ -40,7 +40,7 @@
}
get lateGenericInstanceField() → self::Class::T?
return this.{self::Class::_#Class#lateGenericInstanceField#isSet} ?{self::Class::T?} this.{self::Class::_#Class#lateGenericInstanceField} : throw new _in::LateInitializationErrorImpl::•("Field 'lateGenericInstanceField' has not been initialized.");
- set lateGenericInstanceField(self::Class::T? #t4) → void {
+ set lateGenericInstanceField(generic-covariant-impl self::Class::T? #t4) → void {
this.{self::Class::_#Class#lateGenericInstanceField#isSet} = true;
this.{self::Class::_#Class#lateGenericInstanceField} = #t4;
}
diff --git a/pkg/front_end/testcases/late_lowering/late_nullable_field_without_initializer.dart.weak.expect b/pkg/front_end/testcases/late_lowering/late_nullable_field_without_initializer.dart.weak.expect
index f0bc8b7..fe6ab5e 100644
--- a/pkg/front_end/testcases/late_lowering/late_nullable_field_without_initializer.dart.weak.expect
+++ b/pkg/front_end/testcases/late_lowering/late_nullable_field_without_initializer.dart.weak.expect
@@ -40,7 +40,7 @@
}
get lateGenericInstanceField() → self::Class::T?
return this.{self::Class::_#Class#lateGenericInstanceField#isSet} ?{self::Class::T?} this.{self::Class::_#Class#lateGenericInstanceField} : throw new _in::LateInitializationErrorImpl::•("Field 'lateGenericInstanceField' has not been initialized.");
- set lateGenericInstanceField(self::Class::T? #t4) → void {
+ set lateGenericInstanceField(generic-covariant-impl self::Class::T? #t4) → void {
this.{self::Class::_#Class#lateGenericInstanceField#isSet} = true;
this.{self::Class::_#Class#lateGenericInstanceField} = #t4;
}
diff --git a/pkg/front_end/testcases/late_lowering/late_nullable_field_without_initializer.dart.weak.transformed.expect b/pkg/front_end/testcases/late_lowering/late_nullable_field_without_initializer.dart.weak.transformed.expect
index f0bc8b7..fe6ab5e 100644
--- a/pkg/front_end/testcases/late_lowering/late_nullable_field_without_initializer.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/late_lowering/late_nullable_field_without_initializer.dart.weak.transformed.expect
@@ -40,7 +40,7 @@
}
get lateGenericInstanceField() → self::Class::T?
return this.{self::Class::_#Class#lateGenericInstanceField#isSet} ?{self::Class::T?} this.{self::Class::_#Class#lateGenericInstanceField} : throw new _in::LateInitializationErrorImpl::•("Field 'lateGenericInstanceField' has not been initialized.");
- set lateGenericInstanceField(self::Class::T? #t4) → void {
+ set lateGenericInstanceField(generic-covariant-impl self::Class::T? #t4) → void {
this.{self::Class::_#Class#lateGenericInstanceField#isSet} = true;
this.{self::Class::_#Class#lateGenericInstanceField} = #t4;
}
diff --git a/pkg/front_end/testcases/nnbd_mixed/issue42387/foundation_basic_types_lib.dart b/pkg/front_end/testcases/nnbd_mixed/issue42387/foundation_basic_types_lib.dart
new file mode 100644
index 0000000..c2736fd
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd_mixed/issue42387/foundation_basic_types_lib.dart
@@ -0,0 +1,7 @@
+// 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.
+
+// @dart = 2.9
+
+typedef Future<Null> AsyncCallback();
diff --git a/pkg/front_end/testcases/nnbd_mixed/issue42387/foundation_basic_types_lib.dart.weak.expect b/pkg/front_end/testcases/nnbd_mixed/issue42387/foundation_basic_types_lib.dart.weak.expect
new file mode 100644
index 0000000..4d3ffdd
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd_mixed/issue42387/foundation_basic_types_lib.dart.weak.expect
@@ -0,0 +1,129 @@
+library;
+import self as self;
+import "foundation_binding_lib.dart" as fou;
+import "scheduler_binding_lib.dart" as sch;
+import "services_binding_lib.dart" as ser;
+
+import "org-dartlang-testcase:///foundation_lib.dart";
+import "org-dartlang-testcase:///services_lib.dart";
+import "org-dartlang-testcase:///scheduler_lib.dart";
+
+abstract class _TestSchedulerBinding&BindingBase&SchedulerBinding = fou::BindingBase with sch::SchedulerBinding /*isAnonymousMixin*/ {
+ synthetic constructor •() → self::_TestSchedulerBinding&BindingBase&SchedulerBinding*
+ : super fou::BindingBase::•()
+ ;
+}
+abstract class _TestSchedulerBinding&BindingBase&SchedulerBinding&ServicesBinding = self::_TestSchedulerBinding&BindingBase&SchedulerBinding with ser::ServicesBinding /*isAnonymousMixin*/ {
+ synthetic constructor •() → self::_TestSchedulerBinding&BindingBase&SchedulerBinding&ServicesBinding*
+ : super self::_TestSchedulerBinding&BindingBase&SchedulerBinding::•()
+ ;
+}
+class TestSchedulerBinding extends self::_TestSchedulerBinding&BindingBase&SchedulerBinding&ServicesBinding {
+ synthetic constructor •() → self::TestSchedulerBinding*
+ : super self::_TestSchedulerBinding&BindingBase&SchedulerBinding&ServicesBinding::•()
+ ;
+}
+static method main() → dynamic {}
+
+library /*isNonNullableByDefault*/;
+import self as self2;
+import "dart:async" as asy;
+import "dart:core" as core;
+
+typedef AsyncCallback = () → asy::Future<core::Null?>;
+
+library /*isNonNullableByDefault*/;
+import self as fou;
+import "dart:core" as core;
+import "dart:async" as asy;
+
+import "org-dartlang-testcase:///foundation_basic_types_lib.dart";
+
+abstract class BindingBase extends core::Object {
+ synthetic constructor •() → fou::BindingBase
+ : super core::Object::•()
+ ;
+ method registerSignalServiceExtension({required core::String name = #C1, required () → asy::Future<core::Null?> callback = #C1}) → void {}
+}
+
+library;
+import self as self3;
+import "foundation_basic_types_lib.dart" as self2;
+import "foundation_binding_lib.dart" as fou;
+additionalExports = (self2::AsyncCallback,
+ fou::BindingBase)
+
+export "org-dartlang-testcase:///foundation_basic_types_lib.dart";
+export "org-dartlang-testcase:///foundation_binding_lib.dart";
+
+
+library;
+import self as sch;
+import "foundation_binding_lib.dart" as fou;
+import "dart:core" as core;
+import "dart:async" as asy;
+
+import "org-dartlang-testcase:///foundation_lib.dart";
+
+abstract class SchedulerBinding extends fou::BindingBase /*isMixinDeclaration*/ {
+ abstract member-signature method registerSignalServiceExtension({core::String* name = #C1, () →* asy::Future<core::Null?>* callback = #C1}) → void;
+ abstract member-signature get _identityHashCode() → core::int*;
+ abstract member-signature method _instanceOf(dynamic instantiatorTypeArguments, dynamic functionTypeArguments, dynamic type) → core::bool*;
+ abstract member-signature method _simpleInstanceOf(dynamic type) → core::bool*;
+ abstract member-signature method _simpleInstanceOfTrue(dynamic type) → core::bool*;
+ abstract member-signature method _simpleInstanceOfFalse(dynamic type) → core::bool*;
+ abstract member-signature operator ==(dynamic other) → core::bool*;
+ abstract member-signature get hashCode() → core::int*;
+ abstract member-signature method toString() → core::String*;
+ abstract member-signature method noSuchMethod(core::Invocation* invocation) → dynamic;
+ abstract member-signature get runtimeType() → core::Type*;
+}
+
+library;
+import self as self4;
+import "scheduler_binding_lib.dart" as sch;
+additionalExports = (sch::SchedulerBinding)
+
+export "org-dartlang-testcase:///scheduler_binding_lib.dart";
+
+
+library;
+import self as ser;
+import "dart:core" as core;
+import "foundation_binding_lib.dart" as fou;
+import "scheduler_binding_lib.dart" as sch;
+import "dart:async" as asy;
+
+import "org-dartlang-testcase:///foundation_lib.dart";
+import "org-dartlang-testcase:///scheduler_lib.dart";
+
+abstract class _ServicesBinding&BindingBase&SchedulerBinding extends core::Object implements fou::BindingBase, sch::SchedulerBinding /*isAnonymousMixin*/ {
+ synthetic constructor •() → ser::_ServicesBinding&BindingBase&SchedulerBinding*
+ : super core::Object::•()
+ ;
+ abstract member-signature get _identityHashCode() → core::int*;
+ abstract member-signature method _instanceOf(dynamic instantiatorTypeArguments, dynamic functionTypeArguments, dynamic type) → core::bool*;
+ abstract member-signature method _simpleInstanceOf(dynamic type) → core::bool*;
+ abstract member-signature method _simpleInstanceOfTrue(dynamic type) → core::bool*;
+ abstract member-signature method _simpleInstanceOfFalse(dynamic type) → core::bool*;
+ abstract member-signature operator ==(dynamic other) → core::bool*;
+ abstract member-signature get hashCode() → core::int*;
+ abstract member-signature method toString() → core::String*;
+ abstract member-signature method noSuchMethod(core::Invocation* invocation) → dynamic;
+ abstract member-signature get runtimeType() → core::Type*;
+ abstract member-signature method registerSignalServiceExtension({core::String* name = #C1, () →* asy::Future<core::Null?>* callback = #C1}) → void;
+}
+abstract class ServicesBinding extends ser::_ServicesBinding&BindingBase&SchedulerBinding /*isMixinDeclaration*/ {
+}
+
+library;
+import self as self5;
+import "services_binding_lib.dart" as ser;
+additionalExports = (ser::ServicesBinding)
+
+export "org-dartlang-testcase:///services_binding_lib.dart";
+
+
+constants {
+ #C1 = null
+}
diff --git a/pkg/front_end/testcases/nnbd_mixed/issue42387/foundation_basic_types_lib.dart.weak.transformed.expect b/pkg/front_end/testcases/nnbd_mixed/issue42387/foundation_basic_types_lib.dart.weak.transformed.expect
new file mode 100644
index 0000000..3670831
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd_mixed/issue42387/foundation_basic_types_lib.dart.weak.transformed.expect
@@ -0,0 +1,142 @@
+library;
+import self as self;
+import "foundation_binding_lib.dart" as fou;
+import "scheduler_binding_lib.dart" as sch;
+import "dart:core" as core;
+import "dart:async" as asy;
+import "services_binding_lib.dart" as ser;
+
+import "org-dartlang-testcase:///foundation_lib.dart";
+import "org-dartlang-testcase:///services_lib.dart";
+import "org-dartlang-testcase:///scheduler_lib.dart";
+
+abstract class _TestSchedulerBinding&BindingBase&SchedulerBinding extends fou::BindingBase implements sch::SchedulerBinding /*isAnonymousMixin,isEliminatedMixin*/ {
+ synthetic constructor •() → self::_TestSchedulerBinding&BindingBase&SchedulerBinding*
+ : super fou::BindingBase::•()
+ ;
+ abstract member-signature method /* from org-dartlang-testcase:///scheduler_binding_lib.dart */ registerSignalServiceExtension({core::String* name = #C1, () →* asy::Future<core::Null?>* callback = #C1}) → void;
+ abstract member-signature get /* from org-dartlang-testcase:///scheduler_binding_lib.dart */ _identityHashCode() → core::int*;
+ abstract member-signature method /* from org-dartlang-testcase:///scheduler_binding_lib.dart */ _instanceOf(dynamic instantiatorTypeArguments, dynamic functionTypeArguments, dynamic type) → core::bool*;
+ abstract member-signature method /* from org-dartlang-testcase:///scheduler_binding_lib.dart */ _simpleInstanceOf(dynamic type) → core::bool*;
+ abstract member-signature method /* from org-dartlang-testcase:///scheduler_binding_lib.dart */ _simpleInstanceOfTrue(dynamic type) → core::bool*;
+ abstract member-signature method /* from org-dartlang-testcase:///scheduler_binding_lib.dart */ _simpleInstanceOfFalse(dynamic type) → core::bool*;
+ abstract member-signature operator /* from org-dartlang-testcase:///scheduler_binding_lib.dart */ ==(dynamic other) → core::bool*;
+ abstract member-signature get /* from org-dartlang-testcase:///scheduler_binding_lib.dart */ hashCode() → core::int*;
+ abstract member-signature method /* from org-dartlang-testcase:///scheduler_binding_lib.dart */ toString() → core::String*;
+ abstract member-signature method /* from org-dartlang-testcase:///scheduler_binding_lib.dart */ noSuchMethod(core::Invocation* invocation) → dynamic;
+ abstract member-signature get /* from org-dartlang-testcase:///scheduler_binding_lib.dart */ runtimeType() → core::Type*;
+}
+abstract class _TestSchedulerBinding&BindingBase&SchedulerBinding&ServicesBinding extends self::_TestSchedulerBinding&BindingBase&SchedulerBinding implements ser::ServicesBinding /*isAnonymousMixin,isEliminatedMixin*/ {
+ synthetic constructor •() → self::_TestSchedulerBinding&BindingBase&SchedulerBinding&ServicesBinding*
+ : super self::_TestSchedulerBinding&BindingBase&SchedulerBinding::•()
+ ;
+}
+class TestSchedulerBinding extends self::_TestSchedulerBinding&BindingBase&SchedulerBinding&ServicesBinding {
+ synthetic constructor •() → self::TestSchedulerBinding*
+ : super self::_TestSchedulerBinding&BindingBase&SchedulerBinding&ServicesBinding::•()
+ ;
+}
+static method main() → dynamic {}
+
+library /*isNonNullableByDefault*/;
+import self as self2;
+import "dart:async" as asy;
+import "dart:core" as core;
+
+typedef AsyncCallback = () → asy::Future<core::Null?>;
+
+library /*isNonNullableByDefault*/;
+import self as fou;
+import "dart:core" as core;
+import "dart:async" as asy;
+
+import "org-dartlang-testcase:///foundation_basic_types_lib.dart";
+
+abstract class BindingBase extends core::Object {
+ synthetic constructor •() → fou::BindingBase
+ : super core::Object::•()
+ ;
+ method registerSignalServiceExtension({required core::String name = #C1, required () → asy::Future<core::Null?> callback = #C1}) → void {}
+}
+
+library;
+import self as self3;
+import "foundation_basic_types_lib.dart" as self2;
+import "foundation_binding_lib.dart" as fou;
+additionalExports = (self2::AsyncCallback,
+ fou::BindingBase)
+
+export "org-dartlang-testcase:///foundation_basic_types_lib.dart";
+export "org-dartlang-testcase:///foundation_binding_lib.dart";
+
+
+library;
+import self as sch;
+import "foundation_binding_lib.dart" as fou;
+import "dart:core" as core;
+import "dart:async" as asy;
+
+import "org-dartlang-testcase:///foundation_lib.dart";
+
+abstract class SchedulerBinding extends fou::BindingBase /*isMixinDeclaration*/ {
+ abstract member-signature method registerSignalServiceExtension({core::String* name = #C1, () →* asy::Future<core::Null?>* callback = #C1}) → void;
+ abstract member-signature get _identityHashCode() → core::int*;
+ abstract member-signature method _instanceOf(dynamic instantiatorTypeArguments, dynamic functionTypeArguments, dynamic type) → core::bool*;
+ abstract member-signature method _simpleInstanceOf(dynamic type) → core::bool*;
+ abstract member-signature method _simpleInstanceOfTrue(dynamic type) → core::bool*;
+ abstract member-signature method _simpleInstanceOfFalse(dynamic type) → core::bool*;
+ abstract member-signature operator ==(dynamic other) → core::bool*;
+ abstract member-signature get hashCode() → core::int*;
+ abstract member-signature method toString() → core::String*;
+ abstract member-signature method noSuchMethod(core::Invocation* invocation) → dynamic;
+ abstract member-signature get runtimeType() → core::Type*;
+}
+
+library;
+import self as self4;
+import "scheduler_binding_lib.dart" as sch;
+additionalExports = (sch::SchedulerBinding)
+
+export "org-dartlang-testcase:///scheduler_binding_lib.dart";
+
+
+library;
+import self as ser;
+import "dart:core" as core;
+import "foundation_binding_lib.dart" as fou;
+import "scheduler_binding_lib.dart" as sch;
+import "dart:async" as asy;
+
+import "org-dartlang-testcase:///foundation_lib.dart";
+import "org-dartlang-testcase:///scheduler_lib.dart";
+
+abstract class _ServicesBinding&BindingBase&SchedulerBinding extends core::Object implements fou::BindingBase, sch::SchedulerBinding /*isAnonymousMixin*/ {
+ synthetic constructor •() → ser::_ServicesBinding&BindingBase&SchedulerBinding*
+ : super core::Object::•()
+ ;
+ abstract member-signature get _identityHashCode() → core::int*;
+ abstract member-signature method _instanceOf(dynamic instantiatorTypeArguments, dynamic functionTypeArguments, dynamic type) → core::bool*;
+ abstract member-signature method _simpleInstanceOf(dynamic type) → core::bool*;
+ abstract member-signature method _simpleInstanceOfTrue(dynamic type) → core::bool*;
+ abstract member-signature method _simpleInstanceOfFalse(dynamic type) → core::bool*;
+ abstract member-signature operator ==(dynamic other) → core::bool*;
+ abstract member-signature get hashCode() → core::int*;
+ abstract member-signature method toString() → core::String*;
+ abstract member-signature method noSuchMethod(core::Invocation* invocation) → dynamic;
+ abstract member-signature get runtimeType() → core::Type*;
+ abstract member-signature method registerSignalServiceExtension({core::String* name = #C1, () →* asy::Future<core::Null?>* callback = #C1}) → void;
+}
+abstract class ServicesBinding extends ser::_ServicesBinding&BindingBase&SchedulerBinding /*isMixinDeclaration*/ {
+}
+
+library;
+import self as self5;
+import "services_binding_lib.dart" as ser;
+additionalExports = (ser::ServicesBinding)
+
+export "org-dartlang-testcase:///services_binding_lib.dart";
+
+
+constants {
+ #C1 = null
+}
diff --git a/pkg/front_end/testcases/nnbd_mixed/issue42387/foundation_binding_lib.dart b/pkg/front_end/testcases/nnbd_mixed/issue42387/foundation_binding_lib.dart
new file mode 100644
index 0000000..a1fb380
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd_mixed/issue42387/foundation_binding_lib.dart
@@ -0,0 +1,12 @@
+// 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.
+
+// @dart = 2.9
+
+import 'foundation_basic_types_lib.dart';
+
+abstract class BindingBase {
+ void registerSignalServiceExtension(
+ {required String name, required AsyncCallback callback}) {}
+}
diff --git a/pkg/front_end/testcases/nnbd_mixed/issue42387/foundation_lib.dart b/pkg/front_end/testcases/nnbd_mixed/issue42387/foundation_lib.dart
new file mode 100644
index 0000000..65bff9e
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd_mixed/issue42387/foundation_lib.dart
@@ -0,0 +1,8 @@
+// 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.
+
+// @dart=2.8
+
+export 'foundation_basic_types_lib.dart';
+export 'foundation_binding_lib.dart';
diff --git a/pkg/front_end/testcases/nnbd_mixed/issue42387/link.options b/pkg/front_end/testcases/nnbd_mixed/issue42387/link.options
new file mode 100644
index 0000000..a8d1069
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd_mixed/issue42387/link.options
@@ -0,0 +1,7 @@
+foundation_basic_types_lib.dart
+foundation_binding_lib.dart
+foundation_lib.dart
+scheduler_binding_lib.dart
+scheduler_lib.dart
+services_binding_lib.dart
+services_lib.dart
\ No newline at end of file
diff --git a/pkg/front_end/testcases/nnbd_mixed/issue42387/scheduler_binding_lib.dart b/pkg/front_end/testcases/nnbd_mixed/issue42387/scheduler_binding_lib.dart
new file mode 100644
index 0000000..66baa12
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd_mixed/issue42387/scheduler_binding_lib.dart
@@ -0,0 +1,10 @@
+// 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.
+
+// @dart = 2.8
+
+import 'foundation_lib.dart';
+
+//abstract class SchedulerBinding extends BindingBase with ServicesBinding {}
+mixin SchedulerBinding on BindingBase {}
diff --git a/pkg/front_end/testcases/nnbd_mixed/issue42387/scheduler_lib.dart b/pkg/front_end/testcases/nnbd_mixed/issue42387/scheduler_lib.dart
new file mode 100644
index 0000000..3e8e8e8
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd_mixed/issue42387/scheduler_lib.dart
@@ -0,0 +1,7 @@
+// 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.
+
+// @dart=2.8
+
+export 'scheduler_binding_lib.dart';
diff --git a/pkg/front_end/testcases/nnbd_mixed/issue42387/scheduler_tester.dart b/pkg/front_end/testcases/nnbd_mixed/issue42387/scheduler_tester.dart
new file mode 100644
index 0000000..335e5d3
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd_mixed/issue42387/scheduler_tester.dart
@@ -0,0 +1,14 @@
+// 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.
+
+// @dart = 2.8
+
+import 'foundation_lib.dart';
+import 'services_lib.dart';
+import 'scheduler_lib.dart';
+
+class TestSchedulerBinding extends BindingBase
+ with SchedulerBinding, ServicesBinding {}
+
+main() {}
diff --git a/pkg/front_end/testcases/nnbd_mixed/issue42387/scheduler_tester.dart.textual_outline.expect b/pkg/front_end/testcases/nnbd_mixed/issue42387/scheduler_tester.dart.textual_outline.expect
new file mode 100644
index 0000000..2ee5d32
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd_mixed/issue42387/scheduler_tester.dart.textual_outline.expect
@@ -0,0 +1,9 @@
+// @dart = 2.8
+import 'foundation_lib.dart';
+import 'services_lib.dart';
+import 'scheduler_lib.dart';
+
+class TestSchedulerBinding extends BindingBase
+ with SchedulerBinding, ServicesBinding {}
+
+main() {}
diff --git a/pkg/front_end/testcases/nnbd_mixed/issue42387/scheduler_tester.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/nnbd_mixed/issue42387/scheduler_tester.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..2ee5d32
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd_mixed/issue42387/scheduler_tester.dart.textual_outline_modelled.expect
@@ -0,0 +1,9 @@
+// @dart = 2.8
+import 'foundation_lib.dart';
+import 'services_lib.dart';
+import 'scheduler_lib.dart';
+
+class TestSchedulerBinding extends BindingBase
+ with SchedulerBinding, ServicesBinding {}
+
+main() {}
diff --git a/pkg/front_end/testcases/nnbd_mixed/issue42387/scheduler_tester.dart.weak.expect b/pkg/front_end/testcases/nnbd_mixed/issue42387/scheduler_tester.dart.weak.expect
new file mode 100644
index 0000000..dcefbab
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd_mixed/issue42387/scheduler_tester.dart.weak.expect
@@ -0,0 +1,142 @@
+library;
+import self as self;
+import "foundation_binding_lib.dart" as fou;
+import "scheduler_binding_lib.dart" as sch;
+import "dart:core" as core;
+import "dart:async" as asy;
+import "services_binding_lib.dart" as ser;
+
+import "org-dartlang-testcase:///foundation_lib.dart";
+import "org-dartlang-testcase:///services_lib.dart";
+import "org-dartlang-testcase:///scheduler_lib.dart";
+
+abstract class _TestSchedulerBinding&BindingBase&SchedulerBinding = fou::BindingBase with sch::SchedulerBinding /*isAnonymousMixin*/ {
+ synthetic constructor •() → self::_TestSchedulerBinding&BindingBase&SchedulerBinding*
+ : super fou::BindingBase::•()
+ ;
+ abstract member-signature method registerSignalServiceExtension({core::String* name = #C1, () →* asy::Future<core::Null?>* callback = #C1}) → void;
+ abstract member-signature get _identityHashCode() → core::int*;
+ abstract member-signature method _instanceOf(dynamic instantiatorTypeArguments, dynamic functionTypeArguments, dynamic type) → core::bool*;
+ abstract member-signature method _simpleInstanceOf(dynamic type) → core::bool*;
+ abstract member-signature method _simpleInstanceOfTrue(dynamic type) → core::bool*;
+ abstract member-signature method _simpleInstanceOfFalse(dynamic type) → core::bool*;
+ abstract member-signature operator ==(dynamic other) → core::bool*;
+ abstract member-signature get hashCode() → core::int*;
+ abstract member-signature method toString() → core::String*;
+ abstract member-signature method noSuchMethod(core::Invocation* invocation) → dynamic;
+ abstract member-signature get runtimeType() → core::Type*;
+}
+abstract class _TestSchedulerBinding&BindingBase&SchedulerBinding&ServicesBinding = self::_TestSchedulerBinding&BindingBase&SchedulerBinding with ser::ServicesBinding /*isAnonymousMixin*/ {
+ synthetic constructor •() → self::_TestSchedulerBinding&BindingBase&SchedulerBinding&ServicesBinding*
+ : super self::_TestSchedulerBinding&BindingBase&SchedulerBinding::•()
+ ;
+}
+class TestSchedulerBinding extends self::_TestSchedulerBinding&BindingBase&SchedulerBinding&ServicesBinding {
+ synthetic constructor •() → self::TestSchedulerBinding*
+ : super self::_TestSchedulerBinding&BindingBase&SchedulerBinding&ServicesBinding::•()
+ ;
+}
+static method main() → dynamic {}
+
+library;
+import self as self2;
+import "foundation_basic_types_lib.dart" as fou2;
+import "foundation_binding_lib.dart" as fou;
+additionalExports = (fou2::AsyncCallback,
+ fou::BindingBase)
+
+export "org-dartlang-testcase:///foundation_basic_types_lib.dart";
+export "org-dartlang-testcase:///foundation_binding_lib.dart";
+
+
+library;
+import self as self3;
+import "services_binding_lib.dart" as ser;
+additionalExports = (ser::ServicesBinding)
+
+export "org-dartlang-testcase:///services_binding_lib.dart";
+
+
+library;
+import self as self4;
+import "scheduler_binding_lib.dart" as sch;
+additionalExports = (sch::SchedulerBinding)
+
+export "org-dartlang-testcase:///scheduler_binding_lib.dart";
+
+
+library /*isNonNullableByDefault*/;
+import self as fou2;
+import "dart:async" as asy;
+import "dart:core" as core;
+
+typedef AsyncCallback = () → asy::Future<core::Null?>;
+
+library /*isNonNullableByDefault*/;
+import self as fou;
+import "dart:core" as core;
+import "dart:async" as asy;
+
+import "org-dartlang-testcase:///foundation_basic_types_lib.dart";
+
+abstract class BindingBase extends core::Object {
+ synthetic constructor •() → fou::BindingBase
+ : super core::Object::•()
+ ;
+ method registerSignalServiceExtension({required core::String name = #C1, required () → asy::Future<core::Null?> callback = #C1}) → void {}
+}
+
+library;
+import self as ser;
+import "dart:core" as core;
+import "foundation_binding_lib.dart" as fou;
+import "scheduler_binding_lib.dart" as sch;
+import "dart:async" as asy;
+
+import "org-dartlang-testcase:///foundation_lib.dart";
+import "org-dartlang-testcase:///scheduler_lib.dart";
+
+abstract class _ServicesBinding&BindingBase&SchedulerBinding extends core::Object implements fou::BindingBase, sch::SchedulerBinding /*isAnonymousMixin*/ {
+ synthetic constructor •() → ser::_ServicesBinding&BindingBase&SchedulerBinding*
+ : super core::Object::•()
+ ;
+ abstract member-signature get _identityHashCode() → core::int*;
+ abstract member-signature method _instanceOf(dynamic instantiatorTypeArguments, dynamic functionTypeArguments, dynamic type) → core::bool*;
+ abstract member-signature method _simpleInstanceOf(dynamic type) → core::bool*;
+ abstract member-signature method _simpleInstanceOfTrue(dynamic type) → core::bool*;
+ abstract member-signature method _simpleInstanceOfFalse(dynamic type) → core::bool*;
+ abstract member-signature operator ==(dynamic other) → core::bool*;
+ abstract member-signature get hashCode() → core::int*;
+ abstract member-signature method toString() → core::String*;
+ abstract member-signature method noSuchMethod(core::Invocation* invocation) → dynamic;
+ abstract member-signature get runtimeType() → core::Type*;
+ abstract member-signature method registerSignalServiceExtension({core::String* name = #C1, () →* asy::Future<core::Null?>* callback = #C1}) → void;
+}
+abstract class ServicesBinding extends ser::_ServicesBinding&BindingBase&SchedulerBinding /*isMixinDeclaration*/ {
+}
+
+library;
+import self as sch;
+import "foundation_binding_lib.dart" as fou;
+import "dart:core" as core;
+import "dart:async" as asy;
+
+import "org-dartlang-testcase:///foundation_lib.dart";
+
+abstract class SchedulerBinding extends fou::BindingBase /*isMixinDeclaration*/ {
+ abstract member-signature method registerSignalServiceExtension({core::String* name = #C1, () →* asy::Future<core::Null?>* callback = #C1}) → void;
+ abstract member-signature get _identityHashCode() → core::int*;
+ abstract member-signature method _instanceOf(dynamic instantiatorTypeArguments, dynamic functionTypeArguments, dynamic type) → core::bool*;
+ abstract member-signature method _simpleInstanceOf(dynamic type) → core::bool*;
+ abstract member-signature method _simpleInstanceOfTrue(dynamic type) → core::bool*;
+ abstract member-signature method _simpleInstanceOfFalse(dynamic type) → core::bool*;
+ abstract member-signature operator ==(dynamic other) → core::bool*;
+ abstract member-signature get hashCode() → core::int*;
+ abstract member-signature method toString() → core::String*;
+ abstract member-signature method noSuchMethod(core::Invocation* invocation) → dynamic;
+ abstract member-signature get runtimeType() → core::Type*;
+}
+
+constants {
+ #C1 = null
+}
diff --git a/pkg/front_end/testcases/nnbd_mixed/issue42387/scheduler_tester.dart.weak.transformed.expect b/pkg/front_end/testcases/nnbd_mixed/issue42387/scheduler_tester.dart.weak.transformed.expect
new file mode 100644
index 0000000..bdc0c68
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd_mixed/issue42387/scheduler_tester.dart.weak.transformed.expect
@@ -0,0 +1,142 @@
+library;
+import self as self;
+import "foundation_binding_lib.dart" as fou;
+import "scheduler_binding_lib.dart" as sch;
+import "dart:core" as core;
+import "dart:async" as asy;
+import "services_binding_lib.dart" as ser;
+
+import "org-dartlang-testcase:///foundation_lib.dart";
+import "org-dartlang-testcase:///services_lib.dart";
+import "org-dartlang-testcase:///scheduler_lib.dart";
+
+abstract class _TestSchedulerBinding&BindingBase&SchedulerBinding extends fou::BindingBase implements sch::SchedulerBinding /*isAnonymousMixin,isEliminatedMixin*/ {
+ synthetic constructor •() → self::_TestSchedulerBinding&BindingBase&SchedulerBinding*
+ : super fou::BindingBase::•()
+ ;
+ abstract member-signature method /* from org-dartlang-testcase:///scheduler_binding_lib.dart */ registerSignalServiceExtension({core::String* name = #C1, () →* asy::Future<core::Null?>* callback = #C1}) → void;
+ abstract member-signature get /* from org-dartlang-testcase:///scheduler_binding_lib.dart */ _identityHashCode() → core::int*;
+ abstract member-signature method /* from org-dartlang-testcase:///scheduler_binding_lib.dart */ _instanceOf(dynamic instantiatorTypeArguments, dynamic functionTypeArguments, dynamic type) → core::bool*;
+ abstract member-signature method /* from org-dartlang-testcase:///scheduler_binding_lib.dart */ _simpleInstanceOf(dynamic type) → core::bool*;
+ abstract member-signature method /* from org-dartlang-testcase:///scheduler_binding_lib.dart */ _simpleInstanceOfTrue(dynamic type) → core::bool*;
+ abstract member-signature method /* from org-dartlang-testcase:///scheduler_binding_lib.dart */ _simpleInstanceOfFalse(dynamic type) → core::bool*;
+ abstract member-signature operator /* from org-dartlang-testcase:///scheduler_binding_lib.dart */ ==(dynamic other) → core::bool*;
+ abstract member-signature get /* from org-dartlang-testcase:///scheduler_binding_lib.dart */ hashCode() → core::int*;
+ abstract member-signature method /* from org-dartlang-testcase:///scheduler_binding_lib.dart */ toString() → core::String*;
+ abstract member-signature method /* from org-dartlang-testcase:///scheduler_binding_lib.dart */ noSuchMethod(core::Invocation* invocation) → dynamic;
+ abstract member-signature get /* from org-dartlang-testcase:///scheduler_binding_lib.dart */ runtimeType() → core::Type*;
+}
+abstract class _TestSchedulerBinding&BindingBase&SchedulerBinding&ServicesBinding extends self::_TestSchedulerBinding&BindingBase&SchedulerBinding implements ser::ServicesBinding /*isAnonymousMixin,isEliminatedMixin*/ {
+ synthetic constructor •() → self::_TestSchedulerBinding&BindingBase&SchedulerBinding&ServicesBinding*
+ : super self::_TestSchedulerBinding&BindingBase&SchedulerBinding::•()
+ ;
+}
+class TestSchedulerBinding extends self::_TestSchedulerBinding&BindingBase&SchedulerBinding&ServicesBinding {
+ synthetic constructor •() → self::TestSchedulerBinding*
+ : super self::_TestSchedulerBinding&BindingBase&SchedulerBinding&ServicesBinding::•()
+ ;
+}
+static method main() → dynamic {}
+
+library;
+import self as self2;
+import "foundation_basic_types_lib.dart" as fou2;
+import "foundation_binding_lib.dart" as fou;
+additionalExports = (fou2::AsyncCallback,
+ fou::BindingBase)
+
+export "org-dartlang-testcase:///foundation_basic_types_lib.dart";
+export "org-dartlang-testcase:///foundation_binding_lib.dart";
+
+
+library;
+import self as self3;
+import "services_binding_lib.dart" as ser;
+additionalExports = (ser::ServicesBinding)
+
+export "org-dartlang-testcase:///services_binding_lib.dart";
+
+
+library;
+import self as self4;
+import "scheduler_binding_lib.dart" as sch;
+additionalExports = (sch::SchedulerBinding)
+
+export "org-dartlang-testcase:///scheduler_binding_lib.dart";
+
+
+library /*isNonNullableByDefault*/;
+import self as fou2;
+import "dart:async" as asy;
+import "dart:core" as core;
+
+typedef AsyncCallback = () → asy::Future<core::Null?>;
+
+library /*isNonNullableByDefault*/;
+import self as fou;
+import "dart:core" as core;
+import "dart:async" as asy;
+
+import "org-dartlang-testcase:///foundation_basic_types_lib.dart";
+
+abstract class BindingBase extends core::Object {
+ synthetic constructor •() → fou::BindingBase
+ : super core::Object::•()
+ ;
+ method registerSignalServiceExtension({required core::String name = #C1, required () → asy::Future<core::Null?> callback = #C1}) → void {}
+}
+
+library;
+import self as ser;
+import "dart:core" as core;
+import "foundation_binding_lib.dart" as fou;
+import "scheduler_binding_lib.dart" as sch;
+import "dart:async" as asy;
+
+import "org-dartlang-testcase:///foundation_lib.dart";
+import "org-dartlang-testcase:///scheduler_lib.dart";
+
+abstract class _ServicesBinding&BindingBase&SchedulerBinding extends core::Object implements fou::BindingBase, sch::SchedulerBinding /*isAnonymousMixin*/ {
+ synthetic constructor •() → ser::_ServicesBinding&BindingBase&SchedulerBinding*
+ : super core::Object::•()
+ ;
+ abstract member-signature get _identityHashCode() → core::int*;
+ abstract member-signature method _instanceOf(dynamic instantiatorTypeArguments, dynamic functionTypeArguments, dynamic type) → core::bool*;
+ abstract member-signature method _simpleInstanceOf(dynamic type) → core::bool*;
+ abstract member-signature method _simpleInstanceOfTrue(dynamic type) → core::bool*;
+ abstract member-signature method _simpleInstanceOfFalse(dynamic type) → core::bool*;
+ abstract member-signature operator ==(dynamic other) → core::bool*;
+ abstract member-signature get hashCode() → core::int*;
+ abstract member-signature method toString() → core::String*;
+ abstract member-signature method noSuchMethod(core::Invocation* invocation) → dynamic;
+ abstract member-signature get runtimeType() → core::Type*;
+ abstract member-signature method registerSignalServiceExtension({core::String* name = #C1, () →* asy::Future<core::Null?>* callback = #C1}) → void;
+}
+abstract class ServicesBinding extends ser::_ServicesBinding&BindingBase&SchedulerBinding /*isMixinDeclaration*/ {
+}
+
+library;
+import self as sch;
+import "foundation_binding_lib.dart" as fou;
+import "dart:core" as core;
+import "dart:async" as asy;
+
+import "org-dartlang-testcase:///foundation_lib.dart";
+
+abstract class SchedulerBinding extends fou::BindingBase /*isMixinDeclaration*/ {
+ abstract member-signature method registerSignalServiceExtension({core::String* name = #C1, () →* asy::Future<core::Null?>* callback = #C1}) → void;
+ abstract member-signature get _identityHashCode() → core::int*;
+ abstract member-signature method _instanceOf(dynamic instantiatorTypeArguments, dynamic functionTypeArguments, dynamic type) → core::bool*;
+ abstract member-signature method _simpleInstanceOf(dynamic type) → core::bool*;
+ abstract member-signature method _simpleInstanceOfTrue(dynamic type) → core::bool*;
+ abstract member-signature method _simpleInstanceOfFalse(dynamic type) → core::bool*;
+ abstract member-signature operator ==(dynamic other) → core::bool*;
+ abstract member-signature get hashCode() → core::int*;
+ abstract member-signature method toString() → core::String*;
+ abstract member-signature method noSuchMethod(core::Invocation* invocation) → dynamic;
+ abstract member-signature get runtimeType() → core::Type*;
+}
+
+constants {
+ #C1 = null
+}
diff --git a/pkg/front_end/testcases/nnbd_mixed/issue42387/services_binding_lib.dart b/pkg/front_end/testcases/nnbd_mixed/issue42387/services_binding_lib.dart
new file mode 100644
index 0000000..fabdac7
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd_mixed/issue42387/services_binding_lib.dart
@@ -0,0 +1,10 @@
+// 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.
+
+// @dart = 2.8
+
+import 'foundation_lib.dart';
+import 'scheduler_lib.dart';
+
+mixin ServicesBinding on BindingBase, SchedulerBinding {}
diff --git a/pkg/front_end/testcases/nnbd_mixed/issue42387/services_lib.dart b/pkg/front_end/testcases/nnbd_mixed/issue42387/services_lib.dart
new file mode 100644
index 0000000..4727785
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd_mixed/issue42387/services_lib.dart
@@ -0,0 +1,7 @@
+// 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.
+
+// @dart=2.8
+
+export 'services_binding_lib.dart';
diff --git a/pkg/front_end/testcases/nnbd_mixed/member_inheritance_from_opt_out.dart.weak.expect b/pkg/front_end/testcases/nnbd_mixed/member_inheritance_from_opt_out.dart.weak.expect
index 7e6e97a..d282884 100644
--- a/pkg/front_end/testcases/nnbd_mixed/member_inheritance_from_opt_out.dart.weak.expect
+++ b/pkg/front_end/testcases/nnbd_mixed/member_inheritance_from_opt_out.dart.weak.expect
@@ -1,35 +1,4 @@
library main /*isNonNullableByDefault*/;
-//
-// Problems in library:
-//
-// pkg/front_end/testcases/nnbd_mixed/member_inheritance_from_opt_out.dart:96:30: Error: The required named parameter 'a' in method 'Class2b.method5c' is not required in overridden method 'LegacyClass.method5c'.
-// int method5c({required int a, required int b}) => 0;
-// ^
-// pkg/front_end/testcases/nnbd_mixed/member_inheritance_from_opt_out_lib.dart:30:7: Context: This is the overridden method ('method5c').
-// int method5c({int a, int b}) => 0;
-// ^
-//
-// pkg/front_end/testcases/nnbd_mixed/member_inheritance_from_opt_out.dart:96:46: Error: The required named parameter 'b' in method 'Class2b.method5c' is not required in overridden method 'LegacyClass.method5c'.
-// int method5c({required int a, required int b}) => 0;
-// ^
-// pkg/front_end/testcases/nnbd_mixed/member_inheritance_from_opt_out_lib.dart:30:7: Context: This is the overridden method ('method5c').
-// int method5c({int a, int b}) => 0;
-// ^
-//
-// pkg/front_end/testcases/nnbd_mixed/member_inheritance_from_opt_out.dart:102:32: Error: The required named parameter 'a' in method 'Class2b.method6c' is not required in overridden method 'LegacyClass.method6c'.
-// int? method6c({required int? a, required int? b}) => 0;
-// ^
-// pkg/front_end/testcases/nnbd_mixed/member_inheritance_from_opt_out_lib.dart:36:7: Context: This is the overridden method ('method6c').
-// int method6c({int a, int b}) => 0;
-// ^
-//
-// pkg/front_end/testcases/nnbd_mixed/member_inheritance_from_opt_out.dart:102:49: Error: The required named parameter 'b' in method 'Class2b.method6c' is not required in overridden method 'LegacyClass.method6c'.
-// int? method6c({required int? a, required int? b}) => 0;
-// ^
-// pkg/front_end/testcases/nnbd_mixed/member_inheritance_from_opt_out_lib.dart:36:7: Context: This is the overridden method ('method6c').
-// int method6c({int a, int b}) => 0;
-// ^
-//
import self as self;
import "dart:core" as core;
import "member_inheritance_from_opt_out_lib.dart" as opt;
diff --git a/pkg/front_end/testcases/nnbd_mixed/member_inheritance_from_opt_out.dart.weak.transformed.expect b/pkg/front_end/testcases/nnbd_mixed/member_inheritance_from_opt_out.dart.weak.transformed.expect
index 7e6e97a..d282884 100644
--- a/pkg/front_end/testcases/nnbd_mixed/member_inheritance_from_opt_out.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/nnbd_mixed/member_inheritance_from_opt_out.dart.weak.transformed.expect
@@ -1,35 +1,4 @@
library main /*isNonNullableByDefault*/;
-//
-// Problems in library:
-//
-// pkg/front_end/testcases/nnbd_mixed/member_inheritance_from_opt_out.dart:96:30: Error: The required named parameter 'a' in method 'Class2b.method5c' is not required in overridden method 'LegacyClass.method5c'.
-// int method5c({required int a, required int b}) => 0;
-// ^
-// pkg/front_end/testcases/nnbd_mixed/member_inheritance_from_opt_out_lib.dart:30:7: Context: This is the overridden method ('method5c').
-// int method5c({int a, int b}) => 0;
-// ^
-//
-// pkg/front_end/testcases/nnbd_mixed/member_inheritance_from_opt_out.dart:96:46: Error: The required named parameter 'b' in method 'Class2b.method5c' is not required in overridden method 'LegacyClass.method5c'.
-// int method5c({required int a, required int b}) => 0;
-// ^
-// pkg/front_end/testcases/nnbd_mixed/member_inheritance_from_opt_out_lib.dart:30:7: Context: This is the overridden method ('method5c').
-// int method5c({int a, int b}) => 0;
-// ^
-//
-// pkg/front_end/testcases/nnbd_mixed/member_inheritance_from_opt_out.dart:102:32: Error: The required named parameter 'a' in method 'Class2b.method6c' is not required in overridden method 'LegacyClass.method6c'.
-// int? method6c({required int? a, required int? b}) => 0;
-// ^
-// pkg/front_end/testcases/nnbd_mixed/member_inheritance_from_opt_out_lib.dart:36:7: Context: This is the overridden method ('method6c').
-// int method6c({int a, int b}) => 0;
-// ^
-//
-// pkg/front_end/testcases/nnbd_mixed/member_inheritance_from_opt_out.dart:102:49: Error: The required named parameter 'b' in method 'Class2b.method6c' is not required in overridden method 'LegacyClass.method6c'.
-// int? method6c({required int? a, required int? b}) => 0;
-// ^
-// pkg/front_end/testcases/nnbd_mixed/member_inheritance_from_opt_out_lib.dart:36:7: Context: This is the overridden method ('method6c').
-// int method6c({int a, int b}) => 0;
-// ^
-//
import self as self;
import "dart:core" as core;
import "member_inheritance_from_opt_out_lib.dart" as opt;
diff --git a/pkg/front_end/testcases/nnbd_mixed/required_name_override.dart.weak.expect b/pkg/front_end/testcases/nnbd_mixed/required_name_override.dart.weak.expect
index 2d1b6db..677cac9 100644
--- a/pkg/front_end/testcases/nnbd_mixed/required_name_override.dart.weak.expect
+++ b/pkg/front_end/testcases/nnbd_mixed/required_name_override.dart.weak.expect
@@ -16,13 +16,6 @@
// void test_nondefault({int? i = 1}) {}
// ^
//
-// pkg/front_end/testcases/nnbd_mixed/required_name_override.dart:15:35: Error: The required named parameter 'i' in method 'A.test_legacy' is not required in overridden method 'C.test_legacy'.
-// void test_legacy({required int? i}) {}
-// ^
-// pkg/front_end/testcases/nnbd_mixed/required_name_override_lib.dart:8:8: Context: This is the overridden method ('test_legacy').
-// void test_legacy({int i}) {}
-// ^
-//
import self as self;
import "dart:core" as core;
import "required_name_override_lib.dart" as req;
diff --git a/pkg/front_end/testcases/nnbd_mixed/required_name_override.dart.weak.transformed.expect b/pkg/front_end/testcases/nnbd_mixed/required_name_override.dart.weak.transformed.expect
index 2d1b6db..677cac9 100644
--- a/pkg/front_end/testcases/nnbd_mixed/required_name_override.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/nnbd_mixed/required_name_override.dart.weak.transformed.expect
@@ -16,13 +16,6 @@
// void test_nondefault({int? i = 1}) {}
// ^
//
-// pkg/front_end/testcases/nnbd_mixed/required_name_override.dart:15:35: Error: The required named parameter 'i' in method 'A.test_legacy' is not required in overridden method 'C.test_legacy'.
-// void test_legacy({required int? i}) {}
-// ^
-// pkg/front_end/testcases/nnbd_mixed/required_name_override_lib.dart:8:8: Context: This is the overridden method ('test_legacy').
-// void test_legacy({int i}) {}
-// ^
-//
import self as self;
import "dart:core" as core;
import "required_name_override_lib.dart" as req;
diff --git a/pkg/front_end/testcases/textual_outline.status b/pkg/front_end/testcases/textual_outline.status
index e5a6a25..f05e3ba 100644
--- a/pkg/front_end/testcases/textual_outline.status
+++ b/pkg/front_end/testcases/textual_outline.status
@@ -140,6 +140,7 @@
late_lowering/issue40805: FormatterCrash
late_lowering/issue41436b: FormatterCrash
late_lowering/issue41922: FormatterCrash
+late_lowering/issue42407: FormatterCrash
late_lowering/late_annotations: FormatterCrash
late_lowering/late_field_inference: FormatterCrash
late_lowering/late_field_with_initializer: FormatterCrash
diff --git a/pkg/frontend_server/lib/frontend_server.dart b/pkg/frontend_server/lib/frontend_server.dart
index 714708d..d9e4fbf 100644
--- a/pkg/frontend_server/lib/frontend_server.dart
+++ b/pkg/frontend_server/lib/frontend_server.dart
@@ -64,6 +64,8 @@
..addFlag('protobuf-tree-shaker',
help: 'Enable protobuf tree shaker transformation in AOT mode.',
defaultsTo: false)
+ ..addFlag('protobuf-tree-shaker-v2',
+ help: 'Enable protobuf tree shaker v2 in AOT mode.', defaultsTo: false)
..addFlag('minimal-kernel',
help: 'Produce minimal tree-shaken kernel file.', defaultsTo: false)
..addFlag('link-platform',
@@ -171,6 +173,9 @@
help: 'A path or uri to the libraries specification JSON file')
..addFlag('debugger-module-names',
help: 'Use debugger-friendly modules names', defaultsTo: false)
+ ..addFlag('experimental-emit-debug-metadata',
+ help: 'Emit module and library metadata for the debugger',
+ defaultsTo: false)
..addOption('dartdevc-module-format',
help: 'The module format to use on for the dartdevc compiler',
defaultsTo: 'amd');
@@ -320,7 +325,8 @@
this.transformer,
this.unsafePackageSerialization,
this.incrementalSerialization: true,
- this.useDebuggerModuleNames: false}) {
+ this.useDebuggerModuleNames: false,
+ this.emitDebugMetadata: false}) {
_outputStream ??= stdout;
printerFactory ??= new BinaryPrinterFactory();
}
@@ -330,6 +336,7 @@
bool unsafePackageSerialization;
bool incrementalSerialization;
bool useDebuggerModuleNames;
+ bool emitDebugMetadata;
CompilerOptions _compilerOptions;
BytecodeOptions _bytecodeOptions;
@@ -542,6 +549,7 @@
environmentDefines: environmentDefines,
enableAsserts: options['enable-asserts'],
useProtobufTreeShaker: options['protobuf-tree-shaker'],
+ useProtobufTreeShakerV2: options['protobuf-tree-shaker-v2'],
minimalKernel: options['minimal-kernel'],
treeShakeWriteOnlyFields: options['tree-shake-write-only-fields'],
fromDillFile: options['from-dill']));
@@ -636,27 +644,33 @@
final File sourceFile = File('$filename.sources');
final File manifestFile = File('$filename.json');
final File sourceMapsFile = File('$filename.map');
+ final File metadataFile = File('$filename.metadata');
if (!sourceFile.parent.existsSync()) {
sourceFile.parent.createSync(recursive: true);
}
_bundler = JavaScriptBundler(
component, strongComponents, fileSystemScheme, packageConfig,
useDebuggerModuleNames: useDebuggerModuleNames,
+ emitDebugMetadata: emitDebugMetadata,
moduleFormat: moduleFormat);
final sourceFileSink = sourceFile.openWrite();
final manifestFileSink = manifestFile.openWrite();
final sourceMapsFileSink = sourceMapsFile.openWrite();
+ final metadataFileSink =
+ emitDebugMetadata ? metadataFile.openWrite() : null;
await _bundler.compile(
results.classHierarchy,
results.coreTypes,
results.loadedLibraries,
sourceFileSink,
manifestFileSink,
- sourceMapsFileSink);
+ sourceMapsFileSink,
+ metadataFileSink);
await Future.wait([
sourceFileSink.close(),
manifestFileSink.close(),
- sourceMapsFileSink.close()
+ sourceMapsFileSink.close(),
+ if (metadataFileSink != null) metadataFileSink.close()
]);
}
@@ -1409,7 +1423,8 @@
printerFactory: binaryPrinterFactory,
unsafePackageSerialization: options["unsafe-package-serialization"],
incrementalSerialization: options["incremental-serialization"],
- useDebuggerModuleNames: options['debugger-module-names']);
+ useDebuggerModuleNames: options['debugger-module-names'],
+ emitDebugMetadata: options['experimental-emit-debug-metadata']);
if (options.rest.isNotEmpty) {
return await compiler.compile(options.rest[0], options,
diff --git a/pkg/frontend_server/lib/src/javascript_bundle.dart b/pkg/frontend_server/lib/src/javascript_bundle.dart
index 98443f8..d67f9bc 100644
--- a/pkg/frontend_server/lib/src/javascript_bundle.dart
+++ b/pkg/frontend_server/lib/src/javascript_bundle.dart
@@ -25,7 +25,9 @@
class JavaScriptBundler {
JavaScriptBundler(this._originalComponent, this._strongComponents,
this._fileSystemScheme, this._packageConfig,
- {this.useDebuggerModuleNames = false, String moduleFormat})
+ {this.useDebuggerModuleNames = false,
+ this.emitDebugMetadata = false,
+ String moduleFormat})
: compilers = <String, ProgramCompiler>{},
_moduleFormat = parseModuleFormat(moduleFormat ?? 'amd') {
_summaries = <Component>[];
@@ -58,6 +60,7 @@
final String _fileSystemScheme;
final PackageConfig _packageConfig;
final bool useDebuggerModuleNames;
+ final bool emitDebugMetadata;
final Map<String, ProgramCompiler> compilers;
final ModuleFormat _moduleFormat;
@@ -74,9 +77,11 @@
Set<Library> loadedLibraries,
IOSink codeSink,
IOSink manifestSink,
- IOSink sourceMapsSink) async {
+ IOSink sourceMapsSink,
+ IOSink metadataSink) async {
var codeOffset = 0;
var sourceMapOffset = 0;
+ var metadataOffset = 0;
final manifest = <String, Map<String, List<int>>>{};
final Set<Uri> visited = <Uri>{};
@@ -124,7 +129,11 @@
_originalComponent,
classHierarchy,
SharedCompilerOptions(
- sourceMap: true, summarizeApi: false, moduleName: moduleName),
+ sourceMap: true,
+ summarizeApi: false,
+ emitDebugMetadata: emitDebugMetadata,
+ moduleName: moduleName,
+ ),
importToSummary,
summaryToModule,
coreTypes: coreTypes,
@@ -149,11 +158,13 @@
sourceMapBase =
p.dirname((await _packageConfig.resolve(moduleUri)).path);
}
+
final code = jsProgramToCode(
jsModule,
_moduleFormat,
inlineSourceMap: true,
buildSourceMap: true,
+ emitDebugMetadata: emitDebugMetadata,
jsUrl: '$moduleUrl.lib.js',
mapUrl: '$moduleUrl.lib.js.map',
sourceMapBase: sourceMapBase,
@@ -162,9 +173,14 @@
);
final codeBytes = utf8.encode(code.code);
final sourceMapBytes = utf8.encode(json.encode(code.sourceMap));
+ final metadataBytes =
+ emitDebugMetadata ? utf8.encode(json.encode(code.metadata)) : null;
+
codeSink.add(codeBytes);
sourceMapsSink.add(sourceMapBytes);
-
+ if (emitDebugMetadata) {
+ metadataSink.add(metadataBytes);
+ }
final String moduleKey = _moduleImportForSummary[moduleUri];
manifest[moduleKey] = {
'code': <int>[codeOffset, codeOffset += codeBytes.length],
@@ -172,6 +188,11 @@
sourceMapOffset,
sourceMapOffset += sourceMapBytes.length
],
+ if (emitDebugMetadata)
+ 'metadata': <int>[
+ metadataOffset,
+ metadataOffset += metadataBytes.length
+ ],
};
}
manifestSink.add(utf8.encode(json.encode(manifest)));
diff --git a/pkg/frontend_server/pubspec.yaml b/pkg/frontend_server/pubspec.yaml
index 2effb3b..f682159 100644
--- a/pkg/frontend_server/pubspec.yaml
+++ b/pkg/frontend_server/pubspec.yaml
@@ -1,7 +1,7 @@
-# This package is not intended to be published.
name: frontend_server
-# version: do-not-upload
description: A resident kernel compiler
+# This package is not intended for consumption on pub.dev. DO NOT publish.
+publish_to: none
environment:
sdk: "^2.7.0"
diff --git a/pkg/frontend_server/test/frontend_server_test.dart b/pkg/frontend_server/test/frontend_server_test.dart
index 8e521f4..a042a1e 100644
--- a/pkg/frontend_server/test/frontend_server_test.dart
+++ b/pkg/frontend_server/test/frontend_server_test.dart
@@ -1462,6 +1462,141 @@
expect(await starter(args), 0);
});
+ test('compile to JavaScript with no metadata', () async {
+ var file = File('${tempDir.path}/foo.dart')..createSync();
+ file.writeAsStringSync("main() {\n\n}\n");
+ File('${tempDir.path}/.packages')
+ ..createSync()
+ ..writeAsStringSync("hello:${tempDir.uri}\n");
+
+ var library = 'package:hello/foo.dart';
+
+ var dillFile = File('${tempDir.path}/app.dill');
+ var sourceFile = File('${dillFile.path}.sources');
+ var manifestFile = File('${dillFile.path}.json');
+ var sourceMapsFile = File('${dillFile.path}.map');
+ var metadataFile = File('${dillFile.path}.metadata');
+
+ expect(dillFile.existsSync(), false);
+ expect(sourceFile.existsSync(), false);
+ expect(manifestFile.existsSync(), false);
+ expect(sourceMapsFile.existsSync(), false);
+ expect(metadataFile.existsSync(), false);
+
+ final List<String> args = <String>[
+ '--sdk-root=${sdkRoot.toFilePath()}',
+ '--incremental',
+ '--platform=${ddcPlatformKernel.path}',
+ '--output-dill=${dillFile.path}',
+ '--target=dartdevc',
+ '--packages=${tempDir.path}/.packages',
+ '--debugger-module-names'
+ ];
+
+ final StreamController<List<int>> streamController =
+ StreamController<List<int>>();
+ final StreamController<List<int>> stdoutStreamController =
+ StreamController<List<int>>();
+ final IOSink ioSink = IOSink(stdoutStreamController.sink);
+ StreamController<Result> receivedResults = StreamController<Result>();
+ final outputParser = OutputParser(receivedResults);
+ stdoutStreamController.stream
+ .transform(utf8.decoder)
+ .transform(const LineSplitter())
+ .listen(outputParser.listener);
+
+ Future<int> result =
+ starter(args, input: streamController.stream, output: ioSink);
+ streamController.add('compile $library\n'.codeUnits);
+ var count = 0;
+ receivedResults.stream.listen((Result compiledResult) {
+ CompilationResult result =
+ CompilationResult.parse(compiledResult.status);
+ count++;
+ // Request to 'compile', which results in full JavaScript and no metadata
+ expect(result.errorsCount, equals(0));
+ expect(sourceFile.existsSync(), equals(true));
+ expect(manifestFile.existsSync(), equals(true));
+ expect(sourceMapsFile.existsSync(), equals(true));
+ expect(metadataFile.existsSync(), equals(false));
+ expect(result.filename, dillFile.path);
+ streamController.add('accept\n'.codeUnits);
+ outputParser.expectSources = false;
+ streamController.add('quit\n'.codeUnits);
+ });
+
+ expect(await result, 0);
+ expect(count, 1);
+ });
+
+ test('compile to JavaScript with metadata', () async {
+ var file = File('${tempDir.path}/foo.dart')..createSync();
+ file.writeAsStringSync("main() {\n\n}\n");
+ File('${tempDir.path}/.packages')
+ ..createSync()
+ ..writeAsStringSync("hello:${tempDir.uri}\n");
+
+ var library = 'package:hello/foo.dart';
+
+ var dillFile = File('${tempDir.path}/app.dill');
+ var sourceFile = File('${dillFile.path}.sources');
+ var manifestFile = File('${dillFile.path}.json');
+ var sourceMapsFile = File('${dillFile.path}.map');
+ var metadataFile = File('${dillFile.path}.metadata');
+
+ expect(dillFile.existsSync(), false);
+ expect(sourceFile.existsSync(), false);
+ expect(manifestFile.existsSync(), false);
+ expect(sourceMapsFile.existsSync(), false);
+ expect(metadataFile.existsSync(), false);
+
+ final List<String> args = <String>[
+ '--sdk-root=${sdkRoot.toFilePath()}',
+ '--incremental',
+ '--platform=${ddcPlatformKernel.path}',
+ '--output-dill=${dillFile.path}',
+ '--target=dartdevc',
+ '--packages=${tempDir.path}/.packages',
+ '--debugger-module-names',
+ '--experimental-emit-debug-metadata'
+ ];
+
+ final StreamController<List<int>> streamController =
+ StreamController<List<int>>();
+ final StreamController<List<int>> stdoutStreamController =
+ StreamController<List<int>>();
+ final IOSink ioSink = IOSink(stdoutStreamController.sink);
+ StreamController<Result> receivedResults = StreamController<Result>();
+ final outputParser = OutputParser(receivedResults);
+ stdoutStreamController.stream
+ .transform(utf8.decoder)
+ .transform(const LineSplitter())
+ .listen(outputParser.listener);
+
+ Future<int> result =
+ starter(args, input: streamController.stream, output: ioSink);
+ streamController.add('compile $library\n'.codeUnits);
+ int count = 0;
+ receivedResults.stream.listen((Result compiledResult) {
+ CompilationResult result =
+ CompilationResult.parse(compiledResult.status);
+ count++;
+ // Request to 'compile', which results in full JavaScript and no metadata
+ expect(result.errorsCount, equals(0));
+ expect(sourceFile.existsSync(), equals(true));
+ expect(manifestFile.existsSync(), equals(true));
+ expect(sourceMapsFile.existsSync(), equals(true));
+ expect(metadataFile.existsSync(), equals(true));
+ expect(result.filename, dillFile.path);
+ streamController.add('accept\n'.codeUnits);
+ outputParser.expectSources = false;
+ streamController.add('quit\n'.codeUnits);
+ });
+
+ expect(await result, 0);
+ expect(count, 1);
+ });
+
test('compile expression to Javascript', () async {
var file = File('${tempDir.path}/foo.dart')..createSync();
file.writeAsStringSync("main() {\n\n}\n");
diff --git a/pkg/frontend_server/test/src/javascript_bundle_test.dart b/pkg/frontend_server/test/src/javascript_bundle_test.dart
index 7c5a12d..26a7c21 100644
--- a/pkg/frontend_server/test/src/javascript_bundle_test.dart
+++ b/pkg/frontend_server/test/src/javascript_bundle_test.dart
@@ -103,10 +103,11 @@
final manifestSink = _MemorySink();
final codeSink = _MemorySink();
final sourcemapSink = _MemorySink();
+ final metadataSink = _MemorySink();
final coreTypes = CoreTypes(testComponent);
await javaScriptBundler.compile(ClassHierarchy(testComponent, coreTypes),
- coreTypes, {}, codeSink, manifestSink, sourcemapSink);
+ coreTypes, {}, codeSink, manifestSink, sourcemapSink, metadataSink);
final Map manifest = json.decode(utf8.decode(manifestSink.buffer));
final String code = utf8.decode(codeSink.buffer);
@@ -143,10 +144,11 @@
final manifestSink = _MemorySink();
final codeSink = _MemorySink();
final sourcemapSink = _MemorySink();
+ final metadataSink = _MemorySink();
final coreTypes = CoreTypes(testComponent);
await javaScriptBundler.compile(ClassHierarchy(testComponent, coreTypes),
- coreTypes, {}, codeSink, manifestSink, sourcemapSink);
+ coreTypes, {}, codeSink, manifestSink, sourcemapSink, metadataSink);
final Map manifest = json.decode(utf8.decode(manifestSink.buffer));
final String code = utf8.decode(codeSink.buffer);
@@ -183,10 +185,11 @@
final manifestSink = _MemorySink();
final codeSink = _MemorySink();
final sourcemapSink = _MemorySink();
+ final metadataSink = _MemorySink();
final coreTypes = CoreTypes(testComponent);
await javaScriptBundler.compile(ClassHierarchy(testComponent, coreTypes),
- coreTypes, {}, codeSink, manifestSink, sourcemapSink);
+ coreTypes, {}, codeSink, manifestSink, sourcemapSink, metadataSink);
final Map manifest = json.decode(utf8.decode(manifestSink.buffer));
final String code = utf8.decode(codeSink.buffer);
@@ -232,10 +235,11 @@
final manifestSink = _MemorySink();
final codeSink = _MemorySink();
final sourcemapSink = _MemorySink();
+ final metadataSink = _MemorySink();
final coreTypes = CoreTypes(testComponent);
javaScriptBundler.compile(ClassHierarchy(testComponent, coreTypes),
- coreTypes, {}, codeSink, manifestSink, sourcemapSink);
+ coreTypes, {}, codeSink, manifestSink, sourcemapSink, metadataSink);
final code = utf8.decode(codeSink.buffer);
final manifest = json.decode(utf8.decode(manifestSink.buffer));
diff --git a/pkg/js_ast/pubspec.yaml b/pkg/js_ast/pubspec.yaml
index 8db366a..7349f57 100644
--- a/pkg/js_ast/pubspec.yaml
+++ b/pkg/js_ast/pubspec.yaml
@@ -1,4 +1,5 @@
name: js_ast
+# This package is not intended for consumption on pub.dev. DO NOT publish.
publish_to: none
environment:
diff --git a/pkg/kernel/lib/ast.dart b/pkg/kernel/lib/ast.dart
index c0d3f99..7eff1a0 100644
--- a/pkg/kernel/lib/ast.dart
+++ b/pkg/kernel/lib/ast.dart
@@ -2818,6 +2818,49 @@
.applyToFunctionType(computeThisFunctionType(nullability));
}
+ /// Return function type of node: return [typedefType], reuse type parameters.
+ ///
+ /// When this getter is invoked, the parent must be a [Constructor].
+ /// This getter works similarly to [computeThisFunctionType], but uses
+ /// [typedef] to compute the return type of the returned function type. It
+ /// is useful in some contexts, especially during inference of aliased
+ /// constructor invocations.
+ FunctionType computeAliasedFunctionType(Typedef typedef, Library library) {
+ TreeNode parent = this.parent;
+ assert(parent is Constructor, "Only run this method on constructors");
+ Constructor parentConstructor = parent;
+ // We need create a copy of the list of type parameters, otherwise
+ // transformations like erasure don't work.
+ List<TypeParameter> classTypeParametersCopy =
+ List.from(parentConstructor.enclosingClass.typeParameters);
+ List<TypeParameter> typedefTypeParametersCopy =
+ List.from(typedef.typeParameters);
+ List<DartType> asTypeArguments =
+ getAsTypeArguments(typedefTypeParametersCopy, library);
+ TypedefType typedefType =
+ TypedefType(typedef, library.nonNullable, asTypeArguments);
+ DartType unaliasedTypedef = typedefType.unalias;
+ assert(unaliasedTypedef is InterfaceType,
+ "[typedef] is assumed to resolve to an interface type");
+ InterfaceType targetType = unaliasedTypedef;
+ Substitution substitution = Substitution.fromPairs(
+ classTypeParametersCopy, targetType.typeArguments);
+ List<DartType> positional = positionalParameters
+ .map((VariableDeclaration decl) =>
+ substitution.substituteType(decl.type))
+ .toList(growable: false);
+ List<NamedType> named = namedParameters
+ .map((VariableDeclaration decl) => NamedType(
+ decl.name, substitution.substituteType(decl.type),
+ isRequired: decl.isRequired))
+ .toList(growable: false);
+ named.sort();
+ return new FunctionType(positional, typedefType, library.nonNullable,
+ namedParameters: named,
+ typeParameters: typedefTypeParametersCopy,
+ requiredParameterCount: requiredParameterCount);
+ }
+
R accept<R>(TreeVisitor<R> v) => v.visitFunctionNode(this);
visitChildren(Visitor v) {
diff --git a/pkg/kernel/pubspec.yaml b/pkg/kernel/pubspec.yaml
index 7c83598..2d80a28 100644
--- a/pkg/kernel/pubspec.yaml
+++ b/pkg/kernel/pubspec.yaml
@@ -1,10 +1,10 @@
-name: kernel
# Currently, kernel API is not stable and users should
# not depend on semver semantics when depending on this package.
-version: 0.3.29
-author: Dart Team <misc@dartlang.org>
-description: Dart IR (Intermediate Representation)
-homepage: https://github.com/dart-lang/sdk/tree/master/pkg/kernel
+name: kernel
+
+# This package is not intended for consumption on pub.dev. DO NOT publish.
+publish_to: none
+
environment:
sdk: '>=2.2.2 <3.0.0'
dependencies:
diff --git a/pkg/meta/CHANGELOG.md b/pkg/meta/CHANGELOG.md
index b5b8c99..78a0ddb 100644
--- a/pkg/meta/CHANGELOG.md
+++ b/pkg/meta/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 1.2.0-nnbd
+
+* Opt into null safety.
+
## 1.1.8
* Introduce `@nonVirtual` to annotate instance members that should not be
diff --git a/pkg/meta/analysis_options.yaml b/pkg/meta/analysis_options.yaml
new file mode 100644
index 0000000..4163582
--- /dev/null
+++ b/pkg/meta/analysis_options.yaml
@@ -0,0 +1,3 @@
+analyzer:
+ enable-experiment:
+ - non-nullable
diff --git a/pkg/meta/pubspec.yaml b/pkg/meta/pubspec.yaml
index 4ca8152..0a745e0 100644
--- a/pkg/meta/pubspec.yaml
+++ b/pkg/meta/pubspec.yaml
@@ -1,5 +1,5 @@
name: meta
-version: 1.1.8
+version: 1.2.0-nnbd
author: Dart Team <misc@dartlang.org>
homepage: https://github.com/dart-lang/sdk/tree/master/pkg/meta
description: >
@@ -7,4 +7,6 @@
semantic information about the program being annotated. These annotations are
intended to be used by tools to provide a better user experience.
environment:
- sdk: '>=2.9.0 <3.0.0'
+ # This must remain a tight constraint (only allow dev versions) until nnbd is
+ # stable.
+ sdk: '>=2.9.0-18.0 <2.9.0'
diff --git a/pkg/modular_test/pubspec.yaml b/pkg/modular_test/pubspec.yaml
index 39f65c8..e0a48d0 100644
--- a/pkg/modular_test/pubspec.yaml
+++ b/pkg/modular_test/pubspec.yaml
@@ -1,4 +1,5 @@
name: modular_test
+# This package is not intended for consumption on pub.dev. DO NOT publish.
publish_to: none
description: >
Small framework to test modular pipelines.
diff --git a/pkg/native_stack_traces/CHANGELOG.md b/pkg/native_stack_traces/CHANGELOG.md
index 10e8638..71ca8ba 100644
--- a/pkg/native_stack_traces/CHANGELOG.md
+++ b/pkg/native_stack_traces/CHANGELOG.md
@@ -1,5 +1,9 @@
# Changelog
+## 0.3.8
+
+- Support columns when present in line number programs.
+
## 0.3.7
- Added buildId accessor for retrieving GNU build IDs from DWARF files that
diff --git a/pkg/native_stack_traces/lib/src/dwarf.dart b/pkg/native_stack_traces/lib/src/dwarf.dart
index 72f7623..fd0911b 100644
--- a/pkg/native_stack_traces/lib/src/dwarf.dart
+++ b/pkg/native_stack_traces/lib/src/dwarf.dart
@@ -313,6 +313,11 @@
int get callLine => this[_AttributeName.callLine] as int;
+ // We don't assume that call columns are present for backwards compatibility.
+ int get callColumn => containsKey(_AttributeName.callColumn)
+ ? this[_AttributeName.callColumn] as int
+ : 0;
+
List<CallInfo> callInfo(
CompilationUnit unit, LineNumberProgram lineNumberProgram, int address) {
String callFilename(int index) =>
@@ -333,7 +338,8 @@
function: unit.nameOfOrigin(abstractOrigin),
inlined: inlined,
filename: callFilename(child.callFileIndex),
- line: child.callLine));
+ line: child.callLine,
+ column: child.callColumn));
}
}
@@ -341,12 +347,14 @@
final filename = lineNumberProgram.filename(address);
final line = lineNumberProgram.lineNumber(address);
+ final column = lineNumberProgram.column(address);
return [
DartCallInfo(
function: unit.nameOfOrigin(abstractOrigin),
inlined: inlined,
filename: filename,
- line: line)
+ line: line,
+ column: column)
];
}
@@ -980,6 +988,8 @@
int lineNumber(int address) => this[address]?.line;
+ int column(int address) => this[address]?.column;
+
void writeToStringBuffer(StringBuffer buffer) {
header.writeToStringBuffer(buffer);
@@ -1054,8 +1064,14 @@
final String function;
final String filename;
final int line;
+ final int column;
- DartCallInfo({this.inlined = false, this.function, this.filename, this.line});
+ DartCallInfo(
+ {this.inlined = false,
+ this.function,
+ this.filename,
+ this.line,
+ this.column});
@override
bool get isInternal => false;
@@ -1063,9 +1079,12 @@
@override
int get hashCode => _hashFinish(_hashCombine(
_hashCombine(
- _hashCombine(_hashCombine(0, inlined.hashCode), function.hashCode),
- filename.hashCode),
- line.hashCode));
+ _hashCombine(
+ _hashCombine(
+ _hashCombine(0, inlined.hashCode), function.hashCode),
+ filename.hashCode),
+ line.hashCode),
+ column.hashCode));
@override
bool operator ==(Object other) {
@@ -1073,13 +1092,29 @@
return inlined == other.inlined &&
function == other.function &&
filename == other.filename &&
- line == other.line;
+ line == other.line &&
+ column == other.column;
}
return false;
}
+ void writeToStringBuffer(StringBuffer buffer) {
+ buffer..write(function)..write(' (')..write(filename);
+ if (line > 0) {
+ buffer..write(':')..write(line);
+ if (column > 0) {
+ buffer..write(':')..write(column);
+ }
+ }
+ buffer.write(')');
+ }
+
@override
- String toString() => "${function} (${filename}${line <= 0 ? '' : ':$line'})";
+ String toString() {
+ final buffer = StringBuffer();
+ writeToStringBuffer(buffer);
+ return buffer.toString();
+ }
}
/// Represents the information for a call site located in a Dart stub.
diff --git a/pkg/native_stack_traces/pubspec.yaml b/pkg/native_stack_traces/pubspec.yaml
index b086b607..cc0d214 100644
--- a/pkg/native_stack_traces/pubspec.yaml
+++ b/pkg/native_stack_traces/pubspec.yaml
@@ -1,6 +1,6 @@
name: native_stack_traces
description: Utilities for working with non-symbolic stack traces.
-version: 0.3.7
+version: 0.3.8
homepage: https://github.com/dart-lang/sdk/tree/master/pkg/native_stack_traces
diff --git a/pkg/nnbd_migration/lib/migration_cli.dart b/pkg/nnbd_migration/lib/migration_cli.dart
index 4d74b5d..3c5a8d9 100644
--- a/pkg/nnbd_migration/lib/migration_cli.dart
+++ b/pkg/nnbd_migration/lib/migration_cli.dart
@@ -50,6 +50,7 @@
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 skipPubOutdatedFlag = 'skip-pub-outdated';
@@ -65,6 +66,8 @@
final bool ignoreExceptions;
+ final String previewHostname;
+
final int previewPort;
final String sdkPath;
@@ -80,6 +83,7 @@
@required this.directory,
@required this.ignoreErrors,
@required this.ignoreExceptions,
+ @required this.previewHostname,
@required this.previewPort,
@required this.sdkPath,
@required this.skipPubOutdated,
@@ -311,6 +315,8 @@
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 ??
@@ -394,6 +400,11 @@
help:
'Attempt to perform null safety analysis even if exceptions occur.',
hide: hide);
+ parser.addOption(CommandLineOptions.previewHostnameOption,
+ defaultsTo: 'localhost',
+ help: 'Run the preview server on the specified hostname. If not '
+ 'specified, "localhost" is used. Use "any" to specify IPv6.any or '
+ 'IPv4.any.');
parser.addOption(CommandLineOptions.previewPortOption,
help:
'Run the preview server on the specified port. If not specified, '
@@ -517,10 +528,12 @@
NonNullableFix createNonNullableFix(DartFixListener listener,
ResourceProvider resourceProvider, LineInfo getLineInfo(String path),
{List<String> included = const <String>[],
+ String hostname,
int preferredPort,
String summaryPath}) {
return NonNullableFix(listener, resourceProvider, getLineInfo,
included: included,
+ hostname: hostname,
preferredPort: preferredPort,
summaryPath: summaryPath);
}
@@ -538,8 +551,9 @@
logger.stdout('');
if (hasMultipleAnalysisContext) {
- logger.stdout(
- 'Note: more than one project found; migrating the top-level project.');
+ logger
+ .stdout('Note: more than one project found; migrating the top-level '
+ 'project.');
logger.stdout('');
}
@@ -555,6 +569,7 @@
nonNullableFix = createNonNullableFix(
_dartFixListener, resourceProvider, _fixCodeProcessor.getLineInfo,
included: [options.directory],
+ hostname: options.previewHostname,
preferredPort: options.previewPort,
summaryPath: options.summary);
nonNullableFix.rerunFunction = _rerunFunction;
diff --git a/pkg/nnbd_migration/lib/src/edge_builder.dart b/pkg/nnbd_migration/lib/src/edge_builder.dart
index 78012f8..64bab25 100644
--- a/pkg/nnbd_migration/lib/src/edge_builder.dart
+++ b/pkg/nnbd_migration/lib/src/edge_builder.dart
@@ -1068,6 +1068,8 @@
@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);
@@ -1076,6 +1078,14 @@
// Making it nullable could change runtime behavior.
_graph.makeNonNullable(
decoratedType.node, IsCheckMainTypeOrigin(source, type));
+ _conditionInfo = _ConditionInfo(node,
+ isPure: expression is SimpleIdentifier,
+ postDominatingIntent:
+ _postDominatedLocals.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.
@@ -1085,8 +1095,6 @@
// TODO(brianwilkerson)
_unimplemented(node, 'Is expression with GenericFunctionType');
}
- var expression = node.expression;
- _dispatch(expression);
_flowAnalysis.isExpression_end(
node, expression, node.notOperator != null, decoratedType);
return _makeNonNullableBoolType(node);
diff --git a/pkg/nnbd_migration/lib/src/fix_builder.dart b/pkg/nnbd_migration/lib/src/fix_builder.dart
index 602ef69..ad8710d 100644
--- a/pkg/nnbd_migration/lib/src/fix_builder.dart
+++ b/pkg/nnbd_migration/lib/src/fix_builder.dart
@@ -310,18 +310,6 @@
TypeProvider get typeProvider => _fixBuilder.typeProvider;
- Element get _elementHashCode =>
- _fixBuilder.typeProvider.objectType.getGetter('hashCode');
-
- Element get _elementNoSuchMethod =>
- _fixBuilder.typeProvider.objectType.getGetter('noSuchMethod');
-
- Element get _elementRuntimeType =>
- _fixBuilder.typeProvider.objectType.getMethod('runtimeType');
-
- Element get _elementToString =>
- _fixBuilder.typeProvider.objectType.getMethod('toString');
-
@override
void freshTypeParameterCreated(TypeParameterElement newTypeParameter,
TypeParameterElement oldTypeParameter) {
diff --git a/pkg/nnbd_migration/lib/src/front_end/migration_info.dart b/pkg/nnbd_migration/lib/src/front_end/migration_info.dart
index ddc8ed6..52e5a2f 100644
--- a/pkg/nnbd_migration/lib/src/front_end/migration_info.dart
+++ b/pkg/nnbd_migration/lib/src/front_end/migration_info.dart
@@ -86,6 +86,15 @@
/// The path of the Dart logo displayed in the toolbar.
String get dartLogoPath => PreviewSite.dartLogoPath;
+ /// 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;
+
/// Return the path to [unit] from [includedRoot], to be used as a display
/// name for a library.
String computeName(UnitInfo unit) =>
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
index b34a302..fb43d4e 100644
--- a/pkg/nnbd_migration/lib/src/front_end/non_nullable_fix.dart
+++ b/pkg/nnbd_migration/lib/src/front_end/non_nullable_fix.dart
@@ -38,6 +38,8 @@
static final List<HttpPreviewServer> _allServers = [];
+ final String hostname;
+
final int preferredPort;
final DartFixListener listener;
@@ -60,10 +62,6 @@
/// The HTTP server that serves the preview tool.
HttpPreviewServer _server;
- /// The port on which preview pages should be served, or `null` if no preview
- /// server should be started.
- int port;
-
String authToken;
InstrumentationListener instrumentationListener;
@@ -83,7 +81,10 @@
List<String> previewUrls;
NonNullableFix(this.listener, this.resourceProvider, this._getLineInfo,
- {List<String> included = const [], this.preferredPort, this.summaryPath})
+ {List<String> included = const [],
+ this.hostname,
+ this.preferredPort,
+ this.summaryPath})
: includedRoot =
_getIncludedRoot(included, listener.server.resourceProvider) {
reset();
@@ -183,18 +184,19 @@
Future<void> startPreviewServer(MigrationState state) async {
if (_server == null) {
- _server = HttpPreviewServer(state, rerun, preferredPort);
+ _server = HttpPreviewServer(state, rerun, hostname, preferredPort);
_server.serveHttp();
_allServers.add(_server);
- port = await _server.boundPort;
+ var serverHostname = await _server.boundHostname;
+ var serverPort = await _server.boundPort;
authToken = await _server.authToken;
previewUrls = [
// TODO(jcollins-g): Change protocol to only return a single string.
Uri(
scheme: 'http',
- host: 'localhost',
- port: port,
+ host: serverHostname,
+ port: serverPort,
path: state.pathMapper.map(includedRoot),
queryParameters: {'authToken': authToken}).toString()
];
diff --git a/pkg/nnbd_migration/lib/src/front_end/resources/MaterialIconsRegular.ttf b/pkg/nnbd_migration/lib/src/front_end/resources/MaterialIconsRegular.ttf
new file mode 100644
index 0000000..7015564a
--- /dev/null
+++ b/pkg/nnbd_migration/lib/src/front_end/resources/MaterialIconsRegular.ttf
Binary files 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
new file mode 100644
index 0000000..5919b5d
--- /dev/null
+++ b/pkg/nnbd_migration/lib/src/front_end/resources/RobotoMonoRegular.ttf
Binary files 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
new file mode 100644
index 0000000..2b6392f
--- /dev/null
+++ b/pkg/nnbd_migration/lib/src/front_end/resources/RobotoRegular.ttf
Binary files differ
diff --git a/pkg/nnbd_migration/lib/src/front_end/resources/index.html b/pkg/nnbd_migration/lib/src/front_end/resources/index.html
index ecfd0c3..1b67ad7 100644
--- a/pkg/nnbd_migration/lib/src/front_end/resources/index.html
+++ b/pkg/nnbd_migration/lib/src/front_end/resources/index.html
@@ -21,16 +21,36 @@
<p class="root">{{ root }}</p>
<header class="elevation-z4">
<img src="{{ dartLogoPath }}" alt="DartPad Logo" class="logo"/>
- <h1 class="before-apply">Proposed null safety changes</h1>
- <h1 class="after-apply">✓ Null safety migration applied</h1>
- <h2 id="unit-name"> </h2>
- <button class="apply-migration">✎ Apply Migration</button>
- <button class="apply-migration" disabled>✎ Apply Migration</button>
- <button class="rerun-migration before-apply">
- <span class="optional">↻ Rerun From Sources</span>
+ <h1>Dart</h1>
+ <h2 class="before-apply">Proposed null safety changes</h2>
+ <h2 class="after-apply">✓ Null safety migration applied</h2>
+ <h3 id="unit-name"> </h3>
+ <a target="_blank"
+ href="https://goo.gle/dart-null-safety-migration-tool">
+ <button class="action-button">
+ <i class="material-icons">launch</i>
+ <span>Help</span>
+ </button>
+ </a>
+ <button class="action-button apply-migration">
+ <i class="material-icons">edit</i>
+ <span class="label">
+ Apply Migration
+ </span>
+ </button>
+ <button class="action-button apply-migration" disabled>
+ <i class="material-icons">edit</i>
+ <span class="label">
+ Apply Migration
+ </span>
+ </button>
+ <button class="action-button rerun-migration before-apply">
+ <span class="optional">
+ <i class="material-icons">replay</i>
+ Rerun From Sources
+ </span>
<span class="required">
- <span class="icon"
- title="Disk contents have changed. Rerun to get an up-to-date migration.">!</span>
+ <i class="material-icons">warning</i>
Rerun With Changes
</span>
</button>
@@ -68,12 +88,9 @@
</div><!-- /info-panel -->
</div><!-- /panels -->
<footer>
- <a target="_blank"
- href="https://goo.gle/dart-null-safety-migration-tool">Null safety
- migration help</a>
+ <button class="report-problem">Send Feedback</button>
<span class="wide"> </span>
- <div>Based on <span id="sdk-version">{{ sdkVersion }}</span></div>
- <button class="report-problem">Report a Problem</button>
+ <div class="sdk-version">Based on <span id="sdk-version">{{ sdkVersion }}</span></div>
</footer>
</body>
</html>
diff --git a/pkg/nnbd_migration/lib/src/front_end/resources/migration.css b/pkg/nnbd_migration/lib/src/front_end/resources/migration.css
index 8f8c4e0..53fb7cc 100644
--- a/pkg/nnbd_migration/lib/src/front_end/resources/migration.css
+++ b/pkg/nnbd_migration/lib/src/front_end/resources/migration.css
@@ -2,6 +2,55 @@
/* 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;
@@ -44,7 +93,8 @@
}
header h1,
-header h2 {
+header h2,
+header h3 {
display: inline-block;
font-family: "Google Sans","Roboto",sans-serif;
font-weight: 400;
@@ -57,9 +107,20 @@
header h2 {
font-size: 1.2em;
+
+ /* Shift text up */
+ position: relative;
+ top: -2px;
}
-header .apply-migration, .rerun-migration {
+header h3 {
+ /* Shift text up */
+ position: relative;
+ top: -2px;
+}
+
+
+header .action-button, header a {
right: 0px;
float: right;
margin: 10px;
@@ -69,6 +130,8 @@
height: 24px;
width: 24px;
margin-right: 8px;
+ position: relative;
+ top: 4px;
}
footer .report-problem {
@@ -88,36 +151,6 @@
display:none;
}
-/* Red triangle */
-.rerun-migration .required .icon::before {
- transform: translate(-8px, -11px);
- content: '\25B3';
- font-size: 25px;
- position: fixed;
- color: #e82c2c;
- text-shadow: 0px 0px 5px white;
- z-index: -3;
-}
-
-/* Red triangle fill */
-.rerun-migration .required .icon::after {
- transform: translate(-9px, -10px);
- content: '\25B4';
- font-size: 25px;
- position: fixed;
- color: #b3ecff;
- z-index: -1;
-}
-
-/* Red triangle exclamation */
-.rerun-migration .required .icon {
- display: inline-block;
- margin-right: 8px;
- transform: translate(0px, 2px);
- margin-left: 2px;
- color: #2b2b2b;
-}
-
footer {
color: #ccc;
background-color: #27323a;
@@ -131,6 +164,10 @@
flex: 1;
}
+footer .sdk-version {
+ margin-right: 32px;
+}
+
.horizontal {
display: flex;
}
@@ -142,7 +179,7 @@
}
.panel-heading {
- color: gray;
+ color: #676767;
margin: 8px;
}
@@ -152,10 +189,11 @@
}
.nav-panel {
- background-color: #282b2e;
+ background-color: #12202f;
flex: 1 200px;
margin: 0;
overflow: scroll;
+ user-select: none;
}
.nav-inner {
@@ -176,6 +214,14 @@
padding-left: 6px;
}
+.nav-tree .material-icons {
+ font-size: 20px;
+ position: relative;
+ top: 5px;
+ margin-right: 8px;
+ color: #676767;
+}
+
.nav-inner ul {
padding-left: 12px;
margin: 0;
@@ -220,9 +266,9 @@
}
.edit-count {
- background-color: #37aedc;
+ background-color: #676767;
border-radius: 10px;
- color: #000000;
+ color: #fff;
display: inline-block;
font-size: 11px;
font-weight: 600;
@@ -235,8 +281,8 @@
.content {
flex: 4 300px;
- background: #282b2e;
- font-family: monospace;
+ background: #12202f;
+ font-family: "Roboto Mono", monospace;
margin: 0 6px;
position: relative;
white-space: pre;
@@ -251,13 +297,6 @@
margin-left: 56px;
}
-.hljs {
- background-color: #282b2e;
- display: block;
- overflow-x: auto;
- padding: 0.5em;
-}
-
.code .welcome {
font-family: "Google Sans","Roboto",sans-serif;
font-size: 18px;
@@ -266,12 +305,12 @@
}
.code .nav-link {
- color: inherit;
+ color: #16adca;
text-decoration-line: none;
}
.code .nav-link:visited {
- color: inherit;
+ color: #139bb5; /* #16adca darkened 10% */
text-decoration-line: none;
}
@@ -311,7 +350,7 @@
}
.regions td.line-no {
- border-right: solid #282b2e 2px;
+ border-right: solid #12202f 2px;
color: #999999;
padding-right: 4px;
text-align: right;
@@ -332,22 +371,22 @@
}
.region.added-region {
- background-color: #ccffcc;
- color: #003300;
+ background-color: #263952; /* $dark-selection-color */
+ color: #c0c2c5; /* $dark-editor-text */
}
.region.removed-region {
- background-color: #ff6666;
- color: #001100;
+ background-color: #FA557d; /* $dark-pink */
+ color: #fff;
}
.region.informative-region {
- background-color: #404549; /* #282b2e, lightened 10% */
- border-bottom: solid rgba(255, 255, 0, 0.7) 1px;
+ background-color: #178afd; /* $dark-selection-color */
+ color: #fff;
display: inline-block;
- height: 14px;
+ height: 18px;
position: relative;
- top: 1px;
+ top: 3px;
}
.target {
@@ -366,7 +405,7 @@
}
.info-panel .edit-panel {
- background-color: #282b2e;
+ background-color: #12202f;
overflow: auto;
}
@@ -392,11 +431,15 @@
}
.info-panel a {
- color: #33ccff;
+ color: #139bb5;
+}
+
+.info-panel a:hover {
+ color: #1ec7e7; /* #139bb5 lightened 20% */
}
.info-panel .edit-list {
- background-color: #282b2e;
+ background-color: #12202f;
overflow: auto;
}
@@ -456,7 +499,7 @@
}
.popup-pane pre {
- background: #282b2e;
+ background: #12202f;
padding: 20px;
bottom: 0px;
overflow: auto scroll;
@@ -494,8 +537,8 @@
}
.edit-panel .type-description {
- /* From HLJS's .hljs-keyword, .hljs-selector-tag, .hljs-deletion */
- color: #cc7832;
+ /* From DartPad $dark-orange */
+ color: #ff916e;
font-family: monospace;
}
@@ -510,8 +553,8 @@
}
ul.trace li .function {
- /* From HLJS's .hljs-section, .hljs-title, .hljs-type */
- color: #ffc66d;
+ /* from .hljs-variable */
+ color: #16adca;
font-family: monospace;
font-weight: 600;
}
@@ -538,7 +581,7 @@
}
a:hover {
- color: #fff;
+ color: #dbdbdb; /* #ccc lightented 30%*/
fill: #fff;
}
@@ -547,30 +590,79 @@
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: #33ccff;
- border: 2px solid #37aedc;
+ background-color: rgba(22, 138, 253, 0.15);
+ border: none;
border-radius: 3px;
- padding: 6px 10px;
- font-weight: bold;
- color: #282828;
+ padding: 3px 10px;
+ font-weight: 500;
+ font-font: Roboto, sans-serif;
+ color: #fff;
}
button:hover, .button:hover {
- background-color: #80dfff;
- border: 2px solid #52b8e0;
+ background-color: rgba(22, 138, 253, 0.29);
cursor: pointer;
}
button[disabled] {
- background-color: #7aa8b8;
- color: #507177;
- border: 2px solid #507177;
+ 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;
@@ -581,8 +673,57 @@
* 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
index e03b662..52c6871 100644
--- a/pkg/nnbd_migration/lib/src/front_end/resources/resources.g.dart
+++ b/pkg/nnbd_migration/lib/src/front_end/resources/resources.g.dart
@@ -9,6 +9,19 @@
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);
}
@@ -38,6 +51,6834 @@
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
+PjBOwEw/AVVWRBA0QShNwE03AAMAKwArAdUB1QAHABcAGgAAJTMDIwMzNzMTMhYVERQGIyEiJjURNDYz
+FzcXAVQtbShtLRh4bxEZGRH+qhEZGRF/LCx1ARb+6kABIBkR/qoRGRkRAVYRGfV2dgAAAwBAAEABwAHA
+AAYAEAAgAAABByczNTMVFzUhFTMUFjI2NRMyFhURFAYjISImNRE0NjMBVVVVKlZq/tVWJjQmVREaGhH+
+1RIYGBIBK1ZWQEBr1dUaJiYaAQAaEf7WERoZEgEqEhkAAwBAAEABwAHAAAMACgAeAAATISchFwczFTM1
+MzcWFREUBiMhIiY1ETQ3NzYzITIXbQEmFP8AgXVKVkpBChoR/tYSGQodCg8BAA8KAZUWdnUrK9EMEP72
+ERoaEQEKEAwjDAwAAAMAKwArAdUBwAAFAAkAIwAANzcnBxcHERUzNTUyFhUVMzIWFRUUBiMhIiY1NTQ2
+MzM1NDYz61VVFkBAVhEZVhEZGRH+qhEZGRFWGRF1VlUVQEABCioqKxkSKhoR6xEZGRHrERoqERoAAgAV
+AGsB6wGVABEAJwAAATIWFRUGBhUVITU0Jic1NDYzBDIWFRUUBiMhIiY1NTQ2MhYVFSE1NAGAERoTGP8A
+GBMaEQEvIhoaEf6AERoaIhoBKgGVGREuByAVLCwVIAcuERlqGhFrERkZEWsRGhoRQEARAAAFACsAVQHV
+AasABwARABUAGQAdAAABFSE1MzczFwM1MxUUBiMjIiYlMxUjNTMVIxUzFSMBK/8AQBVVFqvVGRGAERoB
+AICAlZVVVQGVKioWFv7r1dURGhqRK4AqgCsABAArAFUB1QGVABIAFgAaAB4AADY0NjMzFSMiBhQWMzM1
+Fwc1IyI3MxUjNTMVIzUzFSMrUTlLSyg4OCgLQEALOa+qqqqqqqrSclEqOFA4KkBAKysroCugKgAABQAr
+ACsB1QHYAAcADwAVABkAHQAANjI2NCYiBhQ2MhYUBiImNDcVFwcnNScHJzcFByc3wnxXV3xXRaBwcKBw
+y1UQZUNiG2IBSBtiG1VYfFdXfP5xnnFxnhtwMho8gGNSIFJSIVMgAAAFACsAKwHVAdoABwAPABUAGQAd
+AAA2MjY0JiIGFDYyFhQGIiY0NxUXByc1JwcnNwUHJzfCfFdXfFdFoHBwoHDLVRFkQmMbYgFIG2McVVh8
+V1d8/nCgcHCgGnEzGj6AYlEgUVEgVCAAAAMAKwArAdUB1QAFAA0AFQAAARUXByc1AjI2NCYiBhQSMhYU
+BiImNAELYBBwMYxlZYxlU7B9fbB9AWtwORtEgP7qZYxlZYwBG32wfX2wAAUAKwArAdUB2AALABMAGwAf
+ACMAAAEVMxUjFSM1IzUzNQYyNjQmIgYUNjIWFAYiJjQlByc3BwcnNwEVQEAqQEApfFdXfFdFoHBwoHAB
+lRtiG8tiG2IBQEArQEArQOtYfFdXfP5xnnFxnkwhUyAgUiBSAAIAKwArAcAB1QAPABoAABM3AQcnFRcV
+Jwc1NzUHNTc3FxUnJzU0NjIWFUAbAVAbeytLSiqqf2urRKcTGhMBkBv+sBt6TyAgFRUgIHU1KlAbayoV
+p04NExMNAAIAKwArAcAB1QAAABUAABMXJxUXFScHNTc1BzU3NTQ2MhYVFRfZ56srS0oqqqoTGhOrAUCV
+NXUgIBUVICB1NSprdQ0TEw11awAAAwCVACsBawHVAAMABwAbAAAlNSMVFzUjFRMyFhURFAYjIyImNRE0
+NjMzNTMVARUqKipjDBERDJwMEREMI1bVa2tVKysBKxEM/rkMEBAMAUcMESoqAAACAJUAKwFrAdUABQAZ
+AAA3NyM1BzM3MhYVERQGIyMiJjURNDYzMzUzFetVK1UrYwwREQycDBERDCNWVaB2oOARDP65DBAQDAFH
+DBEqKgABAJUAKwFrAdUAEwAAATIWFREUBiMjIiY1ETQ2MzM1MxUBTgwREQycDBERDCNWAasRDP65DBAQ
+DAFHDBEqKgAAAQCVACsBawHVABMAAAEyFhURFAYjIyImNRE0NjMzNTMVAU4MEREMnAwREQwjVgGrEQz+
+uQwQEAwBRwwRKioAAAMAlQArAWsB1QAUABgALAAAJTY1NCYiBhUzNDYyFhQHBwYVMzQ3BzUjFRMyFhUR
+FAYjIyImNRE0NjMzNTMVATEPJjQmIBIcEgkUFCISDyhiDBERDJwMEREMI1bxDxUaJiYaDRMTGgkUFBcQ
+EmEpKQEqEQz+uQwQEAwBRwwRKioAAwBrACsBegHVAAIABQATAAAlJxURFTczBxcHIzUHJzcnNxc1MwE9
+KCg9XFx6FWIed3ceYhWkKVEBCFEpXFx5omIed3ceYqIABQBAACsBwAHVAAMABgAJABcAGwAAARcHJwcn
+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
+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
+KyorKyoRGhoRASoRGhoRKgADAIAAFQGAAesAAwATABcAACU1IxUTMhYVERQGIyMiJjURNDYzETUzFQFV
+qqoRGhoRqhEaGhGqwNXVASoZEf7VERkZEQErERr+KisrAAAEACsAKwHVAdUABAAJAA4AEwAAATMVIycH
+NxcVIwMXByM1JQcnNTMBYHV1QGBAQIAgQEB1ARVAQIABQIBAYEBAdQEVQECAIEBAdQAAAQBAAFUBwAHr
+ABkAABIyFhUVFAYjIzUzNTQmIgYVFTMVIyImNTU0sKBwJhpAVVd8V1VAGiYB63FPlhomqys+V1c+K6sm
+GpZPAAABAEAAFQHAAesAHQAAEjIWFRUUBiMjNTM1IzUzNTQmIgYVFTMVIyImNTU0sKBwJhqAlVVVV3xX
+VUAaJgHrcU/WGiYrFasrPldXPiurJhqWTwAADAArAGsB1QGVAAMABwALAA8AEwAXABsAHwAjACcAKwA7
+AAABNSMVFzUjFSc1IxUXNSMVFzUjFSc1IxUXNSMVNxUzNScVMzUXFTM1JxUzNTcyFhUVFAYjISImNTU0
+NjMBlSoqKhYqKioqqhYqKipAKioqFioqKpYRGRkR/qoRGRkRASsqKkAqKkAqKkAqKlYrK5YqKkAqKioq
+KkAqKkAqKkAqKkAZEdYRGRkR1hEZAAABAIAAuwGAAVkABQAAExc3FwcnnmJiHoCAAVliYh6AgAAAAQCr
+AIsBSQGLAAUAACUHJzcXBwFJHoCAHmKpHoCAHmIAAAEAtwCFAVUBhQAFAAA3Nyc3Fwe3YmIegICjYmIe
+gIAAAQCAALcBgAFVAAUAADcnNxcHJ54egIAeYrcegIAeYgABAEAAgAHAAYAACAAAARUhFwcnNxcHAcD+
+0kwegIAeTAEVKk0egIAeTQACAIAAgAGAAYkAAwAJAAA3NSEVJwcnNxcHgAEAgGIegIAegCsrzWIegIAe
+AA0AKwAVAdUBwAACAAYACgAOABIAFgAaAB4AIgAmACoALgA+AAAlJzM3NSMVFzUjFSc1IxUXNSMVFzUj
+FSc1IxUXNSMVNxUzNScVMzUXFTM1JxUzNTcyFhUVFAYjISImNTU0NjMBAFWqQCoqKhYqKioqqhYqKipA
+KioqFioqKpYRGRkR/qoRGRkRFVbqKytAKytAKytAKytVKyuVKytAKysrKytAKytAKytAKytAGhHVERoa
+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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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
+RJ6e/rwAAAEAjv/sBCkFQAAnAEhARRMBBAMUAQUEAkoLAQoACnIHAQIGAQMEAgNhCAEBAQBZCQEAAD9L
+AAQEBVsABQVFBUwAAAAnACcmJRERFSklEREREQwIHSsBESEVIRUzFSMVFB4CMzI+AjcXDgMjIi4CNTUj
+NTM1ITUhEQJkAZz+ZOrqIDhJKR5APTURGhdCTlcrSH1cNdnZ/uQBHAVA/vqPupf7P1IxFAcKCwSDDhUP
+CClZjWT7l7qPAQYA//8AUQAABJAHIwImACMAAAEHAEL/mQFaAAmxAgG4AVqwMysA//8AUQAABJAHIAIm
+ACMAAAEHAHMAhQFXAAmxAgG4AVewMysA//8AUQAABJAHSAImACMAAAEHAJwAhwFbAAmxAgG4AVuwMysA
+//8AUQAABJAHUgImACMAAAEHAKIAkwFhAAmxAgG4AWGwMysA//8AUQAABJAHIAImACMAAAEHAGgADwFb
+AAmxAgK4AVuwMysA//8AUQAABJAHiwImACMAAAEHAKAADgGkAAmxAgK4AaSwMysA//8AUQAABJAIGAIm
+ACMAAAEHAb7//AGmAAmxAgO4AaawMysA//8Aa/5NBF0FxAImACUAAAAGAHc2AP//ALYAAAQ0ByMCJgAn
+AAABBwBC/48BWgAJsQEBuAFasDMrAP//ALYAAAQ0ByACJgAnAAABBwBzAHsBVwAJsQEBuAFXsDMrAP//
+ALYAAAQ0B0gCJgAnAAABBwCcAH0BWwAJsQEBuAFbsDMrAP//ALYAAAQ0ByACJgAnAAABBwBoAAUBWwAJ
+sQECuAFbsDMrAP//AK4AAAQeByMCJgArAAABBwBC/1sBWgAJsQEBuAFasDMrAP//AK4AAAQeByACJgAr
+AAABBwBzAEcBVwAJsQEBuAFXsDMrAP//AK4AAAQeB0gCJgArAAABBwCcAEkBWwAJsQEBuAFbsDMrAP//
+AK4AAAQeByACJgArAAABBwBo/9IBWwAJsQECuAFbsDMrAP//AI8AAAQ+B1ICJgAwAAABBwCiAGwBYQAJ
+sQEBuAFhsDMrAP//AGr/7ARhBzgCJgAxAAABBwBC/50BbwAJsQIBuAFvsDMrAP//AGr/7ARhBzUCJgAx
+AAABBwBzAIkBbAAJsQIBuAFssDMrAP//AGr/7ARhB10CJgAxAAABBwCcAIsBcAAJsQIBuAFwsDMrAP//
+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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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
+AWNhsAIlRmE4IyA8IzgbISAgRiNHsAErI2E4IVmxLgEUKy2wRSywOCsusS4BFCstsEYssDkrISMgIDyw
+BCNCIzixLgEUK7AEQy6wListsEcssAAVIEewACNCsgABARUUEy6wNCotsEgssAAVIEewACNCsgABARUU
+Ey6wNCotsEkssQABFBOwNSotsEossDcqLbBLLLAAFkUjIC4gRoojYTixLgEUKy2wTCywCCNCsEsrLbBN
+LLIAAEQrLbBOLLIAAUQrLbBPLLIBAEQrLbBQLLIBAUQrLbBRLLIAAEUrLbBSLLIAAUUrLbBTLLIBAEUr
+LbBULLIBAUUrLbBVLLIAAEErLbBWLLIAAUErLbBXLLIBAEErLbBYLLIBAUErLbBZLLIAAEMrLbBaLLIA
+AUMrLbBbLLIBAEMrLbBcLLIBAUMrLbBdLLIAAEYrLbBeLLIAAUYrLbBfLLIBAEYrLbBgLLIBAUYrLbBh
+LLIAAEIrLbBiLLIAAUIrLbBjLLIBAEIrLbBkLLIBAUIrLbBlLLA6Ky6xLgEUKy2wZiywOiuwPistsGcs
+sDorsD8rLbBoLLAAFrA6K7BAKy2waSywOysusS4BFCstsGossDsrsD4rLbBrLLA7K7A/Ky2wbCywOyuw
+QCstsG0ssDwrLrEuARQrLbBuLLA8K7A+Ky2wbyywPCuwPystsHAssDwrsEArLbBxLLA9Ky6xLgEUKy2w
+ciywPSuwPistsHMssD0rsD8rLbB0LLA9K7BAKy2wdSyzCQQCA0VYIRsjIVlCK7AIZbADJFB4sQUBFUVY
+MFktAABLuADIUlixAQGOWbABuQgACABjcLEABkK1AEczHwQAKrEABkJACk0BOggmCBQHBAgqsQAGQkAK
+TgBEBjAGHQUECCqxAApCvROADsAJwAVAAAQACSqxAA5CvQAAAEAAQABAAAQACSqxAwBEsSQBiFFYsECI
+WLEDZESxJgGIUVi6CIAAAQRAiGNUWLEDAERZWVlZQApOADwIKAgWBwQMKrgB/4WwBI2xAgBEsQVkRAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALkAuQCXAJcFsAAABDoAAP5gCGL91QXE
+/+wETv/s/ksIYv3VALkAuQCXAJcFsAAABbAEOv/s/mAIYv3VBcT/7AXFBE7/7P5gCGL91QC5ALkAlwCX
+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
+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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
+AqEAAADwAAAA8AAAAIcAAADxAAAA9gAAAqoAAAD3AAAA+AAAAIgAAAD5AAAA/QAAArAAAAD+AAAA/gAA
+AIoAAAD/AAABDwAAArUAAAEQAAABEAAAAnoAAAERAAABEQAAAIsAAAESAAABJQAAAsYAAAEmAAABJgAA
+AIwAAAEnAAABJwAAAnwAAAEoAAABMAAAAtoAAAExAAABMQAAAI0AAAEyAAABNwAAAuMAAAE4AAABOAAA
+AI4AAAE5AAABQAAAAukAAAFBAAABQgAAAI8AAAFDAAABSQAAAvEAAAFKAAABSwAAAJEAAAFMAAABUQAA
+AvgAAAFSAAABUwAAAJMAAAFUAAABXwAAAv4AAAFgAAABYQAAAwwAAAFiAAABZQAAAxAAAAFmAAABZwAA
+An0AAAFoAAABfgAAAxQAAAF/AAABfwAAAJUAAAGPAAABjwAAAJYAAAGSAAABkgAAAJcAAAGgAAABoQAA
+AJgAAAGvAAABsAAAAJoAAAHwAAAB8AAAA94AAAH6AAAB+gAAAoUAAAH7AAAB+wAAAqAAAAH8AAAB/wAA
+AysAAAIYAAACGQAAAwoAAAIaAAACGwAAAw4AAAI3AAACNwAAAJwAAAJZAAACWQAAAJ0AAAK8AAACvAAA
+A98AAALGAAACxwAAAJ4AAALJAAACyQAAAKAAAALYAAAC3QAAAKEAAALzAAAC8wAAAKcAAAMAAAADAQAA
+AKgAAAMDAAADAwAAAKoAAAMJAAADCQAAAKsAAAMPAAADDwAAAKwAAAMjAAADIwAAAK0AAAOEAAADhQAA
+AK4AAAOGAAADhgAAA4YAAAOHAAADhwAAALAAAAOIAAADigAAA4cAAAOMAAADjAAAA4oAAAOOAAADkgAA
+A4sAAAOTAAADlAAAALEAAAOVAAADlwAAA5AAAAOYAAADmAAAALMAAAOZAAADmgAAA5MAAAObAAADmwAA
+ALQAAAOcAAADnQAAA5UAAAOeAAADngAAALUAAAOfAAADnwAAA5cAAAOgAAADoAAAALYAAAOhAAADoQAA
+A5gAAAOjAAADowAAALcAAAOkAAADpQAAA5kAAAOmAAADpgAAALgAAAOnAAADpwAAA5sAAAOoAAADqQAA
+ALkAAAOqAAADsAAAA5wAAAOxAAADuQAAALsAAAO6AAADugAAA6MAAAO7AAADuwAAAMQAAAO8AAADvQAA
+A6UAAAO+AAADvgAAAMUAAAO/AAADvwAAA6QAAAPAAAADxgAAAMYAAAPHAAADxwAAA6cAAAPIAAADyQAA
+AM0AAAPKAAADzgAAA6gAAAPRAAAD0gAAAM8AAAPWAAAD1gAAANEAAAQAAAAEAAAAA+QAAAQBAAAEAQAA
+A60AAAQCAAAEAgAAANIAAAQDAAAEAwAAA64AAAQEAAAEBAAAANMAAAQFAAAECAAAA68AAAQJAAAECwAA
+ANQAAAQMAAAEDAAAA7QAAAQNAAAEDQAAA+UAAAQOAAAEDgAAA7UAAAQPAAAEDwAAANcAAAQQAAAEEAAA
+A7YAAAQRAAAEEQAAANgAAAQSAAAEEwAAA7cAAAQUAAAEFAAAANkAAAQVAAAEFQAAA7kAAAQWAAAEGAAA
+ANoAAAQZAAAEGQAAA7oAAAQaAAAEGgAAA7MAAAQbAAAEGwAAAN0AAAQcAAAEIgAAA7sAAAQjAAAEJAAA
+AN4AAAQlAAAEJQAAA8IAAAQmAAAELwAAAOAAAAQwAAAEMAAAA8MAAAQxAAAENAAAAOoAAAQ1AAAENQAA
+A8QAAAQ2AAAEOAAAAO4AAAQ5AAAEOQAAA8UAAAQ6AAAEPQAAAPEAAAQ+AAAEPgAAA8YAAAQ/AAAEPwAA
+APUAAARAAAAEQQAAA8cAAARCAAAEQgAAAPYAAARDAAAEQwAAA8kAAAREAAAERAAAAPcAAARFAAAERQAA
+A8oAAARGAAAETwAAAPgAAARQAAAEUAAAA+YAAARRAAAEUQAAA8sAAARSAAAEUgAAAQIAAARTAAAEUwAA
+A8wAAARUAAAEVAAAAQMAAARVAAAEWAAAA80AAARZAAAEWwAAAQQAAARcAAAEXAAAA9EAAARdAAAEXQAA
+A+cAAAReAAAEXgAAA9IAAARfAAAEYQAAAQcAAARiAAAEYgAABIEAAARjAAAEbwAAAQoAAARwAAAEcQAA
+A+gAAARyAAAEdQAAARcAAAR2AAAEdwAAA+oAAAR4AAAEeAAAA+0AAAR5AAAEeQAAA+wAAAR6AAAEhgAA
+ARsAAASIAAAEiwAAASgAAASMAAAEjAAABIAAAASNAAAEkQAAASwAAASSAAAEkwAABIIAAASUAAAElwAA
+ATEAAASYAAAEmQAAA+4AAASaAAAEnQAAATUAAASeAAAEnwAABIQAAASgAAAEqQAAATkAAASqAAAEqwAA
+A/AAAASsAAAErQAABHcAAASuAAAErwAAA/IAAASwAAAEsQAABIYAAASyAAAEugAAAUMAAAS7AAAEuwAA
+BH8AAAS8AAAEvQAAAUwAAAS+AAAEvwAABH0AAATAAAAEwgAAA/QAAATDAAAEygAAAU4AAATLAAAEzAAA
+BHkAAATNAAAEzgAAAVYAAATPAAAE1wAAA/cAAATYAAAE2AAAAVgAAATZAAAE2QAABAEAAATaAAAE2gAA
+BAAAAATbAAAE3wAABAIAAATgAAAE4QAAAVkAAATiAAAE9QAABAcAAAT2AAAE9wAABHsAAAT4AAAE+QAA
+BBsAAAT6AAAE/QAAAVsAAAT+AAAE/wAABIgAAAUAAAAFAAAAAV8AAAUBAAAFAQAABB0AAAUCAAAFEAAA
+AWAAAAURAAAFEQAABIoAAAUSAAAFEwAAAW8AAB4AAAAeAQAAA+IAAB4+AAAePwAAA+AAAB6AAAAehQAA
+A9MAAB6gAAAe8QAABB4AAB7yAAAe8wAAA9kAAB70AAAe+QAABHAAAB9NAAAfTQAABMoAACAAAAAgCQAA
+AXIAACAKAAAgCwAAAX0AACAQAAAgEQAAAX8AACATAAAgFAAAAYEAACAVAAAgFQAABIwAACAXAAAgHgAA
+AYMAACAgAAAgIgAAAYsAACAlAAAgJwAAAY4AACAwAAAgMAAAAZEAACAyAAAgMwAAA9sAACA5AAAgOgAA
+AZIAACA8AAAgPAAAA90AACBEAAAgRAAAAZQAACB0AAAgdAAAAZUAACB/AAAgfwAAAZYAACCjAAAgowAA
+BIsAACCkAAAgpAAAAZcAACCmAAAgqgAAAZgAACCrAAAgqwAABHYAACCsAAAgrAAAAZ0AACCxAAAgsQAA
+AZ4AACC5AAAgugAAAZ8AACC8AAAgvQAAAaEAACEFAAAhBQAAAaMAACETAAAhEwAAAaQAACEWAAAhFgAA
+AaUAACEiAAAhIgAAAaYAACEmAAAhJgAAALoAACEuAAAhLgAAAacAACFbAAAhXgAAAagAACICAAAiAgAA
+AawAACIGAAAiBgAAALIAACIPAAAiDwAAAa0AACIRAAAiEgAAAa4AACIaAAAiGgAAAbAAACIeAAAiHgAA
+AbEAACIrAAAiKwAAAbIAACJIAAAiSAAAAbMAACJgAAAiYAAAAbQAACJkAAAiZQAAAbUAACXKAAAlygAA
+AbcAAO4BAADuAgAAAbgAAPbDAAD2wwAAAboAAPsBAAD7BAAAAbwAAP7/AAD+/wAAAcIAAP/8AAD//QAA
+AcMAALAALEuwCVBYsQEBjlm4Af+FsIQdsQkDX14tsAEsICBFaUSwAWAtsAIssAEqIS2wAywgRrADJUZS
+WCNZIIogiklkiiBGIGhhZLAEJUYgaGFkUlgjZYpZLyCwAFNYaSCwAFRYIbBAWRtpILAAVFghsEBlWVk6
+LbAELCBGsAQlRlJYI4pZIEYgamFksAQlRiBqYWRSWCOKWS/9LbAFLEsgsAMmUFhRWLCARBuwQERZGyEh
+IEWwwFBYsMBEGyFZWS2wBiwgIEVpRLABYCAgRX1pGESwAWAtsAcssAYqLbAILEsgsAMmU1iwQBuwAFmK
+iiCwAyZTWCMhsICKihuKI1kgsAMmU1gjIbDAioobiiNZILADJlNYIyG4AQCKihuKI1kgsAMmU1gjIbgB
+QIqKG4ojWSCwAyZTWLADJUW4AYBQWCMhuAGAIyEbsAMlRSMhIyFZGyFZRC2wCSxLU1hFRBshIVktsAos
+sChFLbALLLApRS2wDCyxJwGIIIpTWLlAAAQAY7gIAIhUWLkAKAPocFkbsCNTWLAgiLgQAFRYuQAoA+hw
+WVlZLbANLLBAiLggAFpYsSkARBu5ACkD6ERZLbAMK7AAKwCyARACKwGyEQECKwG3ETowJRsQAAgrALcB
+SDsuIRQACCu3AlhIOCgUAAgrtwNSQzQlFgAIK7cEXk08KxkACCu3BTYsIhkPAAgrtwZxXUYyGwAIK7cH
+kXdcOiMACCu3CH5nUDkaAAgrtwlURTYmFAAIK7cKdmBLNh0ACCu3C4NkTjojAAgrtwzZsopjPAAIK7cN
+FBAMCQYACCu3DjwyJxwRAAgrtw9ANCkdFAAIK7cQUEEuIRQACCsAshILByuwACBFfWkYRLI/GgFzsl8a
+AXOyfxoBc7IvGgF0sk8aAXSybxoBdLKPGgF0sq8aAXSy/xoBdLIfGgF1sj8aAXWyXxoBdbJ/GgF1sg8e
+AXOyfx4Bc7LvHgFzsh8eAXSyXx4BdLKPHgF0ss8eAXSy/x4BdLI/HgF1sm8eAXWyLyABc7JvIAFzAAAA
+ACoAnQCAAIoAeADUAGQATgBaAIcAYABWADQCPAC8ALIAjgDEAAAAFP5gABQCmwAgAyEACwQ6ABQEjQAQ
+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 = '''
@@ -581,7 +7422,7 @@
''';
String _index_html;
-// index_html md5 is '803a2ed6c6547e0b448d0e28c28cf385'
+// index_html md5 is '39097ee2371c204f0f1e3515a60a7203'
String _index_html_base64 = '''
PGh0bWw+CjxoZWFkPgogICAgPHRpdGxlPk51bGwgU2FmZXR5IFByZXZpZXc8L3RpdGxlPgogICAgPHNj
cmlwdCBzcmM9Int7IGhpZ2hsaWdodEpzUGF0aCB9fSI+PC9zY3JpcHQ+CiAgICA8c2NyaXB0Pnt7IGRh
@@ -598,207 +7439,255 @@
Ym90dG9tIiB0YXJnZXQ9Il9ibGFuayI+RmlsZSBvbiBHaXRIdWI8L2E+PCEtLSBwb3N0IHRvIGdpdGh1
YiBwbGFjZWhvbGRlciBlbGVtZW50IC0tPgo8L2Rpdj4KPHAgY2xhc3M9InJvb3QiPnt7IHJvb3QgfX08
L3A+CjxoZWFkZXIgY2xhc3M9ImVsZXZhdGlvbi16NCI+CiAgICA8aW1nIHNyYz0ie3sgZGFydExvZ29Q
-YXRoIH19IiBhbHQ9IkRhcnRQYWQgTG9nbyIgY2xhc3M9ImxvZ28iLz4KICAgIDxoMSBjbGFzcz0iYmVm
-b3JlLWFwcGx5Ij5Qcm9wb3NlZCBudWxsIHNhZmV0eSBjaGFuZ2VzPC9oMT4KICAgIDxoMSBjbGFzcz0i
-YWZ0ZXItYXBwbHkiPiYjMTAwMDM7IE51bGwgc2FmZXR5IG1pZ3JhdGlvbiBhcHBsaWVkPC9oMT4KICAg
-IDxoMiBpZD0idW5pdC1uYW1lIj4mbmJzcDs8L2gyPgogICAgPGJ1dHRvbiBjbGFzcz0iYXBwbHktbWln
-cmF0aW9uIj4mIzk5OTg7IEFwcGx5IE1pZ3JhdGlvbjwvYnV0dG9uPgogICAgPGJ1dHRvbiBjbGFzcz0i
-YXBwbHktbWlncmF0aW9uIiBkaXNhYmxlZD4mIzk5OTg7IEFwcGx5IE1pZ3JhdGlvbjwvYnV0dG9uPgog
-ICAgPGJ1dHRvbiBjbGFzcz0icmVydW4tbWlncmF0aW9uIGJlZm9yZS1hcHBseSI+CiAgICAgIDxzcGFu
-IGNsYXNzPSJvcHRpb25hbCI+JiM4NjM1OyBSZXJ1biBGcm9tIFNvdXJjZXM8L3NwYW4+CiAgICAgIDxz
-cGFuIGNsYXNzPSJyZXF1aXJlZCI+CiAgICAgICAgPHNwYW4gY2xhc3M9Imljb24iIAogICAgICAgICAg
-dGl0bGU9IkRpc2sgY29udGVudHMgaGF2ZSBjaGFuZ2VkLiBSZXJ1biB0byBnZXQgYW4gdXAtdG8tZGF0
-ZSBtaWdyYXRpb24uIj4hPC9zcGFuPgogICAgICAgIFJlcnVuIFdpdGggQ2hhbmdlcwogICAgICA8L3Nw
-YW4+CiAgICA8L2J1dHRvbj4KPC9oZWFkZXI+CjxkaXYgY2xhc3M9InBhbmVscyBob3Jpem9udGFsIj4K
-ICAgIDxkaXYgY2xhc3M9Im5hdi1wYW5lbCI+CiAgICAgICAgPGRpdiBjbGFzcz0ibmF2LWlubmVyIj4K
-ICAgICAgICAgICAgPGRpdiBjbGFzcz0icGFuZWwtaGVhZGluZyI+UHJvamVjdCBGaWxlczwvZGl2Pgog
-ICAgICAgICAgICA8ZGl2IGNsYXNzPSJuYXYtdHJlZSI+PC9kaXY+CiAgICAgICAgPC9kaXY+PCEtLSAv
-bmF2LWlubmVyIC0tPgogICAgPC9kaXY+PCEtLSAvbmF2IC0tPgogICAgPGRpdiBjbGFzcz0iY29udGVu
-dCI+CiAgICAgICAgPGRpdiBjbGFzcz0icmVnaW9ucyI+CiAgICAgICAgICAgIDwhLS0gVGhlIHJlZ2lv
-bnMgb3ZlcmxheSBjb2RlIGNvcHkgb2YgdGhlIGNvbnRlbnQgdG8gcHJvdmlkZSAtLT4KICAgICAgICAg
-ICAgPCEtLSB0b29sdGlwcyBmb3IgbW9kaWZpZWQgcmVnaW9ucy4gLS0+CiAgICAgICAgPC9kaXY+PCEt
-LSAvcmVnaW9ucyAtLT4KICAgICAgICA8ZGl2IGNsYXNzPSJjb2RlIj4KICAgICAgICAgICAgPCEtLSBD
-b21waWxhdGlvbiB1bml0IGNvbnRlbnQgaXMgd3JpdHRlbiBoZXJlLiAtLT4KICAgICAgICAgICAgPHAg
-Y2xhc3M9IndlbGNvbWUiPgogICAgICAgICAgICAgICAgU2VsZWN0IGEgc291cmNlIGZpbGUgb24gdGhl
-IGxlZnQgdG8gcHJldmlldyB0aGUgcHJvcG9zZWQgZWRpdHMuCiAgICAgICAgICAgIDwvcD4KICAgICAg
-ICA8L2Rpdj4KICAgIDwvZGl2PjwhLS0gL2NvbnRlbnQgLS0+CiAgICA8ZGl2IGNsYXNzPSJpbmZvLXBh
-bmVsIj4KICAgICAgICA8ZGl2IGNsYXNzPSJlZGl0LWxpc3QiPgogICAgICAgICAgICA8ZGl2IGNsYXNz
-PSJwYW5lbC1oZWFkaW5nIj5Qcm9wb3NlZCBFZGl0czwvZGl2PgogICAgICAgICAgICA8ZGl2IGNsYXNz
-PSJwYW5lbC1jb250ZW50Ij48L2Rpdj4KICAgICAgICA8L2Rpdj48IS0tIC9lZGl0LWxpc3QgLS0+CiAg
-ICAgICAgPGRpdiBjbGFzcz0iZWRpdC1wYW5lbCI+CiAgICAgICAgICAgIDxkaXYgY2xhc3M9InBhbmVs
-LWhlYWRpbmciPkVkaXQgRGV0YWlsczwvZGl2PgogICAgICAgICAgICA8ZGl2IGNsYXNzPSJwYW5lbC1j
-b250ZW50Ij4KICAgICAgICAgICAgICAgIDxwIGNsYXNzPSJwbGFjZWhvbGRlciI+U2VlIGRldGFpbHMg
-YWJvdXQgYSBwcm9wb3NlZCBlZGl0LjwvcD4KICAgICAgICAgICAgPC9kaXY+PCEtLSAvcGFuZWwtY29u
-dGVudCAtLT4KICAgICAgICA8L2Rpdj48IS0tIC9lZGl0LXBhbmVsIC0tPgogICAgPC9kaXY+PCEtLSAv
-aW5mby1wYW5lbCAtLT4KPC9kaXY+PCEtLSAvcGFuZWxzIC0tPgo8Zm9vdGVyPgogICAgPGEgdGFyZ2V0
-PSJfYmxhbmsiCiAgICAgIGhyZWY9Imh0dHBzOi8vZ29vLmdsZS9kYXJ0LW51bGwtc2FmZXR5LW1pZ3Jh
-dGlvbi10b29sIj5OdWxsIHNhZmV0eQogICAgICAgIG1pZ3JhdGlvbiBoZWxwPC9hPgogICAgPHNwYW4g
-Y2xhc3M9IndpZGUiPiA8L3NwYW4+CiAgICA8ZGl2PkJhc2VkIG9uIDxzcGFuIGlkPSJzZGstdmVyc2lv
-biI+e3sgc2RrVmVyc2lvbiB9fTwvc3Bhbj48L2Rpdj4KICAgIDxidXR0b24gY2xhc3M9InJlcG9ydC1w
-cm9ibGVtIj5SZXBvcnQgYSBQcm9ibGVtPC9idXR0b24+CjwvZm9vdGVyPgo8L2JvZHk+CjwvaHRtbD4K
+YXRoIH19IiBhbHQ9IkRhcnRQYWQgTG9nbyIgY2xhc3M9ImxvZ28iLz4KICAgIDxoMT5EYXJ0PC9oMT4K
+ICAgIDxoMiBjbGFzcz0iYmVmb3JlLWFwcGx5Ij5Qcm9wb3NlZCBudWxsIHNhZmV0eSBjaGFuZ2VzPC9o
+Mj4KICAgIDxoMiBjbGFzcz0iYWZ0ZXItYXBwbHkiPiYjMTAwMDM7IE51bGwgc2FmZXR5IG1pZ3JhdGlv
+biBhcHBsaWVkPC9oMj4KICAgIDxoMyBpZD0idW5pdC1uYW1lIj4mbmJzcDs8L2gzPgogICAgPGEgdGFy
+Z2V0PSJfYmxhbmsiCiAgICAgICBocmVmPSJodHRwczovL2dvby5nbGUvZGFydC1udWxsLXNhZmV0eS1t
+aWdyYXRpb24tdG9vbCI+CiAgICAgIDxidXR0b24gY2xhc3M9ImFjdGlvbi1idXR0b24iPgogICAgICAg
+IDxpIGNsYXNzPSJtYXRlcmlhbC1pY29ucyI+bGF1bmNoPC9pPgogICAgICAgIDxzcGFuPkhlbHA8L3Nw
+YW4+CiAgICAgIDwvYnV0dG9uPgogICAgPC9hPgogICAgPGJ1dHRvbiBjbGFzcz0iYWN0aW9uLWJ1dHRv
+biBhcHBseS1taWdyYXRpb24iPgogICAgICAgIDxpIGNsYXNzPSJtYXRlcmlhbC1pY29ucyI+ZWRpdDwv
+aT4KICAgICAgICA8c3BhbiBjbGFzcz0ibGFiZWwiPgogICAgICAgICAgQXBwbHkgTWlncmF0aW9uCiAg
+ICAgICAgPC9zcGFuPgogICAgPC9idXR0b24+CiAgICA8YnV0dG9uIGNsYXNzPSJhY3Rpb24tYnV0dG9u
+IGFwcGx5LW1pZ3JhdGlvbiIgZGlzYWJsZWQ+CiAgICAgICAgPGkgY2xhc3M9Im1hdGVyaWFsLWljb25z
+Ij5lZGl0PC9pPgogICAgICAgIDxzcGFuIGNsYXNzPSJsYWJlbCI+CiAgICAgICAgICBBcHBseSBNaWdy
+YXRpb24KICAgICAgICA8L3NwYW4+CiAgICA8L2J1dHRvbj4KICAgIDxidXR0b24gY2xhc3M9ImFjdGlv
+bi1idXR0b24gcmVydW4tbWlncmF0aW9uIGJlZm9yZS1hcHBseSI+CiAgICAgIDxzcGFuIGNsYXNzPSJv
+cHRpb25hbCI+CiAgICAgICAgPGkgY2xhc3M9Im1hdGVyaWFsLWljb25zIj5yZXBsYXk8L2k+CiAgICAg
+ICAgUmVydW4gRnJvbSBTb3VyY2VzCiAgICAgIDwvc3Bhbj4KICAgICAgPHNwYW4gY2xhc3M9InJlcXVp
+cmVkIj4KICAgICAgICA8aSBjbGFzcz0ibWF0ZXJpYWwtaWNvbnMiPndhcm5pbmc8L2k+CiAgICAgICAg
+UmVydW4gV2l0aCBDaGFuZ2VzCiAgICAgIDwvc3Bhbj4KICAgIDwvYnV0dG9uPgo8L2hlYWRlcj4KPGRp
+diBjbGFzcz0icGFuZWxzIGhvcml6b250YWwiPgogICAgPGRpdiBjbGFzcz0ibmF2LXBhbmVsIj4KICAg
+ICAgICA8ZGl2IGNsYXNzPSJuYXYtaW5uZXIiPgogICAgICAgICAgICA8ZGl2IGNsYXNzPSJwYW5lbC1o
+ZWFkaW5nIj5Qcm9qZWN0IEZpbGVzPC9kaXY+CiAgICAgICAgICAgIDxkaXYgY2xhc3M9Im5hdi10cmVl
+Ij48L2Rpdj4KICAgICAgICA8L2Rpdj48IS0tIC9uYXYtaW5uZXIgLS0+CiAgICA8L2Rpdj48IS0tIC9u
+YXYgLS0+CiAgICA8ZGl2IGNsYXNzPSJjb250ZW50Ij4KICAgICAgICA8ZGl2IGNsYXNzPSJyZWdpb25z
+Ij4KICAgICAgICAgICAgPCEtLSBUaGUgcmVnaW9ucyBvdmVybGF5IGNvZGUgY29weSBvZiB0aGUgY29u
+dGVudCB0byBwcm92aWRlIC0tPgogICAgICAgICAgICA8IS0tIHRvb2x0aXBzIGZvciBtb2RpZmllZCBy
+ZWdpb25zLiAtLT4KICAgICAgICA8L2Rpdj48IS0tIC9yZWdpb25zIC0tPgogICAgICAgIDxkaXYgY2xh
+c3M9ImNvZGUiPgogICAgICAgICAgICA8IS0tIENvbXBpbGF0aW9uIHVuaXQgY29udGVudCBpcyB3cml0
+dGVuIGhlcmUuIC0tPgogICAgICAgICAgICA8cCBjbGFzcz0id2VsY29tZSI+CiAgICAgICAgICAgICAg
+ICBTZWxlY3QgYSBzb3VyY2UgZmlsZSBvbiB0aGUgbGVmdCB0byBwcmV2aWV3IHRoZSBwcm9wb3NlZCBl
+ZGl0cy4KICAgICAgICAgICAgPC9wPgogICAgICAgIDwvZGl2PgogICAgPC9kaXY+PCEtLSAvY29udGVu
+dCAtLT4KICAgIDxkaXYgY2xhc3M9ImluZm8tcGFuZWwiPgogICAgICAgIDxkaXYgY2xhc3M9ImVkaXQt
+bGlzdCI+CiAgICAgICAgICAgIDxkaXYgY2xhc3M9InBhbmVsLWhlYWRpbmciPlByb3Bvc2VkIEVkaXRz
+PC9kaXY+CiAgICAgICAgICAgIDxkaXYgY2xhc3M9InBhbmVsLWNvbnRlbnQiPjwvZGl2PgogICAgICAg
+IDwvZGl2PjwhLS0gL2VkaXQtbGlzdCAtLT4KICAgICAgICA8ZGl2IGNsYXNzPSJlZGl0LXBhbmVsIj4K
+ICAgICAgICAgICAgPGRpdiBjbGFzcz0icGFuZWwtaGVhZGluZyI+RWRpdCBEZXRhaWxzPC9kaXY+CiAg
+ICAgICAgICAgIDxkaXYgY2xhc3M9InBhbmVsLWNvbnRlbnQiPgogICAgICAgICAgICAgICAgPHAgY2xh
+c3M9InBsYWNlaG9sZGVyIj5TZWUgZGV0YWlscyBhYm91dCBhIHByb3Bvc2VkIGVkaXQuPC9wPgogICAg
+ICAgICAgICA8L2Rpdj48IS0tIC9wYW5lbC1jb250ZW50IC0tPgogICAgICAgIDwvZGl2PjwhLS0gL2Vk
+aXQtcGFuZWwgLS0+CiAgICA8L2Rpdj48IS0tIC9pbmZvLXBhbmVsIC0tPgo8L2Rpdj48IS0tIC9wYW5l
+bHMgLS0+Cjxmb290ZXI+CiAgPGJ1dHRvbiBjbGFzcz0icmVwb3J0LXByb2JsZW0iPlNlbmQgRmVlZGJh
+Y2s8L2J1dHRvbj4KICAgIDxzcGFuIGNsYXNzPSJ3aWRlIj4gPC9zcGFuPgogICAgPGRpdiBjbGFzcz0i
+c2RrLXZlcnNpb24iPkJhc2VkIG9uIDxzcGFuIGlkPSJzZGstdmVyc2lvbiI+e3sgc2RrVmVyc2lvbiB9
+fTwvc3Bhbj48L2Rpdj4KPC9mb290ZXI+CjwvYm9keT4KPC9odG1sPgo=
''';
String _migration_css;
-// migration_css md5 is 'e6730f8303d73cb852f1717cb20e6c98'
+// migration_css md5 is 'b0c95ec84cbd6de507ca2e6a7d6a45b7'
String _migration_css_base64 = '''
LyogQ29weXJpZ2h0IChjKSAyMDE5LCB0aGUgRGFydCBwcm9qZWN0IGF1dGhvcnMuIFBsZWFzZSBzZWUg
dGhlIEFVVEhPUlMgZmlsZSAgKi8KLyogZm9yIGRldGFpbHMuIEFsbCByaWdodHMgcmVzZXJ2ZWQuIFVz
ZSBvZiB0aGlzIHNvdXJjZSBjb2RlIGlzIGdvdmVybmVkIGJ5IGEgKi8KLyogQlNELXN0eWxlIGxpY2Vu
c2UgdGhhdCBjYW4gYmUgZm91bmQgaW4gdGhlIExJQ0VOU0UgZmlsZS4gICAgICAgICAgICAgICAgICAg
-Ki8KCmJvZHkgewogIGJhY2tncm91bmQtY29sb3I6ICMxMjIwMmY7CiAgY29sb3I6ICNjY2M7CiAgZm9u
-dC1mYW1pbHk6ICJSb2JvdG8iLCBzYW5zLXNlcmlmOwogIGZvbnQtc2l6ZTogMTRweDsKICBkaXNwbGF5
-OiBmbGV4OwogIGZsZXgtZGlyZWN0aW9uOiBjb2x1bW47CiAgcG9zaXRpb246IGFic29sdXRlOwogIHRv
-cDogMDsKICByaWdodDogMDsKICBib3R0b206IDA7CiAgbGVmdDogMDsKICBtYXJnaW46IDA7CiAgcGFk
-ZGluZzogMDsKICBvdmVyZmxvdzogaGlkZGVuOwp9CgoucHJvcG9zZWQgLmFmdGVyLWFwcGx5IHsKICBk
-aXNwbGF5OiBub25lOwp9CgouYXBwbGllZCAuYmVmb3JlLWFwcGx5IHsKICBkaXNwbGF5OiBub25lOwp9
-CgouYXBwbGllZCAuYXBwbHktbWlncmF0aW9uOm5vdChbZGlzYWJsZWRdKSwgLm5lZWRzLXJlcnVuIC5h
-cHBseS1taWdyYXRpb246bm90KFtkaXNhYmxlZF0pIHsKICBkaXNwbGF5OiBub25lOwp9CgoucHJvcG9z
-ZWQ6bm90KC5uZWVkcy1yZXJ1bikgLmFwcGx5LW1pZ3JhdGlvbltkaXNhYmxlZF0gewogIGRpc3BsYXk6
-IG5vbmU7Cn0KCmhlYWRlciB7CiAgYmFja2dyb3VuZC1jb2xvcjogIzFjMjgzNDsKICBoZWlnaHQ6IDQ4
-cHg7CiAgcGFkZGluZy1sZWZ0OiAyNHB4OwogIGFsaWduLWl0ZW1zOiBjZW50ZXI7CiAgei1pbmRleDog
-NDsKfQoKaGVhZGVyIGgxLApoZWFkZXIgaDIgewogIGRpc3BsYXk6IGlubGluZS1ibG9jazsKICBmb250
-LWZhbWlseTogIkdvb2dsZSBTYW5zIiwiUm9ib3RvIixzYW5zLXNlcmlmOwogIGZvbnQtd2VpZ2h0OiA0
-MDA7CiAgbWFyZ2luLXJpZ2h0OiAyNHB4Owp9CgpoMSB7CiAgZm9udC1zaXplOiAxLjVlbTsKfQoKaGVh
-ZGVyIGgyIHsKICBmb250LXNpemU6IDEuMmVtOwp9CgpoZWFkZXIgLmFwcGx5LW1pZ3JhdGlvbiwgLnJl
-cnVuLW1pZ3JhdGlvbiB7CiAgcmlnaHQ6IDBweDsKICBmbG9hdDogcmlnaHQ7CiAgbWFyZ2luOiAxMHB4
-Owp9CgpoZWFkZXIgaW1nLmxvZ28gewogIGhlaWdodDogMjRweDsKICB3aWR0aDogMjRweDsKICBtYXJn
-aW4tcmlnaHQ6IDhweDsKfQoKZm9vdGVyIC5yZXBvcnQtcHJvYmxlbSB7CiAgcmlnaHQ6IDBweDsKICBt
-YXJnaW46IDRweCA4cHg7Cn0KCi5yZXJ1bi1taWdyYXRpb24gLnJlcXVpcmVkIHsKICBkaXNwbGF5OiBu
-b25lOwp9CgoubmVlZHMtcmVydW4gLnJlcnVuLW1pZ3JhdGlvbiAucmVxdWlyZWQgewogIGRpc3BsYXk6
-IGluaXRpYWw7Cn0KCi5uZWVkcy1yZXJ1biAucmVydW4tbWlncmF0aW9uIC5vcHRpb25hbCB7CiAgZGlz
-cGxheTpub25lOwp9CgovKiBSZWQgdHJpYW5nbGUgKi8KLnJlcnVuLW1pZ3JhdGlvbiAucmVxdWlyZWQg
-Lmljb246OmJlZm9yZSB7CiAgdHJhbnNmb3JtOiB0cmFuc2xhdGUoLThweCwgLTExcHgpOwogIGNvbnRl
-bnQ6ICdcMjVCMyc7CiAgZm9udC1zaXplOiAyNXB4OwogIHBvc2l0aW9uOiBmaXhlZDsKICBjb2xvcjog
-I2U4MmMyYzsKICB0ZXh0LXNoYWRvdzogMHB4IDBweCA1cHggd2hpdGU7CiAgei1pbmRleDogLTM7Cn0K
-Ci8qIFJlZCB0cmlhbmdsZSBmaWxsICovCi5yZXJ1bi1taWdyYXRpb24gLnJlcXVpcmVkIC5pY29uOjph
-ZnRlciB7CiAgdHJhbnNmb3JtOiB0cmFuc2xhdGUoLTlweCwgLTEwcHgpOwogIGNvbnRlbnQ6ICdcMjVC
-NCc7CiAgZm9udC1zaXplOiAyNXB4OwogIHBvc2l0aW9uOiBmaXhlZDsKICBjb2xvcjogI2IzZWNmZjsK
-ICB6LWluZGV4OiAtMTsKfQoKLyogUmVkIHRyaWFuZ2xlIGV4Y2xhbWF0aW9uICovCi5yZXJ1bi1taWdy
-YXRpb24gLnJlcXVpcmVkIC5pY29uIHsKICBkaXNwbGF5OiBpbmxpbmUtYmxvY2s7CiAgbWFyZ2luLXJp
-Z2h0OiA4cHg7CiAgdHJhbnNmb3JtOiB0cmFuc2xhdGUoMHB4LCAycHgpOwogIG1hcmdpbi1sZWZ0OiAy
-cHg7CiAgY29sb3I6ICMyYjJiMmI7Cn0KCmZvb3RlciB7CiAgY29sb3I6ICNjY2M7CiAgYmFja2dyb3Vu
-ZC1jb2xvcjogIzI3MzIzYTsKICBkaXNwbGF5OiBmbGV4OwogIGZsZXgtZGlyZWN0aW9uOiByb3c7CiAg
-YWxpZ24taXRlbXM6IGNlbnRlcjsKICBwYWRkaW5nOiA4cHggMCA4cHggMjRweDsKfQoKZm9vdGVyIC53
-aWRlIHsKICBmbGV4OiAxOwp9CgouaG9yaXpvbnRhbCB7CiAgZGlzcGxheTogZmxleDsKfQoKLnBhbmVs
-cyB7CiAgYmFja2dyb3VuZC1jb2xvcjogIzEyMWEyNTsKICBmbGV4OiAxOwogIG92ZXJmbG93OiBoaWRk
-ZW47Cn0KCi5wYW5lbC1oZWFkaW5nIHsKICBjb2xvcjogZ3JheTsKICBtYXJnaW46IDhweDsKfQoKLm5h
-di1saW5rLAoucmVnaW9uIHsKICBjdXJzb3I6IHBvaW50ZXI7Cn0KCi5uYXYtcGFuZWwgewogIGJhY2tn
-cm91bmQtY29sb3I6ICMyODJiMmU7CiAgZmxleDogMSAyMDBweDsKICBtYXJnaW46IDA7CiAgb3ZlcmZs
-b3c6IHNjcm9sbDsKfQoKLm5hdi1pbm5lciB7CiAgcGFkZGluZzogMCAwIDdweCA3cHg7Cn0KCi5maXhl
-ZCB7CiAgcG9zaXRpb246IGZpeGVkOwogIHRvcDogMDsKfQoKLnJvb3QgewogIG1hcmdpbjogMDsKICBk
-aXNwbGF5OiBub25lOwp9CgoubmF2LXRyZWUgPiB1bCB7CiAgcGFkZGluZy1sZWZ0OiA2cHg7Cn0KCi5u
-YXYtaW5uZXIgdWwgewogIHBhZGRpbmctbGVmdDogMTJweDsKICBtYXJnaW46IDA7Cn0KCi5uYXYtaW5u
-ZXIgbGkgewogIGxpc3Qtc3R5bGUtdHlwZTogbm9uZTsKfQoKLm5hdi1pbm5lciBsaTpub3QoLmRpcikg
-ewogIG1hcmdpbi1sZWZ0OiAyMHB4OwogIG1hcmdpbi1ib3R0b206IDNweDsKfQoKLm5hdi1pbm5lciBs
-aS5kaXIgLmFycm93IHsKICBjdXJzb3I6IHBvaW50ZXI7CiAgZGlzcGxheTogaW5saW5lLWJsb2NrOwog
-IGZvbnQtc2l6ZTogMTBweDsKICBtYXJnaW4tcmlnaHQ6IDRweDsKICB0cmFuc2l0aW9uOiB0cmFuc2Zv
-cm0gMC41cyBlYXNlLW91dDsKfQoKLm5hdi1pbm5lciBsaS5kaXIgLmFycm93LmNvbGxhcHNlZCB7CiAg
-dHJhbnNmb3JtOiByb3RhdGUoLTkwZGVnKTsKfQoKLm5hdi1pbm5lciB1bCB7CiAgbWF4LWhlaWdodDog
-MjAwMHB4OwogIHRyYW5zaXRpb246IG1heC1oZWlnaHQgMC41cyBlYXNlLW91dDsKfQoKLm5hdi1pbm5l
-ciB1bC5jb2xsYXBzZWQgewogIG1heC1oZWlnaHQ6IDAgIWltcG9ydGFudDsKICBvdmVyZmxvdzogaGlk
-ZGVuOwp9CgoubmF2LWlubmVyIC5zZWxlY3RlZC1maWxlIHsKICBjb2xvcjogd2hpdGU7CiAgY3Vyc29y
-OiBpbmhlcml0OwogIGZvbnQtd2VpZ2h0OiA2MDA7CiAgdGV4dC1kZWNvcmF0aW9uOiBub25lOwp9Cgou
-ZWRpdC1jb3VudCB7CiAgYmFja2dyb3VuZC1jb2xvcjogIzM3YWVkYzsKICBib3JkZXItcmFkaXVzOiAx
-MHB4OwogIGNvbG9yOiAjMDAwMDAwOwogIGRpc3BsYXk6IGlubGluZS1ibG9jazsKICBmb250LXNpemU6
-IDExcHg7CiAgZm9udC13ZWlnaHQ6IDYwMDsKICBtYXJnaW4tbGVmdDogNXB4OwogIG1pbi13aWR0aDog
-MjVweDsKICBwYWRkaW5nOiA0cHggMCAycHggMDsKICB0ZXh0LWFsaWduOiBjZW50ZXI7CiAgbGluZS1o
-ZWlnaHQ6IDFlbTsKfQoKLmNvbnRlbnQgewogIGZsZXg6IDQgMzAwcHg7CiAgYmFja2dyb3VuZDogIzI4
-MmIyZTsKICBmb250LWZhbWlseTogbW9ub3NwYWNlOwogIG1hcmdpbjogMCA2cHg7CiAgcG9zaXRpb246
-IHJlbGF0aXZlOwogIHdoaXRlLXNwYWNlOiBwcmU7CiAgb3ZlcmZsb3c6IHNjcm9sbDsKfQoKLmNvZGUg
-ewogIHBhZGRpbmc6IDAuNWVtOwogIHBvc2l0aW9uOiBhYnNvbHV0ZTsKICBsZWZ0OiAwOwogIHRvcDog
-MDsKICBtYXJnaW4tbGVmdDogNTZweDsKfQoKLmhsanMgewogIGJhY2tncm91bmQtY29sb3I6ICMyODJi
-MmU7CiAgZGlzcGxheTogYmxvY2s7CiAgb3ZlcmZsb3cteDogYXV0bzsKICBwYWRkaW5nOiAwLjVlbTsK
-fQoKLmNvZGUgLndlbGNvbWUgewogIGZvbnQtZmFtaWx5OiAiR29vZ2xlIFNhbnMiLCJSb2JvdG8iLHNh
-bnMtc2VyaWY7CiAgZm9udC1zaXplOiAxOHB4OwogIG1hcmdpbi1yaWdodDogNjJweDsKICBjb2xvcjog
-Izc3NzsKfQoKLmNvZGUgLm5hdi1saW5rIHsKICBjb2xvcjogaW5oZXJpdDsKICB0ZXh0LWRlY29yYXRp
-b24tbGluZTogbm9uZTsKfQoKLmNvZGUgLm5hdi1saW5rOnZpc2l0ZWQgewogIGNvbG9yOiBpbmhlcml0
-OwogIHRleHQtZGVjb3JhdGlvbi1saW5lOiBub25lOwp9CgouY29kZSAubmF2LWxpbms6aG92ZXIgewog
-IHRleHQtZGVjb3JhdGlvbi1saW5lOiB1bmRlcmxpbmU7CiAgZm9udC13ZWlnaHQ6IDYwMDsKfQoKLnJl
-Z2lvbnMgewogIHBhZGRpbmc6IDAuNWVtOwogIHBvc2l0aW9uOiBhYnNvbHV0ZTsKICBsZWZ0OiAwOwog
-IHRvcDogMDsKfQoKLnJlZ2lvbnMgdGFibGUgewogIGJvcmRlci1zcGFjaW5nOiAwOwogIGZvbnQtc2l6
-ZTogaW5oZXJpdDsKfQoKLnJlZ2lvbnMgdGQgewogIGJvcmRlcjogbm9uZTsKICAvKiBUaGUgY29udGVu
-dCBvZiB0aGUgcmVnaW9ucyBpcyBub3QgdmlzaWJsZTsgdGhlIHVzZXIgaW5zdGVhZCB3aWxsIHNlZSB0
-aGUKICAgKiBoaWdobGlnaHRlZCBjb3B5IG9mIHRoZSBjb250ZW50LiAqLwogIGNvbG9yOiByZ2JhKDI1
-NSwgMjU1LCAyNTUsIDApOwogIHBhZGRpbmc6IDA7CiAgd2hpdGUtc3BhY2U6IHByZTsKfQoKLnJlZ2lv
-bnMgdGQ6ZW1wdHk6YWZ0ZXIgewogIGNvbnRlbnQ6ICJcMDBhMCI7Cn0KCi5yZWdpb25zIHRyLmhpZ2hs
-aWdodCB0ZDpsYXN0LWNoaWxkIHsKICBiYWNrZ3JvdW5kLWNvbG9yOiAjNDQ0NDQ0OwogIGNvbG9yOiB3
-aGl0ZTsKfQoKLnJlZ2lvbnMgdGQubGluZS1ubyB7CiAgYm9yZGVyLXJpZ2h0OiBzb2xpZCAjMjgyYjJl
-IDJweDsKICBjb2xvcjogIzk5OTk5OTsKICBwYWRkaW5nLXJpZ2h0OiA0cHg7CiAgdGV4dC1hbGlnbjog
-cmlnaHQ7CiAgdmlzaWJpbGl0eTogdmlzaWJsZTsKICB3aWR0aDogNTBweDsKICBkaXNwbGF5OiBpbmxp
-bmUtYmxvY2s7Cn0KCi5yZWdpb25zIHRyLmhpZ2hsaWdodCB0ZC5saW5lLW5vIHsKICBib3JkZXItcmln
-aHQ6IHNvbGlkICNjY2MgMnB4Owp9CgoucmVnaW9uIHsKICBkaXNwbGF5OiBpbmxpbmUtYmxvY2s7CiAg
-cG9zaXRpb246IHJlbGF0aXZlOwogIHZpc2liaWxpdHk6IHZpc2libGU7CiAgei1pbmRleDogMjAwOwp9
-CgoucmVnaW9uLmFkZGVkLXJlZ2lvbiB7CiAgYmFja2dyb3VuZC1jb2xvcjogI2NjZmZjYzsKICBjb2xv
-cjogIzAwMzMwMDsKfQoKLnJlZ2lvbi5yZW1vdmVkLXJlZ2lvbiB7CiAgYmFja2dyb3VuZC1jb2xvcjog
-I2ZmNjY2NjsKICBjb2xvcjogIzAwMTEwMDsKfQoKLnJlZ2lvbi5pbmZvcm1hdGl2ZS1yZWdpb24gewog
-IGJhY2tncm91bmQtY29sb3I6ICM0MDQ1NDk7IC8qICMyODJiMmUsIGxpZ2h0ZW5lZCAxMCUgKi8KICBi
-b3JkZXItYm90dG9tOiBzb2xpZCByZ2JhKDI1NSwgMjU1LCAwLCAwLjcpIDFweDsKICBkaXNwbGF5OiBp
-bmxpbmUtYmxvY2s7CiAgaGVpZ2h0OiAxNHB4OyAKICBwb3NpdGlvbjogcmVsYXRpdmU7CiAgdG9wOiAx
-cHg7Cn0KCi50YXJnZXQgewogIGJhY2tncm91bmQtY29sb3I6ICM0NDQ7CiAgcG9zaXRpb246IHJlbGF0
-aXZlOwogIHZpc2liaWxpdHk6IHZpc2libGU7CiAgZm9udC13ZWlnaHQ6IDYwMDsKfQoKLmluZm8tcGFu
-ZWwgewogIGZsZXg6IDEgMjAwcHg7CiAgbWFyZ2luOiAwOwogIGhlaWdodDogMTAwJTsKICBkaXNwbGF5
-OiBmbGV4OwogIGZsZXgtZGlyZWN0aW9uOiBjb2x1bW47Cn0KCi5pbmZvLXBhbmVsIC5lZGl0LXBhbmVs
-IHsKICBiYWNrZ3JvdW5kLWNvbG9yOiAjMjgyYjJlOwogIG92ZXJmbG93OiBhdXRvOwp9CgouaW5mby1w
-YW5lbCAucGFuZWwtY29udGVudCB7CiAgcGFkZGluZzogN3B4Owp9CgouaW5mby1wYW5lbCAucGFuZWwt
-Y29udGVudD4gOmZpcnN0LWNoaWxkIHsKICBtYXJnaW4tdG9wOiAwOwp9CgouaW5mby1wYW5lbCAubm93
-cmFwIHsKICB3aGl0ZS1zcGFjZTogbm93cmFwOwp9CgouaW5mby1wYW5lbCB1bCwKLmluZm8tcGFuZWwg
-b2wgewogIHBhZGRpbmctbGVmdDogMjBweDsKfQoKLmluZm8tcGFuZWwgbGkgewogIG1hcmdpbjogMCAw
-IDVweCAwOwp9CgouaW5mby1wYW5lbCBhIHsKICBjb2xvcjogIzMzY2NmZjsKfQoKLmluZm8tcGFuZWwg
-LmVkaXQtbGlzdCB7CiAgYmFja2dyb3VuZC1jb2xvcjogIzI4MmIyZTsKICBvdmVyZmxvdzogYXV0bzsK
-fQoKLmVkaXQtcGFuZWwgewogIG1hcmdpbi10b3A6IDZweDsKICBmbGV4OiAxIDEwMHB4Owp9CgouZWRp
-dC1saXN0IHsKICBmbGV4OiAyIDEwMHB4Owp9CgouZWRpdC1saXN0IC5lZGl0IHsKICBtYXJnaW46IDNw
-eCAwOwp9CgouZWRpdC1saXN0IC5lZGl0LWxpbmsgewogIGN1cnNvcjogcG9pbnRlcjsKfQoKLnBvcHVw
-LXBhbmUgewogIGRpc3BsYXk6IG5vbmU7CiAgcG9zaXRpb246IGZpeGVkOwogIHRvcDogMTUwcHg7CiAg
-bGVmdDogMTUwcHg7CiAgcmlnaHQ6IDE1MHB4OwogIGJvdHRvbTogMTUwcHg7CiAgYm9yZGVyOiAxcHgg
-c29saWQgYmxhY2s7CiAgYm9yZGVyLXRvcDogMnB4IHNvbGlkIGJsYWNrOwogIGJvcmRlci1yYWRpdXM6
-IDdweDsKICBib3gtc2hhZG93OiAwcHggMHB4IDIwcHggMnB4ICNiNGJmY2IyMjsKICB6LWluZGV4OiA0
-MDA7CiAgYmFja2dyb3VuZDogIzJiMzAzNjsKICBwYWRkaW5nOiAyMHB4Owp9CgoucG9wdXAtcGFuZSAu
-Y2xvc2UgewogIHBvc2l0aW9uOiBhYnNvbHV0ZTsKICByaWdodDogMTBweDsKICB0b3A6IDEwcHg7CiAg
-Y3Vyc29yOiBwb2ludGVyOwogIHRleHQtc2hhZG93OiAxcHggMXB4IDJweCAjODg4OwogIGJveC1zaGFk
-b3c6IDFweCAxcHggMnB4ICMxMTE7Cn0KCi5wb3B1cC1wYW5lIGgyIHsKICBwYWRkaW5nOiAyMXB4Owog
-IGhlaWdodDogMTAlOwogIG1hcmdpbjogMHB4OwogIGJveC1zaXppbmc6IGJvcmRlci1ib3g7Cn0KCi5w
-b3B1cC1wYW5lIHAgewogIGhlaWdodDogMTAlOwogIGJveC1zaXppbmc6IGJvcmRlci1ib3g7CiAgcGFk
-ZGluZzogMHB4IDIwcHg7Cn0KCi5wb3B1cC1wYW5lIHByZSB7CiAgYmFja2dyb3VuZDogIzI4MmIyZTsK
-ICBwYWRkaW5nOiAyMHB4OwogIGJvdHRvbTogMHB4OwogIG92ZXJmbG93OiBhdXRvIHNjcm9sbDsKICBo
-ZWlnaHQ6IDY1JTsKICBtYXJnaW46IDBweDsKICBib3gtc2l6aW5nOiBib3JkZXItYm94Owp9CgoucG9w
-dXAtcGFuZSAuYnV0dG9uLmJvdHRvbSB7CiAgbWFyZ2luOiAyMHB4IDBweDsKICBkaXNwbGF5OiBibG9j
-azsKICB0ZXh0LWFsaWduOiBjZW50ZXI7Cn0KCi5yZXJ1bm5pbmctcGFuZSB7CiAgZGlzcGxheTogbm9u
-ZTsKfQoKYm9keS5yZXJ1bm5pbmcgLnJlcnVubmluZy1wYW5lIHsKICBkaXNwbGF5OiBibG9jazsKICBw
-b3NpdGlvbjogZml4ZWQ7CiAgdG9wOiAwcHg7CiAgYm90dG9tOiAwcHg7CiAgbGVmdDogMHB4OwogIHJp
-Z2h0OiAwcHg7CiAgYmFja2dyb3VuZC1jb2xvcjogIzAwMDAwMEFBOyAvKiB0cmFuc2x1Y2VudCBibGFj
-ayAqLwogIHotaW5kZXg6IDQwMDsKfQoKLnJlcnVubmluZy1wYW5lIGgxIHsKICBwb3NpdGlvbjogYWJz
-b2x1dGU7CiAgdG9wOiA1MCU7CiAgbGVmdDogNTAlOwogIHRyYW5zZm9ybTogdHJhbnNsYXRlKC01MCUs
-IC01MCUpOwp9CgouZWRpdC1wYW5lbCAudHlwZS1kZXNjcmlwdGlvbiB7CiAgLyogRnJvbSBITEpTJ3Mg
-LmhsanMta2V5d29yZCwgLmhsanMtc2VsZWN0b3ItdGFnLCAuaGxqcy1kZWxldGlvbiAqLwogIGNvbG9y
-OiAjY2M3ODMyOwogIGZvbnQtZmFtaWx5OiBtb25vc3BhY2U7Cn0KCnVsLnRyYWNlIHsKICBmb250LXNp
-emU6IDEzcHg7CiAgbGlzdC1zdHlsZS10eXBlOiBub25lOwogIHBhZGRpbmctbGVmdDogMHB4Owp9Cgp1
-bC50cmFjZSBsaSB7CiAgY29sb3I6IHdoaXRlOwp9Cgp1bC50cmFjZSBsaSAuZnVuY3Rpb24gewogIC8q
-IEZyb20gSExKUydzIC5obGpzLXNlY3Rpb24sIC5obGpzLXRpdGxlLCAuaGxqcy10eXBlICovCiAgY29s
-b3I6ICNmZmM2NmQ7CiAgZm9udC1mYW1pbHk6IG1vbm9zcGFjZTsKICBmb250LXdlaWdodDogNjAwOwp9
-Cgp1bC50cmFjZSBsaSBwLmRyYXdlciB7CiAgbWFyZ2luOiAzcHggMHB4OwogIHBhZGRpbmc6IDBweCAw
-cHggMHB4IDE0cHg7Cn0KCnVsLnRyYWNlIGxpIHAuZHJhd2VyIGJ1dHRvbiB7CiAgbWFyZ2luLXJpZ2h0
-OiAzcHg7Cn0KCi5lbGV2YXRpb24tejQgewogIGJveC1zaGFkb3c6IDBweCAycHggNHB4IC0xcHggcmdi
-YSgwLCAwLCAwLCAwLjIpLAogICAgICAwcHggNHB4IDVweCAwcHggcmdiYSgwLCAwLCAwLCAwLjE0KSwK
-ICAgICAgMHB4IDFweCAxMHB4IDBweCByZ2JhKDAsIDAsIDAsIC4xMik7Cn0KCmEgewogIGNvbG9yOiAj
-Y2NjOwogIGZpbGw6ICNjY2M7CiAgdGV4dC1kZWNvcmF0aW9uOiBub25lOwp9CgphOmhvdmVyIHsKICBj
-b2xvcjogI2ZmZjsKICBmaWxsOiAjZmZmOwp9CgouYWRkLWhpbnQtbGluayB7CiAgZGlzcGxheTogaW5s
-aW5lLWJsb2NrOwogIG1hcmdpbjogM3B4Owp9CgovKiBDYXJlZnVsIGhlcmUuIGBhLmJ1dHRvbmAgaXMg
-cmVwZXRpdGl2ZSBidXQgcmVxdWlyZWQgdG8gZ2V0IGNvcnJlY3QKICogc3BlY2lmaWNpdHkgKi8KYnV0
-dG9uLCAuYnV0dG9uLCBhLmJ1dHRvbiB7CiAgYmFja2dyb3VuZC1jb2xvcjogIzMzY2NmZjsKICBib3Jk
-ZXI6IDJweCBzb2xpZCAjMzdhZWRjOwogIGJvcmRlci1yYWRpdXM6IDNweDsKICBwYWRkaW5nOiA2cHgg
-MTBweDsKICBmb250LXdlaWdodDogYm9sZDsKICBjb2xvcjogIzI4MjgyODsKfQoKYnV0dG9uOmhvdmVy
-LCAuYnV0dG9uOmhvdmVyIHsKICBiYWNrZ3JvdW5kLWNvbG9yOiAjODBkZmZmOwogIGJvcmRlcjogMnB4
-IHNvbGlkICM1MmI4ZTA7CiAgY3Vyc29yOiBwb2ludGVyOwp9CgpidXR0b25bZGlzYWJsZWRdIHsKICBi
-YWNrZ3JvdW5kLWNvbG9yOiAjN2FhOGI4OwogIGNvbG9yOiAjNTA3MTc3OwogIGJvcmRlcjogMnB4IHNv
-bGlkICM1MDcxNzc7CiAgY3Vyc29yOiBub3QtYWxsb3dlZDsKfQoKLnBsYWNlaG9sZGVyIHsKICBjb2xv
-cjogIzc3NzsKICB0ZXh0LWFsaWduOiBjZW50ZXI7CiAgbWFyZ2luLXRvcDogM2VtICFpbXBvcnRhbnQ7
-Cn0KCi8qKgogKiBITEpTIE92ZXJyaWRlcwogKi8KLmhsanMgewogIC8qKgogICAqIFRoaXMgYWxsb3dz
-IHRoZSBwZXItbGluZSBoaWdobGlnaHRzIHRvIHNob3cuCiAgICovCiAgYmFja2dyb3VuZDogbm9uZTsK
-fQo=
+Ki8KCi8qCiAqIENvbG9ycyBtZW50aW9uZWQgaW4gdGhpcyBmaWxlLCBlLmcuICIkZGFyay10ZXh0LWNv
+bG9yIiBhcmUgZnJvbSBEYXJ0UGFkOgogKiBodHRwczovL2dpdGh1Yi5jb20vZGFydC1sYW5nL2RhcnQt
+cGFkL2Jsb2IvbWFzdGVyL2xpYi9zY3NzL2NvbG9ycy5zY3NzCiAqLwoKLyogVGV4dCBzZWxlY3Rpb24g
+Ki8KOjpzZWxlY3Rpb24gewogIGJhY2tncm91bmQ6ICM2ZThlYjE7IC8qICRkYXJrLXNlbGVjdGlvbi1j
+b2xvciAqLzsKfQoKLyogTWF0ZXJpYWwgaWNvbnMgY29uZmlndXJhdGlvbiAqLwpAZm9udC1mYWNlIHsK
+ICBmb250LWZhbWlseTogJ01hdGVyaWFsIEljb25zJzsKICBmb250LXN0eWxlOiBub3JtYWw7CiAgZm9u
+dC13ZWlnaHQ6IDQwMDsKICBzcmM6IGxvY2FsKCdNYXRlcmlhbCBJY29ucycpLAogIGxvY2FsKCdNYXRl
+cmlhbEljb25zLVJlZ3VsYXInKSwKICB1cmwoL01hdGVyaWFsSWNvbnNSZWd1bGFyLnR0ZikgZm9ybWF0
+KCd0cnVldHlwZScpOwp9CgovKgogKiBSZXF1aXJlZCBmb3IgTWF0ZXJpYWwgSWNvbnM6CiAqIGh0dHBz
+Oi8vZ29vZ2xlLmdpdGh1Yi5pby9tYXRlcmlhbC1kZXNpZ24taWNvbnMvCiAqLwoubWF0ZXJpYWwtaWNv
+bnMgewogIGZvbnQtZmFtaWx5OiAnTWF0ZXJpYWwgSWNvbnMnOwogIGZvbnQtd2VpZ2h0OiBub3JtYWw7
+CiAgZm9udC1zdHlsZTogbm9ybWFsOwogIGZvbnQtc2l6ZTogMjRweDsgIC8qIFByZWZlcnJlZCBpY29u
+IHNpemUgKi8KICBkaXNwbGF5OiBpbmxpbmUtYmxvY2s7CiAgbGluZS1oZWlnaHQ6IDE7CiAgdGV4dC10
+cmFuc2Zvcm06IG5vbmU7CiAgbGV0dGVyLXNwYWNpbmc6IG5vcm1hbDsKICB3b3JkLXdyYXA6IG5vcm1h
+bDsKICB3aGl0ZS1zcGFjZTogbm93cmFwOwogIGRpcmVjdGlvbjogbHRyOwoKICAvKiBTdXBwb3J0IGZv
+ciBhbGwgV2ViS2l0IGJyb3dzZXJzLiAqLwogIC13ZWJraXQtZm9udC1zbW9vdGhpbmc6IGFudGlhbGlh
+c2VkOwogIC8qIFN1cHBvcnQgZm9yIFNhZmFyaSBhbmQgQ2hyb21lLiAqLwogIHRleHQtcmVuZGVyaW5n
+OiBvcHRpbWl6ZUxlZ2liaWxpdHk7CgogIC8qIFN1cHBvcnQgZm9yIEZpcmVmb3guICovCiAgLW1vei1v
+c3gtZm9udC1zbW9vdGhpbmc6IGdyYXlzY2FsZTsKCiAgLyogU3VwcG9ydCBmb3IgSUUuICovCiAgZm9u
+dC1mZWF0dXJlLXNldHRpbmdzOiAnbGlnYSc7Cn0KCmJvZHkgewogIGJhY2tncm91bmQtY29sb3I6ICMx
+MjIwMmY7CiAgY29sb3I6ICNjY2M7CiAgZm9udC1mYW1pbHk6ICJSb2JvdG8iLCBzYW5zLXNlcmlmOwog
+IGZvbnQtc2l6ZTogMTRweDsKICBkaXNwbGF5OiBmbGV4OwogIGZsZXgtZGlyZWN0aW9uOiBjb2x1bW47
+CiAgcG9zaXRpb246IGFic29sdXRlOwogIHRvcDogMDsKICByaWdodDogMDsKICBib3R0b206IDA7CiAg
+bGVmdDogMDsKICBtYXJnaW46IDA7CiAgcGFkZGluZzogMDsKICBvdmVyZmxvdzogaGlkZGVuOwp9Cgou
+cHJvcG9zZWQgLmFmdGVyLWFwcGx5IHsKICBkaXNwbGF5OiBub25lOwp9CgouYXBwbGllZCAuYmVmb3Jl
+LWFwcGx5IHsKICBkaXNwbGF5OiBub25lOwp9CgouYXBwbGllZCAuYXBwbHktbWlncmF0aW9uOm5vdChb
+ZGlzYWJsZWRdKSwgLm5lZWRzLXJlcnVuIC5hcHBseS1taWdyYXRpb246bm90KFtkaXNhYmxlZF0pIHsK
+ICBkaXNwbGF5OiBub25lOwp9CgoucHJvcG9zZWQ6bm90KC5uZWVkcy1yZXJ1bikgLmFwcGx5LW1pZ3Jh
+dGlvbltkaXNhYmxlZF0gewogIGRpc3BsYXk6IG5vbmU7Cn0KCmhlYWRlciB7CiAgYmFja2dyb3VuZC1j
+b2xvcjogIzFjMjgzNDsKICBoZWlnaHQ6IDQ4cHg7CiAgcGFkZGluZy1sZWZ0OiAyNHB4OwogIGFsaWdu
+LWl0ZW1zOiBjZW50ZXI7CiAgei1pbmRleDogNDsKfQoKaGVhZGVyIGgxLApoZWFkZXIgaDIsCmhlYWRl
+ciBoMyB7CiAgZGlzcGxheTogaW5saW5lLWJsb2NrOwogIGZvbnQtZmFtaWx5OiAiR29vZ2xlIFNhbnMi
+LCJSb2JvdG8iLHNhbnMtc2VyaWY7CiAgZm9udC13ZWlnaHQ6IDQwMDsKICBtYXJnaW4tcmlnaHQ6IDI0
+cHg7Cn0KCmgxIHsKICBmb250LXNpemU6IDEuNWVtOwp9CgpoZWFkZXIgaDIgewogIGZvbnQtc2l6ZTog
+MS4yZW07CgogIC8qIFNoaWZ0IHRleHQgdXAgKi8KICBwb3NpdGlvbjogcmVsYXRpdmU7CiAgdG9wOiAt
+MnB4Owp9CgpoZWFkZXIgaDMgewogIC8qIFNoaWZ0IHRleHQgdXAgKi8KICBwb3NpdGlvbjogcmVsYXRp
+dmU7CiAgdG9wOiAtMnB4Owp9CgoKaGVhZGVyIC5hY3Rpb24tYnV0dG9uLCBoZWFkZXIgYSB7CiAgcmln
+aHQ6IDBweDsKICBmbG9hdDogcmlnaHQ7CiAgbWFyZ2luOiAxMHB4Owp9CgpoZWFkZXIgaW1nLmxvZ28g
+ewogIGhlaWdodDogMjRweDsKICB3aWR0aDogMjRweDsKICBtYXJnaW4tcmlnaHQ6IDhweDsKICBwb3Np
+dGlvbjogcmVsYXRpdmU7CiAgdG9wOiA0cHg7Cn0KCmZvb3RlciAucmVwb3J0LXByb2JsZW0gewogIHJp
+Z2h0OiAwcHg7CiAgbWFyZ2luOiA0cHggOHB4Owp9CgoucmVydW4tbWlncmF0aW9uIC5yZXF1aXJlZCB7
+CiAgZGlzcGxheTogbm9uZTsKfQoKLm5lZWRzLXJlcnVuIC5yZXJ1bi1taWdyYXRpb24gLnJlcXVpcmVk
+IHsKICBkaXNwbGF5OiBpbml0aWFsOwp9CgoubmVlZHMtcmVydW4gLnJlcnVuLW1pZ3JhdGlvbiAub3B0
+aW9uYWwgewogIGRpc3BsYXk6bm9uZTsKfQoKZm9vdGVyIHsKICBjb2xvcjogI2NjYzsKICBiYWNrZ3Jv
+dW5kLWNvbG9yOiAjMjczMjNhOwogIGRpc3BsYXk6IGZsZXg7CiAgZmxleC1kaXJlY3Rpb246IHJvdzsK
+ICBhbGlnbi1pdGVtczogY2VudGVyOwogIHBhZGRpbmc6IDhweCAwIDhweCAyNHB4Owp9Cgpmb290ZXIg
+LndpZGUgewogIGZsZXg6IDE7Cn0KCmZvb3RlciAuc2RrLXZlcnNpb24gewogIG1hcmdpbi1yaWdodDog
+MzJweDsKfQoKLmhvcml6b250YWwgewogIGRpc3BsYXk6IGZsZXg7Cn0KCi5wYW5lbHMgewogIGJhY2tn
+cm91bmQtY29sb3I6ICMxMjFhMjU7CiAgZmxleDogMTsKICBvdmVyZmxvdzogaGlkZGVuOwp9CgoucGFu
+ZWwtaGVhZGluZyB7CiAgY29sb3I6ICM2NzY3Njc7CiAgbWFyZ2luOiA4cHg7Cn0KCi5uYXYtbGluaywK
+LnJlZ2lvbiB7CiAgY3Vyc29yOiBwb2ludGVyOwp9CgoubmF2LXBhbmVsIHsKICBiYWNrZ3JvdW5kLWNv
+bG9yOiAjMTIyMDJmOwogIGZsZXg6IDEgMjAwcHg7CiAgbWFyZ2luOiAwOwogIG92ZXJmbG93OiBzY3Jv
+bGw7CiAgdXNlci1zZWxlY3Q6IG5vbmU7Cn0KCi5uYXYtaW5uZXIgewogIHBhZGRpbmc6IDAgMCA3cHgg
+N3B4Owp9CgouZml4ZWQgewogIHBvc2l0aW9uOiBmaXhlZDsKICB0b3A6IDA7Cn0KCi5yb290IHsKICBt
+YXJnaW46IDA7CiAgZGlzcGxheTogbm9uZTsKfQoKLm5hdi10cmVlID4gdWwgewogIHBhZGRpbmctbGVm
+dDogNnB4Owp9CgoubmF2LXRyZWUgLm1hdGVyaWFsLWljb25zIHsKICBmb250LXNpemU6IDIwcHg7CiAg
+cG9zaXRpb246IHJlbGF0aXZlOwogIHRvcDogNXB4OwogIG1hcmdpbi1yaWdodDogOHB4OwogIGNvbG9y
+OiAjNjc2NzY3Owp9CgoubmF2LWlubmVyIHVsIHsKICBwYWRkaW5nLWxlZnQ6IDEycHg7CiAgbWFyZ2lu
+OiAwOwp9CgoubmF2LWlubmVyIGxpIHsKICBsaXN0LXN0eWxlLXR5cGU6IG5vbmU7Cn0KCi5uYXYtaW5u
+ZXIgbGk6bm90KC5kaXIpIHsKICBtYXJnaW4tbGVmdDogMjBweDsKICBtYXJnaW4tYm90dG9tOiAzcHg7
+Cn0KCi5uYXYtaW5uZXIgbGkuZGlyIC5hcnJvdyB7CiAgY3Vyc29yOiBwb2ludGVyOwogIGRpc3BsYXk6
+IGlubGluZS1ibG9jazsKICBmb250LXNpemU6IDEwcHg7CiAgbWFyZ2luLXJpZ2h0OiA0cHg7CiAgdHJh
+bnNpdGlvbjogdHJhbnNmb3JtIDAuNXMgZWFzZS1vdXQ7Cn0KCi5uYXYtaW5uZXIgbGkuZGlyIC5hcnJv
+dy5jb2xsYXBzZWQgewogIHRyYW5zZm9ybTogcm90YXRlKC05MGRlZyk7Cn0KCi5uYXYtaW5uZXIgdWwg
+ewogIG1heC1oZWlnaHQ6IDIwMDBweDsKICB0cmFuc2l0aW9uOiBtYXgtaGVpZ2h0IDAuNXMgZWFzZS1v
+dXQ7Cn0KCi5uYXYtaW5uZXIgdWwuY29sbGFwc2VkIHsKICBtYXgtaGVpZ2h0OiAwICFpbXBvcnRhbnQ7
+CiAgb3ZlcmZsb3c6IGhpZGRlbjsKfQoKLm5hdi1pbm5lciAuc2VsZWN0ZWQtZmlsZSB7CiAgY29sb3I6
+IHdoaXRlOwogIGN1cnNvcjogaW5oZXJpdDsKICBmb250LXdlaWdodDogNjAwOwogIHRleHQtZGVjb3Jh
+dGlvbjogbm9uZTsKfQoKLmVkaXQtY291bnQgewogIGJhY2tncm91bmQtY29sb3I6ICM2NzY3Njc7CiAg
+Ym9yZGVyLXJhZGl1czogMTBweDsKICBjb2xvcjogI2ZmZjsKICBkaXNwbGF5OiBpbmxpbmUtYmxvY2s7
+CiAgZm9udC1zaXplOiAxMXB4OwogIGZvbnQtd2VpZ2h0OiA2MDA7CiAgbWFyZ2luLWxlZnQ6IDVweDsK
+ICBtaW4td2lkdGg6IDI1cHg7CiAgcGFkZGluZzogNHB4IDAgMnB4IDA7CiAgdGV4dC1hbGlnbjogY2Vu
+dGVyOwogIGxpbmUtaGVpZ2h0OiAxZW07Cn0KCi5jb250ZW50IHsKICBmbGV4OiA0IDMwMHB4OwogIGJh
+Y2tncm91bmQ6ICMxMjIwMmY7CiAgZm9udC1mYW1pbHk6ICJSb2JvdG8gTW9ubyIsIG1vbm9zcGFjZTsK
+ICBtYXJnaW46IDAgNnB4OwogIHBvc2l0aW9uOiByZWxhdGl2ZTsKICB3aGl0ZS1zcGFjZTogcHJlOwog
+IG92ZXJmbG93OiBzY3JvbGw7Cn0KCi5jb2RlIHsKICBwYWRkaW5nOiAwLjVlbTsKICBwb3NpdGlvbjog
+YWJzb2x1dGU7CiAgbGVmdDogMDsKICB0b3A6IDA7CiAgbWFyZ2luLWxlZnQ6IDU2cHg7Cn0KCi5jb2Rl
+IC53ZWxjb21lIHsKICBmb250LWZhbWlseTogIkdvb2dsZSBTYW5zIiwiUm9ib3RvIixzYW5zLXNlcmlm
+OwogIGZvbnQtc2l6ZTogMThweDsKICBtYXJnaW4tcmlnaHQ6IDYycHg7CiAgY29sb3I6ICM3Nzc7Cn0K
+Ci5jb2RlIC5uYXYtbGluayB7CiAgY29sb3I6ICMxNmFkY2E7CiAgdGV4dC1kZWNvcmF0aW9uLWxpbmU6
+IG5vbmU7Cn0KCi5jb2RlIC5uYXYtbGluazp2aXNpdGVkIHsKICBjb2xvcjogIzEzOWJiNTsgLyogIzE2
+YWRjYSBkYXJrZW5lZCAxMCUgKi8KICB0ZXh0LWRlY29yYXRpb24tbGluZTogbm9uZTsKfQoKLmNvZGUg
+Lm5hdi1saW5rOmhvdmVyIHsKICB0ZXh0LWRlY29yYXRpb24tbGluZTogdW5kZXJsaW5lOwogIGZvbnQt
+d2VpZ2h0OiA2MDA7Cn0KCi5yZWdpb25zIHsKICBwYWRkaW5nOiAwLjVlbTsKICBwb3NpdGlvbjogYWJz
+b2x1dGU7CiAgbGVmdDogMDsKICB0b3A6IDA7Cn0KCi5yZWdpb25zIHRhYmxlIHsKICBib3JkZXItc3Bh
+Y2luZzogMDsKICBmb250LXNpemU6IGluaGVyaXQ7Cn0KCi5yZWdpb25zIHRkIHsKICBib3JkZXI6IG5v
+bmU7CiAgLyogVGhlIGNvbnRlbnQgb2YgdGhlIHJlZ2lvbnMgaXMgbm90IHZpc2libGU7IHRoZSB1c2Vy
+IGluc3RlYWQgd2lsbCBzZWUgdGhlCiAgICogaGlnaGxpZ2h0ZWQgY29weSBvZiB0aGUgY29udGVudC4g
+Ki8KICBjb2xvcjogcmdiYSgyNTUsIDI1NSwgMjU1LCAwKTsKICBwYWRkaW5nOiAwOwogIHdoaXRlLXNw
+YWNlOiBwcmU7Cn0KCi5yZWdpb25zIHRkOmVtcHR5OmFmdGVyIHsKICBjb250ZW50OiAiXDAwYTAiOwp9
+CgoucmVnaW9ucyB0ci5oaWdobGlnaHQgdGQ6bGFzdC1jaGlsZCB7CiAgYmFja2dyb3VuZC1jb2xvcjog
+IzQ0NDQ0NDsKICBjb2xvcjogd2hpdGU7Cn0KCi5yZWdpb25zIHRkLmxpbmUtbm8gewogIGJvcmRlci1y
+aWdodDogc29saWQgIzEyMjAyZiAycHg7CiAgY29sb3I6ICM5OTk5OTk7CiAgcGFkZGluZy1yaWdodDog
+NHB4OwogIHRleHQtYWxpZ246IHJpZ2h0OwogIHZpc2liaWxpdHk6IHZpc2libGU7CiAgd2lkdGg6IDUw
+cHg7CiAgZGlzcGxheTogaW5saW5lLWJsb2NrOwp9CgoucmVnaW9ucyB0ci5oaWdobGlnaHQgdGQubGlu
+ZS1ubyB7CiAgYm9yZGVyLXJpZ2h0OiBzb2xpZCAjY2NjIDJweDsKfQoKLnJlZ2lvbiB7CiAgZGlzcGxh
+eTogaW5saW5lLWJsb2NrOwogIHBvc2l0aW9uOiByZWxhdGl2ZTsKICB2aXNpYmlsaXR5OiB2aXNpYmxl
+OwogIHotaW5kZXg6IDIwMDsKfQoKLnJlZ2lvbi5hZGRlZC1yZWdpb24gewogIGJhY2tncm91bmQtY29s
+b3I6ICMyNjM5NTI7IC8qICRkYXJrLXNlbGVjdGlvbi1jb2xvciAqLwogIGNvbG9yOiAjYzBjMmM1OyAv
+KiAkZGFyay1lZGl0b3ItdGV4dCAqLwp9CgoucmVnaW9uLnJlbW92ZWQtcmVnaW9uIHsKICBiYWNrZ3Jv
+dW5kLWNvbG9yOiAjRkE1NTdkOyAvKiAkZGFyay1waW5rICovCiAgY29sb3I6ICNmZmY7Cn0KCi5yZWdp
+b24uaW5mb3JtYXRpdmUtcmVnaW9uIHsKICBiYWNrZ3JvdW5kLWNvbG9yOiAjMTc4YWZkOyAvKiAkZGFy
+ay1zZWxlY3Rpb24tY29sb3IgKi8KICBjb2xvcjogI2ZmZjsKICBkaXNwbGF5OiBpbmxpbmUtYmxvY2s7
+CiAgaGVpZ2h0OiAxOHB4OwogIHBvc2l0aW9uOiByZWxhdGl2ZTsKICB0b3A6IDNweDsKfQoKLnRhcmdl
+dCB7CiAgYmFja2dyb3VuZC1jb2xvcjogIzQ0NDsKICBwb3NpdGlvbjogcmVsYXRpdmU7CiAgdmlzaWJp
+bGl0eTogdmlzaWJsZTsKICBmb250LXdlaWdodDogNjAwOwp9CgouaW5mby1wYW5lbCB7CiAgZmxleDog
+MSAyMDBweDsKICBtYXJnaW46IDA7CiAgaGVpZ2h0OiAxMDAlOwogIGRpc3BsYXk6IGZsZXg7CiAgZmxl
+eC1kaXJlY3Rpb246IGNvbHVtbjsKfQoKLmluZm8tcGFuZWwgLmVkaXQtcGFuZWwgewogIGJhY2tncm91
+bmQtY29sb3I6ICMxMjIwMmY7CiAgb3ZlcmZsb3c6IGF1dG87Cn0KCi5pbmZvLXBhbmVsIC5wYW5lbC1j
+b250ZW50IHsKICBwYWRkaW5nOiA3cHg7Cn0KCi5pbmZvLXBhbmVsIC5wYW5lbC1jb250ZW50PiA6Zmly
+c3QtY2hpbGQgewogIG1hcmdpbi10b3A6IDA7Cn0KCi5pbmZvLXBhbmVsIC5ub3dyYXAgewogIHdoaXRl
+LXNwYWNlOiBub3dyYXA7Cn0KCi5pbmZvLXBhbmVsIHVsLAouaW5mby1wYW5lbCBvbCB7CiAgcGFkZGlu
+Zy1sZWZ0OiAyMHB4Owp9CgouaW5mby1wYW5lbCBsaSB7CiAgbWFyZ2luOiAwIDAgNXB4IDA7Cn0KCi5p
+bmZvLXBhbmVsIGEgewogIGNvbG9yOiAjMTM5YmI1Owp9CgouaW5mby1wYW5lbCBhOmhvdmVyIHsKICBj
+b2xvcjogIzFlYzdlNzsgLyogIzEzOWJiNSBsaWdodGVuZWQgMjAlICovCn0KCi5pbmZvLXBhbmVsIC5l
+ZGl0LWxpc3QgewogIGJhY2tncm91bmQtY29sb3I6ICMxMjIwMmY7CiAgb3ZlcmZsb3c6IGF1dG87Cn0K
+Ci5lZGl0LXBhbmVsIHsKICBtYXJnaW4tdG9wOiA2cHg7CiAgZmxleDogMSAxMDBweDsKfQoKLmVkaXQt
+bGlzdCB7CiAgZmxleDogMiAxMDBweDsKfQoKLmVkaXQtbGlzdCAuZWRpdCB7CiAgbWFyZ2luOiAzcHgg
+MDsKfQoKLmVkaXQtbGlzdCAuZWRpdC1saW5rIHsKICBjdXJzb3I6IHBvaW50ZXI7Cn0KCi5wb3B1cC1w
+YW5lIHsKICBkaXNwbGF5OiBub25lOwogIHBvc2l0aW9uOiBmaXhlZDsKICB0b3A6IDE1MHB4OwogIGxl
+ZnQ6IDE1MHB4OwogIHJpZ2h0OiAxNTBweDsKICBib3R0b206IDE1MHB4OwogIGJvcmRlcjogMXB4IHNv
+bGlkIGJsYWNrOwogIGJvcmRlci10b3A6IDJweCBzb2xpZCBibGFjazsKICBib3JkZXItcmFkaXVzOiA3
+cHg7CiAgYm94LXNoYWRvdzogMHB4IDBweCAyMHB4IDJweCAjYjRiZmNiMjI7CiAgei1pbmRleDogNDAw
+OwogIGJhY2tncm91bmQ6ICMyYjMwMzY7CiAgcGFkZGluZzogMjBweDsKfQoKLnBvcHVwLXBhbmUgLmNs
+b3NlIHsKICBwb3NpdGlvbjogYWJzb2x1dGU7CiAgcmlnaHQ6IDEwcHg7CiAgdG9wOiAxMHB4OwogIGN1
+cnNvcjogcG9pbnRlcjsKICB0ZXh0LXNoYWRvdzogMXB4IDFweCAycHggIzg4ODsKICBib3gtc2hhZG93
+OiAxcHggMXB4IDJweCAjMTExOwp9CgoucG9wdXAtcGFuZSBoMiB7CiAgcGFkZGluZzogMjFweDsKICBo
+ZWlnaHQ6IDEwJTsKICBtYXJnaW46IDBweDsKICBib3gtc2l6aW5nOiBib3JkZXItYm94Owp9CgoucG9w
+dXAtcGFuZSBwIHsKICBoZWlnaHQ6IDEwJTsKICBib3gtc2l6aW5nOiBib3JkZXItYm94OwogIHBhZGRp
+bmc6IDBweCAyMHB4Owp9CgoucG9wdXAtcGFuZSBwcmUgewogIGJhY2tncm91bmQ6ICMxMjIwMmY7CiAg
+cGFkZGluZzogMjBweDsKICBib3R0b206IDBweDsKICBvdmVyZmxvdzogYXV0byBzY3JvbGw7CiAgaGVp
+Z2h0OiA2NSU7CiAgbWFyZ2luOiAwcHg7CiAgYm94LXNpemluZzogYm9yZGVyLWJveDsKfQoKLnBvcHVw
+LXBhbmUgLmJ1dHRvbi5ib3R0b20gewogIG1hcmdpbjogMjBweCAwcHg7CiAgZGlzcGxheTogYmxvY2s7
+CiAgdGV4dC1hbGlnbjogY2VudGVyOwp9CgoucmVydW5uaW5nLXBhbmUgewogIGRpc3BsYXk6IG5vbmU7
+Cn0KCmJvZHkucmVydW5uaW5nIC5yZXJ1bm5pbmctcGFuZSB7CiAgZGlzcGxheTogYmxvY2s7CiAgcG9z
+aXRpb246IGZpeGVkOwogIHRvcDogMHB4OwogIGJvdHRvbTogMHB4OwogIGxlZnQ6IDBweDsKICByaWdo
+dDogMHB4OwogIGJhY2tncm91bmQtY29sb3I6ICMwMDAwMDBBQTsgLyogdHJhbnNsdWNlbnQgYmxhY2sg
+Ki8KICB6LWluZGV4OiA0MDA7Cn0KCi5yZXJ1bm5pbmctcGFuZSBoMSB7CiAgcG9zaXRpb246IGFic29s
+dXRlOwogIHRvcDogNTAlOwogIGxlZnQ6IDUwJTsKICB0cmFuc2Zvcm06IHRyYW5zbGF0ZSgtNTAlLCAt
+NTAlKTsKfQoKLmVkaXQtcGFuZWwgLnR5cGUtZGVzY3JpcHRpb24gewogIC8qIEZyb20gRGFydFBhZCAk
+ZGFyay1vcmFuZ2UgKi8KICBjb2xvcjogI2ZmOTE2ZTsKICBmb250LWZhbWlseTogbW9ub3NwYWNlOwp9
+Cgp1bC50cmFjZSB7CiAgZm9udC1zaXplOiAxM3B4OwogIGxpc3Qtc3R5bGUtdHlwZTogbm9uZTsKICBw
+YWRkaW5nLWxlZnQ6IDBweDsKfQoKdWwudHJhY2UgbGkgewogIGNvbG9yOiB3aGl0ZTsKfQoKdWwudHJh
+Y2UgbGkgLmZ1bmN0aW9uIHsKICAvKiBmcm9tIC5obGpzLXZhcmlhYmxlICovCiAgY29sb3I6ICMxNmFk
+Y2E7CiAgZm9udC1mYW1pbHk6IG1vbm9zcGFjZTsKICBmb250LXdlaWdodDogNjAwOwp9Cgp1bC50cmFj
+ZSBsaSBwLmRyYXdlciB7CiAgbWFyZ2luOiAzcHggMHB4OwogIHBhZGRpbmc6IDBweCAwcHggMHB4IDE0
+cHg7Cn0KCnVsLnRyYWNlIGxpIHAuZHJhd2VyIGJ1dHRvbiB7CiAgbWFyZ2luLXJpZ2h0OiAzcHg7Cn0K
+Ci5lbGV2YXRpb24tejQgewogIGJveC1zaGFkb3c6IDBweCAycHggNHB4IC0xcHggcmdiYSgwLCAwLCAw
+LCAwLjIpLAogICAgICAwcHggNHB4IDVweCAwcHggcmdiYSgwLCAwLCAwLCAwLjE0KSwKICAgICAgMHB4
+IDFweCAxMHB4IDBweCByZ2JhKDAsIDAsIDAsIC4xMik7Cn0KCmEgewogIGNvbG9yOiAjY2NjOwogIGZp
+bGw6ICNjY2M7CiAgdGV4dC1kZWNvcmF0aW9uOiBub25lOwp9CgphOmhvdmVyIHsKICBjb2xvcjogI2Ri
+ZGJkYjsgLyogI2NjYyBsaWdodGVudGVkIDMwJSovCiAgZmlsbDogI2ZmZjsKfQoKLmFkZC1oaW50LWxp
+bmsgewogIGRpc3BsYXk6IGlubGluZS1ibG9jazsKICBtYXJnaW46IDNweDsKfQoKLmFkZC1oaW50LWxp
+bms6aG92ZXIgewogIGNvbG9yOiAjZmZmOwp9CgpoZWFkZXIgYnV0dG9uIHsKICB0ZXh0LXRyYW5zZm9y
+bTogdXBwZXJjYXNlOwp9CgpoZWFkZXIgYSB7CiAgbWFyZ2luOiAwOwp9CgovKiBDYXJlZnVsIGhlcmUu
+IGBhLmJ1dHRvbmAgaXMgcmVwZXRpdGl2ZSBidXQgcmVxdWlyZWQgdG8gZ2V0IGNvcnJlY3QKICogc3Bl
+Y2lmaWNpdHkgKi8KYnV0dG9uLCAuYnV0dG9uLCBhLmJ1dHRvbiB7CiAgYmFja2dyb3VuZC1jb2xvcjog
+cmdiYSgyMiwgMTM4LCAyNTMsIDAuMTUpOwogIGJvcmRlcjogbm9uZTsKICBib3JkZXItcmFkaXVzOiAz
+cHg7CiAgcGFkZGluZzogM3B4IDEwcHg7CiAgZm9udC13ZWlnaHQ6IDUwMDsKICBmb250LWZvbnQ6IFJv
+Ym90bywgc2Fucy1zZXJpZjsKICBjb2xvcjogI2ZmZjsKfQoKYnV0dG9uOmhvdmVyLCAuYnV0dG9uOmhv
+dmVyIHsKICBiYWNrZ3JvdW5kLWNvbG9yOiByZ2JhKDIyLCAxMzgsIDI1MywgMC4yOSk7CiAgY3Vyc29y
+OiBwb2ludGVyOwp9CgpidXR0b25bZGlzYWJsZWRdIHsKICBiYWNrZ3JvdW5kLWNvbG9yOiByZ2JhKDI1
+NSwyNTUsMjU1LC4xMik7CiAgY29sb3I6IHJnYmEoMjU1LDI1NSwyNTUsLjM3KTsKICBjdXJzb3I6IG5v
+dC1hbGxvd2VkOwp9CgovKiBDaGFuZ2UgZWRpdCBwYW5lbCBidXR0b24gY29sb3JzICovCi5lZGl0LXBh
+bmVsIC5idXR0b24sIC5lZGl0LXBhbmVsIGJ1dHRvbiB7CiAgYmFja2dyb3VuZC1jb2xvcjogcmdiYSg2
+MywgMTA0LCAxNDgsIDAuNik7CiAgY29sb3I6IHdoaXRlOwp9Ci5lZGl0LXBhbmVsIC5idXR0b246aG92
+ZXIsIC5lZGl0LXBhbmVsIGJ1dHRvbjpob3ZlciB7CiAgYmFja2dyb3VuZC1jb2xvcjogcmdiYSgxMDEs
+IDE1MywgMjA4LCAwLjYpOwogIGNvbG9yOiB3aGl0ZTsKfQoKLyoKICogQWRqdXN0bWVudHMgdG8gYWxp
+Z24gbWF0ZXJpYWwgaWNvbnMgaW4gdGhlIHRvb2xiYXIgYnV0dG9ucy4KKi8KLmFjdGlvbi1idXR0b24g
+PiBzcGFuIHsKICBwb3NpdGlvbjpyZWxhdGl2ZTsKICB0b3A6IC0zcHg7Cn0KCi5hY3Rpb24tYnV0dG9u
+IC5tYXRlcmlhbC1pY29ucyB7CiAgdG9wOiA0cHg7Cn0KCi8qIERvbid0IHNoaWZ0IHRoZSBpY29uIHdo
+ZW4gaXQncyBhIGRpcmVjdCBjaGlsZCBvZiB0aGUgYnV0dG9uICovCi5hY3Rpb24tYnV0dG9uID4gLm1h
+dGVyaWFsLWljb25zIHsKICB0b3A6IDFweDsKfQoKLyogU2hpZnQgdGhlIHRleHQgdG8gY2VudGVyIHdp
+dGggdGhlIGljb24uICovCi5hY3Rpb24tYnV0dG9uID4gc3Bhbi5sYWJlbCB7CiAgcG9zaXRpb246cmVs
+YXRpdmU7CiAgdG9wOiAtNHB4Owp9CgouYWN0aW9uLWJ1dHRvbiAubWF0ZXJpYWwtaWNvbnMgewogIGZv
+bnQtc2l6ZTogMjBweDsKICBwb3NpdGlvbjogcmVsYXRpdmU7Cn0KCi5wbGFjZWhvbGRlciB7CiAgY29s
+b3I6ICM3Nzc7CiAgdGV4dC1hbGlnbjogY2VudGVyOwogIG1hcmdpbi10b3A6IDNlbSAhaW1wb3J0YW50
+Owp9CgovKioKICogSExKUyBPdmVycmlkZXMKICovCi5obGpzIHsKICBiYWNrZ3JvdW5kLWNvbG9yOiAj
+MTIyMDJmOyAvKiAkZGFyay1jb2RlLWJhY2tncm91bmQtY29sb3IgKi8KICBjb2xvcjogI2MwYzJjNTsg
+LyogJGRhcmstZWRpdG9yLXRleHQgKi8KICBkaXNwbGF5OiBibG9jazsKICBvdmVyZmxvdy14OiBhdXRv
+OwogIHBhZGRpbmc6IDAuNWVtOwogIC8qKgogICAqIFRoaXMgYWxsb3dzIHRoZSBwZXItbGluZSBoaWdo
+bGlnaHRzIHRvIHNob3cuCiAgICovCiAgYmFja2dyb3VuZDogbm9uZTsKfQoKLmhsanMta2V5d29yZCwK
+LmhsanMtc2VsZWN0b3ItdGFnLAouaGxqcy1kZWxldGlvbiB7CiAgY29sb3I6ICM1MWM2ODY7IC8qIGNt
+LWtleXdvcmQgKi8KfQoKLmhsanMtbnVtYmVyIHsKICBjb2xvcjogIzYyNzk3ODsgLyogY20tbnVtYmVy
+ICovCn0KCi5obGpzLWNvbW1lbnQgewogIGNvbG9yOiAjOTE5OGI0OyAvKiBjbS1jb21tZW50ICovCn0K
+Ci5obGpzLWxpdGVyYWwgewogIGNvbG9yOiAjZWU4NjY2OyAvKiBjbS1hdG9tICovCn0KCi5obGpzLXN0
+cmluZyB7CiAgY29sb3I6ICNlNTUwNzQ7IC8qIGNtLXN0cmluZyAqLwp9CgouaGxqcy12YXJpYWJsZSB7
+CiAgY29sb3I6ICMxNmFkY2E7IC8qIGNtLXZhcmlhYmxlICovCn0KCi5obGpzLWxpbmsgewogIGNvbG9y
+OiAjZTU1MDc0OyAvKiBjbS1zdHJpbmcgKi8KfQouaGxqcy1zZWN0aW9uLAouaGxqcy10eXBlLAouaGxq
+cy1idWlsdF9pbiwKLmhsanMtdGl0bGUgewogIGNvbG9yOiAjZWU4NjY2OyAvKiBjbS12YXJpYWJsZS0y
+ICovCn0KCi5obGpzLWFkZGl0aW9uIHsKICBjb2xvcjogIzI2Mzk1MjsgLyogJGRhcmstc2VsZWN0aW9u
+LWNvbG9yICovCn0KCi5obGpzLW1ldGEgewogIGNvbG9yOiAjNjI3OTc4Owp9Cg==
''';
String _migration_js;
-// migration_dart md5 is '7ab04e2be35dbf566710573204ca4c44'
+// migration_dart md5 is '8e9c1ed6d8e48865407a0185e2609d4c'
String _migration_js_base64 = '''
KGZ1bmN0aW9uIGRhcnRQcm9ncmFtKCl7ZnVuY3Rpb24gY29weVByb3BlcnRpZXMoYSxiKXt2YXIgdD1P
YmplY3Qua2V5cyhhKQpmb3IodmFyIHM9MDtzPHQubGVuZ3RoO3MrKyl7dmFyIHI9dFtzXQpiW3JdPWFb
@@ -872,1188 +7761,1221 @@
czpzZXRPclVwZGF0ZUxlYWZUYWdzfX0oKQpmdW5jdGlvbiBpbml0aWFsaXplRGVmZXJyZWRIdW5rKGEp
e3g9di50eXBlcy5sZW5ndGgKYShodW5rSGVscGVycyx2LHcsJCl9ZnVuY3Rpb24gZ2V0R2xvYmFsRnJv
bU5hbWUoYSl7Zm9yKHZhciB0PTA7dDx3Lmxlbmd0aDt0Kyspe2lmKHdbdF09PUMpY29udGludWUKaWYo
-d1t0XVthXSlyZXR1cm4gd1t0XVthXX19dmFyIEM9e30sSD17Rks6ZnVuY3Rpb24gRksoKXt9LApvbzpm
-dW5jdGlvbihhKXt2YXIgdCxzPWFeNDgKaWYoczw9OSlyZXR1cm4gcwp0PWF8MzIKaWYoOTc8PXQmJnQ8
-PTEwMilyZXR1cm4gdC04NwpyZXR1cm4tMX0sCnFDOmZ1bmN0aW9uKGEsYixjLGQpe1AuazEoYiwic3Rh
-cnQiKQppZihjIT1udWxsKXtQLmsxKGMsImVuZCIpCmlmKGI+YylILnZoKFAuVEUoYiwwLGMsInN0YXJ0
-IixudWxsKSl9cmV0dXJuIG5ldyBILm5IKGEsYixjLGQuQygibkg8MD4iKSl9LApLMTpmdW5jdGlvbihh
-LGIsYyxkKXtpZih1Lmd3LmIoYSkpcmV0dXJuIG5ldyBILnh5KGEsYixjLkMoIkA8MD4iKS5LcShkKS5D
-KCJ4eTwxLDI+IikpCnJldHVybiBuZXcgSC5pMShhLGIsYy5DKCJAPDA+IikuS3EoZCkuQygiaTE8MSwy
-PiIpKX0sCldwOmZ1bmN0aW9uKCl7cmV0dXJuIG5ldyBQLmxqKCJObyBlbGVtZW50Iil9LApkVTpmdW5j
-dGlvbigpe3JldHVybiBuZXcgUC5saigiVG9vIG1hbnkgZWxlbWVudHMiKX0sCmFyOmZ1bmN0aW9uKCl7
-cmV0dXJuIG5ldyBQLmxqKCJUb28gZmV3IGVsZW1lbnRzIil9LApxajpmdW5jdGlvbiBxaihhKXt0aGlz
-LmE9YX0sCmJROmZ1bmN0aW9uIGJRKCl7fSwKYUw6ZnVuY3Rpb24gYUwoKXt9LApuSDpmdW5jdGlvbiBu
-SChhLGIsYyxkKXt2YXIgXz10aGlzCl8uYT1hCl8uYj1iCl8uYz1jCl8uJHRpPWR9LAphNzpmdW5jdGlv
-biBhNyhhLGIsYyl7dmFyIF89dGhpcwpfLmE9YQpfLmI9YgpfLmM9MApfLmQ9bnVsbApfLiR0aT1jfSwK
-aTE6ZnVuY3Rpb24gaTEoYSxiLGMpe3RoaXMuYT1hCnRoaXMuYj1iCnRoaXMuJHRpPWN9LAp4eTpmdW5j
-dGlvbiB4eShhLGIsYyl7dGhpcy5hPWEKdGhpcy5iPWIKdGhpcy4kdGk9Y30sCk1IOmZ1bmN0aW9uIE1I
-KGEsYixjKXt2YXIgXz10aGlzCl8uYT1udWxsCl8uYj1hCl8uYz1iCl8uJHRpPWN9LApsSjpmdW5jdGlv
-biBsSihhLGIsYyl7dGhpcy5hPWEKdGhpcy5iPWIKdGhpcy4kdGk9Y30sClU1OmZ1bmN0aW9uIFU1KGEs
-YixjKXt0aGlzLmE9YQp0aGlzLmI9Ygp0aGlzLiR0aT1jfSwKdkc6ZnVuY3Rpb24gdkcoYSxiLGMpe3Ro
-aXMuYT1hCnRoaXMuYj1iCnRoaXMuJHRpPWN9LApTVTpmdW5jdGlvbiBTVSgpe30sClJlOmZ1bmN0aW9u
-IFJlKCl7fSwKdzI6ZnVuY3Rpb24gdzIoKXt9LAp3djpmdW5jdGlvbiB3dihhKXt0aGlzLmE9YX0sCmRj
-OmZ1bmN0aW9uKCl7dGhyb3cgSC5iKFAuTDQoIkNhbm5vdCBtb2RpZnkgdW5tb2RpZmlhYmxlIE1hcCIp
-KX0sCk5ROmZ1bmN0aW9uKGEpe3ZhciB0LHM9SC5KZyhhKQppZih0eXBlb2Ygcz09InN0cmluZyIpcmV0
-dXJuIHMKdD0ibWluaWZpZWQ6IithCnJldHVybiB0fSwKd1Y6ZnVuY3Rpb24oYSxiKXt2YXIgdAppZihi
-IT1udWxsKXt0PWIueAppZih0IT1udWxsKXJldHVybiB0fXJldHVybiB1LmFVLmIoYSl9LApkOmZ1bmN0
-aW9uKGEpe3ZhciB0CmlmKHR5cGVvZiBhPT0ic3RyaW5nIilyZXR1cm4gYQppZih0eXBlb2YgYT09Im51
-bWJlciIpe2lmKGEhPT0wKXJldHVybiIiK2F9ZWxzZSBpZighMD09PWEpcmV0dXJuInRydWUiCmVsc2Ug
-aWYoITE9PT1hKXJldHVybiJmYWxzZSIKZWxzZSBpZihhPT1udWxsKXJldHVybiJudWxsIgp0PUouQWMo
-YSkKaWYodHlwZW9mIHQhPSJzdHJpbmciKXRocm93IEguYihILkkoYSkpCnJldHVybiB0fSwKZVE6ZnVu
-Y3Rpb24oYSl7dmFyIHQ9YS4kaWRlbnRpdHlIYXNoCmlmKHQ9PW51bGwpe3Q9TWF0aC5yYW5kb20oKSow
-eDNmZmZmZmZmfDAKYS4kaWRlbnRpdHlIYXNoPXR9cmV0dXJuIHR9LApIcDpmdW5jdGlvbihhLGIpe3Zh
-ciB0LHMscixxLHAsbyxuPW51bGwKaWYodHlwZW9mIGEhPSJzdHJpbmciKUgudmgoSC5JKGEpKQp0PS9e
-XHMqWystXT8oKDB4W2EtZjAtOV0rKXwoXGQrKXwoW2EtejAtOV0rKSlccyokL2kuZXhlYyhhKQppZih0
-PT1udWxsKXJldHVybiBuCmlmKDM+PXQubGVuZ3RoKXJldHVybiBILmsodCwzKQpzPUguYyh0WzNdKQpp
-ZihiPT1udWxsKXtpZihzIT1udWxsKXJldHVybiBwYXJzZUludChhLDEwKQppZih0WzJdIT1udWxsKXJl
-dHVybiBwYXJzZUludChhLDE2KQpyZXR1cm4gbn1pZihiPDJ8fGI+MzYpdGhyb3cgSC5iKFAuVEUoYiwy
-LDM2LCJyYWRpeCIsbikpCmlmKGI9PT0xMCYmcyE9bnVsbClyZXR1cm4gcGFyc2VJbnQoYSwxMCkKaWYo
-YjwxMHx8cz09bnVsbCl7cj1iPD0xMD80NytiOjg2K2IKcT10WzFdCmZvcihwPXEubGVuZ3RoLG89MDtv
-PHA7KytvKWlmKChDLnhCLlcocSxvKXwzMik+cilyZXR1cm4gbn1yZXR1cm4gcGFyc2VJbnQoYSxiKX0s
-CmxoOmZ1bmN0aW9uKGEpe3ZhciB0PUguSDUoYSkKcmV0dXJuIHR9LApINTpmdW5jdGlvbihhKXt2YXIg
-dCxzLHIKaWYoYSBpbnN0YW5jZW9mIFAuTWgpcmV0dXJuIEguSihILnEoYSksbnVsbCkKaWYoSi5pYShh
-KT09PUMuT2t8fHUuYWsuYihhKSl7dD1DLndiKGEpCmlmKEguQmUodCkpcmV0dXJuIHQKcz1hLmNvbnN0
-cnVjdG9yCmlmKHR5cGVvZiBzPT0iZnVuY3Rpb24iKXtyPXMubmFtZQppZih0eXBlb2Ygcj09InN0cmlu
-ZyImJkguQmUocikpcmV0dXJuIHJ9fXJldHVybiBILkooSC5xKGEpLG51bGwpfSwKQmU6ZnVuY3Rpb24o
-YSl7dmFyIHQ9YSE9PSJPYmplY3QiJiZhIT09IiIKcmV0dXJuIHR9LApNMDpmdW5jdGlvbigpe2lmKCEh
-c2VsZi5sb2NhdGlvbilyZXR1cm4gc2VsZi5sb2NhdGlvbi5ocmVmCnJldHVybiBudWxsfSwKVks6ZnVu
-Y3Rpb24oYSl7dmFyIHQscyxyLHEscD1hLmxlbmd0aAppZihwPD01MDApcmV0dXJuIFN0cmluZy5mcm9t
-Q2hhckNvZGUuYXBwbHkobnVsbCxhKQpmb3IodD0iIixzPTA7czxwO3M9cil7cj1zKzUwMApxPXI8cD9y
-OnAKdCs9U3RyaW5nLmZyb21DaGFyQ29kZS5hcHBseShudWxsLGEuc2xpY2UocyxxKSl9cmV0dXJuIHR9
-LApDcTpmdW5jdGlvbihhKXt2YXIgdCxzLHIscT1ILlZNKFtdLHUudCkKZm9yKHQ9YS5sZW5ndGgscz0w
-O3M8YS5sZW5ndGg7YS5sZW5ndGg9PT10fHwoMCxILmxrKShhKSwrK3Mpe3I9YVtzXQppZighSC5vayhy
-KSl0aHJvdyBILmIoSC5JKHIpKQppZihyPD02NTUzNSlDLk5tLmkocSxyKQplbHNlIGlmKHI8PTExMTQx
-MTEpe0MuTm0uaShxLDU1Mjk2KyhDLmpuLndHKHItNjU1MzYsMTApJjEwMjMpKQpDLk5tLmkocSw1NjMy
-MCsociYxMDIzKSl9ZWxzZSB0aHJvdyBILmIoSC5JKHIpKX1yZXR1cm4gSC5WSyhxKX0sCmVUOmZ1bmN0
-aW9uKGEpe3ZhciB0LHMscgpmb3IodD1hLmxlbmd0aCxzPTA7czx0Oysrcyl7cj1hW3NdCmlmKCFILm9r
-KHIpKXRocm93IEguYihILkkocikpCmlmKHI8MCl0aHJvdyBILmIoSC5JKHIpKQppZihyPjY1NTM1KXJl
-dHVybiBILkNxKGEpfXJldHVybiBILlZLKGEpfSwKZnc6ZnVuY3Rpb24oYSxiLGMpe3ZhciB0LHMscixx
-CmlmKGM8PTUwMCYmYj09PTAmJmM9PT1hLmxlbmd0aClyZXR1cm4gU3RyaW5nLmZyb21DaGFyQ29kZS5h
-cHBseShudWxsLGEpCmZvcih0PWIscz0iIjt0PGM7dD1yKXtyPXQrNTAwCnE9cjxjP3I6YwpzKz1TdHJp
-bmcuZnJvbUNoYXJDb2RlLmFwcGx5KG51bGwsYS5zdWJhcnJheSh0LHEpKX1yZXR1cm4gc30sCkx3OmZ1
-bmN0aW9uKGEpe3ZhciB0CmlmKDA8PWEpe2lmKGE8PTY1NTM1KXJldHVybiBTdHJpbmcuZnJvbUNoYXJD
-b2RlKGEpCmlmKGE8PTExMTQxMTEpe3Q9YS02NTUzNgpyZXR1cm4gU3RyaW5nLmZyb21DaGFyQ29kZSgo
-NTUyOTZ8Qy5qbi53Ryh0LDEwKSk+Pj4wLDU2MzIwfHQmMTAyMyl9fXRocm93IEguYihQLlRFKGEsMCwx
-MTE0MTExLG51bGwsbnVsbCkpfSwKbzI6ZnVuY3Rpb24oYSl7aWYoYS5kYXRlPT09dm9pZCAwKWEuZGF0
-ZT1uZXcgRGF0ZShhLmEpCnJldHVybiBhLmRhdGV9LAp0SjpmdW5jdGlvbihhKXt2YXIgdD1ILm8yKGEp
-LmdldEZ1bGxZZWFyKCkrMApyZXR1cm4gdH0sCk5TOmZ1bmN0aW9uKGEpe3ZhciB0PUgubzIoYSkuZ2V0
-TW9udGgoKSsxCnJldHVybiB0fSwKakE6ZnVuY3Rpb24oYSl7dmFyIHQ9SC5vMihhKS5nZXREYXRlKCkr
-MApyZXR1cm4gdH0sCklYOmZ1bmN0aW9uKGEpe3ZhciB0PUgubzIoYSkuZ2V0SG91cnMoKSswCnJldHVy
-biB0fSwKY2g6ZnVuY3Rpb24oYSl7dmFyIHQ9SC5vMihhKS5nZXRNaW51dGVzKCkrMApyZXR1cm4gdH0s
-CkpkOmZ1bmN0aW9uKGEpe3ZhciB0PUgubzIoYSkuZ2V0U2Vjb25kcygpKzAKcmV0dXJuIHR9LApWYTpm
-dW5jdGlvbihhKXt2YXIgdD1ILm8yKGEpLmdldE1pbGxpc2Vjb25kcygpKzAKcmV0dXJuIHR9LAp6bzpm
-dW5jdGlvbihhLGIsYyl7dmFyIHQscyxyPXt9CnIuYT0wCnQ9W10Kcz1bXQpyLmE9Yi5sZW5ndGgKQy5O
-bS5GVih0LGIpCnIuYj0iIgppZihjIT1udWxsJiZjLmEhPT0wKWMuSygwLG5ldyBILkNqKHIscyx0KSkK
-IiIrci5hCnJldHVybiBKLkp5KGEsbmV3IEguTEkoQy5UZSwwLHQscywwKSl9LApFazpmdW5jdGlvbihh
-LGIsYyl7dmFyIHQscyxyLHEKaWYoYiBpbnN0YW5jZW9mIEFycmF5KXQ9Yz09bnVsbHx8Yy5hPT09MApl
-bHNlIHQ9ITEKaWYodCl7cz1iCnI9cy5sZW5ndGgKaWYocj09PTApe2lmKCEhYS4kMClyZXR1cm4gYS4k
-MCgpfWVsc2UgaWYocj09PTEpe2lmKCEhYS4kMSlyZXR1cm4gYS4kMShzWzBdKX1lbHNlIGlmKHI9PT0y
-KXtpZighIWEuJDIpcmV0dXJuIGEuJDIoc1swXSxzWzFdKX1lbHNlIGlmKHI9PT0zKXtpZighIWEuJDMp
-cmV0dXJuIGEuJDMoc1swXSxzWzFdLHNbMl0pfWVsc2UgaWYocj09PTQpe2lmKCEhYS4kNClyZXR1cm4g
-YS4kNChzWzBdLHNbMV0sc1syXSxzWzNdKX1lbHNlIGlmKHI9PT01KWlmKCEhYS4kNSlyZXR1cm4gYS4k
-NShzWzBdLHNbMV0sc1syXSxzWzNdLHNbNF0pCnE9YVsiIisiJCIrcl0KaWYocSE9bnVsbClyZXR1cm4g
-cS5hcHBseShhLHMpfXJldHVybiBILkV3KGEsYixjKX0sCkV3OmZ1bmN0aW9uKGEsYixjKXt2YXIgdCxz
-LHIscSxwLG8sbixtLGwsaz1iIGluc3RhbmNlb2YgQXJyYXk/YjpQLkNIKGIsITAsdS56KSxqPWsubGVu
-Z3RoLGk9YS4kUgppZihqPGkpcmV0dXJuIEguem8oYSxrLGMpCnQ9YS4kRApzPXQ9PW51bGwKcj0hcz90
-KCk6bnVsbApxPUouaWEoYSkKcD1xLiRDCmlmKHR5cGVvZiBwPT0ic3RyaW5nIilwPXFbcF0KaWYocyl7
-aWYoYyE9bnVsbCYmYy5hIT09MClyZXR1cm4gSC56byhhLGssYykKaWYoaj09PWkpcmV0dXJuIHAuYXBw
-bHkoYSxrKQpyZXR1cm4gSC56byhhLGssYyl9aWYociBpbnN0YW5jZW9mIEFycmF5KXtpZihjIT1udWxs
-JiZjLmEhPT0wKXJldHVybiBILnpvKGEsayxjKQppZihqPmkrci5sZW5ndGgpcmV0dXJuIEguem8oYSxr
-LG51bGwpCkMuTm0uRlYoayxyLnNsaWNlKGotaSkpCnJldHVybiBwLmFwcGx5KGEsayl9ZWxzZXtpZihq
-PmkpcmV0dXJuIEguem8oYSxrLGMpCm89T2JqZWN0LmtleXMocikKaWYoYz09bnVsbClmb3Iocz1vLmxl
-bmd0aCxuPTA7bjxvLmxlbmd0aDtvLmxlbmd0aD09PXN8fCgwLEgubGspKG8pLCsrbilDLk5tLmkoayxy
-W0guYyhvW25dKV0pCmVsc2V7Zm9yKHM9by5sZW5ndGgsbT0wLG49MDtuPG8ubGVuZ3RoO28ubGVuZ3Ro
-PT09c3x8KDAsSC5saykobyksKytuKXtsPUguYyhvW25dKQppZihjLng0KGwpKXsrK20KQy5ObS5pKGss
-Yy5xKDAsbCkpfWVsc2UgQy5ObS5pKGsscltsXSl9aWYobSE9PWMuYSlyZXR1cm4gSC56byhhLGssYyl9
-cmV0dXJuIHAuYXBwbHkoYSxrKX19LApwWTpmdW5jdGlvbihhKXt0aHJvdyBILmIoSC5JKGEpKX0sCms6
-ZnVuY3Rpb24oYSxiKXtpZihhPT1udWxsKUouSChhKQp0aHJvdyBILmIoSC5IWShhLGIpKX0sCkhZOmZ1
-bmN0aW9uKGEsYil7dmFyIHQscyxyPSJpbmRleCIKaWYoIUgub2soYikpcmV0dXJuIG5ldyBQLkFUKCEw
-LGIscixudWxsKQp0PUguV1koSi5IKGEpKQppZighKGI8MCkpe2lmKHR5cGVvZiB0IT09Im51bWJlciIp
-cmV0dXJuIEgucFkodCkKcz1iPj10fWVsc2Ugcz0hMAppZihzKXJldHVybiBQLnQoYixhLHIsbnVsbCx0
-KQpyZXR1cm4gUC5PNyhiLHIpfSwKYXU6ZnVuY3Rpb24oYSxiLGMpe3ZhciB0PSJJbnZhbGlkIHZhbHVl
-IgppZihhPmMpcmV0dXJuIG5ldyBQLmJKKDAsYywhMCxhLCJzdGFydCIsdCkKaWYoYiE9bnVsbCl7aWYo
-IUgub2soYikpcmV0dXJuIG5ldyBQLkFUKCEwLGIsImVuZCIsbnVsbCkKaWYoYjxhfHxiPmMpcmV0dXJu
-IG5ldyBQLmJKKGEsYywhMCxiLCJlbmQiLHQpfXJldHVybiBuZXcgUC5BVCghMCxiLCJlbmQiLG51bGwp
-fSwKSTpmdW5jdGlvbihhKXtyZXR1cm4gbmV3IFAuQVQoITAsYSxudWxsLG51bGwpfSwKYjpmdW5jdGlv
-bihhKXt2YXIgdAppZihhPT1udWxsKWE9bmV3IFAuTEsoKQp0PW5ldyBFcnJvcigpCnQuZGFydEV4Y2Vw
-dGlvbj1hCmlmKCJkZWZpbmVQcm9wZXJ0eSIgaW4gT2JqZWN0KXtPYmplY3QuZGVmaW5lUHJvcGVydHko
-dCwibWVzc2FnZSIse2dldDpILkp1fSkKdC5uYW1lPSIifWVsc2UgdC50b1N0cmluZz1ILkp1CnJldHVy
-biB0fSwKSnU6ZnVuY3Rpb24oKXtyZXR1cm4gSi5BYyh0aGlzLmRhcnRFeGNlcHRpb24pfSwKdmg6ZnVu
-Y3Rpb24oYSl7dGhyb3cgSC5iKGEpfSwKbGs6ZnVuY3Rpb24oYSl7dGhyb3cgSC5iKFAuYTQoYSkpfSwK
-Y006ZnVuY3Rpb24oYSl7dmFyIHQscyxyLHEscCxvCmE9SC5lQShhLnJlcGxhY2UoU3RyaW5nKHt9KSwn
-JHJlY2VpdmVyJCcpKQp0PWEubWF0Y2goL1xcXCRbYS16QS1aXStcXFwkL2cpCmlmKHQ9PW51bGwpdD1I
-LlZNKFtdLHUucykKcz10LmluZGV4T2YoIlxcJGFyZ3VtZW50c1xcJCIpCnI9dC5pbmRleE9mKCJcXCRh
-cmd1bWVudHNFeHByXFwkIikKcT10LmluZGV4T2YoIlxcJGV4cHJcXCQiKQpwPXQuaW5kZXhPZigiXFwk
-bWV0aG9kXFwkIikKbz10LmluZGV4T2YoIlxcJHJlY2VpdmVyXFwkIikKcmV0dXJuIG5ldyBILmY5KGEu
-cmVwbGFjZShuZXcgUmVnRXhwKCdcXFxcXFwkYXJndW1lbnRzXFxcXFxcJCcsJ2cnKSwnKCg/Onh8W154
-XSkqKScpLnJlcGxhY2UobmV3IFJlZ0V4cCgnXFxcXFxcJGFyZ3VtZW50c0V4cHJcXFxcXFwkJywnZycp
-LCcoKD86eHxbXnhdKSopJykucmVwbGFjZShuZXcgUmVnRXhwKCdcXFxcXFwkZXhwclxcXFxcXCQnLCdn
-JyksJygoPzp4fFteeF0pKiknKS5yZXBsYWNlKG5ldyBSZWdFeHAoJ1xcXFxcXCRtZXRob2RcXFxcXFwk
-JywnZycpLCcoKD86eHxbXnhdKSopJykucmVwbGFjZShuZXcgUmVnRXhwKCdcXFxcXFwkcmVjZWl2ZXJc
-XFxcXFwkJywnZycpLCcoKD86eHxbXnhdKSopJykscyxyLHEscCxvKX0sClM3OmZ1bmN0aW9uKGEpe3Jl
-dHVybiBmdW5jdGlvbigkZXhwciQpe3ZhciAkYXJndW1lbnRzRXhwciQ9JyRhcmd1bWVudHMkJwp0cnl7
-JGV4cHIkLiRtZXRob2QkKCRhcmd1bWVudHNFeHByJCl9Y2F0Y2godCl7cmV0dXJuIHQubWVzc2FnZX19
-KGEpfSwKTWo6ZnVuY3Rpb24oYSl7cmV0dXJuIGZ1bmN0aW9uKCRleHByJCl7dHJ5eyRleHByJC4kbWV0
-aG9kJH1jYXRjaCh0KXtyZXR1cm4gdC5tZXNzYWdlfX0oYSl9LApJajpmdW5jdGlvbihhLGIpe3JldHVy
-biBuZXcgSC5XMChhLGI9PW51bGw/bnVsbDpiLm1ldGhvZCl9LApUMzpmdW5jdGlvbihhLGIpe3ZhciB0
-PWI9PW51bGwscz10P251bGw6Yi5tZXRob2QKcmV0dXJuIG5ldyBILmF6KGEscyx0P251bGw6Yi5yZWNl
-aXZlcil9LApSdTpmdW5jdGlvbihhKXt2YXIgdCxzLHIscSxwLG8sbixtLGwsayxqLGksaCxnLGY9bnVs
-bCxlPW5ldyBILkFtKGEpCmlmKGE9PW51bGwpcmV0dXJuIGYKaWYoYSBpbnN0YW5jZW9mIEguYnEpcmV0
-dXJuIGUuJDEoYS5hKQppZih0eXBlb2YgYSE9PSJvYmplY3QiKXJldHVybiBhCmlmKCJkYXJ0RXhjZXB0
-aW9uIiBpbiBhKXJldHVybiBlLiQxKGEuZGFydEV4Y2VwdGlvbikKZWxzZSBpZighKCJtZXNzYWdlIiBp
-biBhKSlyZXR1cm4gYQp0PWEubWVzc2FnZQppZigibnVtYmVyIiBpbiBhJiZ0eXBlb2YgYS5udW1iZXI9
-PSJudW1iZXIiKXtzPWEubnVtYmVyCnI9cyY2NTUzNQppZigoQy5qbi53RyhzLDE2KSY4MTkxKT09PTEw
-KXN3aXRjaChyKXtjYXNlIDQzODpyZXR1cm4gZS4kMShILlQzKEguZCh0KSsiIChFcnJvciAiK3IrIiki
-LGYpKQpjYXNlIDQ0NTpjYXNlIDUwMDc6cmV0dXJuIGUuJDEoSC5JaihILmQodCkrIiAoRXJyb3IgIity
-KyIpIixmKSl9fWlmKGEgaW5zdGFuY2VvZiBUeXBlRXJyb3Ipe3E9JC5TbigpCnA9JC5scSgpCm89JC5O
-OSgpCm49JC5pSSgpCm09JC5VTigpCmw9JC5aaCgpCms9JC5yTigpCiQuYzMoKQpqPSQuSEsoKQppPSQu
-cjEoKQpoPXEucVModCkKaWYoaCE9bnVsbClyZXR1cm4gZS4kMShILlQzKEguYyh0KSxoKSkKZWxzZXto
-PXAucVModCkKaWYoaCE9bnVsbCl7aC5tZXRob2Q9ImNhbGwiCnJldHVybiBlLiQxKEguVDMoSC5jKHQp
-LGgpKX1lbHNle2g9by5xUyh0KQppZihoPT1udWxsKXtoPW4ucVModCkKaWYoaD09bnVsbCl7aD1tLnFT
-KHQpCmlmKGg9PW51bGwpe2g9bC5xUyh0KQppZihoPT1udWxsKXtoPWsucVModCkKaWYoaD09bnVsbCl7
-aD1uLnFTKHQpCmlmKGg9PW51bGwpe2g9ai5xUyh0KQppZihoPT1udWxsKXtoPWkucVModCkKZz1oIT1u
-dWxsfWVsc2UgZz0hMH1lbHNlIGc9ITB9ZWxzZSBnPSEwfWVsc2UgZz0hMH1lbHNlIGc9ITB9ZWxzZSBn
-PSEwfWVsc2UgZz0hMAppZihnKXJldHVybiBlLiQxKEguSWooSC5jKHQpLGgpKX19cmV0dXJuIGUuJDEo
-bmV3IEgudlYodHlwZW9mIHQ9PSJzdHJpbmciP3Q6IiIpKX1pZihhIGluc3RhbmNlb2YgUmFuZ2VFcnJv
-cil7aWYodHlwZW9mIHQ9PSJzdHJpbmciJiZ0LmluZGV4T2YoImNhbGwgc3RhY2siKSE9PS0xKXJldHVy
-biBuZXcgUC5LWSgpCnQ9ZnVuY3Rpb24oYil7dHJ5e3JldHVybiBTdHJpbmcoYil9Y2F0Y2goZCl7fXJl
-dHVybiBudWxsfShhKQpyZXR1cm4gZS4kMShuZXcgUC5BVCghMSxmLGYsdHlwZW9mIHQ9PSJzdHJpbmci
-P3QucmVwbGFjZSgvXlJhbmdlRXJyb3I6XHMqLywiIik6dCkpfWlmKHR5cGVvZiBJbnRlcm5hbEVycm9y
-PT0iZnVuY3Rpb24iJiZhIGluc3RhbmNlb2YgSW50ZXJuYWxFcnJvcilpZih0eXBlb2YgdD09InN0cmlu
-ZyImJnQ9PT0idG9vIG11Y2ggcmVjdXJzaW9uIilyZXR1cm4gbmV3IFAuS1koKQpyZXR1cm4gYX0sCnRz
-OmZ1bmN0aW9uKGEpe3ZhciB0CmlmKGEgaW5zdGFuY2VvZiBILmJxKXJldHVybiBhLmIKaWYoYT09bnVs
-bClyZXR1cm4gbmV3IEguWE8oYSkKdD1hLiRjYWNoZWRUcmFjZQppZih0IT1udWxsKXJldHVybiB0CnJl
-dHVybiBhLiRjYWNoZWRUcmFjZT1uZXcgSC5YTyhhKX0sCkI3OmZ1bmN0aW9uKGEsYil7dmFyIHQscyxy
-LHE9YS5sZW5ndGgKZm9yKHQ9MDt0PHE7dD1yKXtzPXQrMQpyPXMrMQpiLlkoMCxhW3RdLGFbc10pfXJl
-dHVybiBifSwKZnQ6ZnVuY3Rpb24oYSxiLGMsZCxlLGYpe3UuWi5hKGEpCnN3aXRjaChILldZKGIpKXtj
-YXNlIDA6cmV0dXJuIGEuJDAoKQpjYXNlIDE6cmV0dXJuIGEuJDEoYykKY2FzZSAyOnJldHVybiBhLiQy
-KGMsZCkKY2FzZSAzOnJldHVybiBhLiQzKGMsZCxlKQpjYXNlIDQ6cmV0dXJuIGEuJDQoYyxkLGUsZil9
-dGhyb3cgSC5iKG5ldyBQLkNEKCJVbnN1cHBvcnRlZCBudW1iZXIgb2YgYXJndW1lbnRzIGZvciB3cmFw
-cGVkIGNsb3N1cmUiKSl9LAp0UjpmdW5jdGlvbihhLGIpe3ZhciB0CmlmKGE9PW51bGwpcmV0dXJuIG51
-bGwKdD1hLiRpZGVudGl0eQppZighIXQpcmV0dXJuIHQKdD1mdW5jdGlvbihjLGQsZSl7cmV0dXJuIGZ1
-bmN0aW9uKGYsZyxoLGkpe3JldHVybiBlKGMsZCxmLGcsaCxpKX19KGEsYixILmZ0KQphLiRpZGVudGl0
-eT10CnJldHVybiB0fSwKaUE6ZnVuY3Rpb24oYSxiLGMsZCxlLGYsZyl7dmFyIHQscyxyLHEscCxvLG4s
-bSxsPW51bGwsaz1iWzBdLGo9ay4kY2FsbE5hbWUsaT1lP09iamVjdC5jcmVhdGUobmV3IEguengoKS5j
-b25zdHJ1Y3Rvci5wcm90b3R5cGUpOk9iamVjdC5jcmVhdGUobmV3IEguankobCxsLGwsbCkuY29uc3Ry
-dWN0b3IucHJvdG90eXBlKQppLiRpbml0aWFsaXplPWkuY29uc3RydWN0b3IKaWYoZSl0PWZ1bmN0aW9u
-IHN0YXRpY190ZWFyX29mZigpe3RoaXMuJGluaXRpYWxpemUoKX0KZWxzZXtzPSQueWoKaWYodHlwZW9m
-IHMhPT0ibnVtYmVyIilyZXR1cm4gcy5oKCkKJC55aj1zKzEKcz1uZXcgRnVuY3Rpb24oImEsYixjLGQi
-K3MsInRoaXMuJGluaXRpYWxpemUoYSxiLGMsZCIrcysiKSIpCnQ9c31pLmNvbnN0cnVjdG9yPXQKdC5w
-cm90b3R5cGU9aQppZighZSl7cj1ILmJ4KGEsayxmKQpyLiRyZWZsZWN0aW9uSW5mbz1kfWVsc2V7aS4k
-c3RhdGljX25hbWU9ZwpyPWt9cT1ILmltKGQsZSxmKQppLiRTPXEKaVtqXT1yCmZvcihwPXIsbz0xO288
-Yi5sZW5ndGg7KytvKXtuPWJbb10KbT1uLiRjYWxsTmFtZQppZihtIT1udWxsKXtuPWU/bjpILmJ4KGEs
-bixmKQppW21dPW59aWYobz09PWMpe24uJHJlZmxlY3Rpb25JbmZvPWQKcD1ufX1pLiRDPXAKaS4kUj1r
-LiRSCmkuJEQ9ay4kRApyZXR1cm4gdH0sCmltOmZ1bmN0aW9uKGEsYixjKXt2YXIgdAppZih0eXBlb2Yg
-YT09Im51bWJlciIpcmV0dXJuIGZ1bmN0aW9uKGQsZSl7cmV0dXJuIGZ1bmN0aW9uKCl7cmV0dXJuIGQo
-ZSl9fShILkJwLGEpCmlmKHR5cGVvZiBhPT0ic3RyaW5nIil7aWYoYil0aHJvdyBILmIoIkNhbm5vdCBj
-b21wdXRlIHNpZ25hdHVyZSBmb3Igc3RhdGljIHRlYXJvZmYuIikKdD1jP0guUFc6SC5UbgpyZXR1cm4g
-ZnVuY3Rpb24oZCxlKXtyZXR1cm4gZnVuY3Rpb24oKXtyZXR1cm4gZSh0aGlzLGQpfX0oYSx0KX10aHJv
-dyBILmIoIkVycm9yIGluIGZ1bmN0aW9uVHlwZSBvZiB0ZWFyb2ZmIil9LAp2cTpmdW5jdGlvbihhLGIs
-YyxkKXt2YXIgdD1ILkRWCnN3aXRjaChiPy0xOmEpe2Nhc2UgMDpyZXR1cm4gZnVuY3Rpb24oZSxmKXty
-ZXR1cm4gZnVuY3Rpb24oKXtyZXR1cm4gZih0aGlzKVtlXSgpfX0oYyx0KQpjYXNlIDE6cmV0dXJuIGZ1
-bmN0aW9uKGUsZil7cmV0dXJuIGZ1bmN0aW9uKGcpe3JldHVybiBmKHRoaXMpW2VdKGcpfX0oYyx0KQpj
-YXNlIDI6cmV0dXJuIGZ1bmN0aW9uKGUsZil7cmV0dXJuIGZ1bmN0aW9uKGcsaCl7cmV0dXJuIGYodGhp
-cylbZV0oZyxoKX19KGMsdCkKY2FzZSAzOnJldHVybiBmdW5jdGlvbihlLGYpe3JldHVybiBmdW5jdGlv
-bihnLGgsaSl7cmV0dXJuIGYodGhpcylbZV0oZyxoLGkpfX0oYyx0KQpjYXNlIDQ6cmV0dXJuIGZ1bmN0
-aW9uKGUsZil7cmV0dXJuIGZ1bmN0aW9uKGcsaCxpLGope3JldHVybiBmKHRoaXMpW2VdKGcsaCxpLGop
-fX0oYyx0KQpjYXNlIDU6cmV0dXJuIGZ1bmN0aW9uKGUsZil7cmV0dXJuIGZ1bmN0aW9uKGcsaCxpLGos
-ayl7cmV0dXJuIGYodGhpcylbZV0oZyxoLGksaixrKX19KGMsdCkKZGVmYXVsdDpyZXR1cm4gZnVuY3Rp
-b24oZSxmKXtyZXR1cm4gZnVuY3Rpb24oKXtyZXR1cm4gZS5hcHBseShmKHRoaXMpLGFyZ3VtZW50cyl9
-fShkLHQpfX0sCmJ4OmZ1bmN0aW9uKGEsYixjKXt2YXIgdCxzLHIscSxwLG8sbgppZihjKXJldHVybiBI
-LkhmKGEsYikKdD1iLiRzdHViTmFtZQpzPWIubGVuZ3RoCnI9YVt0XQpxPWI9PW51bGw/cj09bnVsbDpi
-PT09cgpwPSFxfHxzPj0yNwppZihwKXJldHVybiBILnZxKHMsIXEsdCxiKQppZihzPT09MCl7cT0kLnlq
-CmlmKHR5cGVvZiBxIT09Im51bWJlciIpcmV0dXJuIHEuaCgpCiQueWo9cSsxCm89InNlbGYiK3EKcT0i
-cmV0dXJuIGZ1bmN0aW9uKCl7dmFyICIrbysiID0gdGhpcy4iCnA9JC5tSgpyZXR1cm4gbmV3IEZ1bmN0
-aW9uKHErSC5kKHA9PW51bGw/JC5tSj1ILkUyKCJzZWxmIik6cCkrIjtyZXR1cm4gIitvKyIuIitILmQo
-dCkrIigpO30iKSgpfW49ImFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6Ii5zcGxpdCgiIikuc3BsaWNl
-KDAscykuam9pbigiLCIpCnE9JC55agppZih0eXBlb2YgcSE9PSJudW1iZXIiKXJldHVybiBxLmgoKQok
-LnlqPXErMQpuKz1xCnE9InJldHVybiBmdW5jdGlvbigiK24rIil7cmV0dXJuIHRoaXMuIgpwPSQubUoK
-cmV0dXJuIG5ldyBGdW5jdGlvbihxK0guZChwPT1udWxsPyQubUo9SC5FMigic2VsZiIpOnApKyIuIitI
-LmQodCkrIigiK24rIik7fSIpKCl9LApaNDpmdW5jdGlvbihhLGIsYyxkKXt2YXIgdD1ILkRWLHM9SC55
-Uwpzd2l0Y2goYj8tMTphKXtjYXNlIDA6dGhyb3cgSC5iKEguRWYoIkludGVyY2VwdGVkIGZ1bmN0aW9u
-IHdpdGggbm8gYXJndW1lbnRzLiIpKQpjYXNlIDE6cmV0dXJuIGZ1bmN0aW9uKGUsZixnKXtyZXR1cm4g
-ZnVuY3Rpb24oKXtyZXR1cm4gZih0aGlzKVtlXShnKHRoaXMpKX19KGMsdCxzKQpjYXNlIDI6cmV0dXJu
-IGZ1bmN0aW9uKGUsZixnKXtyZXR1cm4gZnVuY3Rpb24oaCl7cmV0dXJuIGYodGhpcylbZV0oZyh0aGlz
-KSxoKX19KGMsdCxzKQpjYXNlIDM6cmV0dXJuIGZ1bmN0aW9uKGUsZixnKXtyZXR1cm4gZnVuY3Rpb24o
-aCxpKXtyZXR1cm4gZih0aGlzKVtlXShnKHRoaXMpLGgsaSl9fShjLHQscykKY2FzZSA0OnJldHVybiBm
-dW5jdGlvbihlLGYsZyl7cmV0dXJuIGZ1bmN0aW9uKGgsaSxqKXtyZXR1cm4gZih0aGlzKVtlXShnKHRo
-aXMpLGgsaSxqKX19KGMsdCxzKQpjYXNlIDU6cmV0dXJuIGZ1bmN0aW9uKGUsZixnKXtyZXR1cm4gZnVu
-Y3Rpb24oaCxpLGosayl7cmV0dXJuIGYodGhpcylbZV0oZyh0aGlzKSxoLGksaixrKX19KGMsdCxzKQpj
-YXNlIDY6cmV0dXJuIGZ1bmN0aW9uKGUsZixnKXtyZXR1cm4gZnVuY3Rpb24oaCxpLGosayxsKXtyZXR1
-cm4gZih0aGlzKVtlXShnKHRoaXMpLGgsaSxqLGssbCl9fShjLHQscykKZGVmYXVsdDpyZXR1cm4gZnVu
-Y3Rpb24oZSxmLGcsaCl7cmV0dXJuIGZ1bmN0aW9uKCl7aD1bZyh0aGlzKV0KQXJyYXkucHJvdG90eXBl
-LnB1c2guYXBwbHkoaCxhcmd1bWVudHMpCnJldHVybiBlLmFwcGx5KGYodGhpcyksaCl9fShkLHQscyl9
-fSwKSGY6ZnVuY3Rpb24oYSxiKXt2YXIgdCxzLHIscSxwLG8sbixtPSQubUoKaWYobT09bnVsbCltPSQu
-bUo9SC5FMigic2VsZiIpCnQ9JC5QNAppZih0PT1udWxsKXQ9JC5QND1ILkUyKCJyZWNlaXZlciIpCnM9
-Yi4kc3R1Yk5hbWUKcj1iLmxlbmd0aApxPWFbc10KcD1iPT1udWxsP3E9PW51bGw6Yj09PXEKbz0hcHx8
-cj49MjgKaWYobylyZXR1cm4gSC5aNChyLCFwLHMsYikKaWYocj09PTEpe209InJldHVybiBmdW5jdGlv
-bigpe3JldHVybiB0aGlzLiIrSC5kKG0pKyIuIitILmQocykrIih0aGlzLiIrSC5kKHQpKyIpOyIKdD0k
-LnlqCmlmKHR5cGVvZiB0IT09Im51bWJlciIpcmV0dXJuIHQuaCgpCiQueWo9dCsxCnJldHVybiBuZXcg
-RnVuY3Rpb24obSt0KyJ9IikoKX1uPSJhYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5eiIuc3BsaXQoIiIp
-LnNwbGljZSgwLHItMSkuam9pbigiLCIpCm09InJldHVybiBmdW5jdGlvbigiK24rIil7cmV0dXJuIHRo
-aXMuIitILmQobSkrIi4iK0guZChzKSsiKHRoaXMuIitILmQodCkrIiwgIituKyIpOyIKdD0kLnlqCmlm
-KHR5cGVvZiB0IT09Im51bWJlciIpcmV0dXJuIHQuaCgpCiQueWo9dCsxCnJldHVybiBuZXcgRnVuY3Rp
-b24obSt0KyJ9IikoKX0sCktxOmZ1bmN0aW9uKGEsYixjLGQsZSxmLGcpe3JldHVybiBILmlBKGEsYixj
-LGQsISFlLCEhZixnKX0sClRuOmZ1bmN0aW9uKGEsYil7cmV0dXJuIEguY0Uodi50eXBlVW5pdmVyc2Us
-SC5xKGEuYSksYil9LApQVzpmdW5jdGlvbihhLGIpe3JldHVybiBILmNFKHYudHlwZVVuaXZlcnNlLEgu
-cShhLmMpLGIpfSwKRFY6ZnVuY3Rpb24oYSl7cmV0dXJuIGEuYX0sCnlTOmZ1bmN0aW9uKGEpe3JldHVy
-biBhLmN9LApFMjpmdW5jdGlvbihhKXt2YXIgdCxzLHIscT1uZXcgSC5qeSgic2VsZiIsInRhcmdldCIs
-InJlY2VpdmVyIiwibmFtZSIpLHA9Si5FcChPYmplY3QuZ2V0T3duUHJvcGVydHlOYW1lcyhxKSkKZm9y
-KHQ9cC5sZW5ndGgscz0wO3M8dDsrK3Mpe3I9cFtzXQppZihxW3JdPT09YSlyZXR1cm4gcn19LApvVDpm
-dW5jdGlvbihhKXtpZihhPT1udWxsKUguZk8oImJvb2xlYW4gZXhwcmVzc2lvbiBtdXN0IG5vdCBiZSBu
-dWxsIikKcmV0dXJuIGF9LApmTzpmdW5jdGlvbihhKXt0aHJvdyBILmIobmV3IEgua1koYSkpfSwKYWc6
-ZnVuY3Rpb24oYSl7dGhyb3cgSC5iKG5ldyBQLnQ3KGEpKX0sCkVmOmZ1bmN0aW9uKGEpe3JldHVybiBu
-ZXcgSC5FcShhKX0sCllnOmZ1bmN0aW9uKGEpe3JldHVybiB2LmdldElzb2xhdGVUYWcoYSl9LApWTTpm
-dW5jdGlvbihhLGIpe2Fbdi5hcnJheVJ0aV09YgpyZXR1cm4gYX0sCm9YOmZ1bmN0aW9uKGEpe2lmKGE9
-PW51bGwpcmV0dXJuIG51bGwKcmV0dXJuIGEuJHRpfSwKSU06ZnVuY3Rpb24oYSxiLGMpe3JldHVybiBI
-Llk5KGFbIiRhIitILmQoYyldLEgub1goYikpfSwKWTk6ZnVuY3Rpb24oYSxiKXtpZihhPT1udWxsKXJl
-dHVybiBiCmE9YS5hcHBseShudWxsLGIpCmlmKGE9PW51bGwpcmV0dXJuIG51bGwKaWYoQXJyYXkuaXNB
-cnJheShhKSlyZXR1cm4gYQppZih0eXBlb2YgYT09ImZ1bmN0aW9uIilyZXR1cm4gYS5hcHBseShudWxs
-LGIpCnJldHVybiBifSwKSUc6ZnVuY3Rpb24oYSxiLGMpe3JldHVybiBhLmFwcGx5KGIsSC5ZOShKLmlh
-KGIpWyIkYSIrSC5kKGMpXSxILm9YKGIpKSl9LAppdzpmdW5jdGlvbihhLGIsYyl7T2JqZWN0LmRlZmlu
-ZVByb3BlcnR5KGEsYix7dmFsdWU6YyxlbnVtZXJhYmxlOmZhbHNlLHdyaXRhYmxlOnRydWUsY29uZmln
-dXJhYmxlOnRydWV9KX0sCkc6ZnVuY3Rpb24oYSl7dmFyIHQscyxyLHEscD1ILmMoJC55LiQxKGEpKSxv
-PSQualtwXQppZihvIT1udWxsKXtPYmplY3QuZGVmaW5lUHJvcGVydHkoYSx2LmRpc3BhdGNoUHJvcGVy
-dHlOYW1lLHt2YWx1ZTpvLGVudW1lcmFibGU6ZmFsc2Usd3JpdGFibGU6dHJ1ZSxjb25maWd1cmFibGU6
-dHJ1ZX0pCnJldHVybiBvLml9dD0kLnZbcF0KaWYodCE9bnVsbClyZXR1cm4gdApzPXYuaW50ZXJjZXB0
-b3JzQnlUYWdbcF0KaWYocz09bnVsbCl7cD1ILmMoJC51LiQyKGEscCkpCmlmKHAhPW51bGwpe289JC5q
-W3BdCmlmKG8hPW51bGwpe09iamVjdC5kZWZpbmVQcm9wZXJ0eShhLHYuZGlzcGF0Y2hQcm9wZXJ0eU5h
-bWUse3ZhbHVlOm8sZW51bWVyYWJsZTpmYWxzZSx3cml0YWJsZTp0cnVlLGNvbmZpZ3VyYWJsZTp0cnVl
-fSkKcmV0dXJuIG8uaX10PSQudltwXQppZih0IT1udWxsKXJldHVybiB0CnM9di5pbnRlcmNlcHRvcnNC
-eVRhZ1twXX19aWYocz09bnVsbClyZXR1cm4gbnVsbAp0PXMucHJvdG90eXBlCnI9cFswXQppZihyPT09
-IiEiKXtvPUgubCh0KQokLmpbcF09bwpPYmplY3QuZGVmaW5lUHJvcGVydHkoYSx2LmRpc3BhdGNoUHJv
-cGVydHlOYW1lLHt2YWx1ZTpvLGVudW1lcmFibGU6ZmFsc2Usd3JpdGFibGU6dHJ1ZSxjb25maWd1cmFi
-bGU6dHJ1ZX0pCnJldHVybiBvLml9aWYocj09PSJ+Iil7JC52W3BdPXQKcmV0dXJuIHR9aWYocj09PSIt
-Iil7cT1ILmwodCkKT2JqZWN0LmRlZmluZVByb3BlcnR5KE9iamVjdC5nZXRQcm90b3R5cGVPZihhKSx2
-LmRpc3BhdGNoUHJvcGVydHlOYW1lLHt2YWx1ZTpxLGVudW1lcmFibGU6ZmFsc2Usd3JpdGFibGU6dHJ1
-ZSxjb25maWd1cmFibGU6dHJ1ZX0pCnJldHVybiBxLml9aWYocj09PSIrIilyZXR1cm4gSC5MYyhhLHQp
-CmlmKHI9PT0iKiIpdGhyb3cgSC5iKFAubihwKSkKaWYodi5sZWFmVGFnc1twXT09PXRydWUpe3E9SC5s
-KHQpCk9iamVjdC5kZWZpbmVQcm9wZXJ0eShPYmplY3QuZ2V0UHJvdG90eXBlT2YoYSksdi5kaXNwYXRj
-aFByb3BlcnR5TmFtZSx7dmFsdWU6cSxlbnVtZXJhYmxlOmZhbHNlLHdyaXRhYmxlOnRydWUsY29uZmln
-dXJhYmxlOnRydWV9KQpyZXR1cm4gcS5pfWVsc2UgcmV0dXJuIEguTGMoYSx0KX0sCkxjOmZ1bmN0aW9u
-KGEsYil7dmFyIHQ9T2JqZWN0LmdldFByb3RvdHlwZU9mKGEpCk9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0
-LHYuZGlzcGF0Y2hQcm9wZXJ0eU5hbWUse3ZhbHVlOkouUXUoYix0LG51bGwsbnVsbCksZW51bWVyYWJs
-ZTpmYWxzZSx3cml0YWJsZTp0cnVlLGNvbmZpZ3VyYWJsZTp0cnVlfSkKcmV0dXJuIGJ9LApsOmZ1bmN0
-aW9uKGEpe3JldHVybiBKLlF1KGEsITEsbnVsbCwhIWEuJGlYail9LApWRjpmdW5jdGlvbihhLGIsYyl7
-dmFyIHQ9Yi5wcm90b3R5cGUKaWYodi5sZWFmVGFnc1thXT09PXRydWUpcmV0dXJuIEgubCh0KQplbHNl
-IHJldHVybiBKLlF1KHQsYyxudWxsLG51bGwpfSwKTTpmdW5jdGlvbigpe2lmKCEwPT09JC5LKXJldHVy
-bgokLks9ITAKSC5EKCl9LApEOmZ1bmN0aW9uKCl7dmFyIHQscyxyLHEscCxvLG4sbQokLmo9T2JqZWN0
-LmNyZWF0ZShudWxsKQokLnY9T2JqZWN0LmNyZWF0ZShudWxsKQpILmEoKQp0PXYuaW50ZXJjZXB0b3Jz
-QnlUYWcKcz1PYmplY3QuZ2V0T3duUHJvcGVydHlOYW1lcyh0KQppZih0eXBlb2Ygd2luZG93IT0idW5k
-ZWZpbmVkIil7d2luZG93CnI9ZnVuY3Rpb24oKXt9CmZvcihxPTA7cTxzLmxlbmd0aDsrK3Epe3A9c1tx
-XQpvPSQueDcuJDEocCkKaWYobyE9bnVsbCl7bj1ILlZGKHAsdFtwXSxvKQppZihuIT1udWxsKXtPYmpl
-Y3QuZGVmaW5lUHJvcGVydHkobyx2LmRpc3BhdGNoUHJvcGVydHlOYW1lLHt2YWx1ZTpuLGVudW1lcmFi
-bGU6ZmFsc2Usd3JpdGFibGU6dHJ1ZSxjb25maWd1cmFibGU6dHJ1ZX0pCnIucHJvdG90eXBlPW99fX19
-Zm9yKHE9MDtxPHMubGVuZ3RoOysrcSl7cD1zW3FdCmlmKC9eW0EtWmEtel9dLy50ZXN0KHApKXttPXRb
-cF0KdFsiISIrcF09bQp0WyJ+IitwXT1tCnRbIi0iK3BdPW0KdFsiKyIrcF09bQp0WyIqIitwXT1tfX19
-LAphOmZ1bmN0aW9uKCl7dmFyIHQscyxyLHEscCxvLG49Qy5PNCgpCm49SC5GKEMuWXEsSC5GKEMuS1Us
-SC5GKEMuZlEsSC5GKEMuZlEsSC5GKEMuaTcsSC5GKEMueGksSC5GKEMuZGsoQy53YiksbikpKSkpKSkK
-aWYodHlwZW9mIGRhcnROYXRpdmVEaXNwYXRjaEhvb2tzVHJhbnNmb3JtZXIhPSJ1bmRlZmluZWQiKXt0
-PWRhcnROYXRpdmVEaXNwYXRjaEhvb2tzVHJhbnNmb3JtZXIKaWYodHlwZW9mIHQ9PSJmdW5jdGlvbiIp
-dD1bdF0KaWYodC5jb25zdHJ1Y3Rvcj09QXJyYXkpZm9yKHM9MDtzPHQubGVuZ3RoOysrcyl7cj10W3Nd
-CmlmKHR5cGVvZiByPT0iZnVuY3Rpb24iKW49cihuKXx8bn19cT1uLmdldFRhZwpwPW4uZ2V0VW5rbm93
-blRhZwpvPW4ucHJvdG90eXBlRm9yVGFnCiQueT1uZXcgSC5yKHEpCiQudT1uZXcgSC5kQyhwKQokLng3
-PW5ldyBILndOKG8pfSwKRjpmdW5jdGlvbihhLGIpe3JldHVybiBhKGIpfHxifSwKdjQ6ZnVuY3Rpb24o
-YSxiLGMsZCxlLGYpe3ZhciB0PWI/Im0iOiIiLHM9Yz8iIjoiaSIscj1kPyJ1IjoiIixxPWU/InMiOiIi
-LHA9Zj8iZyI6IiIsbz1mdW5jdGlvbihnLGgpe3RyeXtyZXR1cm4gbmV3IFJlZ0V4cChnLGgpfWNhdGNo
-KG4pe3JldHVybiBufX0oYSx0K3MrcitxK3ApCmlmKG8gaW5zdGFuY2VvZiBSZWdFeHApcmV0dXJuIG8K
-dGhyb3cgSC5iKFAucnIoIklsbGVnYWwgUmVnRXhwIHBhdHRlcm4gKCIrU3RyaW5nKG8pKyIpIixhLG51
-bGwpKX0sCm0yOmZ1bmN0aW9uKGEsYixjKXt2YXIgdAppZih0eXBlb2YgYj09InN0cmluZyIpcmV0dXJu
-IGEuaW5kZXhPZihiLGMpPj0wCmVsc2UgaWYoYiBpbnN0YW5jZW9mIEguVlIpe3Q9Qy54Qi5HKGEsYykK
-cmV0dXJuIGIuYi50ZXN0KHQpfWVsc2V7dD1KLkZMKGIsQy54Qi5HKGEsYykpCnJldHVybiF0LmdsMCh0
-KX19LApBNDpmdW5jdGlvbihhKXtpZihhLmluZGV4T2YoIiQiLDApPj0wKXJldHVybiBhLnJlcGxhY2Uo
-L1wkL2csIiQkJCQiKQpyZXR1cm4gYX0sCmVBOmZ1bmN0aW9uKGEpe2lmKC9bW1xde30oKSorPy5cXF4k
-fF0vLnRlc3QoYSkpcmV0dXJuIGEucmVwbGFjZSgvW1tcXXt9KCkqKz8uXFxeJHxdL2csIlxcJCYiKQpy
-ZXR1cm4gYX0sCnlzOmZ1bmN0aW9uKGEsYixjKXt2YXIgdD1ILm5NKGEsYixjKQpyZXR1cm4gdH0sCm5N
-OmZ1bmN0aW9uKGEsYixjKXt2YXIgdCxzLHIscQppZihiPT09IiIpe2lmKGE9PT0iIilyZXR1cm4gYwp0
-PWEubGVuZ3RoCmZvcihzPWMscj0wO3I8dDsrK3Ipcz1zK2Fbcl0rYwpyZXR1cm4gcy5jaGFyQ29kZUF0
-KDApPT0wP3M6c31xPWEuaW5kZXhPZihiLDApCmlmKHE8MClyZXR1cm4gYQppZihhLmxlbmd0aDw1MDB8
-fGMuaW5kZXhPZigiJCIsMCk+PTApcmV0dXJuIGEuc3BsaXQoYikuam9pbihjKQpyZXR1cm4gYS5yZXBs
-YWNlKG5ldyBSZWdFeHAoSC5lQShiKSwnZycpLEguQTQoYykpfSwKUEQ6ZnVuY3Rpb24gUEQoYSxiKXt0
-aGlzLmE9YQp0aGlzLiR0aT1ifSwKV1U6ZnVuY3Rpb24gV1UoKXt9LApMUDpmdW5jdGlvbiBMUChhLGIs
-YyxkKXt2YXIgXz10aGlzCl8uYT1hCl8uYj1iCl8uYz1jCl8uJHRpPWR9LApYUjpmdW5jdGlvbiBYUihh
-LGIpe3RoaXMuYT1hCnRoaXMuJHRpPWJ9LApMSTpmdW5jdGlvbiBMSShhLGIsYyxkLGUpe3ZhciBfPXRo
-aXMKXy5hPWEKXy5jPWIKXy5kPWMKXy5lPWQKXy5mPWV9LApDajpmdW5jdGlvbiBDaihhLGIsYyl7dGhp
-cy5hPWEKdGhpcy5iPWIKdGhpcy5jPWN9LApmOTpmdW5jdGlvbiBmOShhLGIsYyxkLGUsZil7dmFyIF89
-dGhpcwpfLmE9YQpfLmI9YgpfLmM9YwpfLmQ9ZApfLmU9ZQpfLmY9Zn0sClcwOmZ1bmN0aW9uIFcwKGEs
-Yil7dGhpcy5hPWEKdGhpcy5iPWJ9LAphejpmdW5jdGlvbiBheihhLGIsYyl7dGhpcy5hPWEKdGhpcy5i
-PWIKdGhpcy5jPWN9LAp2VjpmdW5jdGlvbiB2VihhKXt0aGlzLmE9YX0sCmJxOmZ1bmN0aW9uIGJxKGEs
-Yil7dGhpcy5hPWEKdGhpcy5iPWJ9LApBbTpmdW5jdGlvbiBBbShhKXt0aGlzLmE9YX0sClhPOmZ1bmN0
-aW9uIFhPKGEpe3RoaXMuYT1hCnRoaXMuYj1udWxsfSwKVHA6ZnVuY3Rpb24gVHAoKXt9LApsYzpmdW5j
-dGlvbiBsYygpe30sCnp4OmZ1bmN0aW9uIHp4KCl7fSwKank6ZnVuY3Rpb24gankoYSxiLGMsZCl7dmFy
-IF89dGhpcwpfLmE9YQpfLmI9YgpfLmM9YwpfLmQ9ZH0sCkVxOmZ1bmN0aW9uIEVxKGEpe3RoaXMuYT1h
-fSwKa1k6ZnVuY3Rpb24ga1koYSl7dGhpcy5hPWF9LApONTpmdW5jdGlvbiBONShhKXt2YXIgXz10aGlz
-Cl8uYT0wCl8uZj1fLmU9Xy5kPV8uYz1fLmI9bnVsbApfLnI9MApfLiR0aT1hfSwKZGI6ZnVuY3Rpb24g
-ZGIoYSxiKXt2YXIgXz10aGlzCl8uYT1hCl8uYj1iCl8uZD1fLmM9bnVsbH0sCmk1OmZ1bmN0aW9uIGk1
-KGEsYil7dGhpcy5hPWEKdGhpcy4kdGk9Yn0sCk42OmZ1bmN0aW9uIE42KGEsYixjKXt2YXIgXz10aGlz
-Cl8uYT1hCl8uYj1iCl8uZD1fLmM9bnVsbApfLiR0aT1jfSwKcjpmdW5jdGlvbiByKGEpe3RoaXMuYT1h
-fSwKZEM6ZnVuY3Rpb24gZEMoYSl7dGhpcy5hPWF9LAp3TjpmdW5jdGlvbiB3TihhKXt0aGlzLmE9YX0s
-ClZSOmZ1bmN0aW9uIFZSKGEsYil7dmFyIF89dGhpcwpfLmE9YQpfLmI9YgpfLmQ9Xy5jPW51bGx9LApF
-SzpmdW5jdGlvbiBFSyhhKXt0aGlzLmI9YX0sCktXOmZ1bmN0aW9uIEtXKGEsYixjKXt0aGlzLmE9YQp0
-aGlzLmI9Ygp0aGlzLmM9Y30sClBiOmZ1bmN0aW9uIFBiKGEsYixjKXt2YXIgXz10aGlzCl8uYT1hCl8u
-Yj1iCl8uYz1jCl8uZD1udWxsfSwKdFE6ZnVuY3Rpb24gdFEoYSxiKXt0aGlzLmE9YQp0aGlzLmM9Yn0s
-Ck5GOmZ1bmN0aW9uIE5GKGEsYixjKXt0aGlzLmE9YQp0aGlzLmI9Ygp0aGlzLmM9Y30sClNkOmZ1bmN0
-aW9uIFNkKGEsYixjKXt2YXIgXz10aGlzCl8uYT1hCl8uYj1iCl8uYz1jCl8uZD1udWxsfSwKWEY6ZnVu
-Y3Rpb24oYSl7cmV0dXJuIGF9LApEUTpmdW5jdGlvbihhKXtyZXR1cm4gbmV3IEludDhBcnJheShhKX0s
-Cm9kOmZ1bmN0aW9uKGEsYixjKXtpZihhPj4+MCE9PWF8fGE+PWMpdGhyb3cgSC5iKEguSFkoYixhKSl9
-LApyTTpmdW5jdGlvbihhLGIsYyl7dmFyIHQKaWYoIShhPj4+MCE9PWEpKXQ9Yj4+PjAhPT1ifHxhPmJ8
-fGI+YwplbHNlIHQ9ITAKaWYodCl0aHJvdyBILmIoSC5hdShhLGIsYykpCnJldHVybiBifSwKZUg6ZnVu
-Y3Rpb24gZUgoKXt9LApMWjpmdW5jdGlvbiBMWigpe30sCkRnOmZ1bmN0aW9uIERnKCl7fSwKUGc6ZnVu
-Y3Rpb24gUGcoKXt9LAp4ajpmdW5jdGlvbiB4aigpe30sCmRFOmZ1bmN0aW9uIGRFKCl7fSwKWkE6ZnVu
-Y3Rpb24gWkEoKXt9LAp3ZjpmdW5jdGlvbiB3Zigpe30sClBxOmZ1bmN0aW9uIFBxKCl7fSwKZUU6ZnVu
-Y3Rpb24gZUUoKXt9LApWNjpmdW5jdGlvbiBWNigpe30sClJHOmZ1bmN0aW9uIFJHKCl7fSwKVlA6ZnVu
-Y3Rpb24gVlAoKXt9LApXQjpmdW5jdGlvbiBXQigpe30sClpHOmZ1bmN0aW9uIFpHKCl7fSwKY3o6ZnVu
-Y3Rpb24oYSxiKXt2YXIgdD1iLmMKcmV0dXJuIHQ9PW51bGw/Yi5jPUguQmMoYSxiLnosITApOnR9LAp4
-WjpmdW5jdGlvbihhLGIpe3ZhciB0PWIuYwpyZXR1cm4gdD09bnVsbD9iLmM9SC5RMihhLCJiOCIsW2Iu
-el0pOnR9LApRMTpmdW5jdGlvbihhKXt2YXIgdD1hLnkKaWYodD09PTZ8fHQ9PT03fHx0PT09OClyZXR1
-cm4gSC5RMShhLnopCnJldHVybiB0PT09MTF8fHQ9PT0xMn0sCm1EOmZ1bmN0aW9uKGEpe3JldHVybiBh
-LmN5fSwKTjA6ZnVuY3Rpb24oYSl7cmV0dXJuIEguRSh2LnR5cGVVbml2ZXJzZSxhLCExKX0sClBMOmZ1
-bmN0aW9uKGEsYixjLGEwKXt2YXIgdCxzLHIscSxwLG8sbixtLGwsayxqLGksaCxnLGYsZSxkPWIueQpz
-d2l0Y2goZCl7Y2FzZSA1OmNhc2UgMTpjYXNlIDI6Y2FzZSAzOmNhc2UgNDpyZXR1cm4gYgpjYXNlIDY6
-dD1iLnoKcz1ILlBMKGEsdCxjLGEwKQppZihzPT09dClyZXR1cm4gYgpyZXR1cm4gSC5TTyhhLHMsITAp
-CmNhc2UgNzp0PWIuegpzPUguUEwoYSx0LGMsYTApCmlmKHM9PT10KXJldHVybiBiCnJldHVybiBILkJj
-KGEscywhMCkKY2FzZSA4OnQ9Yi56CnM9SC5QTChhLHQsYyxhMCkKaWYocz09PXQpcmV0dXJuIGIKcmV0
-dXJuIEguTE4oYSxzLCEwKQpjYXNlIDk6cj1iLlEKcT1ILmJaKGEscixjLGEwKQppZihxPT09cilyZXR1
-cm4gYgpyZXR1cm4gSC5RMihhLGIueixxKQpjYXNlIDEwOnA9Yi56Cm89SC5QTChhLHAsYyxhMCkKbj1i
-LlEKbT1ILmJaKGEsbixjLGEwKQppZihvPT09cCYmbT09PW4pcmV0dXJuIGIKcmV0dXJuIEguYXAoYSxv
-LG0pCmNhc2UgMTE6bD1iLnoKaz1ILlBMKGEsbCxjLGEwKQpqPWIuUQppPUgucVQoYSxqLGMsYTApCmlm
-KGs9PT1sJiZpPT09ailyZXR1cm4gYgpyZXR1cm4gSC5OZihhLGssaSkKY2FzZSAxMjpoPWIuUQphMCs9
-aC5sZW5ndGgKZz1ILmJaKGEsaCxjLGEwKQpwPWIuegpvPUguUEwoYSxwLGMsYTApCmlmKGc9PT1oJiZv
-PT09cClyZXR1cm4gYgpyZXR1cm4gSC5EUyhhLG8sZywhMCkKY2FzZSAxMzpmPWIuegppZihmPGEwKXJl
-dHVybiBiCmU9Y1tmLWEwXQppZihlPT1udWxsKXJldHVybiBiCnJldHVybiBlCmRlZmF1bHQ6dGhyb3cg
-SC5iKFAuaFYoIkF0dGVtcHRlZCB0byBzdWJzdGl0dXRlIHVuZXhwZWN0ZWQgUlRJIGtpbmQgIitkKSl9
-fSwKYlo6ZnVuY3Rpb24oYSxiLGMsZCl7dmFyIHQscyxyLHEscD1iLmxlbmd0aCxvPVtdCmZvcih0PSEx
-LHM9MDtzPHA7KytzKXtyPWJbc10KcT1ILlBMKGEscixjLGQpCmlmKHEhPT1yKXQ9ITAKby5wdXNoKHEp
-fXJldHVybiB0P286Yn0sCnZPOmZ1bmN0aW9uKGEsYixjLGQpe3ZhciB0LHMscixxLHAsbz1iLmxlbmd0
-aCxuPVtdCmZvcih0PSExLHM9MDtzPG87cys9Mil7cj1iW3NdCnE9YltzKzFdCnA9SC5QTChhLHEsYyxk
-KQppZihwIT09cSl0PSEwCm4ucHVzaChyKQpuLnB1c2gocCl9cmV0dXJuIHQ/bjpifSwKcVQ6ZnVuY3Rp
-b24oYSxiLGMsZCl7dmFyIHQscz1iLmEscj1ILmJaKGEscyxjLGQpLHE9Yi5iLHA9SC5iWihhLHEsYyxk
-KSxvPWIuYyxuPUgudk8oYSxvLGMsZCkKaWYocj09PXMmJnA9PT1xJiZuPT09bylyZXR1cm4gYgp0PW5l
-dyBILkVUKCkKdC5hPXIKdC5iPXAKdC5jPW4KcmV0dXJuIHR9LApKUzpmdW5jdGlvbihhKXt2YXIgdD1h
-LiRTCmlmKHQhPW51bGwpe2lmKHR5cGVvZiB0PT0ibnVtYmVyIilyZXR1cm4gSC5CcCh0KQpyZXR1cm4g
-YS4kUygpfXJldHVybiBudWxsfSwKVWU6ZnVuY3Rpb24oYSxiKXt2YXIgdAppZihILlExKGIpKWlmKGEg
-aW5zdGFuY2VvZiBILlRwKXt0PUguSlMoYSkKaWYodCE9bnVsbClyZXR1cm4gdH1yZXR1cm4gSC5xKGEp
-fSwKcTpmdW5jdGlvbihhKXt2YXIgdAppZihhIGluc3RhbmNlb2YgUC5NaCl7dD1hLiR0aQpyZXR1cm4g
-dCE9bnVsbD90OkguVlUoYSl9aWYoQXJyYXkuaXNBcnJheShhKSlyZXR1cm4gSC50NihhKQpyZXR1cm4g
-SC5WVShKLmlhKGEpKX0sCnQ2OmZ1bmN0aW9uKGEpe3ZhciB0PWFbdi5hcnJheVJ0aV0scz11LmIKaWYo
-dD09bnVsbClyZXR1cm4gcwppZih0LmNvbnN0cnVjdG9yIT09cy5jb25zdHJ1Y3RvcilyZXR1cm4gcwpy
-ZXR1cm4gdH0sCkxoOmZ1bmN0aW9uKGEpe3ZhciB0PWEuJHRpCnJldHVybiB0IT1udWxsP3Q6SC5WVShh
-KX0sClZVOmZ1bmN0aW9uKGEpe3ZhciB0PWEuY29uc3RydWN0b3Iscz10LiRjY2FjaGUKaWYocyE9bnVs
-bClyZXR1cm4gcwpyZXR1cm4gSC5yOShhLHQpfSwKcjk6ZnVuY3Rpb24oYSxiKXt2YXIgdD1hIGluc3Rh
-bmNlb2YgSC5UcD9hLl9fcHJvdG9fXy5fX3Byb3RvX18uY29uc3RydWN0b3I6YixzPUguYWkodi50eXBl
-VW5pdmVyc2UsdC5uYW1lKQpiLiRjY2FjaGU9cwpyZXR1cm4gc30sCkJwOmZ1bmN0aW9uKGEpe3ZhciB0
-LHM9YSxyPXYudHlwZXMscT1yW3NdCmlmKHR5cGVvZiBxPT0ic3RyaW5nIil7dD1ILkUodi50eXBlVW5p
-dmVyc2UscSwhMSkKcltzXT10CnJldHVybiB0fXJldHVybiBxfSwKSko6ZnVuY3Rpb24oYSl7dmFyIHQ9
-dGhpcyxzPUguWU8scj11LksKaWYodD09PXIpe3M9SC5rZQp0LmE9SC5UaX1lbHNlIGlmKEguQTgodCl8
-fHQ9PT1yKXtzPUguSXcKdC5hPUguaG59ZWxzZSBpZih0PT09dS5wKXM9SC5vawplbHNlIGlmKHQ9PT11
-LmdSKXM9SC5LSAplbHNlIGlmKHQ9PT11LmRpKXM9SC5LSAplbHNlIGlmKHQ9PT11Lk4pcz1ILk1NCmVs
-c2UgaWYodD09PXUueSlzPUguclEKZWxzZSBpZih0Lnk9PT05KXtyPXQuegppZih0LlEuZXZlcnkoSC5j
-Yykpe3Qucj0iJGkiK3IKcz1ILnQ0fX10LmI9cwpyZXR1cm4gdC5iKGEpfSwKWU86ZnVuY3Rpb24oYSl7
-dmFyIHQ9dGhpcwpyZXR1cm4gSC5XZSh2LnR5cGVVbml2ZXJzZSxILlVlKGEsdCksbnVsbCx0LG51bGwp
-fSwKdDQ6ZnVuY3Rpb24oYSl7dmFyIHQ9dGhpcyxzPXQucgppZihhIGluc3RhbmNlb2YgUC5NaClyZXR1
-cm4hIWFbc10KcmV0dXJuISFKLmlhKGEpW3NdfSwKT3o6ZnVuY3Rpb24oYSl7dmFyIHQ9dGhpcwppZihh
-PT1udWxsKXJldHVybiBhCmVsc2UgaWYodC5iKGEpKXJldHVybiBhCnRocm93IEguYihILlpjKEgucChh
-LEguVWUoYSx0KSxILkoodCxudWxsKSkpKX0sCkRoOmZ1bmN0aW9uKGEsYixjLGQpe3ZhciB0PW51bGwK
-aWYoSC5XZSh2LnR5cGVVbml2ZXJzZSxhLHQsYix0KSlyZXR1cm4gYQp0aHJvdyBILmIoSC5aYygiVGhl
-IHR5cGUgYXJndW1lbnQgJyIrSC5kKEguSihhLHQpKSsiJyBpcyBub3QgYSBzdWJ0eXBlIG9mIHRoZSB0
-eXBlIHZhcmlhYmxlIGJvdW5kICciK0guZChILkooYix0KSkrIicgb2YgdHlwZSB2YXJpYWJsZSAnIitj
-KyInIGluICciK0guZChkKSsiJy4iKSl9LApwOmZ1bmN0aW9uKGEsYixjKXt2YXIgdD1QLmgoYSkscz1I
-LkooYj09bnVsbD9ILnEoYSk6YixudWxsKQpyZXR1cm4gdCsiOiB0eXBlICciK0guZChzKSsiJyBpcyBu
-b3QgYSBzdWJ0eXBlIG9mIHR5cGUgJyIrSC5kKGMpKyInIn0sClpjOmZ1bmN0aW9uKGEpe3JldHVybiBu
-ZXcgSC54KCJUeXBlRXJyb3I6ICIrYSl9LApCOmZ1bmN0aW9uKGEsYil7cmV0dXJuIG5ldyBILngoIlR5
-cGVFcnJvcjogIitILnAoYSxudWxsLGIpKX0sCmtlOmZ1bmN0aW9uKGEpe3JldHVybiEwfSwKVGk6ZnVu
-Y3Rpb24oYSl7cmV0dXJuIGF9LApJdzpmdW5jdGlvbihhKXtyZXR1cm4hMH0sCmhuOmZ1bmN0aW9uKGEp
-e3JldHVybiBhfSwKclE6ZnVuY3Rpb24oYSl7cmV0dXJuITA9PT1hfHwhMT09PWF9LApFOTpmdW5jdGlv
-bihhKXtpZighMD09PWF8fCExPT09YSlyZXR1cm4gYQppZihhPT1udWxsKXJldHVybiBhCnRocm93IEgu
-YihILkIoYSwiYm9vbCIpKX0sCmRqOmZ1bmN0aW9uKGEpe2lmKHR5cGVvZiBhPT0ibnVtYmVyIilyZXR1
-cm4gYQppZihhPT1udWxsKXJldHVybiBhCnRocm93IEguYihILkIoYSwiZG91YmxlIikpfSwKb2s6ZnVu
-Y3Rpb24oYSl7cmV0dXJuIHR5cGVvZiBhPT0ibnVtYmVyIiYmTWF0aC5mbG9vcihhKT09PWF9LApXWTpm
-dW5jdGlvbihhKXtpZih0eXBlb2YgYT09Im51bWJlciImJk1hdGguZmxvb3IoYSk9PT1hKXJldHVybiBh
-CmlmKGE9PW51bGwpcmV0dXJuIGEKdGhyb3cgSC5iKEguQihhLCJpbnQiKSl9LApLSDpmdW5jdGlvbihh
-KXtyZXR1cm4gdHlwZW9mIGE9PSJudW1iZXIifSwKdVU6ZnVuY3Rpb24oYSl7aWYodHlwZW9mIGE9PSJu
-dW1iZXIiKXJldHVybiBhCmlmKGE9PW51bGwpcmV0dXJuIGEKdGhyb3cgSC5iKEguQihhLCJudW0iKSl9
-LApNTTpmdW5jdGlvbihhKXtyZXR1cm4gdHlwZW9mIGE9PSJzdHJpbmcifSwKYzpmdW5jdGlvbihhKXtp
-Zih0eXBlb2YgYT09InN0cmluZyIpcmV0dXJuIGEKaWYoYT09bnVsbClyZXR1cm4gYQp0aHJvdyBILmIo
-SC5CKGEsIlN0cmluZyIpKX0sCnc6ZnVuY3Rpb24oYSxiKXt2YXIgdCxzLHIKZm9yKHQ9IiIscz0iIixy
-PTA7cjxhLmxlbmd0aDsrK3Iscz0iLCAiKXQrPUMueEIuaChzLEguSihhW3JdLGIpKQpyZXR1cm4gdH0s
-CmY6ZnVuY3Rpb24oYTEsYTIsYTMpe3ZhciB0LHMscixxLHAsbyxuLG0sbCxrLGosaSxoLGcsZixlLGQs
-YyxiLGEsYTA9IiwgIgppZihhMyE9bnVsbCl7dD1hMy5sZW5ndGgKaWYoYTI9PW51bGwpe2EyPUguVk0o
-W10sdS5zKQpzPW51bGx9ZWxzZSBzPWEyLmxlbmd0aApyPWEyLmxlbmd0aApmb3IocT10O3E+MDstLXEp
-Qy5ObS5pKGEyLCJUIisocitxKSkKZm9yKHA9dS5LLG89IjwiLG49IiIscT0wO3E8dDsrK3Esbj1hMCl7
-bys9bgptPWEyLmxlbmd0aApsPW0tMS1xCmlmKGw8MClyZXR1cm4gSC5rKGEyLGwpCm89Qy54Qi5oKG8s
-YTJbbF0pCms9YTNbcV0KaWYoIShILkE4KGspfHxrPT09cCkpbT0hKGs9PT1wKQplbHNlIG09ITEKaWYo
-bSlvKz1DLnhCLmgoIiBleHRlbmRzICIsSC5KKGssYTIpKX1vKz0iPiJ9ZWxzZXtvPSIiCnM9bnVsbH1w
-PWExLnoKaj1hMS5RCmk9ai5hCmg9aS5sZW5ndGgKZz1qLmIKZj1nLmxlbmd0aAplPWouYwpkPWUubGVu
-Z3RoCmM9SC5KKHAsYTIpCmZvcihiPSIiLGE9IiIscT0wO3E8aDsrK3EsYT1hMCliKz1DLnhCLmgoYSxI
-LkooaVtxXSxhMikpCmlmKGY+MCl7Yis9YSsiWyIKZm9yKGE9IiIscT0wO3E8ZjsrK3EsYT1hMCliKz1D
-LnhCLmgoYSxILkooZ1txXSxhMikpCmIrPSJdIn1pZihkPjApe2IrPWErInsiCmZvcihhPSIiLHE9MDtx
-PGQ7cSs9MixhPWEwKWIrPUMueEIuaChhLEguSihlW3ErMV0sYTIpKSsiICIrZVtxXQpiKz0ifSJ9aWYo
-cyE9bnVsbClhMi5sZW5ndGg9cwpyZXR1cm4gbysiKCIrYisiKSA9PiAiK0guZChjKX0sCko6ZnVuY3Rp
-b24oYSxiKXt2YXIgdCxzLHIscSxwLG8sbixtPWEueQppZihtPT09NSlyZXR1cm4iZXJhc2VkIgppZiht
-PT09MilyZXR1cm4iZHluYW1pYyIKaWYobT09PTMpcmV0dXJuInZvaWQiCmlmKG09PT0xKXJldHVybiJO
-ZXZlciIKaWYobT09PTQpcmV0dXJuImFueSIKaWYobT09PTYpe3Q9SC5KKGEueixiKQpyZXR1cm4gdH1p
-ZihtPT09Nyl7cz1hLnoKdD1ILkoocyxiKQpyPXMueQpyZXR1cm4gSi5tKHI9PT0xMXx8cj09PTEyP0Mu
-eEIuaCgiKCIsdCkrIikiOnQsIj8iKX1pZihtPT09OClyZXR1cm4iRnV0dXJlT3I8IitILmQoSC5KKGEu
-eixiKSkrIj4iCmlmKG09PT05KXtxPUguQyhhLnopCnA9YS5RCnJldHVybiBwLmxlbmd0aCE9PTA/cSso
-IjwiK0gudyhwLGIpKyI+Iik6cX1pZihtPT09MTEpcmV0dXJuIEguZihhLGIsbnVsbCkKaWYobT09PTEy
-KXJldHVybiBILmYoYS56LGIsYS5RKQppZihtPT09MTMpe289YS56Cm49Yi5sZW5ndGgKbz1uLTEtbwpp
-ZihvPDB8fG8+PW4pcmV0dXJuIEguayhiLG8pCnJldHVybiBiW29dfXJldHVybiI/In0sCkM6ZnVuY3Rp
-b24oYSl7dmFyIHQscz1ILkpnKGEpCmlmKHMhPW51bGwpcmV0dXJuIHMKdD0ibWluaWZpZWQ6IithCnJl
-dHVybiB0fSwKUW86ZnVuY3Rpb24oYSxiKXt2YXIgdD1hLnRSW2JdCmZvcig7dHlwZW9mIHQ9PSJzdHJp
-bmciOyl0PWEudFJbdF0KcmV0dXJuIHR9LAphaTpmdW5jdGlvbihhLGIpe3ZhciB0LHMscixxLHAsbz1h
-LmVULG49b1tiXQppZihuPT1udWxsKXJldHVybiBILkUoYSxiLCExKQplbHNlIGlmKHR5cGVvZiBuPT0i
-bnVtYmVyIil7dD1uCnM9SC5tWihhLDUsIiMiKQpyPVtdCmZvcihxPTA7cTx0OysrcSlyLnB1c2gocykK
-cD1ILlEyKGEsYixyKQpvW2JdPXAKcmV0dXJuIHB9ZWxzZSByZXR1cm4gbn0sCnhiOmZ1bmN0aW9uKGEs
-Yil7cmV0dXJuIEguSXgoYS50UixiKX0sCkZGOmZ1bmN0aW9uKGEsYil7cmV0dXJuIEguSXgoYS5lVCxi
-KX0sCkU6ZnVuY3Rpb24oYSxiLGMpe3ZhciB0LHM9YS5lQyxyPXMuZ2V0KGIpCmlmKHIhPW51bGwpcmV0
-dXJuIHIKdD1ILnooYSxudWxsLGIsYykKcy5zZXQoYix0KQpyZXR1cm4gdH0sCmNFOmZ1bmN0aW9uKGEs
-YixjKXt2YXIgdCxzLHI9Yi5jaAppZihyPT1udWxsKXI9Yi5jaD1uZXcgTWFwKCkKdD1yLmdldChjKQpp
-Zih0IT1udWxsKXJldHVybiB0CnM9SC56KGEsYixjLCEwKQpyLnNldChjLHMpCnJldHVybiBzfSwKdjU6
-ZnVuY3Rpb24oYSxiLGMpe3ZhciB0LHMscixxPWIuY3gKaWYocT09bnVsbClxPWIuY3g9bmV3IE1hcCgp
-CnQ9Yy5jeQpzPXEuZ2V0KHQpCmlmKHMhPW51bGwpcmV0dXJuIHMKcj1ILmFwKGEsYixjLnk9PT0xMD9j
-LlE6W2NdKQpxLnNldCh0LHIpCnJldHVybiByfSwKejpmdW5jdGlvbihhLGIsYyxkKXt2YXIgdD1ILmko
-SC5vKGEsYixjLGQpKQppZih0IT1udWxsKXJldHVybiB0CnRocm93IEguYihQLm4oJ19Vbml2ZXJzZS5f
-cGFyc2VSZWNpcGUoIicrSC5kKGMpKyciKScpKX0sCkJEOmZ1bmN0aW9uKGEsYil7Yi5hPUguT3oKYi5i
-PUguSkoKcmV0dXJuIGJ9LAptWjpmdW5jdGlvbihhLGIsYyl7dmFyIHQscyxyPWEuZUMuZ2V0KGMpCmlm
-KHIhPW51bGwpcmV0dXJuIHIKdD1uZXcgSC5KYyhudWxsLG51bGwpCnQueT1iCnQuY3k9YwpzPUguQkQo
-YSx0KQphLmVDLnNldChjLHMpCnJldHVybiBzfSwKU086ZnVuY3Rpb24oYSxiLGMpe3ZhciB0LHM9Yi5j
-eSsiKiIscj1hLmVDLmdldChzKQppZihyIT1udWxsKXJldHVybiByCnQ9SC5aNyhhLGIscyxjKQphLmVD
-LnNldChzLHQpCnJldHVybiB0fSwKWjc6ZnVuY3Rpb24oYSxiLGMsZCl7dmFyIHQscwppZihkKXt0PWIu
-eQppZihILkE4KGIpfHxiPT09dS5LfHxiPT09dS5QfHx0PT09N3x8dD09PTYpcmV0dXJuIGJ9cz1uZXcg
-SC5KYyhudWxsLG51bGwpCnMueT02CnMuej1iCnMuY3k9YwpyZXR1cm4gSC5CRChhLHMpfSwKQmM6ZnVu
-Y3Rpb24oYSxiLGMpe3ZhciB0LHM9Yi5jeSsiPyIscj1hLmVDLmdldChzKQppZihyIT1udWxsKXJldHVy
-biByCnQ9SC5sbChhLGIscyxjKQphLmVDLnNldChzLHQpCnJldHVybiB0fSwKbGw6ZnVuY3Rpb24oYSxi
-LGMsZCl7dmFyIHQscyxyLHEscAppZihkKXt0PWIueQppZighSC5BOChiKSlpZighKGI9PT11LlApKWlm
-KHQhPT03KXM9dD09PTgmJkgubFIoYi56KQplbHNlIHM9ITAKZWxzZSBzPSEwCmVsc2Ugcz0hMAppZihz
-KXJldHVybiBiCmVsc2UgaWYodD09PTEpcmV0dXJuIHUuUAplbHNlIGlmKHQ9PT02KXtyPWIuegpxPXIu
-eQppZihxPT09MSlyZXR1cm4gdS5QCmVsc2UgaWYocT09PTgmJkgubFIoci56KSlyZXR1cm4gcgplbHNl
-IHJldHVybiBILmN6KGEsYil9fXA9bmV3IEguSmMobnVsbCxudWxsKQpwLnk9NwpwLno9YgpwLmN5PWMK
-cmV0dXJuIEguQkQoYSxwKX0sCkxOOmZ1bmN0aW9uKGEsYixjKXt2YXIgdCxzPWIuY3krIi8iLHI9YS5l
-Qy5nZXQocykKaWYociE9bnVsbClyZXR1cm4gcgp0PUguZVYoYSxiLHMsYykKYS5lQy5zZXQocyx0KQpy
-ZXR1cm4gdH0sCmVWOmZ1bmN0aW9uKGEsYixjLGQpe3ZhciB0LHMKaWYoZCl7dD1iLnkKaWYoSC5BOChi
-KXx8Yj09PXUuS3x8Yj09PXUuSylyZXR1cm4gYgplbHNlIGlmKHQ9PT0xKXJldHVybiBILlEyKGEsImI4
-IixbYl0pCmVsc2UgaWYoYj09PXUuUClyZXR1cm4gdS5hUX1zPW5ldyBILkpjKG51bGwsbnVsbCkKcy55
-PTgKcy56PWIKcy5jeT1jCnJldHVybiBILkJEKGEscyl9LApIYzpmdW5jdGlvbihhLGIpe3ZhciB0LHMs
-cj0iIitiKyJeIixxPWEuZUMuZ2V0KHIpCmlmKHEhPW51bGwpcmV0dXJuIHEKdD1uZXcgSC5KYyhudWxs
-LG51bGwpCnQueT0xMwp0Lno9Ygp0LmN5PXIKcz1ILkJEKGEsdCkKYS5lQy5zZXQocixzKQpyZXR1cm4g
-c30sClV4OmZ1bmN0aW9uKGEpe3ZhciB0LHMscixxPWEubGVuZ3RoCmZvcih0PSIiLHM9IiIscj0wO3I8
-cTsrK3Iscz0iLCIpdCs9cythW3JdLmN5CnJldHVybiB0fSwKUzQ6ZnVuY3Rpb24oYSl7dmFyIHQscyxy
-LHEscCxvPWEubGVuZ3RoCmZvcih0PSIiLHM9IiIscj0wO3I8bztyKz0yLHM9IiwiKXtxPWFbcl0KcD1h
-W3IrMV0uY3kKdCs9cytxKyI6IitwfXJldHVybiB0fSwKUTI6ZnVuY3Rpb24oYSxiLGMpe3ZhciB0LHMs
-cixxPWIKaWYoYy5sZW5ndGghPT0wKXErPSI8IitILlV4KGMpKyI+Igp0PWEuZUMuZ2V0KHEpCmlmKHQh
-PW51bGwpcmV0dXJuIHQKcz1uZXcgSC5KYyhudWxsLG51bGwpCnMueT05CnMuej1iCnMuUT1jCmlmKGMu
-bGVuZ3RoPjApcy5jPWNbMF0Kcy5jeT1xCnI9SC5CRChhLHMpCmEuZUMuc2V0KHEscikKcmV0dXJuIHJ9
-LAphcDpmdW5jdGlvbihhLGIsYyl7dmFyIHQscyxyLHEscCxvCmlmKGIueT09PTEwKXt0PWIuegpzPWIu
-US5jb25jYXQoYyl9ZWxzZXtzPWMKdD1ifXI9dC5jeSsiOyIrKCI8IitILlV4KHMpKyI+IikKcT1hLmVD
-LmdldChyKQppZihxIT1udWxsKXJldHVybiBxCnA9bmV3IEguSmMobnVsbCxudWxsKQpwLnk9MTAKcC56
-PXQKcC5RPXMKcC5jeT1yCm89SC5CRChhLHApCmEuZUMuc2V0KHIsbykKcmV0dXJuIG99LApOZjpmdW5j
-dGlvbihhLGIsYyl7dmFyIHQscyxyLHEscD1iLmN5LG89Yy5hLG49by5sZW5ndGgsbT1jLmIsbD1tLmxl
-bmd0aCxrPWMuYyxqPWsubGVuZ3RoLGk9IigiK0guVXgobykKaWYobD4wKWkrPShuPjA/IiwiOiIiKSsi
-WyIrSC5VeChtKSsiXSIKaWYoaj4wKWkrPShuPjA/IiwiOiIiKSsieyIrSC5TNChrKSsifSIKdD1wKyhp
-KyIpIikKcz1hLmVDLmdldCh0KQppZihzIT1udWxsKXJldHVybiBzCnI9bmV3IEguSmMobnVsbCxudWxs
-KQpyLnk9MTEKci56PWIKci5RPWMKci5jeT10CnE9SC5CRChhLHIpCmEuZUMuc2V0KHQscSkKcmV0dXJu
-IHF9LApEUzpmdW5jdGlvbihhLGIsYyxkKXt2YXIgdCxzPWIuY3krIjwiK0guVXgoYykrIj4iLHI9YS5l
-Qy5nZXQocykKaWYociE9bnVsbClyZXR1cm4gcgp0PUguaHcoYSxiLGMscyxkKQphLmVDLnNldChzLHQp
-CnJldHVybiB0fSwKaHc6ZnVuY3Rpb24oYSxiLGMsZCxlKXt2YXIgdCxzLHIscSxwLG8sbixtCmlmKGUp
-e3Q9Yy5sZW5ndGgKcz1uZXcgQXJyYXkodCkKZm9yKHI9MCxxPTA7cTx0OysrcSl7cD1jW3FdCmlmKHAu
-eT09PTEpe3NbcV09cDsrK3J9fWlmKHI+MCl7bz1ILlBMKGEsYixzLDApCm49SC5iWihhLGMscywwKQpy
-ZXR1cm4gSC5EUyhhLG8sbixjIT09bil9fW09bmV3IEguSmMobnVsbCxudWxsKQptLnk9MTIKbS56PWIK
-bS5RPWMKbS5jeT1kCnJldHVybiBILkJEKGEsbSl9LApvOmZ1bmN0aW9uKGEsYixjLGQpe3JldHVybnt1
-OmEsZTpiLHI6YyxzOltdLHA6MCxuOmR9fSwKaTpmdW5jdGlvbihhKXt2YXIgdCxzLHIscSxwLG8sbixt
-LGwsayxqLGksaD1hLnIsZz1hLnMKZm9yKHQ9aC5sZW5ndGgscz0wO3M8dDspe3I9aC5jaGFyQ29kZUF0
-KHMpCmlmKHI+PTQ4JiZyPD01NylzPUguQWwocysxLHIsaCxnKQplbHNlIGlmKCgoKHJ8MzIpPj4+MCkt
-OTcmNjU1MzUpPDI2fHxyPT09OTV8fHI9PT0zNilzPUguUjgoYSxzLGgsZywhMSkKZWxzZSBpZihyPT09
-NDYpcz1ILlI4KGEscyxoLGcsITApCmVsc2V7KytzCnN3aXRjaChyKXtjYXNlIDQ0OmJyZWFrCmNhc2Ug
-NTg6YnJlYWsKY2FzZSA1OTpnLnB1c2goSC5LUShhLnUsYS5lLGcucG9wKCkpKQpicmVhawpjYXNlIDk0
-OmcucHVzaChILkhjKGEudSxnLnBvcCgpKSkKYnJlYWsKY2FzZSAzNTpnLnB1c2goSC5tWihhLnUsNSwi
-IyIpKQpicmVhawpjYXNlIDY0OmcucHVzaChILm1aKGEudSwyLCJAIikpCmJyZWFrCmNhc2UgMTI2Omcu
-cHVzaChILm1aKGEudSwzLCJ+IikpCmJyZWFrCmNhc2UgNjA6Zy5wdXNoKGEucCkKYS5wPWcubGVuZ3Ro
-CmJyZWFrCmNhc2UgNjI6cT1hLnUKcD1nLnNwbGljZShhLnApCkguclQoYS51LGEuZSxwKQphLnA9Zy5w
-b3AoKQpvPWcucG9wKCkKaWYodHlwZW9mIG89PSJzdHJpbmciKWcucHVzaChILlEyKHEsbyxwKSkKZWxz
-ZXtuPUguS1EocSxhLmUsbykKc3dpdGNoKG4ueSl7Y2FzZSAxMTpnLnB1c2goSC5EUyhxLG4scCxhLm4p
-KQpicmVhawpkZWZhdWx0OmcucHVzaChILmFwKHEsbixwKSkKYnJlYWt9fWJyZWFrCmNhc2UgMzg6SC5J
-MyhhLGcpCmJyZWFrCmNhc2UgNDI6bT1hLnUKZy5wdXNoKEguU08obSxILktRKG0sYS5lLGcucG9wKCkp
-LGEubikpCmJyZWFrCmNhc2UgNjM6bT1hLnUKZy5wdXNoKEguQmMobSxILktRKG0sYS5lLGcucG9wKCkp
-LGEubikpCmJyZWFrCmNhc2UgNDc6bT1hLnUKZy5wdXNoKEguTE4obSxILktRKG0sYS5lLGcucG9wKCkp
-LGEubikpCmJyZWFrCmNhc2UgNDA6Zy5wdXNoKGEucCkKYS5wPWcubGVuZ3RoCmJyZWFrCmNhc2UgNDE6
-cT1hLnUKbD1uZXcgSC5FVCgpCms9cS5zRUEKaj1xLnNFQQpvPWcucG9wKCkKaWYodHlwZW9mIG89PSJu
-dW1iZXIiKXN3aXRjaChvKXtjYXNlLTE6az1nLnBvcCgpCmJyZWFrCmNhc2UtMjpqPWcucG9wKCkKYnJl
-YWsKZGVmYXVsdDpnLnB1c2gobykKYnJlYWt9ZWxzZSBnLnB1c2gobykKcD1nLnNwbGljZShhLnApCkgu
-clQoYS51LGEuZSxwKQphLnA9Zy5wb3AoKQpsLmE9cApsLmI9awpsLmM9agpnLnB1c2goSC5OZihxLEgu
-S1EocSxhLmUsZy5wb3AoKSksbCkpCmJyZWFrCmNhc2UgOTE6Zy5wdXNoKGEucCkKYS5wPWcubGVuZ3Ro
-CmJyZWFrCmNhc2UgOTM6cD1nLnNwbGljZShhLnApCkguclQoYS51LGEuZSxwKQphLnA9Zy5wb3AoKQpn
-LnB1c2gocCkKZy5wdXNoKC0xKQpicmVhawpjYXNlIDEyMzpnLnB1c2goYS5wKQphLnA9Zy5sZW5ndGgK
-YnJlYWsKY2FzZSAxMjU6cD1nLnNwbGljZShhLnApCkguV1MoYS51LGEuZSxwKQphLnA9Zy5wb3AoKQpn
-LnB1c2gocCkKZy5wdXNoKC0yKQpicmVhawpkZWZhdWx0OnRocm93IkJhZCBjaGFyYWN0ZXIgIityfX19
-aT1nLnBvcCgpCnJldHVybiBILktRKGEudSxhLmUsaSl9LApBbDpmdW5jdGlvbihhLGIsYyxkKXt2YXIg
-dCxzLHI9Yi00OApmb3IodD1jLmxlbmd0aDthPHQ7KythKXtzPWMuY2hhckNvZGVBdChhKQppZighKHM+
-PTQ4JiZzPD01NykpYnJlYWsKcj1yKjEwKyhzLTQ4KX1kLnB1c2gocikKcmV0dXJuIGF9LApSODpmdW5j
-dGlvbihhLGIsYyxkLGUpe3ZhciB0LHMscixxLHAsbyxuPWIrMQpmb3IodD1jLmxlbmd0aDtuPHQ7Kytu
-KXtzPWMuY2hhckNvZGVBdChuKQppZihzPT09NDYpe2lmKGUpYnJlYWsKZT0hMH1lbHNle2lmKCEoKCgo
-c3wzMik+Pj4wKS05NyY2NTUzNSk8MjZ8fHM9PT05NXx8cz09PTM2KSlyPXM+PTQ4JiZzPD01NwplbHNl
-IHI9ITAKaWYoIXIpYnJlYWt9fXE9Yy5zdWJzdHJpbmcoYixuKQppZihlKXt0PWEudQpwPWEuZQppZihw
-Lnk9PT0xMClwPXAuegpvPUguUW8odCxwLnopW3FdCmlmKG89PW51bGwpSC52aCgnTm8gIicrcSsnIiBp
-biAiJytILm1EKHApKyciJykKZC5wdXNoKEguY0UodCxwLG8pKX1lbHNlIGQucHVzaChxKQpyZXR1cm4g
-bn0sCkkzOmZ1bmN0aW9uKGEsYil7dmFyIHQ9Yi5wb3AoKQppZigwPT09dCl7Yi5wdXNoKEgubVooYS51
-LDEsIjAmIikpCnJldHVybn1pZigxPT09dCl7Yi5wdXNoKEgubVooYS51LDQsIjEmIikpCnJldHVybn10
-aHJvdyBILmIoUC5oVigiVW5leHBlY3RlZCBleHRlbmRlZCBvcGVyYXRpb24gIitILmQodCkpKX0sCktR
-OmZ1bmN0aW9uKGEsYixjKXtpZih0eXBlb2YgYz09InN0cmluZyIpcmV0dXJuIEguUTIoYSxjLGEuc0VB
-KQplbHNlIGlmKHR5cGVvZiBjPT0ibnVtYmVyIilyZXR1cm4gSC5UVihhLGIsYykKZWxzZSByZXR1cm4g
-Y30sCnJUOmZ1bmN0aW9uKGEsYixjKXt2YXIgdCxzPWMubGVuZ3RoCmZvcih0PTA7dDxzOysrdCljW3Rd
-PUguS1EoYSxiLGNbdF0pfSwKV1M6ZnVuY3Rpb24oYSxiLGMpe3ZhciB0LHM9Yy5sZW5ndGgKZm9yKHQ9
-MTt0PHM7dCs9MiljW3RdPUguS1EoYSxiLGNbdF0pfSwKVFY6ZnVuY3Rpb24oYSxiLGMpe3ZhciB0LHMs
-cj1iLnkKaWYocj09PTEwKXtpZihjPT09MClyZXR1cm4gYi56CnQ9Yi5RCnM9dC5sZW5ndGgKaWYoYzw9
-cylyZXR1cm4gdFtjLTFdCmMtPXMKYj1iLnoKcj1iLnl9ZWxzZSBpZihjPT09MClyZXR1cm4gYgppZihy
-IT09OSl0aHJvdyBILmIoUC5oVigiSW5kZXhlZCBiYXNlIG11c3QgYmUgYW4gaW50ZXJmYWNlIHR5cGUi
-KSkKdD1iLlEKaWYoYzw9dC5sZW5ndGgpcmV0dXJuIHRbYy0xXQp0aHJvdyBILmIoUC5oVigiQmFkIGlu
-ZGV4ICIrYysiIGZvciAiK2IuWigwKSkpfSwKV2U6ZnVuY3Rpb24oYSxiLGMsZCxlKXt2YXIgdCxzLHIs
-cSxwLG8sbixtLGwsawppZihiPT09ZClyZXR1cm4hMAppZihILkE4KGQpfHxkPT09dS5LKXJldHVybiEw
-CnQ9Yi55CmlmKHQ9PT00KXJldHVybiEwCmlmKEguQTgoYikpcmV0dXJuITEKaWYoYj09PXUuUClyZXR1
-cm4hMApzPXQ9PT0xMwppZihzKWlmKEguV2UoYSxjW2Iuel0sYyxkLGUpKXJldHVybiEwCnI9ZC55Cmlm
-KHQ9PT02KXJldHVybiBILldlKGEsYi56LGMsZCxlKQppZihyPT09Nil7cT1kLnoKcmV0dXJuIEguV2Uo
-YSxiLGMscSxlKX1pZih0PT09OCl7aWYoIUguV2UoYSxiLnosYyxkLGUpKXJldHVybiExCnJldHVybiBI
-LldlKGEsSC54WihhLGIpLGMsZCxlKX1pZih0PT09Nyl7cT1ILldlKGEsYi56LGMsZCxlKQpyZXR1cm4g
-cX1pZihyPT09OCl7aWYoSC5XZShhLGIsYyxkLnosZSkpcmV0dXJuITAKcmV0dXJuIEguV2UoYSxiLGMs
-SC54WihhLGQpLGUpfWlmKHI9PT03KXtxPUguV2UoYSxiLGMsZC56LGUpCnJldHVybiBxfWlmKHMpcmV0
-dXJuITEKcT10IT09MTEKaWYoKCFxfHx0PT09MTIpJiZkPT09dS5aKXJldHVybiEwCmlmKHI9PT0xMil7
-aWYoYj09PXUuZylyZXR1cm4hMAppZih0IT09MTIpcmV0dXJuITEKcD1iLlEKbz1kLlEKbj1wLmxlbmd0
-aAppZihuIT09by5sZW5ndGgpcmV0dXJuITEKYz1jPT1udWxsP3A6cC5jb25jYXQoYykKZT1lPT1udWxs
-P286by5jb25jYXQoZSkKZm9yKHE9dS5hdixtPTA7bTxuOysrbSl7bD1wW21dCms9b1ttXQpxLmEobCkK
-cS5hKGspCmlmKCFILldlKGEsbCxjLGssZSl8fCFILldlKGEsayxlLGwsYykpcmV0dXJuITF9cmV0dXJu
-IEguYk8oYSxiLnosYyxkLnosZSl9aWYocj09PTExKXtpZihiPT09dS5nKXJldHVybiEwCmlmKHEpcmV0
-dXJuITEKcmV0dXJuIEguYk8oYSxiLGMsZCxlKX1pZih0PT09OSl7aWYociE9PTkpcmV0dXJuITEKcmV0
-dXJuIEgucEcoYSxiLGMsZCxlKX1yZXR1cm4hMX0sCmJPOmZ1bmN0aW9uKGEwLGExLGEyLGEzLGE0KXt2
-YXIgdCxzLHIscSxwLG8sbixtLGwsayxqLGksaCxnLGYsZSxkLGMsYixhCmlmKCFILldlKGEwLGExLnos
-YTIsYTMueixhNCkpcmV0dXJuITEKdD1hMS5RCnM9YTMuUQpyPXQuYQpxPXMuYQpwPXIubGVuZ3RoCm89
-cS5sZW5ndGgKaWYocD5vKXJldHVybiExCm49by1wCm09dC5iCmw9cy5iCms9bS5sZW5ndGgKaj1sLmxl
-bmd0aAppZihwK2s8bytqKXJldHVybiExCmZvcihpPTA7aTxwOysraSl7aD1yW2ldCmlmKCFILldlKGEw
-LHFbaV0sYTQsaCxhMikpcmV0dXJuITF9Zm9yKGk9MDtpPG47KytpKXtoPW1baV0KaWYoIUguV2UoYTAs
-cVtwK2ldLGE0LGgsYTIpKXJldHVybiExfWZvcihpPTA7aTxqOysraSl7aD1tW24raV0KaWYoIUguV2Uo
-YTAsbFtpXSxhNCxoLGEyKSlyZXR1cm4hMX1nPXQuYwpmPXMuYwplPWcubGVuZ3RoCmQ9Zi5sZW5ndGgK
-Zm9yKGk9MCxjPTA7YzxkO2MrPTIpe2I9ZltjXQpkb3tpZihpPj1lKXJldHVybiExCmE9Z1tpXQppKz0y
-fXdoaWxlKGE8YikKaWYoYjxhKXJldHVybiExCmg9Z1tpLTFdCmlmKCFILldlKGEwLGZbYysxXSxhNCxo
-LGEyKSlyZXR1cm4hMX1yZXR1cm4hMH0sCnBHOmZ1bmN0aW9uKGEsYixjLGQsZSl7dmFyIHQscyxyLHEs
-cCxvLG4sbSxsPWIueixrPWQuegppZihsPT09ayl7dD1iLlEKcz1kLlEKcj10Lmxlbmd0aApmb3IocT0w
-O3E8cjsrK3Epe3A9dFtxXQpvPXNbcV0KaWYoIUguV2UoYSxwLGMsbyxlKSlyZXR1cm4hMX1yZXR1cm4h
-MH1pZihkPT09dS5LKXJldHVybiEwCm49SC5RbyhhLGwpCmlmKG49PW51bGwpcmV0dXJuITEKbT1uW2td
-CmlmKG09PW51bGwpcmV0dXJuITEKcj1tLmxlbmd0aApzPWQuUQpmb3IocT0wO3E8cjsrK3EpaWYoIUgu
-V2UoYSxILmNFKGEsYixtW3FdKSxjLHNbcV0sZSkpcmV0dXJuITEKcmV0dXJuITB9LApsUjpmdW5jdGlv
-bihhKXt2YXIgdCxzPWEueQppZighKGE9PT11LlApKWlmKCFILkE4KGEpKWlmKHMhPT03KWlmKCEocz09
-PTYmJkgubFIoYS56KSkpdD1zPT09OCYmSC5sUihhLnopCmVsc2UgdD0hMAplbHNlIHQ9ITAKZWxzZSB0
-PSEwCmVsc2UgdD0hMApyZXR1cm4gdH0sCmNjOmZ1bmN0aW9uKGEpe3JldHVybiBILkE4KGEpfHxhPT09
-dS5LfSwKQTg6ZnVuY3Rpb24oYSl7dmFyIHQscz1hLnkscj1zCmlmKHIhPT0yKWlmKHIhPT0zKWlmKHIh
-PT00KWlmKHIhPT01KXt0PXUuSwppZighKGE9PT10KSlzPXM9PT03JiZhLno9PT10CmVsc2Ugcz0hMH1l
-bHNlIHM9ITAKZWxzZSBzPSEwCmVsc2Ugcz0hMAplbHNlIHM9ITAKcmV0dXJuIHN9LApJeDpmdW5jdGlv
-bihhLGIpe3ZhciB0LHMscj1PYmplY3Qua2V5cyhiKSxxPXIubGVuZ3RoCmZvcih0PTA7dDxxOysrdCl7
-cz1yW3RdCmFbc109YltzXX19LApKYzpmdW5jdGlvbiBKYyhhLGIpe3ZhciBfPXRoaXMKXy5hPWEKXy5i
-PWIKXy54PV8ucj1fLmM9bnVsbApfLnk9MApfLmN5PV8uY3g9Xy5jaD1fLlE9Xy56PW51bGx9LApFVDpm
-dW5jdGlvbiBFVCgpe3RoaXMuYz10aGlzLmI9dGhpcy5hPW51bGx9LAp1OTpmdW5jdGlvbiB1OSgpe30s
-Cng6ZnVuY3Rpb24geChhKXt0aGlzLmE9YX0sClI5OmZ1bmN0aW9uKGEpe3JldHVybiB1LmQuYihhKXx8
-dS5CLmIoYSl8fHUuZHouYihhKXx8dS5JLmIoYSl8fHUuQS5iKGEpfHx1Lmc0LmIoYSl8fHUuZzIuYihh
-KX0sCkpnOmZ1bmN0aW9uKGEpe3JldHVybiB2Lm1hbmdsZWRHbG9iYWxOYW1lc1thXX19LEo9ewpRdTpm
-dW5jdGlvbihhLGIsYyxkKXtyZXR1cm57aTphLHA6YixlOmMseDpkfX0sCmtzOmZ1bmN0aW9uKGEpe3Zh
-ciB0LHMscixxLHA9YVt2LmRpc3BhdGNoUHJvcGVydHlOYW1lXQppZihwPT1udWxsKWlmKCQuSz09bnVs
-bCl7SC5NKCkKcD1hW3YuZGlzcGF0Y2hQcm9wZXJ0eU5hbWVdfWlmKHAhPW51bGwpe3Q9cC5wCmlmKCEx
-PT09dClyZXR1cm4gcC5pCmlmKCEwPT09dClyZXR1cm4gYQpzPU9iamVjdC5nZXRQcm90b3R5cGVPZihh
-KQppZih0PT09cylyZXR1cm4gcC5pCmlmKHAuZT09PXMpdGhyb3cgSC5iKFAubigiUmV0dXJuIGludGVy
-Y2VwdG9yIGZvciAiK0guZCh0KGEscCkpKSl9cj1hLmNvbnN0cnVjdG9yCnE9cj09bnVsbD9udWxsOnJb
-JC5BKCldCmlmKHEhPW51bGwpcmV0dXJuIHEKcT1ILkcoYSkKaWYocSE9bnVsbClyZXR1cm4gcQppZih0
-eXBlb2YgYT09ImZ1bmN0aW9uIilyZXR1cm4gQy5ERwp0PU9iamVjdC5nZXRQcm90b3R5cGVPZihhKQpp
-Zih0PT1udWxsKXJldHVybiBDLlpRCmlmKHQ9PT1PYmplY3QucHJvdG90eXBlKXJldHVybiBDLlpRCmlm
-KHR5cGVvZiByPT0iZnVuY3Rpb24iKXtPYmplY3QuZGVmaW5lUHJvcGVydHkociwkLkEoKSx7dmFsdWU6
-Qy52QixlbnVtZXJhYmxlOmZhbHNlLHdyaXRhYmxlOnRydWUsY29uZmlndXJhYmxlOnRydWV9KQpyZXR1
-cm4gQy52Qn1yZXR1cm4gQy52Qn0sClFpOmZ1bmN0aW9uKGEsYil7aWYoYTwwfHxhPjQyOTQ5NjcyOTUp
-dGhyb3cgSC5iKFAuVEUoYSwwLDQyOTQ5NjcyOTUsImxlbmd0aCIsbnVsbCkpCnJldHVybiBKLnB5KG5l
-dyBBcnJheShhKSxiKX0sCnB5OmZ1bmN0aW9uKGEsYil7cmV0dXJuIEouRXAoSC5WTShhLGIuQygiamQ8
-MD4iKSkpfSwKRXA6ZnVuY3Rpb24oYSl7YS5maXhlZCRsZW5ndGg9QXJyYXkKcmV0dXJuIGF9LAp1bjpm
-dW5jdGlvbihhKXthLmZpeGVkJGxlbmd0aD1BcnJheQphLmltbXV0YWJsZSRsaXN0PUFycmF5CnJldHVy
-biBhfSwKR2E6ZnVuY3Rpb24oYSl7aWYoYTwyNTYpc3dpdGNoKGEpe2Nhc2UgOTpjYXNlIDEwOmNhc2Ug
-MTE6Y2FzZSAxMjpjYXNlIDEzOmNhc2UgMzI6Y2FzZSAxMzM6Y2FzZSAxNjA6cmV0dXJuITAKZGVmYXVs
-dDpyZXR1cm4hMX1zd2l0Y2goYSl7Y2FzZSA1NzYwOmNhc2UgODE5MjpjYXNlIDgxOTM6Y2FzZSA4MTk0
-OmNhc2UgODE5NTpjYXNlIDgxOTY6Y2FzZSA4MTk3OmNhc2UgODE5ODpjYXNlIDgxOTk6Y2FzZSA4MjAw
-OmNhc2UgODIwMTpjYXNlIDgyMDI6Y2FzZSA4MjMyOmNhc2UgODIzMzpjYXNlIDgyMzk6Y2FzZSA4Mjg3
-OmNhc2UgMTIyODg6Y2FzZSA2NTI3OTpyZXR1cm4hMApkZWZhdWx0OnJldHVybiExfX0sCm1tOmZ1bmN0
-aW9uKGEsYil7dmFyIHQscwpmb3IodD1hLmxlbmd0aDtiPHQ7KXtzPUMueEIuVyhhLGIpCmlmKHMhPT0z
-MiYmcyE9PTEzJiYhSi5HYShzKSlicmVhazsrK2J9cmV0dXJuIGJ9LApjMTpmdW5jdGlvbihhLGIpe3Zh
-ciB0LHMKZm9yKDtiPjA7Yj10KXt0PWItMQpzPUMueEIubShhLHQpCmlmKHMhPT0zMiYmcyE9PTEzJiYh
-Si5HYShzKSlicmVha31yZXR1cm4gYn0sClJFOmZ1bmN0aW9uKGEpe2lmKGE9PW51bGwpcmV0dXJuIGEK
-aWYodHlwZW9mIGEhPSJvYmplY3QiKXtpZih0eXBlb2YgYT09ImZ1bmN0aW9uIilyZXR1cm4gSi5jNS5w
-cm90b3R5cGUKcmV0dXJuIGF9aWYoYSBpbnN0YW5jZW9mIFAuTWgpcmV0dXJuIGEKcmV0dXJuIEoua3Mo
-YSl9LApUSjpmdW5jdGlvbihhKXtpZih0eXBlb2YgYT09Im51bWJlciIpcmV0dXJuIEoucUkucHJvdG90
-eXBlCmlmKHR5cGVvZiBhPT0ic3RyaW5nIilyZXR1cm4gSi5Eci5wcm90b3R5cGUKaWYoYT09bnVsbCly
-ZXR1cm4gYQppZihhLmNvbnN0cnVjdG9yPT1BcnJheSlyZXR1cm4gSi5qZC5wcm90b3R5cGUKaWYodHlw
-ZW9mIGEhPSJvYmplY3QiKXtpZih0eXBlb2YgYT09ImZ1bmN0aW9uIilyZXR1cm4gSi5jNS5wcm90b3R5
-cGUKcmV0dXJuIGF9aWYoYSBpbnN0YW5jZW9mIFAuTWgpcmV0dXJuIGEKcmV0dXJuIEoua3MoYSl9LApV
-NjpmdW5jdGlvbihhKXtpZih0eXBlb2YgYT09InN0cmluZyIpcmV0dXJuIEouRHIucHJvdG90eXBlCmlm
-KGE9PW51bGwpcmV0dXJuIGEKaWYoYS5jb25zdHJ1Y3Rvcj09QXJyYXkpcmV0dXJuIEouamQucHJvdG90
-eXBlCmlmKHR5cGVvZiBhIT0ib2JqZWN0Iil7aWYodHlwZW9mIGE9PSJmdW5jdGlvbiIpcmV0dXJuIEou
-YzUucHJvdG90eXBlCnJldHVybiBhfWlmKGEgaW5zdGFuY2VvZiBQLk1oKXJldHVybiBhCnJldHVybiBK
-LmtzKGEpfSwKaWE6ZnVuY3Rpb24oYSl7aWYodHlwZW9mIGE9PSJudW1iZXIiKXtpZihNYXRoLmZsb29y
-KGEpPT1hKXJldHVybiBKLmJVLnByb3RvdHlwZQpyZXR1cm4gSi5WQS5wcm90b3R5cGV9aWYodHlwZW9m
-IGE9PSJzdHJpbmciKXJldHVybiBKLkRyLnByb3RvdHlwZQppZihhPT1udWxsKXJldHVybiBKLllFLnBy
-b3RvdHlwZQppZih0eXBlb2YgYT09ImJvb2xlYW4iKXJldHVybiBKLnlFLnByb3RvdHlwZQppZihhLmNv
-bnN0cnVjdG9yPT1BcnJheSlyZXR1cm4gSi5qZC5wcm90b3R5cGUKaWYodHlwZW9mIGEhPSJvYmplY3Qi
-KXtpZih0eXBlb2YgYT09ImZ1bmN0aW9uIilyZXR1cm4gSi5jNS5wcm90b3R5cGUKcmV0dXJuIGF9aWYo
-YSBpbnN0YW5jZW9mIFAuTWgpcmV0dXJuIGEKcmV0dXJuIEoua3MoYSl9LApyWTpmdW5jdGlvbihhKXtp
-Zih0eXBlb2YgYT09InN0cmluZyIpcmV0dXJuIEouRHIucHJvdG90eXBlCmlmKGE9PW51bGwpcmV0dXJu
-IGEKaWYoIShhIGluc3RhbmNlb2YgUC5NaCkpcmV0dXJuIEoua2QucHJvdG90eXBlCnJldHVybiBhfSwK
-dzE6ZnVuY3Rpb24oYSl7aWYoYT09bnVsbClyZXR1cm4gYQppZihhLmNvbnN0cnVjdG9yPT1BcnJheSly
-ZXR1cm4gSi5qZC5wcm90b3R5cGUKaWYodHlwZW9mIGEhPSJvYmplY3QiKXtpZih0eXBlb2YgYT09ImZ1
-bmN0aW9uIilyZXR1cm4gSi5jNS5wcm90b3R5cGUKcmV0dXJuIGF9aWYoYSBpbnN0YW5jZW9mIFAuTWgp
-cmV0dXJuIGEKcmV0dXJuIEoua3MoYSl9LApBYzpmdW5jdGlvbihhKXtyZXR1cm4gSi5pYShhKS5aKGEp
-fSwKQ006ZnVuY3Rpb24oYSxiLGMsZCl7cmV0dXJuIEouUkUoYSkuZHUoYSxiLGMsZCl9LApDaTpmdW5j
-dGlvbihhKXtyZXR1cm4gSi5VNihhKS5nbDAoYSl9LApGTDpmdW5jdGlvbihhLGIpe3JldHVybiBKLnJZ
-KGEpLmRkKGEsYil9LApHQTpmdW5jdGlvbihhLGIpe3JldHVybiBKLncxKGEpLkUoYSxiKX0sCkdyOmZ1
-bmN0aW9uKGEpe3JldHVybiBKLlJFKGEpLmdtVyhhKX0sCkg6ZnVuY3Rpb24oYSl7cmV0dXJuIEouVTYo
-YSkuZ0EoYSl9LApJVDpmdW5jdGlvbihhKXtyZXR1cm4gSi53MShhKS5na3ooYSl9LApKeTpmdW5jdGlv
-bihhLGIpe3JldHVybiBKLmlhKGEpLmU3KGEsYil9LApLVjpmdW5jdGlvbihhLGIpe3JldHVybiBKLnJZ
-KGEpLkcoYSxiKX0sCkx0OmZ1bmN0aW9uKGEpe3JldHVybiBKLlJFKGEpLndnKGEpfSwKTTE6ZnVuY3Rp
-b24oYSxiLGMpe3JldHVybiBKLncxKGEpLkUyKGEsYixjKX0sCk11OmZ1bmN0aW9uKGEsYil7cmV0dXJu
-IEouUkUoYSkuc1AoYSxiKX0sClF6OmZ1bmN0aW9uKGEsYil7cmV0dXJuIEouclkoYSkuVyhhLGIpfSwK
-Uk06ZnVuY3Rpb24oYSxiKXtpZihhPT1udWxsKXJldHVybiBiPT1udWxsCmlmKHR5cGVvZiBhIT0ib2Jq
-ZWN0IilyZXR1cm4gYiE9bnVsbCYmYT09PWIKcmV0dXJuIEouaWEoYSkuRE4oYSxiKX0sClQwOmZ1bmN0
-aW9uKGEpe3JldHVybiBKLnJZKGEpLmJTKGEpfSwKYTY6ZnVuY3Rpb24oYSxiKXtyZXR1cm4gSi5yWShh
-KS5tKGEsYil9LApiVDpmdW5jdGlvbihhKXtyZXR1cm4gSi5SRShhKS5ENChhKX0sCmNIOmZ1bmN0aW9u
-KGEpe3JldHVybiBKLnJZKGEpLmhjKGEpfSwKZFI6ZnVuY3Rpb24oYSl7cmV0dXJuIEouUkUoYSkuZ1Ao
-YSl9LApkWjpmdW5jdGlvbihhLGIsYyxkKXtyZXR1cm4gSi5SRShhKS5PbihhLGIsYyxkKX0sCmRnOmZ1
-bmN0aW9uKGEsYixjLGQpe3JldHVybiBKLnJZKGEpLmk3KGEsYixjLGQpfSwKZGg6ZnVuY3Rpb24oYSl7
-cmV0dXJuIEouUkUoYSkuRkYoYSl9LApoZjpmdW5jdGlvbihhKXtyZXR1cm4gSi5pYShhKS5naU8oYSl9
-LAppZzpmdW5jdGlvbihhKXtyZXR1cm4gSi5SRShhKS5nUWcoYSl9LApsNTpmdW5jdGlvbihhLGIpe3Jl
-dHVybiBKLlJFKGEpLnNoZihhLGIpfSwKbGQ6ZnVuY3Rpb24oYSxiLGMpe3JldHVybiBKLnJZKGEpLk5q
-KGEsYixjKX0sCm06ZnVuY3Rpb24oYSxiKXtpZih0eXBlb2YgYT09Im51bWJlciImJnR5cGVvZiBiPT0i
-bnVtYmVyIilyZXR1cm4gYStiCnJldHVybiBKLlRKKGEpLmgoYSxiKX0sCnEwOmZ1bmN0aW9uKGEsYixj
-KXtyZXR1cm4gSi5yWShhKS5RaShhLGIsYyl9LApxRjpmdW5jdGlvbihhKXtyZXR1cm4gSi5SRShhKS5n
-VmwoYSl9LAp0SDpmdW5jdGlvbihhLGIsYyl7cmV0dXJuIEouUkUoYSkucGsoYSxiLGMpfSwKeDk6ZnVu
-Y3Rpb24oYSxiKXtpZih0eXBlb2YgYj09PSJudW1iZXIiKWlmKGEuY29uc3RydWN0b3I9PUFycmF5fHx0
-eXBlb2YgYT09InN0cmluZyJ8fEgud1YoYSxhW3YuZGlzcGF0Y2hQcm9wZXJ0eU5hbWVdKSlpZihiPj4+
-MD09PWImJmI8YS5sZW5ndGgpcmV0dXJuIGFbYl0KcmV0dXJuIEouVTYoYSkucShhLGIpfSwKemw6ZnVu
-Y3Rpb24oYSxiKXtyZXR1cm4gSi5VNihhKS50ZyhhLGIpfSwKdkI6ZnVuY3Rpb24gdkIoKXt9LAp5RTpm
-dW5jdGlvbiB5RSgpe30sCllFOmZ1bmN0aW9uIFlFKCl7fSwKTUY6ZnVuY3Rpb24gTUYoKXt9LAppQzpm
-dW5jdGlvbiBpQygpe30sCmtkOmZ1bmN0aW9uIGtkKCl7fSwKYzU6ZnVuY3Rpb24gYzUoKXt9LApqZDpm
-dW5jdGlvbiBqZChhKXt0aGlzLiR0aT1hfSwKUG86ZnVuY3Rpb24gUG8oYSl7dGhpcy4kdGk9YX0sCm0x
-OmZ1bmN0aW9uIG0xKGEsYixjKXt2YXIgXz10aGlzCl8uYT1hCl8uYj1iCl8uYz0wCl8uZD1udWxsCl8u
-JHRpPWN9LApxSTpmdW5jdGlvbiBxSSgpe30sCmJVOmZ1bmN0aW9uIGJVKCl7fSwKVkE6ZnVuY3Rpb24g
-VkEoKXt9LApEcjpmdW5jdGlvbiBEcigpe319LFA9ewpPajpmdW5jdGlvbigpe3ZhciB0LHMscj17fQpp
-ZihzZWxmLnNjaGVkdWxlSW1tZWRpYXRlIT1udWxsKXJldHVybiBQLkVYKCkKaWYoc2VsZi5NdXRhdGlv
-bk9ic2VydmVyIT1udWxsJiZzZWxmLmRvY3VtZW50IT1udWxsKXt0PXNlbGYuZG9jdW1lbnQuY3JlYXRl
-RWxlbWVudCgiZGl2IikKcz1zZWxmLmRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoInNwYW4iKQpyLmE9bnVs
-bApuZXcgc2VsZi5NdXRhdGlvbk9ic2VydmVyKEgudFIobmV3IFAudGgociksMSkpLm9ic2VydmUodCx7
-Y2hpbGRMaXN0OnRydWV9KQpyZXR1cm4gbmV3IFAuaGEocix0LHMpfWVsc2UgaWYoc2VsZi5zZXRJbW1l
-ZGlhdGUhPW51bGwpcmV0dXJuIFAueXQoKQpyZXR1cm4gUC5xVygpfSwKWlY6ZnVuY3Rpb24oYSl7c2Vs
-Zi5zY2hlZHVsZUltbWVkaWF0ZShILnRSKG5ldyBQLlZzKHUuTS5hKGEpKSwwKSl9LApvQTpmdW5jdGlv
-bihhKXtzZWxmLnNldEltbWVkaWF0ZShILnRSKG5ldyBQLkZ0KHUuTS5hKGEpKSwwKSl9LApCejpmdW5j
-dGlvbihhKXt1Lk0uYShhKQpQLlFOKDAsYSl9LApRTjpmdW5jdGlvbihhLGIpe3ZhciB0PW5ldyBQLlcz
-KCkKdC5DWShhLGIpCnJldHVybiB0fSwKRlg6ZnVuY3Rpb24oYSl7cmV0dXJuIG5ldyBQLmloKG5ldyBQ
-LnZzKCQuWDMsYS5DKCJ2czwwPiIpKSxhLkMoImloPDA+IikpfSwKREk6ZnVuY3Rpb24oYSxiKXthLiQy
-KDAsbnVsbCkKYi5iPSEwCnJldHVybiBiLmF9LApqUTpmdW5jdGlvbihhLGIpe1AuSmUoYSxiKX0sCnlD
-OmZ1bmN0aW9uKGEsYil7Yi5hTSgwLGEpfSwKZjM6ZnVuY3Rpb24oYSxiKXtiLncwKEguUnUoYSksSC50
-cyhhKSl9LApKZTpmdW5jdGlvbihhLGIpe3ZhciB0LHMscj1uZXcgUC5XTShiKSxxPW5ldyBQLlNYKGIp
-CmlmKGEgaW5zdGFuY2VvZiBQLnZzKWEuUWQocixxLHUueikKZWxzZXt0PXUuegppZih1LmMuYihhKSlh
-LlNxKHIscSx0KQplbHNle3M9bmV3IFAudnMoJC5YMyx1Ll8pCnMuYT00CnMuYz1hCnMuUWQocixxLHQp
-fX19LApsejpmdW5jdGlvbihhKXt2YXIgdD1mdW5jdGlvbihiLGMpe3JldHVybiBmdW5jdGlvbihkLGUp
-e3doaWxlKHRydWUpdHJ5e2IoZCxlKQpicmVha31jYXRjaChzKXtlPXMKZD1jfX19KGEsMSkKcmV0dXJu
-ICQuWDMuTGoobmV3IFAuR3ModCksdS5QLHUucCx1LnopfSwKR1E6ZnVuY3Rpb24oYSl7cmV0dXJuIG5l
-dyBQLkZ5KGEsMSl9LApUaDpmdW5jdGlvbigpe3JldHVybiBDLndRfSwKWW06ZnVuY3Rpb24oYSl7cmV0
-dXJuIG5ldyBQLkZ5KGEsMyl9LApsMDpmdW5jdGlvbihhLGIpe3JldHVybiBuZXcgUC5xNChhLGIuQygi
-cTQ8MD4iKSl9LAprMzpmdW5jdGlvbihhLGIpe3ZhciB0LHMscgpiLmE9MQp0cnl7YS5TcShuZXcgUC5w
-VihiKSxuZXcgUC5VNyhiKSx1LlApfWNhdGNoKHIpe3Q9SC5SdShyKQpzPUgudHMocikKUC5yYihuZXcg
-UC52cihiLHQscykpfX0sCkE5OmZ1bmN0aW9uKGEsYil7dmFyIHQscyxyCmZvcih0PXUuXztzPWEuYSxz
-PT09MjspYT10LmEoYS5jKQppZihzPj00KXtyPWIuYWgoKQpiLmE9YS5hCmIuYz1hLmMKUC5IWihiLHIp
-fWVsc2V7cj11LnguYShiLmMpCmIuYT0yCmIuYz1hCmEualEocil9fSwKSFo6ZnVuY3Rpb24oYSxiKXt2
-YXIgdCxzLHIscSxwLG8sbixtLGwsayxqLGksaCxnLGYsZT1udWxsLGQ9e30sYz1kLmE9YQpmb3IodD11
-Lm4scz11Lngscj11LmM7ITA7KXtxPXt9CnA9Yy5hPT09OAppZihiPT1udWxsKXtpZihwKXtvPXQuYShj
-LmMpClAuTDIoZSxlLGMuYixvLmEsby5iKX1yZXR1cm59Zm9yKDtuPWIuYSxuIT1udWxsO2I9bil7Yi5h
-PW51bGwKUC5IWihkLmEsYil9Yz1kLmEKbT1jLmMKcS5hPXAKcS5iPW0KbD0hcAppZihsKXtrPWIuYwpr
-PShrJjEpIT09MHx8KGsmMTUpPT09OH1lbHNlIGs9ITAKaWYoayl7az1iLmIKaj1rLmIKaWYocCl7aT1j
-LmI9PT1qCmk9IShpfHxpKX1lbHNlIGk9ITEKaWYoaSl7dC5hKG0pClAuTDIoZSxlLGMuYixtLmEsbS5i
-KQpyZXR1cm59aD0kLlgzCmlmKGghPT1qKSQuWDM9agplbHNlIGg9ZQpjPWIuYwppZigoYyYxNSk9PT04
-KW5ldyBQLlJUKGQscSxiLHApLiQwKCkKZWxzZSBpZihsKXtpZigoYyYxKSE9PTApbmV3IFAucnEocSxi
-LG0pLiQwKCl9ZWxzZSBpZigoYyYyKSE9PTApbmV3IFAuUlcoZCxxLGIpLiQwKCkKaWYoaCE9bnVsbCkk
-LlgzPWgKYz1xLmIKaWYoci5iKGMpKXtpZihjLmE+PTQpe2c9cy5hKGsuYykKay5jPW51bGwKYj1rLk44
-KGcpCmsuYT1jLmEKay5jPWMuYwpkLmE9Ywpjb250aW51ZX1lbHNlIFAuQTkoYyxrKQpyZXR1cm59fWY9
-Yi5iCmc9cy5hKGYuYykKZi5jPW51bGwKYj1mLk44KGcpCmM9cS5hCmw9cS5iCmlmKCFjKXtmLiR0aS5j
-LmEobCkKZi5hPTQKZi5jPWx9ZWxzZXt0LmEobCkKZi5hPTgKZi5jPWx9ZC5hPWYKYz1mfX0sClZIOmZ1
-bmN0aW9uKGEsYil7dmFyIHQKaWYodS5hZy5iKGEpKXJldHVybiBiLkxqKGEsdS56LHUuSyx1LmwpCnQ9
-dS5iSQppZih0LmIoYSkpcmV0dXJuIHQuYShhKQp0aHJvdyBILmIoUC5MMyhhLCJvbkVycm9yIiwiRXJy
-b3IgaGFuZGxlciBtdXN0IGFjY2VwdCBvbmUgT2JqZWN0IG9yIG9uZSBPYmplY3QgYW5kIGEgU3RhY2tU
-cmFjZSBhcyBhcmd1bWVudHMsIGFuZCByZXR1cm4gYSBhIHZhbGlkIHJlc3VsdCIpKX0sCnB1OmZ1bmN0
-aW9uKCl7dmFyIHQscwpmb3IoO3Q9JC5TNix0IT1udWxsOyl7JC5tZz1udWxsCnM9dC5iCiQuUzY9cwpp
-ZihzPT1udWxsKSQuazg9bnVsbAp0LmEuJDAoKX19LAplTjpmdW5jdGlvbigpeyQuVUQ9ITAKdHJ5e1Au
-cHUoKX1maW5hbGx5eyQubWc9bnVsbAokLlVEPSExCmlmKCQuUzYhPW51bGwpJC51dCgpLiQxKFAuVjko
-KSl9fSwKZVc6ZnVuY3Rpb24oYSl7dmFyIHQ9bmV3IFAuT00oYSkKaWYoJC5TNj09bnVsbCl7JC5TNj0k
-Lms4PXQKaWYoISQuVUQpJC51dCgpLiQxKFAuVjkoKSl9ZWxzZSAkLms4PSQuazguYj10fSwKclI6ZnVu
-Y3Rpb24oYSl7dmFyIHQscyxyPSQuUzYKaWYocj09bnVsbCl7UC5lVyhhKQokLm1nPSQuazgKcmV0dXJu
-fXQ9bmV3IFAuT00oYSkKcz0kLm1nCmlmKHM9PW51bGwpe3QuYj1yCiQuUzY9JC5tZz10fWVsc2V7dC5i
-PXMuYgokLm1nPXMuYj10CmlmKHQuYj09bnVsbCkkLms4PXR9fSwKcmI6ZnVuY3Rpb24oYSl7dmFyIHQ9
-bnVsbCxzPSQuWDMKaWYoQy5OVT09PXMpe1AuVGsodCx0LEMuTlUsYSkKcmV0dXJufVAuVGsodCx0LHMs
-dS5NLmEocy5HWShhKSkpfSwKUXc6ZnVuY3Rpb24oYSxiKXtpZihhPT1udWxsKUgudmgoUC5FZSgic3Ry
-ZWFtIikpCnJldHVybiBuZXcgUC54SShiLkMoInhJPDA+IikpfSwKVGw6ZnVuY3Rpb24oYSxiKXt2YXIg
-dD1iPT1udWxsP1AudjAoYSk6YgpQLlVJKGEsImVycm9yIix1LkspCnJldHVybiBuZXcgUC5PSChhLHQp
-fSwKdjA6ZnVuY3Rpb24oYSl7dmFyIHQKaWYodS5XLmIoYSkpe3Q9YS5nSUkoKQppZih0IT1udWxsKXJl
-dHVybiB0fXJldHVybiBDLnBkfSwKTDI6ZnVuY3Rpb24oYSxiLGMsZCxlKXt2YXIgdD17fQp0LmE9ZAp0
-LmI9ZQppZihkPT1udWxsKXt0LmE9bmV3IFAuQVQoITEsbnVsbCwiZXJyb3IiLCJNdXN0IG5vdCBiZSBu
-dWxsIikKdC5iPVAuWmIoKX1QLnJSKG5ldyBQLnBLKHQpKX0sClQ4OmZ1bmN0aW9uKGEsYixjLGQsZSl7
-dmFyIHQscz0kLlgzCmlmKHM9PT1jKXJldHVybiBkLiQwKCkKJC5YMz1jCnQ9cwp0cnl7cz1kLiQwKCkK
-cmV0dXJuIHN9ZmluYWxseXskLlgzPXR9fSwKeXY6ZnVuY3Rpb24oYSxiLGMsZCxlLGYsZyl7dmFyIHQs
-cz0kLlgzCmlmKHM9PT1jKXJldHVybiBkLiQxKGUpCiQuWDM9Ywp0PXMKdHJ5e3M9ZC4kMShlKQpyZXR1
-cm4gc31maW5hbGx5eyQuWDM9dH19LApReDpmdW5jdGlvbihhLGIsYyxkLGUsZixnLGgsaSl7dmFyIHQs
-cz0kLlgzCmlmKHM9PT1jKXJldHVybiBkLiQyKGUsZikKJC5YMz1jCnQ9cwp0cnl7cz1kLiQyKGUsZikK
-cmV0dXJuIHN9ZmluYWxseXskLlgzPXR9fSwKVGs6ZnVuY3Rpb24oYSxiLGMsZCl7dmFyIHQKdS5NLmEo
-ZCkKdD1DLk5VIT09YwppZih0KWQ9ISghdHx8ITEpP2MuR1koZCk6Yy5SVChkLHUuSCkKUC5lVyhkKX0s
-CnRoOmZ1bmN0aW9uIHRoKGEpe3RoaXMuYT1hfSwKaGE6ZnVuY3Rpb24gaGEoYSxiLGMpe3RoaXMuYT1h
-CnRoaXMuYj1iCnRoaXMuYz1jfSwKVnM6ZnVuY3Rpb24gVnMoYSl7dGhpcy5hPWF9LApGdDpmdW5jdGlv
-biBGdChhKXt0aGlzLmE9YX0sClczOmZ1bmN0aW9uIFczKCl7fSwKeUg6ZnVuY3Rpb24geUgoYSxiKXt0
-aGlzLmE9YQp0aGlzLmI9Yn0sCmloOmZ1bmN0aW9uIGloKGEsYil7dGhpcy5hPWEKdGhpcy5iPSExCnRo
-aXMuJHRpPWJ9LApXTTpmdW5jdGlvbiBXTShhKXt0aGlzLmE9YX0sClNYOmZ1bmN0aW9uIFNYKGEpe3Ro
-aXMuYT1hfSwKR3M6ZnVuY3Rpb24gR3MoYSl7dGhpcy5hPWF9LApGeTpmdW5jdGlvbiBGeShhLGIpe3Ro
-aXMuYT1hCnRoaXMuYj1ifSwKR1Y6ZnVuY3Rpb24gR1YoYSxiKXt2YXIgXz10aGlzCl8uYT1hCl8uZD1f
-LmM9Xy5iPW51bGwKXy4kdGk9Yn0sCnE0OmZ1bmN0aW9uIHE0KGEsYil7dGhpcy5hPWEKdGhpcy4kdGk9
-Yn0sCmI4OmZ1bmN0aW9uIGI4KCl7fSwKUGY6ZnVuY3Rpb24gUGYoKXt9LApaZjpmdW5jdGlvbiBaZihh
-LGIpe3RoaXMuYT1hCnRoaXMuJHRpPWJ9LApGZTpmdW5jdGlvbiBGZShhLGIsYyxkLGUpe3ZhciBfPXRo
-aXMKXy5hPW51bGwKXy5iPWEKXy5jPWIKXy5kPWMKXy5lPWQKXy4kdGk9ZX0sCnZzOmZ1bmN0aW9uIHZz
-KGEsYil7dmFyIF89dGhpcwpfLmE9MApfLmI9YQpfLmM9bnVsbApfLiR0aT1ifSwKZGE6ZnVuY3Rpb24g
-ZGEoYSxiKXt0aGlzLmE9YQp0aGlzLmI9Yn0sCm9ROmZ1bmN0aW9uIG9RKGEsYil7dGhpcy5hPWEKdGhp
-cy5iPWJ9LApwVjpmdW5jdGlvbiBwVihhKXt0aGlzLmE9YX0sClU3OmZ1bmN0aW9uIFU3KGEpe3RoaXMu
-YT1hfSwKdnI6ZnVuY3Rpb24gdnIoYSxiLGMpe3RoaXMuYT1hCnRoaXMuYj1iCnRoaXMuYz1jfSwKckg6
-ZnVuY3Rpb24gckgoYSxiKXt0aGlzLmE9YQp0aGlzLmI9Yn0sCktGOmZ1bmN0aW9uIEtGKGEsYil7dGhp
-cy5hPWEKdGhpcy5iPWJ9LApaTDpmdW5jdGlvbiBaTChhLGIsYyl7dGhpcy5hPWEKdGhpcy5iPWIKdGhp
-cy5jPWN9LApSVDpmdW5jdGlvbiBSVChhLGIsYyxkKXt2YXIgXz10aGlzCl8uYT1hCl8uYj1iCl8uYz1j
-Cl8uZD1kfSwKalo6ZnVuY3Rpb24galooYSl7dGhpcy5hPWF9LApycTpmdW5jdGlvbiBycShhLGIsYyl7
-dGhpcy5hPWEKdGhpcy5iPWIKdGhpcy5jPWN9LApSVzpmdW5jdGlvbiBSVyhhLGIsYyl7dGhpcy5hPWEK
-dGhpcy5iPWIKdGhpcy5jPWN9LApPTTpmdW5jdGlvbiBPTShhKXt0aGlzLmE9YQp0aGlzLmI9bnVsbH0s
-CnFoOmZ1bmN0aW9uIHFoKCl7fSwKQjU6ZnVuY3Rpb24gQjUoYSxiKXt0aGlzLmE9YQp0aGlzLmI9Yn0s
-CnVPOmZ1bmN0aW9uIHVPKGEsYil7dGhpcy5hPWEKdGhpcy5iPWJ9LApNTzpmdW5jdGlvbiBNTygpe30s
-CmtUOmZ1bmN0aW9uIGtUKCl7fSwKeEk6ZnVuY3Rpb24geEkoYSl7dGhpcy4kdGk9YX0sCk9IOmZ1bmN0
-aW9uIE9IKGEsYil7dGhpcy5hPWEKdGhpcy5iPWJ9LAptMDpmdW5jdGlvbiBtMCgpe30sCnBLOmZ1bmN0
-aW9uIHBLKGEpe3RoaXMuYT1hfSwKSmk6ZnVuY3Rpb24gSmkoKXt9LApoajpmdW5jdGlvbiBoaihhLGIs
-Yyl7dGhpcy5hPWEKdGhpcy5iPWIKdGhpcy5jPWN9LApWcDpmdW5jdGlvbiBWcChhLGIpe3RoaXMuYT1h
-CnRoaXMuYj1ifSwKT1I6ZnVuY3Rpb24gT1IoYSxiLGMpe3RoaXMuYT1hCnRoaXMuYj1iCnRoaXMuYz1j
-fSwKRUY6ZnVuY3Rpb24oYSxiLGMpe3JldHVybiBiLkMoIkA8MD4iKS5LcShjKS5DKCJGbzwxLDI+Iiku
-YShILkI3KGEsbmV3IEguTjUoYi5DKCJAPDA+IikuS3EoYykuQygiTjU8MSwyPiIpKSkpfSwKRmw6ZnVu
-Y3Rpb24oYSxiKXtyZXR1cm4gbmV3IEguTjUoYS5DKCJAPDA+IikuS3EoYikuQygiTjU8MSwyPiIpKX0s
-CkxzOmZ1bmN0aW9uKGEpe3JldHVybiBuZXcgUC5iNihhLkMoImI2PDA+IikpfSwKVDI6ZnVuY3Rpb24o
-KXt2YXIgdD1PYmplY3QuY3JlYXRlKG51bGwpCnRbIjxub24taWRlbnRpZmllci1rZXk+Il09dApkZWxl
-dGUgdFsiPG5vbi1pZGVudGlmaWVyLWtleT4iXQpyZXR1cm4gdH0sCnJqOmZ1bmN0aW9uKGEsYixjKXt2
-YXIgdD1uZXcgUC5sbShhLGIsYy5DKCJsbTwwPiIpKQp0LmM9YS5lCnJldHVybiB0fSwKRVA6ZnVuY3Rp
-b24oYSxiLGMpe3ZhciB0LHMKaWYoUC5oQihhKSl7aWYoYj09PSIoIiYmYz09PSIpIilyZXR1cm4iKC4u
-LikiCnJldHVybiBiKyIuLi4iK2N9dD1ILlZNKFtdLHUucykKQy5ObS5pKCQueGcsYSkKdHJ5e1AuVnIo
-YSx0KX1maW5hbGx5e2lmKDA+PSQueGcubGVuZ3RoKXJldHVybiBILmsoJC54ZywtMSkKJC54Zy5wb3Ao
-KX1zPVAudmcoYix1LlIuYSh0KSwiLCAiKStjCnJldHVybiBzLmNoYXJDb2RlQXQoMCk9PTA/czpzfSwK
-V0U6ZnVuY3Rpb24oYSxiLGMpe3ZhciB0LHMKaWYoUC5oQihhKSlyZXR1cm4gYisiLi4uIitjCnQ9bmV3
-IFAuUm4oYikKQy5ObS5pKCQueGcsYSkKdHJ5e3M9dApzLmE9UC52ZyhzLmEsYSwiLCAiKX1maW5hbGx5
-e2lmKDA+PSQueGcubGVuZ3RoKXJldHVybiBILmsoJC54ZywtMSkKJC54Zy5wb3AoKX10LmErPWMKcz10
-LmEKcmV0dXJuIHMuY2hhckNvZGVBdCgwKT09MD9zOnN9LApoQjpmdW5jdGlvbihhKXt2YXIgdCxzCmZv
-cih0PSQueGcubGVuZ3RoLHM9MDtzPHQ7KytzKWlmKGE9PT0kLnhnW3NdKXJldHVybiEwCnJldHVybiEx
-fSwKVnI6ZnVuY3Rpb24oYSxiKXt2YXIgdCxzLHIscSxwLG8sbixtPWEuZ2t6KGEpLGw9MCxrPTAKd2hp
-bGUoITApe2lmKCEobDw4MHx8azwzKSlicmVhawppZighbS5GKCkpcmV0dXJuCnQ9SC5kKG0uZ2woKSkK
-Qy5ObS5pKGIsdCkKbCs9dC5sZW5ndGgrMjsrK2t9aWYoIW0uRigpKXtpZihrPD01KXJldHVybgppZigw
-Pj1iLmxlbmd0aClyZXR1cm4gSC5rKGIsLTEpCnM9Yi5wb3AoKQppZigwPj1iLmxlbmd0aClyZXR1cm4g
-SC5rKGIsLTEpCnI9Yi5wb3AoKX1lbHNle3E9bS5nbCgpOysrawppZighbS5GKCkpe2lmKGs8PTQpe0Mu
-Tm0uaShiLEguZChxKSkKcmV0dXJufXM9SC5kKHEpCmlmKDA+PWIubGVuZ3RoKXJldHVybiBILmsoYiwt
+d1t0XVthXSlyZXR1cm4gd1t0XVthXX19dmFyIEM9e30sSD17Rks6ZnVuY3Rpb24gRksoKXt9LAp5Ujpm
+dW5jdGlvbihhKXtyZXR1cm4gbmV3IEgubmQoYSl9LApvbzpmdW5jdGlvbihhKXt2YXIgdCxzPWFeNDgK
+aWYoczw9OSlyZXR1cm4gcwp0PWF8MzIKaWYoOTc8PXQmJnQ8PTEwMilyZXR1cm4gdC04NwpyZXR1cm4t
+MX0sCnFDOmZ1bmN0aW9uKGEsYixjLGQpe1AuazEoYiwic3RhcnQiKQppZihjIT1udWxsKXtQLmsxKGMs
+ImVuZCIpCmlmKGI+YylILnZoKFAuVEUoYiwwLGMsInN0YXJ0IixudWxsKSl9cmV0dXJuIG5ldyBILm5I
+KGEsYixjLGQuQygibkg8MD4iKSl9LApLMTpmdW5jdGlvbihhLGIsYyxkKXtpZih1Lmd3LmIoYSkpcmV0
+dXJuIG5ldyBILnh5KGEsYixjLkMoIkA8MD4iKS5LcShkKS5DKCJ4eTwxLDI+IikpCnJldHVybiBuZXcg
+SC5pMShhLGIsYy5DKCJAPDA+IikuS3EoZCkuQygiaTE8MSwyPiIpKX0sCldwOmZ1bmN0aW9uKCl7cmV0
+dXJuIG5ldyBQLmxqKCJObyBlbGVtZW50Iil9LApBbTpmdW5jdGlvbigpe3JldHVybiBuZXcgUC5saigi
+VG9vIG1hbnkgZWxlbWVudHMiKX0sCmFyOmZ1bmN0aW9uKCl7cmV0dXJuIG5ldyBQLmxqKCJUb28gZmV3
+IGVsZW1lbnRzIil9LApuZDpmdW5jdGlvbiBuZChhKXt0aGlzLmE9YX0sCnFqOmZ1bmN0aW9uIHFqKGEp
+e3RoaXMuYT1hfSwKYlE6ZnVuY3Rpb24gYlEoKXt9LAphTDpmdW5jdGlvbiBhTCgpe30sCm5IOmZ1bmN0
+aW9uIG5IKGEsYixjLGQpe3ZhciBfPXRoaXMKXy5hPWEKXy5iPWIKXy5jPWMKXy4kdGk9ZH0sCmE3OmZ1
+bmN0aW9uIGE3KGEsYixjKXt2YXIgXz10aGlzCl8uYT1hCl8uYj1iCl8uYz0wCl8uZD1udWxsCl8uJHRp
+PWN9LAppMTpmdW5jdGlvbiBpMShhLGIsYyl7dGhpcy5hPWEKdGhpcy5iPWIKdGhpcy4kdGk9Y30sCnh5
+OmZ1bmN0aW9uIHh5KGEsYixjKXt0aGlzLmE9YQp0aGlzLmI9Ygp0aGlzLiR0aT1jfSwKTUg6ZnVuY3Rp
+b24gTUgoYSxiLGMpe3ZhciBfPXRoaXMKXy5hPW51bGwKXy5iPWEKXy5jPWIKXy4kdGk9Y30sCmxKOmZ1
+bmN0aW9uIGxKKGEsYixjKXt0aGlzLmE9YQp0aGlzLmI9Ygp0aGlzLiR0aT1jfSwKVTU6ZnVuY3Rpb24g
+VTUoYSxiLGMpe3RoaXMuYT1hCnRoaXMuYj1iCnRoaXMuJHRpPWN9LApTTzpmdW5jdGlvbiBTTyhhLGIs
+Yyl7dGhpcy5hPWEKdGhpcy5iPWIKdGhpcy4kdGk9Y30sClNVOmZ1bmN0aW9uIFNVKCl7fSwKUmU6ZnVu
+Y3Rpb24gUmUoKXt9LAp3MjpmdW5jdGlvbiB3Migpe30sCnd2OmZ1bmN0aW9uIHd2KGEpe3RoaXMuYT1h
+fSwKZGM6ZnVuY3Rpb24oKXt0aHJvdyBILmIoUC5MNCgiQ2Fubm90IG1vZGlmeSB1bm1vZGlmaWFibGUg
+TWFwIikpfSwKTlE6ZnVuY3Rpb24oYSl7dmFyIHQscz1ILkpnKGEpCmlmKHMhPW51bGwpcmV0dXJuIHMK
+dD0ibWluaWZpZWQ6IithCnJldHVybiB0fSwKd1Y6ZnVuY3Rpb24oYSxiKXt2YXIgdAppZihiIT1udWxs
+KXt0PWIueAppZih0IT1udWxsKXJldHVybiB0fXJldHVybiB1LmFVLmIoYSl9LApFajpmdW5jdGlvbihh
+KXt2YXIgdAppZih0eXBlb2YgYT09InN0cmluZyIpcmV0dXJuIGEKaWYodHlwZW9mIGE9PSJudW1iZXIi
+KXtpZihhIT09MClyZXR1cm4iIithfWVsc2UgaWYoITA9PT1hKXJldHVybiJ0cnVlIgplbHNlIGlmKCEx
+PT09YSlyZXR1cm4iZmFsc2UiCmVsc2UgaWYoYT09bnVsbClyZXR1cm4ibnVsbCIKdD1KLmooYSkKaWYo
+dHlwZW9mIHQhPSJzdHJpbmciKXRocm93IEguYihILnRMKGEpKQpyZXR1cm4gdH0sCmVROmZ1bmN0aW9u
+KGEpe3ZhciB0PWEuJGlkZW50aXR5SGFzaAppZih0PT1udWxsKXt0PU1hdGgucmFuZG9tKCkqMHgzZmZm
+ZmZmZnwwCmEuJGlkZW50aXR5SGFzaD10fXJldHVybiB0fSwKSHA6ZnVuY3Rpb24oYSxiKXt2YXIgdCxz
+LHIscSxwLG8sbj1udWxsCmlmKHR5cGVvZiBhIT0ic3RyaW5nIilILnZoKEgudEwoYSkpCnQ9L15ccypb
+Ky1dPygoMHhbYS1mMC05XSspfChcZCspfChbYS16MC05XSspKVxzKiQvaS5leGVjKGEpCmlmKHQ9PW51
+bGwpcmV0dXJuIG4KaWYoMz49dC5sZW5ndGgpcmV0dXJuIEguT0godCwzKQpzPXRbM10KaWYoYj09bnVs
+bCl7aWYocyE9bnVsbClyZXR1cm4gcGFyc2VJbnQoYSwxMCkKaWYodFsyXSE9bnVsbClyZXR1cm4gcGFy
+c2VJbnQoYSwxNikKcmV0dXJuIG59aWYoYjwyfHxiPjM2KXRocm93IEguYihQLlRFKGIsMiwzNiwicmFk
+aXgiLG4pKQppZihiPT09MTAmJnMhPW51bGwpcmV0dXJuIHBhcnNlSW50KGEsMTApCmlmKGI8MTB8fHM9
+PW51bGwpe3I9Yjw9MTA/NDcrYjo4NitiCnE9dFsxXQpmb3IocD1xLmxlbmd0aCxvPTA7bzxwOysrbylp
+ZigoQy54Qi5XKHEsbyl8MzIpPnIpcmV0dXJuIG59cmV0dXJuIHBhcnNlSW50KGEsYil9LApNOmZ1bmN0
+aW9uKGEpe3JldHVybiBILkg1KGEpfSwKSDU6ZnVuY3Rpb24oYSl7dmFyIHQscyxyCmlmKGEgaW5zdGFu
+Y2VvZiBQLk1oKXJldHVybiBILmRtKEgueihhKSxudWxsKQppZihKLmlhKGEpPT09Qy5Pa3x8dS5hay5i
+KGEpKXt0PUMuTzQoYSkKaWYoSC5CZSh0KSlyZXR1cm4gdApzPWEuY29uc3RydWN0b3IKaWYodHlwZW9m
+IHM9PSJmdW5jdGlvbiIpe3I9cy5uYW1lCmlmKHR5cGVvZiByPT0ic3RyaW5nIiYmSC5CZShyKSlyZXR1
+cm4gcn19cmV0dXJuIEguZG0oSC56KGEpLG51bGwpfSwKQmU6ZnVuY3Rpb24oYSl7dmFyIHQ9YSE9PSJP
+YmplY3QiJiZhIT09IiIKcmV0dXJuIHR9LApNMDpmdW5jdGlvbigpe2lmKCEhc2VsZi5sb2NhdGlvbily
+ZXR1cm4gc2VsZi5sb2NhdGlvbi5ocmVmCnJldHVybiBudWxsfSwKVks6ZnVuY3Rpb24oYSl7dmFyIHQs
+cyxyLHEscD1hLmxlbmd0aAppZihwPD01MDApcmV0dXJuIFN0cmluZy5mcm9tQ2hhckNvZGUuYXBwbHko
+bnVsbCxhKQpmb3IodD0iIixzPTA7czxwO3M9cil7cj1zKzUwMApxPXI8cD9yOnAKdCs9U3RyaW5nLmZy
+b21DaGFyQ29kZS5hcHBseShudWxsLGEuc2xpY2UocyxxKSl9cmV0dXJuIHR9LApDcTpmdW5jdGlvbihh
+KXt2YXIgdCxzLHIscT1ILlZNKFtdLHUuYSkKZm9yKHQ9YS5sZW5ndGgscz0wO3M8YS5sZW5ndGg7YS5s
+ZW5ndGg9PT10fHwoMCxILmxrKShhKSwrK3Mpe3I9YVtzXQppZighSC5vayhyKSl0aHJvdyBILmIoSC50
+TChyKSkKaWYocjw9NjU1MzUpQy5ObS5pKHEscikKZWxzZSBpZihyPD0xMTE0MTExKXtDLk5tLmkocSw1
+NTI5NisoQy5qbi53RyhyLTY1NTM2LDEwKSYxMDIzKSkKQy5ObS5pKHEsNTYzMjArKHImMTAyMykpfWVs
+c2UgdGhyb3cgSC5iKEgudEwocikpfXJldHVybiBILlZLKHEpfSwKZVQ6ZnVuY3Rpb24oYSl7dmFyIHQs
+cyxyCmZvcih0PWEubGVuZ3RoLHM9MDtzPHQ7KytzKXtyPWFbc10KaWYoIUgub2socikpdGhyb3cgSC5i
+KEgudEwocikpCmlmKHI8MCl0aHJvdyBILmIoSC50TChyKSkKaWYocj42NTUzNSlyZXR1cm4gSC5DcShh
+KX1yZXR1cm4gSC5WSyhhKX0sCmZ3OmZ1bmN0aW9uKGEsYixjKXt2YXIgdCxzLHIscQppZihjPD01MDAm
+JmI9PT0wJiZjPT09YS5sZW5ndGgpcmV0dXJuIFN0cmluZy5mcm9tQ2hhckNvZGUuYXBwbHkobnVsbCxh
+KQpmb3IodD1iLHM9IiI7dDxjO3Q9cil7cj10KzUwMApxPXI8Yz9yOmMKcys9U3RyaW5nLmZyb21DaGFy
+Q29kZS5hcHBseShudWxsLGEuc3ViYXJyYXkodCxxKSl9cmV0dXJuIHN9LApMdzpmdW5jdGlvbihhKXt2
+YXIgdAppZigwPD1hKXtpZihhPD02NTUzNSlyZXR1cm4gU3RyaW5nLmZyb21DaGFyQ29kZShhKQppZihh
+PD0xMTE0MTExKXt0PWEtNjU1MzYKcmV0dXJuIFN0cmluZy5mcm9tQ2hhckNvZGUoKDU1Mjk2fEMuam4u
+d0codCwxMCkpPj4+MCw1NjMyMHx0JjEwMjMpfX10aHJvdyBILmIoUC5URShhLDAsMTExNDExMSxudWxs
+LG51bGwpKX0sCm8yOmZ1bmN0aW9uKGEpe2lmKGEuZGF0ZT09PXZvaWQgMClhLmRhdGU9bmV3IERhdGUo
+YS5hKQpyZXR1cm4gYS5kYXRlfSwKdEo6ZnVuY3Rpb24oYSl7dmFyIHQ9SC5vMihhKS5nZXRGdWxsWWVh
+cigpKzAKcmV0dXJuIHR9LApOUzpmdW5jdGlvbihhKXt2YXIgdD1ILm8yKGEpLmdldE1vbnRoKCkrMQpy
+ZXR1cm4gdH0sCmpBOmZ1bmN0aW9uKGEpe3ZhciB0PUgubzIoYSkuZ2V0RGF0ZSgpKzAKcmV0dXJuIHR9
+LApJWDpmdW5jdGlvbihhKXt2YXIgdD1ILm8yKGEpLmdldEhvdXJzKCkrMApyZXR1cm4gdH0sCmNoOmZ1
+bmN0aW9uKGEpe3ZhciB0PUgubzIoYSkuZ2V0TWludXRlcygpKzAKcmV0dXJuIHR9LApKZDpmdW5jdGlv
+bihhKXt2YXIgdD1ILm8yKGEpLmdldFNlY29uZHMoKSswCnJldHVybiB0fSwKbzE6ZnVuY3Rpb24oYSl7
+dmFyIHQ9SC5vMihhKS5nZXRNaWxsaXNlY29uZHMoKSswCnJldHVybiB0fSwKem86ZnVuY3Rpb24oYSxi
+LGMpe3ZhciB0LHMscj17fQpyLmE9MAp0PVtdCnM9W10Kci5hPWIubGVuZ3RoCkMuTm0uRlYodCxiKQpy
+LmI9IiIKaWYoYyE9bnVsbCYmYy5hIT09MCljLksoMCxuZXcgSC5DaihyLHMsdCkpCiIiK3IuYQpyZXR1
+cm4gSi5KeShhLG5ldyBILkxJKEMuVGUsMCx0LHMsMCkpfSwKRWs6ZnVuY3Rpb24oYSxiLGMpe3ZhciB0
+LHMscixxCmlmKGIgaW5zdGFuY2VvZiBBcnJheSl0PWM9PW51bGx8fGMuYT09PTAKZWxzZSB0PSExCmlm
+KHQpe3M9YgpyPXMubGVuZ3RoCmlmKHI9PT0wKXtpZighIWEuJDApcmV0dXJuIGEuJDAoKX1lbHNlIGlm
+KHI9PT0xKXtpZighIWEuJDEpcmV0dXJuIGEuJDEoc1swXSl9ZWxzZSBpZihyPT09Mil7aWYoISFhLiQy
+KXJldHVybiBhLiQyKHNbMF0sc1sxXSl9ZWxzZSBpZihyPT09Myl7aWYoISFhLiQzKXJldHVybiBhLiQz
+KHNbMF0sc1sxXSxzWzJdKX1lbHNlIGlmKHI9PT00KXtpZighIWEuJDQpcmV0dXJuIGEuJDQoc1swXSxz
+WzFdLHNbMl0sc1szXSl9ZWxzZSBpZihyPT09NSlpZighIWEuJDUpcmV0dXJuIGEuJDUoc1swXSxzWzFd
+LHNbMl0sc1szXSxzWzRdKQpxPWFbIiIrIiQiK3JdCmlmKHEhPW51bGwpcmV0dXJuIHEuYXBwbHkoYSxz
+KX1yZXR1cm4gSC5lMShhLGIsYyl9LAplMTpmdW5jdGlvbihhLGIsYyl7dmFyIHQscyxyLHEscCxvLG4s
+bSxsLGssaj1iIGluc3RhbmNlb2YgQXJyYXk/YjpQLkNIKGIsITAsdS56KSxpPWoubGVuZ3RoLGg9YS4k
+UgppZihpPGgpcmV0dXJuIEguem8oYSxqLGMpCnQ9YS4kRApzPXQ9PW51bGwKcj0hcz90KCk6bnVsbApx
+PUouaWEoYSkKcD1xLiRDCmlmKHR5cGVvZiBwPT0ic3RyaW5nIilwPXFbcF0KaWYocyl7aWYoYyE9bnVs
+bCYmYy5hIT09MClyZXR1cm4gSC56byhhLGosYykKaWYoaT09PWgpcmV0dXJuIHAuYXBwbHkoYSxqKQpy
+ZXR1cm4gSC56byhhLGosYyl9aWYociBpbnN0YW5jZW9mIEFycmF5KXtpZihjIT1udWxsJiZjLmEhPT0w
+KXJldHVybiBILnpvKGEsaixjKQppZihpPmgrci5sZW5ndGgpcmV0dXJuIEguem8oYSxqLG51bGwpCkMu
+Tm0uRlYoaixyLnNsaWNlKGktaCkpCnJldHVybiBwLmFwcGx5KGEsail9ZWxzZXtpZihpPmgpcmV0dXJu
+IEguem8oYSxqLGMpCm89T2JqZWN0LmtleXMocikKaWYoYz09bnVsbClmb3Iocz1vLmxlbmd0aCxuPTA7
+bjxvLmxlbmd0aDtvLmxlbmd0aD09PXN8fCgwLEgubGspKG8pLCsrbil7bT1yW0guaChvW25dKV0KaWYo
+Qy5Odj09PW0pcmV0dXJuIEguem8oYSxqLGMpCkMuTm0uaShqLG0pfWVsc2V7Zm9yKHM9by5sZW5ndGgs
+bD0wLG49MDtuPG8ubGVuZ3RoO28ubGVuZ3RoPT09c3x8KDAsSC5saykobyksKytuKXtrPUguaChvW25d
+KQppZihjLng0KGspKXsrK2wKQy5ObS5pKGosYy5xKDAsaykpfWVsc2V7bT1yW2tdCmlmKEMuTnY9PT1t
+KXJldHVybiBILnpvKGEsaixjKQpDLk5tLmkoaixtKX19aWYobCE9PWMuYSlyZXR1cm4gSC56byhhLGos
+Yyl9cmV0dXJuIHAuYXBwbHkoYSxqKX19LApwWTpmdW5jdGlvbihhKXt0aHJvdyBILmIoSC50TChhKSl9
+LApPSDpmdW5jdGlvbihhLGIpe2lmKGE9PW51bGwpSi5IbShhKQp0aHJvdyBILmIoSC5IWShhLGIpKX0s
+CkhZOmZ1bmN0aW9uKGEsYil7dmFyIHQscyxyPSJpbmRleCIKaWYoIUgub2soYikpcmV0dXJuIG5ldyBQ
+LnUoITAsYixyLG51bGwpCnQ9SC51UChKLkhtKGEpKQppZighKGI8MCkpe2lmKHR5cGVvZiB0IT09Im51
+bWJlciIpcmV0dXJuIEgucFkodCkKcz1iPj10fWVsc2Ugcz0hMAppZihzKXJldHVybiBQLkNmKGIsYSxy
+LG51bGwsdCkKcmV0dXJuIFAuTzcoYixyKX0sCmF1OmZ1bmN0aW9uKGEsYixjKXtpZihhPmMpcmV0dXJu
+IFAuVEUoYSwwLGMsInN0YXJ0IixudWxsKQppZihiIT1udWxsKWlmKGI8YXx8Yj5jKXJldHVybiBQLlRF
+KGIsYSxjLCJlbmQiLG51bGwpCnJldHVybiBuZXcgUC51KCEwLGIsImVuZCIsbnVsbCl9LAp0TDpmdW5j
+dGlvbihhKXtyZXR1cm4gbmV3IFAudSghMCxhLG51bGwsbnVsbCl9LApiOmZ1bmN0aW9uKGEpe3ZhciB0
+LHMKaWYoYT09bnVsbClhPW5ldyBQLm4oKQp0PW5ldyBFcnJvcigpCnQuZGFydEV4Y2VwdGlvbj1hCnM9
+SC54CmlmKCJkZWZpbmVQcm9wZXJ0eSIgaW4gT2JqZWN0KXtPYmplY3QuZGVmaW5lUHJvcGVydHkodCwi
+bWVzc2FnZSIse2dldDpzfSkKdC5uYW1lPSIifWVsc2UgdC50b1N0cmluZz1zCnJldHVybiB0fSwKeDpm
+dW5jdGlvbigpe3JldHVybiBKLmoodGhpcy5kYXJ0RXhjZXB0aW9uKX0sCnZoOmZ1bmN0aW9uKGEpe3Ro
+cm93IEguYihhKX0sCmxrOmZ1bmN0aW9uKGEpe3Rocm93IEguYihQLmE0KGEpKX0sCmNNOmZ1bmN0aW9u
+KGEpe3ZhciB0LHMscixxLHAsbwphPUguZUEoYS5yZXBsYWNlKFN0cmluZyh7fSksJyRyZWNlaXZlciQn
+KSkKdD1hLm1hdGNoKC9cXFwkW2EtekEtWl0rXFxcJC9nKQppZih0PT1udWxsKXQ9SC5WTShbXSx1LnMp
+CnM9dC5pbmRleE9mKCJcXCRhcmd1bWVudHNcXCQiKQpyPXQuaW5kZXhPZigiXFwkYXJndW1lbnRzRXhw
+clxcJCIpCnE9dC5pbmRleE9mKCJcXCRleHByXFwkIikKcD10LmluZGV4T2YoIlxcJG1ldGhvZFxcJCIp
+Cm89dC5pbmRleE9mKCJcXCRyZWNlaXZlclxcJCIpCnJldHVybiBuZXcgSC5mOShhLnJlcGxhY2UobmV3
+IFJlZ0V4cCgnXFxcXFxcJGFyZ3VtZW50c1xcXFxcXCQnLCdnJyksJygoPzp4fFteeF0pKiknKS5yZXBs
+YWNlKG5ldyBSZWdFeHAoJ1xcXFxcXCRhcmd1bWVudHNFeHByXFxcXFxcJCcsJ2cnKSwnKCg/Onh8W154
+XSkqKScpLnJlcGxhY2UobmV3IFJlZ0V4cCgnXFxcXFxcJGV4cHJcXFxcXFwkJywnZycpLCcoKD86eHxb
+XnhdKSopJykucmVwbGFjZShuZXcgUmVnRXhwKCdcXFxcXFwkbWV0aG9kXFxcXFxcJCcsJ2cnKSwnKCg/
+Onh8W154XSkqKScpLnJlcGxhY2UobmV3IFJlZ0V4cCgnXFxcXFxcJHJlY2VpdmVyXFxcXFxcJCcsJ2cn
+KSwnKCg/Onh8W154XSkqKScpLHMscixxLHAsbyl9LApTNzpmdW5jdGlvbihhKXtyZXR1cm4gZnVuY3Rp
+b24oJGV4cHIkKXt2YXIgJGFyZ3VtZW50c0V4cHIkPSckYXJndW1lbnRzJCcKdHJ5eyRleHByJC4kbWV0
+aG9kJCgkYXJndW1lbnRzRXhwciQpfWNhdGNoKHQpe3JldHVybiB0Lm1lc3NhZ2V9fShhKX0sCk1qOmZ1
+bmN0aW9uKGEpe3JldHVybiBmdW5jdGlvbigkZXhwciQpe3RyeXskZXhwciQuJG1ldGhvZCR9Y2F0Y2go
+dCl7cmV0dXJuIHQubWVzc2FnZX19KGEpfSwKSWo6ZnVuY3Rpb24oYSxiKXtyZXR1cm4gbmV3IEguVzAo
+YSxiPT1udWxsP251bGw6Yi5tZXRob2QpfSwKVDM6ZnVuY3Rpb24oYSxiKXt2YXIgdD1iPT1udWxsLHM9
+dD9udWxsOmIubWV0aG9kCnJldHVybiBuZXcgSC5heihhLHMsdD9udWxsOmIucmVjZWl2ZXIpfSwKUnU6
+ZnVuY3Rpb24oYSl7aWYoYT09bnVsbClyZXR1cm4gbmV3IEgudGUoYSkKaWYoYSBpbnN0YW5jZW9mIEgu
+YnEpcmV0dXJuIEgudFcoYSxhLmEpCmlmKHR5cGVvZiBhIT09Im9iamVjdCIpcmV0dXJuIGEKaWYoImRh
+cnRFeGNlcHRpb24iIGluIGEpcmV0dXJuIEgudFcoYSxhLmRhcnRFeGNlcHRpb24pCnJldHVybiBILnRs
+KGEpfSwKdFc6ZnVuY3Rpb24oYSxiKXtpZih1Lm0uYihiKSlpZihiLiR0aHJvd25Kc0Vycm9yPT1udWxs
+KWIuJHRocm93bkpzRXJyb3I9YQpyZXR1cm4gYn0sCnRsOmZ1bmN0aW9uKGEpe3ZhciB0LHMscixxLHAs
+byxuLG0sbCxrLGosaSxoLGcsZj1udWxsCmlmKCEoIm1lc3NhZ2UiIGluIGEpKXJldHVybiBhCnQ9YS5t
+ZXNzYWdlCmlmKCJudW1iZXIiIGluIGEmJnR5cGVvZiBhLm51bWJlcj09Im51bWJlciIpe3M9YS5udW1i
+ZXIKcj1zJjY1NTM1CmlmKChDLmpuLndHKHMsMTYpJjgxOTEpPT09MTApc3dpdGNoKHIpe2Nhc2UgNDM4
+OnJldHVybiBILnRXKGEsSC5UMyhILkVqKHQpKyIgKEVycm9yICIrcisiKSIsZikpCmNhc2UgNDQ1OmNh
+c2UgNTAwNzpyZXR1cm4gSC50VyhhLEguSWooSC5Faih0KSsiIChFcnJvciAiK3IrIikiLGYpKX19aWYo
+YSBpbnN0YW5jZW9mIFR5cGVFcnJvcil7cT0kLlNuKCkKcD0kLmxxKCkKbz0kLk45KCkKbj0kLmlJKCkK
+bT0kLlVOKCkKbD0kLlpoKCkKaz0kLnJOKCkKJC5jMygpCmo9JC5ISygpCmk9JC5yMSgpCmg9cS5xUyh0
+KQppZihoIT1udWxsKXJldHVybiBILnRXKGEsSC5UMyhILmgodCksaCkpCmVsc2V7aD1wLnFTKHQpCmlm
+KGghPW51bGwpe2gubWV0aG9kPSJjYWxsIgpyZXR1cm4gSC50VyhhLEguVDMoSC5oKHQpLGgpKX1lbHNl
+e2g9by5xUyh0KQppZihoPT1udWxsKXtoPW4ucVModCkKaWYoaD09bnVsbCl7aD1tLnFTKHQpCmlmKGg9
+PW51bGwpe2g9bC5xUyh0KQppZihoPT1udWxsKXtoPWsucVModCkKaWYoaD09bnVsbCl7aD1uLnFTKHQp
+CmlmKGg9PW51bGwpe2g9ai5xUyh0KQppZihoPT1udWxsKXtoPWkucVModCkKZz1oIT1udWxsfWVsc2Ug
+Zz0hMH1lbHNlIGc9ITB9ZWxzZSBnPSEwfWVsc2UgZz0hMH1lbHNlIGc9ITB9ZWxzZSBnPSEwfWVsc2Ug
+Zz0hMAppZihnKXJldHVybiBILnRXKGEsSC5JaihILmgodCksaCkpfX1yZXR1cm4gSC50VyhhLG5ldyBI
+LnZWKHR5cGVvZiB0PT0ic3RyaW5nIj90OiIiKSl9aWYoYSBpbnN0YW5jZW9mIFJhbmdlRXJyb3Ipe2lm
+KHR5cGVvZiB0PT0ic3RyaW5nIiYmdC5pbmRleE9mKCJjYWxsIHN0YWNrIikhPT0tMSlyZXR1cm4gbmV3
+IFAuS1koKQp0PWZ1bmN0aW9uKGIpe3RyeXtyZXR1cm4gU3RyaW5nKGIpfWNhdGNoKGUpe31yZXR1cm4g
+bnVsbH0oYSkKcmV0dXJuIEgudFcoYSxuZXcgUC51KCExLGYsZix0eXBlb2YgdD09InN0cmluZyI/dC5y
+ZXBsYWNlKC9eUmFuZ2VFcnJvcjpccyovLCIiKTp0KSl9aWYodHlwZW9mIEludGVybmFsRXJyb3I9PSJm
+dW5jdGlvbiImJmEgaW5zdGFuY2VvZiBJbnRlcm5hbEVycm9yKWlmKHR5cGVvZiB0PT0ic3RyaW5nIiYm
+dD09PSJ0b28gbXVjaCByZWN1cnNpb24iKXJldHVybiBuZXcgUC5LWSgpCnJldHVybiBhfSwKdHM6ZnVu
+Y3Rpb24oYSl7dmFyIHQKaWYoYSBpbnN0YW5jZW9mIEguYnEpcmV0dXJuIGEuYgppZihhPT1udWxsKXJl
+dHVybiBuZXcgSC5YTyhhKQp0PWEuJGNhY2hlZFRyYWNlCmlmKHQhPW51bGwpcmV0dXJuIHQKcmV0dXJu
+IGEuJGNhY2hlZFRyYWNlPW5ldyBILlhPKGEpfSwKQjc6ZnVuY3Rpb24oYSxiKXt2YXIgdCxzLHIscT1h
+Lmxlbmd0aApmb3IodD0wO3Q8cTt0PXIpe3M9dCsxCnI9cysxCmIuWSgwLGFbdF0sYVtzXSl9cmV0dXJu
+IGJ9LApmdDpmdW5jdGlvbihhLGIsYyxkLGUsZil7dS5ZLmEoYSkKc3dpdGNoKEgudVAoYikpe2Nhc2Ug
+MDpyZXR1cm4gYS4kMCgpCmNhc2UgMTpyZXR1cm4gYS4kMShjKQpjYXNlIDI6cmV0dXJuIGEuJDIoYyxk
+KQpjYXNlIDM6cmV0dXJuIGEuJDMoYyxkLGUpCmNhc2UgNDpyZXR1cm4gYS4kNChjLGQsZSxmKX10aHJv
+dyBILmIobmV3IFAuQ0QoIlVuc3VwcG9ydGVkIG51bWJlciBvZiBhcmd1bWVudHMgZm9yIHdyYXBwZWQg
+Y2xvc3VyZSIpKX0sCnRSOmZ1bmN0aW9uKGEsYil7dmFyIHQKaWYoYT09bnVsbClyZXR1cm4gbnVsbAp0
+PWEuJGlkZW50aXR5CmlmKCEhdClyZXR1cm4gdAp0PWZ1bmN0aW9uKGMsZCxlKXtyZXR1cm4gZnVuY3Rp
+b24oZixnLGgsaSl7cmV0dXJuIGUoYyxkLGYsZyxoLGkpfX0oYSxiLEguZnQpCmEuJGlkZW50aXR5PXQK
+cmV0dXJuIHR9LAppQTpmdW5jdGlvbihhLGIsYyxkLGUsZixnKXt2YXIgdCxzLHIscSxwLG8sbixtPWJb
+MF0sbD1tLiRjYWxsTmFtZSxrPWU/T2JqZWN0LmNyZWF0ZShuZXcgSC56eCgpLmNvbnN0cnVjdG9yLnBy
+b3RvdHlwZSk6T2JqZWN0LmNyZWF0ZShuZXcgSC5yVChudWxsLG51bGwsbnVsbCwiIikuY29uc3RydWN0
+b3IucHJvdG90eXBlKQprLiRpbml0aWFsaXplPWsuY29uc3RydWN0b3IKaWYoZSl0PWZ1bmN0aW9uIHN0
+YXRpY190ZWFyX29mZigpe3RoaXMuJGluaXRpYWxpemUoKX0KZWxzZXtzPSQueWoKaWYodHlwZW9mIHMh
+PT0ibnVtYmVyIilyZXR1cm4gcy5oKCkKJC55aj1zKzEKcz1uZXcgRnVuY3Rpb24oImEsYixjLGQiK3Ms
+InRoaXMuJGluaXRpYWxpemUoYSxiLGMsZCIrcysiKSIpCnQ9c31rLmNvbnN0cnVjdG9yPXQKdC5wcm90
+b3R5cGU9awppZighZSl7cj1ILmJ4KGEsbSxmKQpyLiRyZWZsZWN0aW9uSW5mbz1kfWVsc2V7ay4kc3Rh
+dGljX25hbWU9ZwpyPW19ay4kUz1ILmltKGQsZSxmKQprW2xdPXIKZm9yKHE9cixwPTE7cDxiLmxlbmd0
+aDsrK3Ape289YltwXQpuPW8uJGNhbGxOYW1lCmlmKG4hPW51bGwpe289ZT9vOkguYngoYSxvLGYpCmtb
+bl09b31pZihwPT09Yyl7by4kcmVmbGVjdGlvbkluZm89ZApxPW99fWsuJEM9cQprLiRSPW0uJFIKay4k
+RD1tLiRECnJldHVybiB0fSwKaW06ZnVuY3Rpb24oYSxiLGMpe3ZhciB0CmlmKHR5cGVvZiBhPT0ibnVt
+YmVyIilyZXR1cm4gZnVuY3Rpb24oZCxlKXtyZXR1cm4gZnVuY3Rpb24oKXtyZXR1cm4gZChlKX19KEgu
+QnAsYSkKaWYodHlwZW9mIGE9PSJzdHJpbmciKXtpZihiKXRocm93IEguYigiQ2Fubm90IGNvbXB1dGUg
+c2lnbmF0dXJlIGZvciBzdGF0aWMgdGVhcm9mZi4iKQp0PWM/SC5QVzpILlRuCnJldHVybiBmdW5jdGlv
+bihkLGUpe3JldHVybiBmdW5jdGlvbigpe3JldHVybiBlKHRoaXMsZCl9fShhLHQpfXRocm93IEguYigi
+RXJyb3IgaW4gZnVuY3Rpb25UeXBlIG9mIHRlYXJvZmYiKX0sCnZxOmZ1bmN0aW9uKGEsYixjLGQpe3Zh
+ciB0PUguRFYKc3dpdGNoKGI/LTE6YSl7Y2FzZSAwOnJldHVybiBmdW5jdGlvbihlLGYpe3JldHVybiBm
+dW5jdGlvbigpe3JldHVybiBmKHRoaXMpW2VdKCl9fShjLHQpCmNhc2UgMTpyZXR1cm4gZnVuY3Rpb24o
+ZSxmKXtyZXR1cm4gZnVuY3Rpb24oZyl7cmV0dXJuIGYodGhpcylbZV0oZyl9fShjLHQpCmNhc2UgMjpy
+ZXR1cm4gZnVuY3Rpb24oZSxmKXtyZXR1cm4gZnVuY3Rpb24oZyxoKXtyZXR1cm4gZih0aGlzKVtlXShn
+LGgpfX0oYyx0KQpjYXNlIDM6cmV0dXJuIGZ1bmN0aW9uKGUsZil7cmV0dXJuIGZ1bmN0aW9uKGcsaCxp
+KXtyZXR1cm4gZih0aGlzKVtlXShnLGgsaSl9fShjLHQpCmNhc2UgNDpyZXR1cm4gZnVuY3Rpb24oZSxm
+KXtyZXR1cm4gZnVuY3Rpb24oZyxoLGksail7cmV0dXJuIGYodGhpcylbZV0oZyxoLGksail9fShjLHQp
+CmNhc2UgNTpyZXR1cm4gZnVuY3Rpb24oZSxmKXtyZXR1cm4gZnVuY3Rpb24oZyxoLGksaixrKXtyZXR1
+cm4gZih0aGlzKVtlXShnLGgsaSxqLGspfX0oYyx0KQpkZWZhdWx0OnJldHVybiBmdW5jdGlvbihlLGYp
+e3JldHVybiBmdW5jdGlvbigpe3JldHVybiBlLmFwcGx5KGYodGhpcyksYXJndW1lbnRzKX19KGQsdCl9
+fSwKYng6ZnVuY3Rpb24oYSxiLGMpe3ZhciB0LHMscixxLHAsbyxuCmlmKGMpcmV0dXJuIEguSGYoYSxi
+KQp0PWIuJHN0dWJOYW1lCnM9Yi5sZW5ndGgKcj1hW3RdCnE9Yj09bnVsbD9yPT1udWxsOmI9PT1yCnA9
+IXF8fHM+PTI3CmlmKHApcmV0dXJuIEgudnEocywhcSx0LGIpCmlmKHM9PT0wKXtxPSQueWoKaWYodHlw
+ZW9mIHEhPT0ibnVtYmVyIilyZXR1cm4gcS5oKCkKJC55aj1xKzEKbz0ic2VsZiIrcQpyZXR1cm4gbmV3
+IEZ1bmN0aW9uKCJyZXR1cm4gZnVuY3Rpb24oKXt2YXIgIitvKyIgPSB0aGlzLiIrSC5FaihILm9OKCkp
+KyI7cmV0dXJuICIrbysiLiIrSC5Faih0KSsiKCk7fSIpKCl9bj0iYWJjZGVmZ2hpamtsbW5vcHFyc3R1
+dnd4eXoiLnNwbGl0KCIiKS5zcGxpY2UoMCxzKS5qb2luKCIsIikKcT0kLnlqCmlmKHR5cGVvZiBxIT09
+Im51bWJlciIpcmV0dXJuIHEuaCgpCiQueWo9cSsxCm4rPXEKcmV0dXJuIG5ldyBGdW5jdGlvbigicmV0
+dXJuIGZ1bmN0aW9uKCIrbisiKXtyZXR1cm4gdGhpcy4iK0guRWooSC5vTigpKSsiLiIrSC5Faih0KSsi
+KCIrbisiKTt9IikoKX0sClo0OmZ1bmN0aW9uKGEsYixjLGQpe3ZhciB0PUguRFYscz1ILnlTCnN3aXRj
+aChiPy0xOmEpe2Nhc2UgMDp0aHJvdyBILmIoSC5FZigiSW50ZXJjZXB0ZWQgZnVuY3Rpb24gd2l0aCBu
+byBhcmd1bWVudHMuIikpCmNhc2UgMTpyZXR1cm4gZnVuY3Rpb24oZSxmLGcpe3JldHVybiBmdW5jdGlv
+bigpe3JldHVybiBmKHRoaXMpW2VdKGcodGhpcykpfX0oYyx0LHMpCmNhc2UgMjpyZXR1cm4gZnVuY3Rp
+b24oZSxmLGcpe3JldHVybiBmdW5jdGlvbihoKXtyZXR1cm4gZih0aGlzKVtlXShnKHRoaXMpLGgpfX0o
+Yyx0LHMpCmNhc2UgMzpyZXR1cm4gZnVuY3Rpb24oZSxmLGcpe3JldHVybiBmdW5jdGlvbihoLGkpe3Jl
+dHVybiBmKHRoaXMpW2VdKGcodGhpcyksaCxpKX19KGMsdCxzKQpjYXNlIDQ6cmV0dXJuIGZ1bmN0aW9u
+KGUsZixnKXtyZXR1cm4gZnVuY3Rpb24oaCxpLGope3JldHVybiBmKHRoaXMpW2VdKGcodGhpcyksaCxp
+LGopfX0oYyx0LHMpCmNhc2UgNTpyZXR1cm4gZnVuY3Rpb24oZSxmLGcpe3JldHVybiBmdW5jdGlvbiho
+LGksaixrKXtyZXR1cm4gZih0aGlzKVtlXShnKHRoaXMpLGgsaSxqLGspfX0oYyx0LHMpCmNhc2UgNjpy
+ZXR1cm4gZnVuY3Rpb24oZSxmLGcpe3JldHVybiBmdW5jdGlvbihoLGksaixrLGwpe3JldHVybiBmKHRo
+aXMpW2VdKGcodGhpcyksaCxpLGosayxsKX19KGMsdCxzKQpkZWZhdWx0OnJldHVybiBmdW5jdGlvbihl
+LGYsZyxoKXtyZXR1cm4gZnVuY3Rpb24oKXtoPVtnKHRoaXMpXQpBcnJheS5wcm90b3R5cGUucHVzaC5h
+cHBseShoLGFyZ3VtZW50cykKcmV0dXJuIGUuYXBwbHkoZih0aGlzKSxoKX19KGQsdCxzKX19LApIZjpm
+dW5jdGlvbihhLGIpe3ZhciB0LHMscixxLHAsbyxuPUgub04oKSxtPSQuUDQKaWYobT09bnVsbCltPSQu
+UDQ9SC5FMigicmVjZWl2ZXIiKQp0PWIuJHN0dWJOYW1lCnM9Yi5sZW5ndGgKcj1hW3RdCnE9Yj09bnVs
+bD9yPT1udWxsOmI9PT1yCnA9IXF8fHM+PTI4CmlmKHApcmV0dXJuIEguWjQocywhcSx0LGIpCmlmKHM9
+PT0xKXtxPSJyZXR1cm4gZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy4iK0guRWoobikrIi4iK0guRWoodCkr
+Iih0aGlzLiIrbSsiKTsiCnA9JC55agppZih0eXBlb2YgcCE9PSJudW1iZXIiKXJldHVybiBwLmgoKQok
+LnlqPXArMQpyZXR1cm4gbmV3IEZ1bmN0aW9uKHErcCsifSIpKCl9bz0iYWJjZGVmZ2hpamtsbW5vcHFy
+c3R1dnd4eXoiLnNwbGl0KCIiKS5zcGxpY2UoMCxzLTEpLmpvaW4oIiwiKQpxPSJyZXR1cm4gZnVuY3Rp
+b24oIitvKyIpe3JldHVybiB0aGlzLiIrSC5FaihuKSsiLiIrSC5Faih0KSsiKHRoaXMuIittKyIsICIr
+bysiKTsiCnA9JC55agppZih0eXBlb2YgcCE9PSJudW1iZXIiKXJldHVybiBwLmgoKQokLnlqPXArMQpy
+ZXR1cm4gbmV3IEZ1bmN0aW9uKHErcCsifSIpKCl9LApLcTpmdW5jdGlvbihhLGIsYyxkLGUsZixnKXty
+ZXR1cm4gSC5pQShhLGIsYyxkLCEhZSwhIWYsZyl9LApUbjpmdW5jdGlvbihhLGIpe3JldHVybiBILmNF
+KHYudHlwZVVuaXZlcnNlLEgueihhLmEpLGIpfSwKUFc6ZnVuY3Rpb24oYSxiKXtyZXR1cm4gSC5jRSh2
+LnR5cGVVbml2ZXJzZSxILnooYS5jKSxiKX0sCkRWOmZ1bmN0aW9uKGEpe3JldHVybiBhLmF9LAp5Uzpm
+dW5jdGlvbihhKXtyZXR1cm4gYS5jfSwKb046ZnVuY3Rpb24oKXt2YXIgdD0kLm1KCnJldHVybiB0PT1u
+dWxsPyQubUo9SC5FMigic2VsZiIpOnR9LApFMjpmdW5jdGlvbihhKXt2YXIgdCxzLHIscT1uZXcgSC5y
+VCgic2VsZiIsInRhcmdldCIsInJlY2VpdmVyIiwibmFtZSIpLHA9Si5FcChPYmplY3QuZ2V0T3duUHJv
+cGVydHlOYW1lcyhxKSx1LnopCmZvcih0PXAubGVuZ3RoLHM9MDtzPHQ7KytzKXtyPXBbc10KaWYocVty
+XT09PWEpcmV0dXJuIHJ9dGhyb3cgSC5iKFAueFkoIkZpZWxkIG5hbWUgIithKyIgbm90IGZvdW5kLiIp
+KX0sCm9UOmZ1bmN0aW9uKGEpe2lmKGE9PW51bGwpSC5mTygiYm9vbGVhbiBleHByZXNzaW9uIG11c3Qg
+bm90IGJlIG51bGwiKQpyZXR1cm4gYX0sCmZPOmZ1bmN0aW9uKGEpe3Rocm93IEguYihuZXcgSC5rWShh
+KSl9LAphZzpmdW5jdGlvbihhKXt0aHJvdyBILmIobmV3IFAuYyhhKSl9LApFZjpmdW5jdGlvbihhKXty
+ZXR1cm4gbmV3IEguRXEoYSl9LApZZzpmdW5jdGlvbihhKXtyZXR1cm4gdi5nZXRJc29sYXRlVGFnKGEp
+fSwKVk06ZnVuY3Rpb24oYSxiKXthW3YuYXJyYXlSdGldPWIKcmV0dXJuIGF9LApvWDpmdW5jdGlvbihh
+KXtpZihhPT1udWxsKXJldHVybiBudWxsCnJldHVybiBhLiR0aX0sCklNOmZ1bmN0aW9uKGEsYixjKXty
+ZXR1cm4gSC5ZOShhWyIkYSIrSC5FaihjKV0sSC5vWChiKSl9LApZOTpmdW5jdGlvbihhLGIpe2lmKGE9
+PW51bGwpcmV0dXJuIGIKYT1hLmFwcGx5KG51bGwsYikKaWYoYT09bnVsbClyZXR1cm4gbnVsbAppZihB
+cnJheS5pc0FycmF5KGEpKXJldHVybiBhCmlmKHR5cGVvZiBhPT0iZnVuY3Rpb24iKXJldHVybiBhLmFw
+cGx5KG51bGwsYikKcmV0dXJuIGJ9LApJRzpmdW5jdGlvbihhLGIsYyl7cmV0dXJuIGEuYXBwbHkoYixI
+Llk5KEouaWEoYilbIiRhIitILkVqKGMpXSxILm9YKGIpKSl9LAppdzpmdW5jdGlvbihhLGIsYyl7T2Jq
+ZWN0LmRlZmluZVByb3BlcnR5KGEsYix7dmFsdWU6YyxlbnVtZXJhYmxlOmZhbHNlLHdyaXRhYmxlOnRy
+dWUsY29uZmlndXJhYmxlOnRydWV9KX0sCnczOmZ1bmN0aW9uKGEpe3ZhciB0LHMscixxLHAsbz1ILmgo
+JC5ORi4kMShhKSksbj0kLm53W29dCmlmKG4hPW51bGwpe09iamVjdC5kZWZpbmVQcm9wZXJ0eShhLHYu
+ZGlzcGF0Y2hQcm9wZXJ0eU5hbWUse3ZhbHVlOm4sZW51bWVyYWJsZTpmYWxzZSx3cml0YWJsZTp0cnVl
+LGNvbmZpZ3VyYWJsZTp0cnVlfSkKcmV0dXJuIG4uaX10PSQudnZbb10KaWYodCE9bnVsbClyZXR1cm4g
+dApzPXYuaW50ZXJjZXB0b3JzQnlUYWdbb10KaWYocz09bnVsbCl7cj1ILmsoJC5UWC4kMihhLG8pKQpp
+ZihyIT1udWxsKXtuPSQubndbcl0KaWYobiE9bnVsbCl7T2JqZWN0LmRlZmluZVByb3BlcnR5KGEsdi5k
+aXNwYXRjaFByb3BlcnR5TmFtZSx7dmFsdWU6bixlbnVtZXJhYmxlOmZhbHNlLHdyaXRhYmxlOnRydWUs
+Y29uZmlndXJhYmxlOnRydWV9KQpyZXR1cm4gbi5pfXQ9JC52dltyXQppZih0IT1udWxsKXJldHVybiB0
+CnM9di5pbnRlcmNlcHRvcnNCeVRhZ1tyXQpvPXJ9fWlmKHM9PW51bGwpcmV0dXJuIG51bGwKdD1zLnBy
+b3RvdHlwZQpxPW9bMF0KaWYocT09PSIhIil7bj1ILlZhKHQpCiQubndbb109bgpPYmplY3QuZGVmaW5l
+UHJvcGVydHkoYSx2LmRpc3BhdGNoUHJvcGVydHlOYW1lLHt2YWx1ZTpuLGVudW1lcmFibGU6ZmFsc2Us
+d3JpdGFibGU6dHJ1ZSxjb25maWd1cmFibGU6dHJ1ZX0pCnJldHVybiBuLml9aWYocT09PSJ+Iil7JC52
+dltvXT10CnJldHVybiB0fWlmKHE9PT0iLSIpe3A9SC5WYSh0KQpPYmplY3QuZGVmaW5lUHJvcGVydHko
+T2JqZWN0LmdldFByb3RvdHlwZU9mKGEpLHYuZGlzcGF0Y2hQcm9wZXJ0eU5hbWUse3ZhbHVlOnAsZW51
+bWVyYWJsZTpmYWxzZSx3cml0YWJsZTp0cnVlLGNvbmZpZ3VyYWJsZTp0cnVlfSkKcmV0dXJuIHAuaX1p
+ZihxPT09IisiKXJldHVybiBILkxjKGEsdCkKaWYocT09PSIqIil0aHJvdyBILmIoUC5TWShvKSkKaWYo
+di5sZWFmVGFnc1tvXT09PXRydWUpe3A9SC5WYSh0KQpPYmplY3QuZGVmaW5lUHJvcGVydHkoT2JqZWN0
+LmdldFByb3RvdHlwZU9mKGEpLHYuZGlzcGF0Y2hQcm9wZXJ0eU5hbWUse3ZhbHVlOnAsZW51bWVyYWJs
+ZTpmYWxzZSx3cml0YWJsZTp0cnVlLGNvbmZpZ3VyYWJsZTp0cnVlfSkKcmV0dXJuIHAuaX1lbHNlIHJl
+dHVybiBILkxjKGEsdCl9LApMYzpmdW5jdGlvbihhLGIpe3ZhciB0PU9iamVjdC5nZXRQcm90b3R5cGVP
+ZihhKQpPYmplY3QuZGVmaW5lUHJvcGVydHkodCx2LmRpc3BhdGNoUHJvcGVydHlOYW1lLHt2YWx1ZTpK
+LlF1KGIsdCxudWxsLG51bGwpLGVudW1lcmFibGU6ZmFsc2Usd3JpdGFibGU6dHJ1ZSxjb25maWd1cmFi
+bGU6dHJ1ZX0pCnJldHVybiBifSwKVmE6ZnVuY3Rpb24oYSl7cmV0dXJuIEouUXUoYSwhMSxudWxsLCEh
+YS4kaVhqKX0sClZGOmZ1bmN0aW9uKGEsYixjKXt2YXIgdD1iLnByb3RvdHlwZQppZih2LmxlYWZUYWdz
+W2FdPT09dHJ1ZSlyZXR1cm4gSC5WYSh0KQplbHNlIHJldHVybiBKLlF1KHQsYyxudWxsLG51bGwpfSwK
+WEQ6ZnVuY3Rpb24oKXtpZighMD09PSQuQnYpcmV0dXJuCiQuQnY9ITAKSC5aMSgpfSwKWjE6ZnVuY3Rp
+b24oKXt2YXIgdCxzLHIscSxwLG8sbixtCiQubnc9T2JqZWN0LmNyZWF0ZShudWxsKQokLnZ2PU9iamVj
+dC5jcmVhdGUobnVsbCkKSC5rTygpCnQ9di5pbnRlcmNlcHRvcnNCeVRhZwpzPU9iamVjdC5nZXRPd25Q
+cm9wZXJ0eU5hbWVzKHQpCmlmKHR5cGVvZiB3aW5kb3chPSJ1bmRlZmluZWQiKXt3aW5kb3cKcj1mdW5j
+dGlvbigpe30KZm9yKHE9MDtxPHMubGVuZ3RoOysrcSl7cD1zW3FdCm89JC54Ny4kMShwKQppZihvIT1u
+dWxsKXtuPUguVkYocCx0W3BdLG8pCmlmKG4hPW51bGwpe09iamVjdC5kZWZpbmVQcm9wZXJ0eShvLHYu
+ZGlzcGF0Y2hQcm9wZXJ0eU5hbWUse3ZhbHVlOm4sZW51bWVyYWJsZTpmYWxzZSx3cml0YWJsZTp0cnVl
+LGNvbmZpZ3VyYWJsZTp0cnVlfSkKci5wcm90b3R5cGU9b319fX1mb3IocT0wO3E8cy5sZW5ndGg7Kytx
+KXtwPXNbcV0KaWYoL15bQS1aYS16X10vLnRlc3QocCkpe209dFtwXQp0WyIhIitwXT1tCnRbIn4iK3Bd
+PW0KdFsiLSIrcF09bQp0WyIrIitwXT1tCnRbIioiK3BdPW19fX0sCmtPOmZ1bmN0aW9uKCl7dmFyIHQs
+cyxyLHEscCxvLG49Qy5ZcSgpCm49SC51ZChDLktVLEgudWQoQy5mUSxILnVkKEMuaTcsSC51ZChDLmk3
+LEgudWQoQy54aSxILnVkKEMuZGssSC51ZChDLndiKEMuTzQpLG4pKSkpKSkpCmlmKHR5cGVvZiBkYXJ0
+TmF0aXZlRGlzcGF0Y2hIb29rc1RyYW5zZm9ybWVyIT0idW5kZWZpbmVkIil7dD1kYXJ0TmF0aXZlRGlz
+cGF0Y2hIb29rc1RyYW5zZm9ybWVyCmlmKHR5cGVvZiB0PT0iZnVuY3Rpb24iKXQ9W3RdCmlmKHQuY29u
+c3RydWN0b3I9PUFycmF5KWZvcihzPTA7czx0Lmxlbmd0aDsrK3Mpe3I9dFtzXQppZih0eXBlb2Ygcj09
+ImZ1bmN0aW9uIiluPXIobil8fG59fXE9bi5nZXRUYWcKcD1uLmdldFVua25vd25UYWcKbz1uLnByb3Rv
+dHlwZUZvclRhZwokLk5GPW5ldyBILmRDKHEpCiQuVFg9bmV3IEgud04ocCkKJC54Nz1uZXcgSC5WWChv
+KX0sCnVkOmZ1bmN0aW9uKGEsYil7cmV0dXJuIGEoYil8fGJ9LAp2NDpmdW5jdGlvbihhLGIsYyxkLGUs
+Zil7dmFyIHQ9Yj8ibSI6IiIscz1jPyIiOiJpIixyPWQ/InUiOiIiLHE9ZT8icyI6IiIscD1mPyJnIjoi
+IixvPWZ1bmN0aW9uKGcsaCl7dHJ5e3JldHVybiBuZXcgUmVnRXhwKGcsaCl9Y2F0Y2gobil7cmV0dXJu
+IG59fShhLHQrcytyK3ErcCkKaWYobyBpbnN0YW5jZW9mIFJlZ0V4cClyZXR1cm4gbwp0aHJvdyBILmIo
+UC5ycigiSWxsZWdhbCBSZWdFeHAgcGF0dGVybiAoIitTdHJpbmcobykrIikiLGEsbnVsbCkpfSwKbTI6
+ZnVuY3Rpb24oYSxiLGMpe3ZhciB0CmlmKHR5cGVvZiBiPT0ic3RyaW5nIilyZXR1cm4gYS5pbmRleE9m
+KGIsYyk+PTAKZWxzZSBpZihiIGluc3RhbmNlb2YgSC5WUil7dD1DLnhCLkcoYSxjKQpyZXR1cm4gYi5i
+LnRlc3QodCl9ZWxzZXt0PUouRkwoYixDLnhCLkcoYSxjKSkKcmV0dXJuIXQuZ2wwKHQpfX0sCkE0OmZ1
+bmN0aW9uKGEpe2lmKGEuaW5kZXhPZigiJCIsMCk+PTApcmV0dXJuIGEucmVwbGFjZSgvXCQvZywiJCQk
+JCIpCnJldHVybiBhfSwKZUE6ZnVuY3Rpb24oYSl7aWYoL1tbXF17fSgpKis/LlxcXiR8XS8udGVzdChh
+KSlyZXR1cm4gYS5yZXBsYWNlKC9bW1xde30oKSorPy5cXF4kfF0vZywiXFwkJiIpCnJldHVybiBhfSwK
+eXM6ZnVuY3Rpb24oYSxiLGMpe3ZhciB0PUgubk0oYSxiLGMpCnJldHVybiB0fSwKbk06ZnVuY3Rpb24o
+YSxiLGMpe3ZhciB0LHMscixxCmlmKGI9PT0iIil7aWYoYT09PSIiKXJldHVybiBjCnQ9YS5sZW5ndGgK
+Zm9yKHM9YyxyPTA7cjx0OysrcilzPXMrYVtyXStjCnJldHVybiBzLmNoYXJDb2RlQXQoMCk9PTA/czpz
+fXE9YS5pbmRleE9mKGIsMCkKaWYocTwwKXJldHVybiBhCmlmKGEubGVuZ3RoPDUwMHx8Yy5pbmRleE9m
+KCIkIiwwKT49MClyZXR1cm4gYS5zcGxpdChiKS5qb2luKGMpCnJldHVybiBhLnJlcGxhY2UobmV3IFJl
+Z0V4cChILmVBKGIpLCdnJyksSC5BNChjKSl9LApQRDpmdW5jdGlvbiBQRChhLGIpe3RoaXMuYT1hCnRo
+aXMuJHRpPWJ9LApXVTpmdW5jdGlvbiBXVSgpe30sCkxQOmZ1bmN0aW9uIExQKGEsYixjLGQpe3ZhciBf
+PXRoaXMKXy5hPWEKXy5iPWIKXy5jPWMKXy4kdGk9ZH0sClhSOmZ1bmN0aW9uIFhSKGEsYil7dGhpcy5h
+PWEKdGhpcy4kdGk9Yn0sCkxJOmZ1bmN0aW9uIExJKGEsYixjLGQsZSl7dmFyIF89dGhpcwpfLmE9YQpf
+LmM9YgpfLmQ9YwpfLmU9ZApfLmY9ZX0sCkNqOmZ1bmN0aW9uIENqKGEsYixjKXt0aGlzLmE9YQp0aGlz
+LmI9Ygp0aGlzLmM9Y30sCmY5OmZ1bmN0aW9uIGY5KGEsYixjLGQsZSxmKXt2YXIgXz10aGlzCl8uYT1h
+Cl8uYj1iCl8uYz1jCl8uZD1kCl8uZT1lCl8uZj1mfSwKVzA6ZnVuY3Rpb24gVzAoYSxiKXt0aGlzLmE9
+YQp0aGlzLmI9Yn0sCmF6OmZ1bmN0aW9uIGF6KGEsYixjKXt0aGlzLmE9YQp0aGlzLmI9Ygp0aGlzLmM9
+Y30sCnZWOmZ1bmN0aW9uIHZWKGEpe3RoaXMuYT1hfSwKdGU6ZnVuY3Rpb24gdGUoYSl7dGhpcy5hPWF9
+LApicTpmdW5jdGlvbiBicShhLGIpe3RoaXMuYT1hCnRoaXMuYj1ifSwKWE86ZnVuY3Rpb24gWE8oYSl7
+dGhpcy5hPWEKdGhpcy5iPW51bGx9LAp2OmZ1bmN0aW9uIHYoKXt9LApsYzpmdW5jdGlvbiBsYygpe30s
+Cnp4OmZ1bmN0aW9uIHp4KCl7fSwKclQ6ZnVuY3Rpb24gclQoYSxiLGMsZCl7dmFyIF89dGhpcwpfLmE9
+YQpfLmI9YgpfLmM9YwpfLmQ9ZH0sCkVxOmZ1bmN0aW9uIEVxKGEpe3RoaXMuYT1hfSwKa1k6ZnVuY3Rp
+b24ga1koYSl7dGhpcy5hPWF9LAprcjpmdW5jdGlvbiBrcigpe30sCk41OmZ1bmN0aW9uIE41KGEpe3Zh
+ciBfPXRoaXMKXy5hPTAKXy5mPV8uZT1fLmQ9Xy5jPV8uYj1udWxsCl8ucj0wCl8uJHRpPWF9LApkYjpm
+dW5jdGlvbiBkYihhLGIpe3ZhciBfPXRoaXMKXy5hPWEKXy5iPWIKXy5kPV8uYz1udWxsfSwKaTU6ZnVu
+Y3Rpb24gaTUoYSxiKXt0aGlzLmE9YQp0aGlzLiR0aT1ifSwKTjY6ZnVuY3Rpb24gTjYoYSxiLGMpe3Zh
+ciBfPXRoaXMKXy5hPWEKXy5iPWIKXy5kPV8uYz1udWxsCl8uJHRpPWN9LApkQzpmdW5jdGlvbiBkQyhh
+KXt0aGlzLmE9YX0sCndOOmZ1bmN0aW9uIHdOKGEpe3RoaXMuYT1hfSwKVlg6ZnVuY3Rpb24gVlgoYSl7
+dGhpcy5hPWF9LApWUjpmdW5jdGlvbiBWUihhLGIpe3ZhciBfPXRoaXMKXy5hPWEKXy5iPWIKXy5kPV8u
+Yz1udWxsfSwKRUs6ZnVuY3Rpb24gRUsoYSl7dGhpcy5iPWF9LApLVzpmdW5jdGlvbiBLVyhhLGIsYyl7
+dGhpcy5hPWEKdGhpcy5iPWIKdGhpcy5jPWN9LApQYjpmdW5jdGlvbiBQYihhLGIsYyl7dmFyIF89dGhp
+cwpfLmE9YQpfLmI9YgpfLmM9YwpfLmQ9bnVsbH0sCnRROmZ1bmN0aW9uIHRRKGEsYil7dGhpcy5hPWEK
+dGhpcy5jPWJ9LAp1bjpmdW5jdGlvbiB1bihhLGIsYyl7dGhpcy5hPWEKdGhpcy5iPWIKdGhpcy5jPWN9
+LApTZDpmdW5jdGlvbiBTZChhLGIsYyl7dmFyIF89dGhpcwpfLmE9YQpfLmI9YgpfLmM9YwpfLmQ9bnVs
+bH0sClhGOmZ1bmN0aW9uKGEpe3JldHVybiBhfSwKb2Q6ZnVuY3Rpb24oYSxiLGMpe2lmKGE+Pj4wIT09
+YXx8YT49Yyl0aHJvdyBILmIoSC5IWShiLGEpKX0sCnJNOmZ1bmN0aW9uKGEsYixjKXt2YXIgdAppZigh
+KGE+Pj4wIT09YSkpdD1iPj4+MCE9PWJ8fGE+Ynx8Yj5jCmVsc2UgdD0hMAppZih0KXRocm93IEguYihI
+LmF1KGEsYixjKSkKcmV0dXJuIGJ9LApFVDpmdW5jdGlvbiBFVCgpe30sCkxaOmZ1bmN0aW9uIExaKCl7
+fSwKRGc6ZnVuY3Rpb24gRGcoKXt9LApQZzpmdW5jdGlvbiBQZygpe30sCnhqOmZ1bmN0aW9uIHhqKCl7
+fSwKZEU6ZnVuY3Rpb24gZEUoKXt9LApaQTpmdW5jdGlvbiBaQSgpe30sCmRUOmZ1bmN0aW9uIGRUKCl7
+fSwKUHE6ZnVuY3Rpb24gUHEoKXt9LAplRTpmdW5jdGlvbiBlRSgpe30sClY2OmZ1bmN0aW9uIFY2KCl7
+fSwKUkc6ZnVuY3Rpb24gUkcoKXt9LApWUDpmdW5jdGlvbiBWUCgpe30sCldCOmZ1bmN0aW9uIFdCKCl7
+fSwKWkc6ZnVuY3Rpb24gWkcoKXt9LApjejpmdW5jdGlvbihhLGIpe3ZhciB0PWIuYwpyZXR1cm4gdD09
+bnVsbD9iLmM9SC5CKGEsYi56LCEwKTp0fSwKeFo6ZnVuY3Rpb24oYSxiKXt2YXIgdD1iLmMKcmV0dXJu
+IHQ9PW51bGw/Yi5jPUguSihhLCJiOCIsW2Iuel0pOnR9LApRMTpmdW5jdGlvbihhKXt2YXIgdD1hLnkK
+aWYodD09PTZ8fHQ9PT03fHx0PT09OClyZXR1cm4gSC5RMShhLnopCnJldHVybiB0PT09MTF8fHQ9PT0x
+Mn0sCm1EOmZ1bmN0aW9uKGEpe3JldHVybiBhLmN5fSwKTjA6ZnVuY3Rpb24oYSl7cmV0dXJuIEguRSh2
+LnR5cGVVbml2ZXJzZSxhLCExKX0sClBMOmZ1bmN0aW9uKGEsYixjLGEwKXt2YXIgdCxzLHIscSxwLG8s
+bixtLGwsayxqLGksaCxnLGYsZSxkPWIueQpzd2l0Y2goZCl7Y2FzZSA1OmNhc2UgMTpjYXNlIDI6Y2Fz
+ZSAzOmNhc2UgNDpyZXR1cm4gYgpjYXNlIDY6dD1iLnoKcz1ILlBMKGEsdCxjLGEwKQppZihzPT09dCly
+ZXR1cm4gYgpyZXR1cm4gSC5DKGEscywhMCkKY2FzZSA3OnQ9Yi56CnM9SC5QTChhLHQsYyxhMCkKaWYo
+cz09PXQpcmV0dXJuIGIKcmV0dXJuIEguQihhLHMsITApCmNhc2UgODp0PWIuegpzPUguUEwoYSx0LGMs
+YTApCmlmKHM9PT10KXJldHVybiBiCnJldHVybiBILmYoYSxzLCEwKQpjYXNlIDk6cj1iLlEKcT1ILmJa
+KGEscixjLGEwKQppZihxPT09cilyZXR1cm4gYgpyZXR1cm4gSC5KKGEsYi56LHEpCmNhc2UgMTA6cD1i
+LnoKbz1ILlBMKGEscCxjLGEwKQpuPWIuUQptPUguYlooYSxuLGMsYTApCmlmKG89PT1wJiZtPT09bily
+ZXR1cm4gYgpyZXR1cm4gSC5hKGEsbyxtKQpjYXNlIDExOmw9Yi56Cms9SC5QTChhLGwsYyxhMCkKaj1i
+LlEKaT1ILnFUKGEsaixjLGEwKQppZihrPT09bCYmaT09PWopcmV0dXJuIGIKcmV0dXJuIEguZChhLGss
+aSkKY2FzZSAxMjpoPWIuUQphMCs9aC5sZW5ndGgKZz1ILmJaKGEsaCxjLGEwKQpwPWIuegpvPUguUEwo
+YSxwLGMsYTApCmlmKGc9PT1oJiZvPT09cClyZXR1cm4gYgpyZXR1cm4gSC5EKGEsbyxnLCEwKQpjYXNl
+IDEzOmY9Yi56CmlmKGY8YTApcmV0dXJuIGIKZT1jW2YtYTBdCmlmKGU9PW51bGwpcmV0dXJuIGIKcmV0
+dXJuIGUKZGVmYXVsdDp0aHJvdyBILmIoUC5oVigiQXR0ZW1wdGVkIHRvIHN1YnN0aXR1dGUgdW5leHBl
+Y3RlZCBSVEkga2luZCAiK2QpKX19LApiWjpmdW5jdGlvbihhLGIsYyxkKXt2YXIgdCxzLHIscSxwPWIu
+bGVuZ3RoLG89W10KZm9yKHQ9ITEscz0wO3M8cDsrK3Mpe3I9YltzXQpxPUguUEwoYSxyLGMsZCkKaWYo
+cSE9PXIpdD0hMApvLnB1c2gocSl9cmV0dXJuIHQ/bzpifSwKdk86ZnVuY3Rpb24oYSxiLGMsZCl7dmFy
+IHQscyxyLHEscCxvLG49Yi5sZW5ndGgsbT1bXQpmb3IodD0hMSxzPTA7czxuO3MrPTMpe3I9YltzXQpx
+PWJbcysxXQpwPWJbcysyXQpvPUguUEwoYSxwLGMsZCkKaWYobyE9PXApdD0hMAptLnB1c2gocikKbS5w
+dXNoKHEpCm0ucHVzaChvKX1yZXR1cm4gdD9tOmJ9LApxVDpmdW5jdGlvbihhLGIsYyxkKXt2YXIgdCxz
+PWIuYSxyPUguYlooYSxzLGMsZCkscT1iLmIscD1ILmJaKGEscSxjLGQpLG89Yi5jLG49SC52TyhhLG8s
+YyxkKQppZihyPT09cyYmcD09PXEmJm49PT1vKXJldHVybiBiCnQ9bmV3IEguRygpCnQuYT1yCnQuYj1w
+CnQuYz1uCnJldHVybiB0fSwKSlM6ZnVuY3Rpb24oYSl7dmFyIHQ9YS4kUwppZih0IT1udWxsKXtpZih0
+eXBlb2YgdD09Im51bWJlciIpcmV0dXJuIEguQnAodCkKcmV0dXJuIGEuJFMoKX1yZXR1cm4gbnVsbH0s
+ClVlOmZ1bmN0aW9uKGEsYil7dmFyIHQKaWYoSC5RMShiKSlpZihhIGluc3RhbmNlb2YgSC52KXt0PUgu
+SlMoYSkKaWYodCE9bnVsbClyZXR1cm4gdH1yZXR1cm4gSC56KGEpfSwKejpmdW5jdGlvbihhKXt2YXIg
+dAppZihhIGluc3RhbmNlb2YgUC5NaCl7dD1hLiR0aQpyZXR1cm4gdCE9bnVsbD90OkguVlUoYSl9aWYo
+QXJyYXkuaXNBcnJheShhKSlyZXR1cm4gSC50NihhKQpyZXR1cm4gSC5WVShKLmlhKGEpKX0sCnQ2OmZ1
+bmN0aW9uKGEpe3ZhciB0PWFbdi5hcnJheVJ0aV0scz11LnUKaWYodD09bnVsbClyZXR1cm4gcwppZih0
+LmNvbnN0cnVjdG9yIT09cy5jb25zdHJ1Y3RvcilyZXR1cm4gcwpyZXR1cm4gdH0sCkxoOmZ1bmN0aW9u
+KGEpe3ZhciB0PWEuJHRpCnJldHVybiB0IT1udWxsP3Q6SC5WVShhKX0sClZVOmZ1bmN0aW9uKGEpe3Zh
+ciB0PWEuY29uc3RydWN0b3Iscz10LiRjY2FjaGUKaWYocyE9bnVsbClyZXR1cm4gcwpyZXR1cm4gSC5y
+OShhLHQpfSwKcjk6ZnVuY3Rpb24oYSxiKXt2YXIgdD1hIGluc3RhbmNlb2YgSC52P2EuX19wcm90b19f
+Ll9fcHJvdG9fXy5jb25zdHJ1Y3RvcjpiLHM9SC5haSh2LnR5cGVVbml2ZXJzZSx0Lm5hbWUpCmIuJGNj
+YWNoZT1zCnJldHVybiBzfSwKQnA6ZnVuY3Rpb24oYSl7dmFyIHQscyxyCkgudVAoYSkKdD12LnR5cGVz
+CnM9dFthXQppZih0eXBlb2Ygcz09InN0cmluZyIpe3I9SC5FKHYudHlwZVVuaXZlcnNlLHMsITEpCnRb
+YV09cgpyZXR1cm4gcn1yZXR1cm4gc30sCkpKOmZ1bmN0aW9uKGEpe3ZhciB0LHMscj10aGlzLHE9dS5L
+CmlmKHI9PT1xKXJldHVybiBILlJFKHIsYSxILmtlKQppZighSC5BOChyKSlpZighKHI9PT11Ll8pKXE9
+cj09PXEKZWxzZSBxPSEwCmVsc2UgcT0hMAppZihxKXJldHVybiBILlJFKHIsYSxILkl3KQpxPXIueQp0
+PXE9PT02P3IuejpyCmlmKHQ9PT11LlMpcz1ILm9rCmVsc2UgaWYodD09PXUuZ1J8fHQ9PT11LmRpKXM9
+SC5LSAplbHNlIGlmKHQ9PT11Lk4pcz1ILk1NCmVsc2Ugcz10PT09dS55P0gubDpudWxsCmlmKHMhPW51
+bGwpcmV0dXJuIEguUkUocixhLHMpCmlmKHQueT09PTkpe3E9dC56CmlmKHQuUS5ldmVyeShILmNjKSl7
+ci5yPSIkaSIrcQpyZXR1cm4gSC5SRShyLGEsSC50NCl9fWVsc2UgaWYocT09PTcpcmV0dXJuIEguUkUo
+cixhLEguQVEpCnJldHVybiBILlJFKHIsYSxILllPKX0sClJFOmZ1bmN0aW9uKGEsYixjKXthLmI9Ywpy
+ZXR1cm4gYS5iKGIpfSwKQXU6ZnVuY3Rpb24oYSl7dmFyIHQscyxyPXRoaXMKaWYoIUguQTgocikpaWYo
+IShyPT09dS5fKSl0PXI9PT11LksKZWxzZSB0PSEwCmVsc2UgdD0hMAppZih0KXM9SC5obgplbHNlIGlm
+KHI9PT11Lkspcz1ILlRpCmVsc2Ugcz1ILmw0CnIuYT1zCnJldHVybiByLmEoYSl9LApRajpmdW5jdGlv
+bihhKXt2YXIgdCxzPWEueQppZighSC5BOChhKSlpZighKGE9PT11Ll8pKXQ9YT09PXUuSwplbHNlIHQ9
+ITAKZWxzZSB0PSEwCnJldHVybiB0fHxhPT09dS5hd3x8cz09PTd8fGE9PT11LlB8fGE9PT11LlR9LApZ
+TzpmdW5jdGlvbihhKXt2YXIgdD10aGlzCmlmKGE9PW51bGwpcmV0dXJuIEguUWoodCkKcmV0dXJuIEgu
+V2Uodi50eXBlVW5pdmVyc2UsSC5VZShhLHQpLG51bGwsdCxudWxsKX0sCkFROmZ1bmN0aW9uKGEpe2lm
+KGE9PW51bGwpcmV0dXJuITAKcmV0dXJuIHRoaXMuei5iKGEpfSwKdDQ6ZnVuY3Rpb24oYSl7dmFyIHQ9
+dGhpcyxzPXQucgppZihhIGluc3RhbmNlb2YgUC5NaClyZXR1cm4hIWFbc10KcmV0dXJuISFKLmlhKGEp
+W3NdfSwKT3o6ZnVuY3Rpb24oYSl7dmFyIHQ9dGhpcwppZihhPT1udWxsKXJldHVybiBhCmVsc2UgaWYo
+dC5iKGEpKXJldHVybiBhCkgubTQoYSx0KX0sCmw0OmZ1bmN0aW9uKGEpe3ZhciB0PXRoaXMKaWYoYT09
+bnVsbClyZXR1cm4gYQplbHNlIGlmKHQuYihhKSlyZXR1cm4gYQpILm00KGEsdCl9LAptNDpmdW5jdGlv
+bihhLGIpe3Rocm93IEguYihILlpjKEguV0soYSxILlVlKGEsYiksSC5kbShiLG51bGwpKSkpfSwKRGg6
+ZnVuY3Rpb24oYSxiLGMsZCl7dmFyIHQ9bnVsbAppZihILldlKHYudHlwZVVuaXZlcnNlLGEsdCxiLHQp
+KXJldHVybiBhCnRocm93IEguYihILlpjKCJUaGUgdHlwZSBhcmd1bWVudCAnIitILkVqKEguZG0oYSx0
+KSkrIicgaXMgbm90IGEgc3VidHlwZSBvZiB0aGUgdHlwZSB2YXJpYWJsZSBib3VuZCAnIitILkVqKEgu
+ZG0oYix0KSkrIicgb2YgdHlwZSB2YXJpYWJsZSAnIitILkVqKGMpKyInIGluICciK0guRWooZCkrIicu
+IikpfSwKV0s6ZnVuY3Rpb24oYSxiLGMpe3ZhciB0PVAucChhKSxzPUguZG0oYj09bnVsbD9ILnooYSk6
+YixudWxsKQpyZXR1cm4gdCsiOiB0eXBlICciK0guRWoocykrIicgaXMgbm90IGEgc3VidHlwZSBvZiB0
+eXBlICciK0guRWooYykrIicifSwKWmM6ZnVuY3Rpb24oYSl7cmV0dXJuIG5ldyBILmlNKCJUeXBlRXJy
+b3I6ICIrYSl9LApxOmZ1bmN0aW9uKGEsYil7cmV0dXJuIG5ldyBILmlNKCJUeXBlRXJyb3I6ICIrSC5X
+SyhhLG51bGwsYikpfSwKa2U6ZnVuY3Rpb24oYSl7cmV0dXJuIGEhPW51bGx9LApUaTpmdW5jdGlvbihh
+KXtyZXR1cm4gYX0sCkl3OmZ1bmN0aW9uKGEpe3JldHVybiEwfSwKaG46ZnVuY3Rpb24oYSl7cmV0dXJu
+IGF9LApsOmZ1bmN0aW9uKGEpe3JldHVybiEwPT09YXx8ITE9PT1hfSwKcDg6ZnVuY3Rpb24oYSl7aWYo
+ITA9PT1hKXJldHVybiEwCmlmKCExPT09YSlyZXR1cm4hMQp0aHJvdyBILmIoSC5xKGEsImJvb2wiKSl9
+LAp5ODpmdW5jdGlvbihhKXtpZighMD09PWEpcmV0dXJuITAKaWYoITE9PT1hKXJldHVybiExCmlmKGE9
+PW51bGwpcmV0dXJuIGEKdGhyb3cgSC5iKEgucShhLCJib29sIikpfSwKQlI6ZnVuY3Rpb24oYSl7aWYo
+ITA9PT1hKXJldHVybiEwCmlmKCExPT09YSlyZXR1cm4hMQppZihhPT1udWxsKXJldHVybiBhCnRocm93
+IEguYihILnEoYSwiYm9vbD8iKSl9LApGRzpmdW5jdGlvbihhKXtpZih0eXBlb2YgYT09Im51bWJlciIp
+cmV0dXJuIGEKdGhyb3cgSC5iKEgucShhLCJkb3VibGUiKSl9LApHSDpmdW5jdGlvbihhKXtpZih0eXBl
+b2YgYT09Im51bWJlciIpcmV0dXJuIGEKaWYoYT09bnVsbClyZXR1cm4gYQp0aHJvdyBILmIoSC5xKGEs
+ImRvdWJsZSIpKX0sClFrOmZ1bmN0aW9uKGEpe2lmKHR5cGVvZiBhPT0ibnVtYmVyIilyZXR1cm4gYQpp
+ZihhPT1udWxsKXJldHVybiBhCnRocm93IEguYihILnEoYSwiZG91YmxlPyIpKX0sCm9rOmZ1bmN0aW9u
+KGEpe3JldHVybiB0eXBlb2YgYT09Im51bWJlciImJk1hdGguZmxvb3IoYSk9PT1hfSwKSVo6ZnVuY3Rp
+b24oYSl7aWYodHlwZW9mIGE9PSJudW1iZXIiJiZNYXRoLmZsb29yKGEpPT09YSlyZXR1cm4gYQp0aHJv
+dyBILmIoSC5xKGEsImludCIpKX0sCnVQOmZ1bmN0aW9uKGEpe2lmKHR5cGVvZiBhPT0ibnVtYmVyIiYm
+TWF0aC5mbG9vcihhKT09PWEpcmV0dXJuIGEKaWYoYT09bnVsbClyZXR1cm4gYQp0aHJvdyBILmIoSC5x
+KGEsImludCIpKX0sClVjOmZ1bmN0aW9uKGEpe2lmKHR5cGVvZiBhPT0ibnVtYmVyIiYmTWF0aC5mbG9v
+cihhKT09PWEpcmV0dXJuIGEKaWYoYT09bnVsbClyZXR1cm4gYQp0aHJvdyBILmIoSC5xKGEsImludD8i
+KSl9LApLSDpmdW5jdGlvbihhKXtyZXR1cm4gdHlwZW9mIGE9PSJudW1iZXIifSwKejU6ZnVuY3Rpb24o
+YSl7aWYodHlwZW9mIGE9PSJudW1iZXIiKXJldHVybiBhCnRocm93IEguYihILnEoYSwibnVtIikpfSwK
+b0k6ZnVuY3Rpb24oYSl7aWYodHlwZW9mIGE9PSJudW1iZXIiKXJldHVybiBhCmlmKGE9PW51bGwpcmV0
+dXJuIGEKdGhyb3cgSC5iKEgucShhLCJudW0iKSl9LApjVTpmdW5jdGlvbihhKXtpZih0eXBlb2YgYT09
+Im51bWJlciIpcmV0dXJuIGEKaWYoYT09bnVsbClyZXR1cm4gYQp0aHJvdyBILmIoSC5xKGEsIm51bT8i
+KSl9LApNTTpmdW5jdGlvbihhKXtyZXR1cm4gdHlwZW9mIGE9PSJzdHJpbmcifSwKQnQ6ZnVuY3Rpb24o
+YSl7aWYodHlwZW9mIGE9PSJzdHJpbmciKXJldHVybiBhCnRocm93IEguYihILnEoYSwiU3RyaW5nIikp
+fSwKaDpmdW5jdGlvbihhKXtpZih0eXBlb2YgYT09InN0cmluZyIpcmV0dXJuIGEKaWYoYT09bnVsbCly
+ZXR1cm4gYQp0aHJvdyBILmIoSC5xKGEsIlN0cmluZyIpKX0sCms6ZnVuY3Rpb24oYSl7aWYodHlwZW9m
+IGE9PSJzdHJpbmciKXJldHVybiBhCmlmKGE9PW51bGwpcmV0dXJuIGEKdGhyb3cgSC5iKEgucShhLCJT
+dHJpbmc/IikpfSwKaW86ZnVuY3Rpb24oYSxiKXt2YXIgdCxzLHIKZm9yKHQ9IiIscz0iIixyPTA7cjxh
+Lmxlbmd0aDsrK3Iscz0iLCAiKXQrPUMueEIuaChzLEguZG0oYVtyXSxiKSkKcmV0dXJuIHR9LApiSTpm
+dW5jdGlvbihhNCxhNSxhNil7dmFyIHQscyxyLHEscCxvLG4sbSxsLGssaixpLGgsZyxmLGUsZCxjLGIs
+YSxhMCxhMSxhMixhMz0iLCAiCmlmKGE2IT1udWxsKXt0PWE2Lmxlbmd0aAppZihhNT09bnVsbCl7YTU9
+SC5WTShbXSx1LnMpCnM9bnVsbH1lbHNlIHM9YTUubGVuZ3RoCnI9YTUubGVuZ3RoCmZvcihxPXQ7cT4w
+Oy0tcSlDLk5tLmkoYTUsIlQiKyhyK3EpKQpmb3IocD11Llcsbz11Ll8sbj11LkssbT0iPCIsbD0iIixx
+PTA7cTx0OysrcSxsPWEzKXttKz1sCms9YTUubGVuZ3RoCmo9ay0xLXEKaWYoajwwKXJldHVybiBILk9I
+KGE1LGopCm09Qy54Qi5oKG0sYTVbal0pCmk9YTZbcV0KaD1pLnkKaWYoIShoPT09Mnx8aD09PTN8fGg9
+PT00fHxoPT09NXx8aT09PXApKWlmKCEoaT09PW8pKWs9aT09PW4KZWxzZSBrPSEwCmVsc2Ugaz0hMApp
+ZighayltKz1DLnhCLmgoIiBleHRlbmRzICIsSC5kbShpLGE1KSl9bSs9Ij4ifWVsc2V7bT0iIgpzPW51
+bGx9cD1hNC56Cmc9YTQuUQpmPWcuYQplPWYubGVuZ3RoCmQ9Zy5iCmM9ZC5sZW5ndGgKYj1nLmMKYT1i
+Lmxlbmd0aAphMD1ILmRtKHAsYTUpCmZvcihhMT0iIixhMj0iIixxPTA7cTxlOysrcSxhMj1hMylhMSs9
+Qy54Qi5oKGEyLEguZG0oZltxXSxhNSkpCmlmKGM+MCl7YTErPWEyKyJbIgpmb3IoYTI9IiIscT0wO3E8
+YzsrK3EsYTI9YTMpYTErPUMueEIuaChhMixILmRtKGRbcV0sYTUpKQphMSs9Il0ifWlmKGE+MCl7YTEr
+PWEyKyJ7Igpmb3IoYTI9IiIscT0wO3E8YTtxKz0zLGEyPWEzKXthMSs9YTIKaWYoYltxKzFdKWExKz0i
+cmVxdWlyZWQgIgphMSs9Si5iYihILmRtKGJbcSsyXSxhNSksIiAiKStiW3FdfWExKz0ifSJ9aWYocyE9
+bnVsbCl7YTUudG9TdHJpbmcKYTUubGVuZ3RoPXN9cmV0dXJuIG0rIigiK2ExKyIpID0+ICIrSC5Faihh
+MCl9LApkbTpmdW5jdGlvbihhLGIpe3ZhciB0LHMscixxLHAsbyxuLG09YS55CmlmKG09PT01KXJldHVy
+biJlcmFzZWQiCmlmKG09PT0yKXJldHVybiJkeW5hbWljIgppZihtPT09MylyZXR1cm4idm9pZCIKaWYo
+bT09PTEpcmV0dXJuIk5ldmVyIgppZihtPT09NClyZXR1cm4iYW55IgppZihtPT09Nil7dD1ILmRtKGEu
+eixiKQpyZXR1cm4gdH1pZihtPT09Nyl7cz1hLnoKdD1ILmRtKHMsYikKcj1zLnkKcmV0dXJuIEouYmIo
+cj09PTExfHxyPT09MTI/Qy54Qi5oKCIoIix0KSsiKSI6dCwiPyIpfWlmKG09PT04KXJldHVybiJGdXR1
+cmVPcjwiK0guRWooSC5kbShhLnosYikpKyI+IgppZihtPT09OSl7cT1ILm8zKGEueikKcD1hLlEKcmV0
+dXJuIHAubGVuZ3RoIT09MD9xKygiPCIrSC5pbyhwLGIpKyI+Iik6cX1pZihtPT09MTEpcmV0dXJuIEgu
+YkkoYSxiLG51bGwpCmlmKG09PT0xMilyZXR1cm4gSC5iSShhLnosYixhLlEpCmlmKG09PT0xMyl7Yi50
+b1N0cmluZwpvPWEuegpuPWIubGVuZ3RoCm89bi0xLW8KaWYobzwwfHxvPj1uKXJldHVybiBILk9IKGIs
+bykKcmV0dXJuIGJbb119cmV0dXJuIj8ifSwKbzM6ZnVuY3Rpb24oYSl7dmFyIHQscz1ILkpnKGEpCmlm
+KHMhPW51bGwpcmV0dXJuIHMKdD0ibWluaWZpZWQ6IithCnJldHVybiB0fSwKUW86ZnVuY3Rpb24oYSxi
+KXt2YXIgdD1hLnRSW2JdCmZvcig7dHlwZW9mIHQ9PSJzdHJpbmciOyl0PWEudFJbdF0KcmV0dXJuIHR9
+LAphaTpmdW5jdGlvbihhLGIpe3ZhciB0LHMscixxLHAsbz1hLmVULG49b1tiXQppZihuPT1udWxsKXJl
+dHVybiBILkUoYSxiLCExKQplbHNlIGlmKHR5cGVvZiBuPT0ibnVtYmVyIil7dD1uCnM9SC5tKGEsNSwi
+IyIpCnI9W10KZm9yKHE9MDtxPHQ7KytxKXIucHVzaChzKQpwPUguSihhLGIscikKb1tiXT1wCnJldHVy
+biBwfWVsc2UgcmV0dXJuIG59LAp4YjpmdW5jdGlvbihhLGIpe3JldHVybiBILkl4KGEudFIsYil9LApG
+RjpmdW5jdGlvbihhLGIpe3JldHVybiBILkl4KGEuZVQsYil9LApFOmZ1bmN0aW9uKGEsYixjKXt2YXIg
+dCxzPWEuZUMscj1zLmdldChiKQppZihyIT1udWxsKXJldHVybiByCnQ9SC5pKEgubyhhLG51bGwsYixj
+KSkKcy5zZXQoYix0KQpyZXR1cm4gdH0sCmNFOmZ1bmN0aW9uKGEsYixjKXt2YXIgdCxzLHI9Yi5jaApp
+ZihyPT1udWxsKXI9Yi5jaD1uZXcgTWFwKCkKdD1yLmdldChjKQppZih0IT1udWxsKXJldHVybiB0CnM9
+SC5pKEgubyhhLGIsYywhMCkpCnIuc2V0KGMscykKcmV0dXJuIHN9LAp2NTpmdW5jdGlvbihhLGIsYyl7
+dmFyIHQscyxyLHE9Yi5jeAppZihxPT1udWxsKXE9Yi5jeD1uZXcgTWFwKCkKdD1jLmN5CnM9cS5nZXQo
+dCkKaWYocyE9bnVsbClyZXR1cm4gcwpyPUguYShhLGIsYy55PT09MTA/Yy5ROltjXSkKcS5zZXQodCxy
+KQpyZXR1cm4gcn0sCkJEOmZ1bmN0aW9uKGEsYil7Yi5hPUguQXUKYi5iPUguSkoKcmV0dXJuIGJ9LApt
+OmZ1bmN0aW9uKGEsYixjKXt2YXIgdCxzLHI9YS5lQy5nZXQoYykKaWYociE9bnVsbClyZXR1cm4gcgp0
+PW5ldyBILkpjKG51bGwsbnVsbCkKdC55PWIKdC5jeT1jCnM9SC5CRChhLHQpCmEuZUMuc2V0KGMscykK
+cmV0dXJuIHN9LApDOmZ1bmN0aW9uKGEsYixjKXt2YXIgdCxzPWIuY3krIioiLHI9YS5lQy5nZXQocykK
+aWYociE9bnVsbClyZXR1cm4gcgp0PUguWjcoYSxiLHMsYykKYS5lQy5zZXQocyx0KQpyZXR1cm4gdH0s
+Clo3OmZ1bmN0aW9uKGEsYixjLGQpe3ZhciB0LHMscgppZihkKXt0PWIueQppZighSC5BOChiKSlzPWI9
+PT11LlB8fGI9PT11LlR8fHQ9PT03fHx0PT09NgplbHNlIHM9ITAKaWYocylyZXR1cm4gYn1yPW5ldyBI
+LkpjKG51bGwsbnVsbCkKci55PTYKci56PWIKci5jeT1jCnJldHVybiBILkJEKGEscil9LApCOmZ1bmN0
+aW9uKGEsYixjKXt2YXIgdCxzPWIuY3krIj8iLHI9YS5lQy5nZXQocykKaWYociE9bnVsbClyZXR1cm4g
+cgp0PUgubGwoYSxiLHMsYykKYS5lQy5zZXQocyx0KQpyZXR1cm4gdH0sCmxsOmZ1bmN0aW9uKGEsYixj
+LGQpe3ZhciB0LHMscixxCmlmKGQpe3Q9Yi55CmlmKCFILkE4KGIpKWlmKCEoYj09PXUuUHx8Yj09PXUu
+VCkpaWYodCE9PTcpcz10PT09OCYmSC5sUihiLnopCmVsc2Ugcz0hMAplbHNlIHM9ITAKZWxzZSBzPSEw
+CmlmKHMpcmV0dXJuIGIKZWxzZSBpZih0PT09MXx8Yj09PXUuYXcpcmV0dXJuIHUuUAplbHNlIGlmKHQ9
+PT02KXtyPWIuegppZihyLnk9PT04JiZILmxSKHIueikpcmV0dXJuIHIKZWxzZSByZXR1cm4gSC5jeihh
+LGIpfX1xPW5ldyBILkpjKG51bGwsbnVsbCkKcS55PTcKcS56PWIKcS5jeT1jCnJldHVybiBILkJEKGEs
+cSl9LApmOmZ1bmN0aW9uKGEsYixjKXt2YXIgdCxzPWIuY3krIi8iLHI9YS5lQy5nZXQocykKaWYociE9
+bnVsbClyZXR1cm4gcgp0PUguZVYoYSxiLHMsYykKYS5lQy5zZXQocyx0KQpyZXR1cm4gdH0sCmVWOmZ1
+bmN0aW9uKGEsYixjLGQpe3ZhciB0LHMscgppZihkKXt0PWIueQppZighSC5BOChiKSlpZighKGI9PT11
+Ll8pKXM9Yj09PXUuSwplbHNlIHM9ITAKZWxzZSBzPSEwCmlmKHN8fGI9PT11LkspcmV0dXJuIGIKZWxz
+ZSBpZih0PT09MSlyZXR1cm4gSC5KKGEsImI4IixbYl0pCmVsc2UgaWYoYj09PXUuUHx8Yj09PXUuVCly
+ZXR1cm4gdS5iR31yPW5ldyBILkpjKG51bGwsbnVsbCkKci55PTgKci56PWIKci5jeT1jCnJldHVybiBI
+LkJEKGEscil9LApIOmZ1bmN0aW9uKGEsYil7dmFyIHQscyxyPSIiK2IrIl4iLHE9YS5lQy5nZXQocikK
+aWYocSE9bnVsbClyZXR1cm4gcQp0PW5ldyBILkpjKG51bGwsbnVsbCkKdC55PTEzCnQuej1iCnQuY3k9
+cgpzPUguQkQoYSx0KQphLmVDLnNldChyLHMpCnJldHVybiBzfSwKVXg6ZnVuY3Rpb24oYSl7dmFyIHQs
+cyxyLHE9YS5sZW5ndGgKZm9yKHQ9IiIscz0iIixyPTA7cjxxOysrcixzPSIsIil0Kz1zK2Fbcl0uY3kK
+cmV0dXJuIHR9LApTNDpmdW5jdGlvbihhKXt2YXIgdCxzLHIscSxwLG8sbj1hLmxlbmd0aApmb3IodD0i
+IixzPSIiLHI9MDtyPG47cis9MyxzPSIsIil7cT1hW3JdCnA9YVtyKzFdPyIhIjoiOiIKbz1hW3IrMl0u
+Y3kKdCs9cytxK3Arb31yZXR1cm4gdH0sCko6ZnVuY3Rpb24oYSxiLGMpe3ZhciB0LHMscixxPWIKaWYo
+Yy5sZW5ndGghPT0wKXErPSI8IitILlV4KGMpKyI+Igp0PWEuZUMuZ2V0KHEpCmlmKHQhPW51bGwpcmV0
+dXJuIHQKcz1uZXcgSC5KYyhudWxsLG51bGwpCnMueT05CnMuej1iCnMuUT1jCmlmKGMubGVuZ3RoPjAp
+cy5jPWNbMF0Kcy5jeT1xCnI9SC5CRChhLHMpCmEuZUMuc2V0KHEscikKcmV0dXJuIHJ9LAphOmZ1bmN0
+aW9uKGEsYixjKXt2YXIgdCxzLHIscSxwLG8KaWYoYi55PT09MTApe3Q9Yi56CnM9Yi5RLmNvbmNhdChj
+KX1lbHNle3M9Ywp0PWJ9cj10LmN5KygiOzwiK0guVXgocykrIj4iKQpxPWEuZUMuZ2V0KHIpCmlmKHEh
+PW51bGwpcmV0dXJuIHEKcD1uZXcgSC5KYyhudWxsLG51bGwpCnAueT0xMApwLno9dApwLlE9cwpwLmN5
+PXIKbz1ILkJEKGEscCkKYS5lQy5zZXQocixvKQpyZXR1cm4gb30sCmQ6ZnVuY3Rpb24oYSxiLGMpe3Zh
+ciB0LHMscixxLHAsbz1iLmN5LG49Yy5hLG09bi5sZW5ndGgsbD1jLmIsaz1sLmxlbmd0aCxqPWMuYyxp
+PWoubGVuZ3RoLGg9IigiK0guVXgobikKaWYoaz4wKXt0PW0+MD8iLCI6IiIKcz1ILlV4KGwpCmgrPXQr
+IlsiK3MrIl0ifWlmKGk+MCl7dD1tPjA/IiwiOiIiCnM9SC5TNChqKQpoKz10KyJ7IitzKyJ9In1yPW8r
+KGgrIikiKQpxPWEuZUMuZ2V0KHIpCmlmKHEhPW51bGwpcmV0dXJuIHEKcD1uZXcgSC5KYyhudWxsLG51
+bGwpCnAueT0xMQpwLno9YgpwLlE9YwpwLmN5PXIKcz1ILkJEKGEscCkKYS5lQy5zZXQocixzKQpyZXR1
+cm4gc30sCkQ6ZnVuY3Rpb24oYSxiLGMsZCl7dmFyIHQscz1iLmN5KygiPCIrSC5VeChjKSsiPiIpLHI9
+YS5lQy5nZXQocykKaWYociE9bnVsbClyZXR1cm4gcgp0PUguaHcoYSxiLGMscyxkKQphLmVDLnNldChz
+LHQpCnJldHVybiB0fSwKaHc6ZnVuY3Rpb24oYSxiLGMsZCxlKXt2YXIgdCxzLHIscSxwLG8sbixtCmlm
+KGUpe3Q9Yy5sZW5ndGgKcz1uZXcgQXJyYXkodCkKZm9yKHI9MCxxPTA7cTx0OysrcSl7cD1jW3FdCmlm
+KHAueT09PTEpe3NbcV09cDsrK3J9fWlmKHI+MCl7bz1ILlBMKGEsYixzLDApCm49SC5iWihhLGMscyww
+KQpyZXR1cm4gSC5EKGEsbyxuLGMhPT1uKX19bT1uZXcgSC5KYyhudWxsLG51bGwpCm0ueT0xMgptLno9
+YgptLlE9YwptLmN5PWQKcmV0dXJuIEguQkQoYSxtKX0sCm86ZnVuY3Rpb24oYSxiLGMsZCl7cmV0dXJu
+e3U6YSxlOmIscjpjLHM6W10scDowLG46ZH19LAppOmZ1bmN0aW9uKGEpe3ZhciB0LHMscixxLHAsbyxu
+LG0sbCxrLGosaSxoPWEucixnPWEucwpmb3IodD1oLmxlbmd0aCxzPTA7czx0Oyl7cj1oLmNoYXJDb2Rl
+QXQocykKaWYocj49NDgmJnI8PTU3KXM9SC5BKHMrMSxyLGgsZykKZWxzZSBpZigoKChyfDMyKT4+PjAp
+LTk3JjY1NTM1KTwyNnx8cj09PTk1fHxyPT09MzYpcz1ILnQoYSxzLGgsZywhMSkKZWxzZSBpZihyPT09
+NDYpcz1ILnQoYSxzLGgsZywhMCkKZWxzZXsrK3MKc3dpdGNoKHIpe2Nhc2UgNDQ6YnJlYWsKY2FzZSA1
+ODpnLnB1c2goITEpCmJyZWFrCmNhc2UgMzM6Zy5wdXNoKCEwKQpicmVhawpjYXNlIDU5OmcucHVzaChI
+LksoYS51LGEuZSxnLnBvcCgpKSkKYnJlYWsKY2FzZSA5NDpnLnB1c2goSC5IKGEudSxnLnBvcCgpKSkK
+YnJlYWsKY2FzZSAzNTpnLnB1c2goSC5tKGEudSw1LCIjIikpCmJyZWFrCmNhc2UgNjQ6Zy5wdXNoKEgu
+bShhLnUsMiwiQCIpKQpicmVhawpjYXNlIDEyNjpnLnB1c2goSC5tKGEudSwzLCJ+IikpCmJyZWFrCmNh
+c2UgNjA6Zy5wdXNoKGEucCkKYS5wPWcubGVuZ3RoCmJyZWFrCmNhc2UgNjI6cT1hLnUKcD1nLnNwbGlj
+ZShhLnApCkgucihhLnUsYS5lLHApCmEucD1nLnBvcCgpCm89Zy5wb3AoKQppZih0eXBlb2Ygbz09InN0
+cmluZyIpZy5wdXNoKEguSihxLG8scCkpCmVsc2V7bj1ILksocSxhLmUsbykKc3dpdGNoKG4ueSl7Y2Fz
+ZSAxMTpnLnB1c2goSC5EKHEsbixwLGEubikpCmJyZWFrCmRlZmF1bHQ6Zy5wdXNoKEguYShxLG4scCkp
+CmJyZWFrfX1icmVhawpjYXNlIDM4OkguSShhLGcpCmJyZWFrCmNhc2UgNDI6bT1hLnUKZy5wdXNoKEgu
+QyhtLEguSyhtLGEuZSxnLnBvcCgpKSxhLm4pKQpicmVhawpjYXNlIDYzOm09YS51CmcucHVzaChILkIo
+bSxILksobSxhLmUsZy5wb3AoKSksYS5uKSkKYnJlYWsKY2FzZSA0NzptPWEudQpnLnB1c2goSC5mKG0s
+SC5LKG0sYS5lLGcucG9wKCkpLGEubikpCmJyZWFrCmNhc2UgNDA6Zy5wdXNoKGEucCkKYS5wPWcubGVu
+Z3RoCmJyZWFrCmNhc2UgNDE6cT1hLnUKbD1uZXcgSC5HKCkKaz1xLnNFQQpqPXEuc0VBCm89Zy5wb3Ao
+KQppZih0eXBlb2Ygbz09Im51bWJlciIpc3dpdGNoKG8pe2Nhc2UtMTprPWcucG9wKCkKYnJlYWsKY2Fz
+ZS0yOmo9Zy5wb3AoKQpicmVhawpkZWZhdWx0OmcucHVzaChvKQpicmVha31lbHNlIGcucHVzaChvKQpw
+PWcuc3BsaWNlKGEucCkKSC5yKGEudSxhLmUscCkKYS5wPWcucG9wKCkKbC5hPXAKbC5iPWsKbC5jPWoK
+Zy5wdXNoKEguZChxLEguSyhxLGEuZSxnLnBvcCgpKSxsKSkKYnJlYWsKY2FzZSA5MTpnLnB1c2goYS5w
+KQphLnA9Zy5sZW5ndGgKYnJlYWsKY2FzZSA5MzpwPWcuc3BsaWNlKGEucCkKSC5yKGEudSxhLmUscCkK
+YS5wPWcucG9wKCkKZy5wdXNoKHApCmcucHVzaCgtMSkKYnJlYWsKY2FzZSAxMjM6Zy5wdXNoKGEucCkK
+YS5wPWcubGVuZ3RoCmJyZWFrCmNhc2UgMTI1OnA9Zy5zcGxpY2UoYS5wKQpILnkoYS51LGEuZSxwKQph
+LnA9Zy5wb3AoKQpnLnB1c2gocCkKZy5wdXNoKC0yKQpicmVhawpkZWZhdWx0OnRocm93IkJhZCBjaGFy
+YWN0ZXIgIityfX19aT1nLnBvcCgpCnJldHVybiBILksoYS51LGEuZSxpKX0sCkE6ZnVuY3Rpb24oYSxi
+LGMsZCl7dmFyIHQscyxyPWItNDgKZm9yKHQ9Yy5sZW5ndGg7YTx0OysrYSl7cz1jLmNoYXJDb2RlQXQo
+YSkKaWYoIShzPj00OCYmczw9NTcpKWJyZWFrCnI9cioxMCsocy00OCl9ZC5wdXNoKHIpCnJldHVybiBh
+fSwKdDpmdW5jdGlvbihhLGIsYyxkLGUpe3ZhciB0LHMscixxLHAsbyxuPWIrMQpmb3IodD1jLmxlbmd0
+aDtuPHQ7KytuKXtzPWMuY2hhckNvZGVBdChuKQppZihzPT09NDYpe2lmKGUpYnJlYWsKZT0hMH1lbHNl
+e2lmKCEoKCgoc3wzMik+Pj4wKS05NyY2NTUzNSk8MjZ8fHM9PT05NXx8cz09PTM2KSlyPXM+PTQ4JiZz
+PD01NwplbHNlIHI9ITAKaWYoIXIpYnJlYWt9fXE9Yy5zdWJzdHJpbmcoYixuKQppZihlKXt0PWEudQpw
+PWEuZQppZihwLnk9PT0xMClwPXAuegpvPUguUW8odCxwLnopW3FdCmlmKG89PW51bGwpSC52aCgnTm8g
+IicrcSsnIiBpbiAiJytILm1EKHApKyciJykKZC5wdXNoKEguY0UodCxwLG8pKX1lbHNlIGQucHVzaChx
+KQpyZXR1cm4gbn0sCkk6ZnVuY3Rpb24oYSxiKXt2YXIgdD1iLnBvcCgpCmlmKDA9PT10KXtiLnB1c2go
+SC5tKGEudSwxLCIwJiIpKQpyZXR1cm59aWYoMT09PXQpe2IucHVzaChILm0oYS51LDQsIjEmIikpCnJl
+dHVybn10aHJvdyBILmIoUC5oVigiVW5leHBlY3RlZCBleHRlbmRlZCBvcGVyYXRpb24gIitILkVqKHQp
+KSl9LApLOmZ1bmN0aW9uKGEsYixjKXtpZih0eXBlb2YgYz09InN0cmluZyIpcmV0dXJuIEguSihhLGMs
+YS5zRUEpCmVsc2UgaWYodHlwZW9mIGM9PSJudW1iZXIiKXJldHVybiBILlRWKGEsYixjKQplbHNlIHJl
+dHVybiBjfSwKcjpmdW5jdGlvbihhLGIsYyl7dmFyIHQscz1jLmxlbmd0aApmb3IodD0wO3Q8czsrK3Qp
+Y1t0XT1ILksoYSxiLGNbdF0pfSwKeTpmdW5jdGlvbihhLGIsYyl7dmFyIHQscz1jLmxlbmd0aApmb3Io
+dD0yO3Q8czt0Kz0zKWNbdF09SC5LKGEsYixjW3RdKX0sClRWOmZ1bmN0aW9uKGEsYixjKXt2YXIgdCxz
+LHI9Yi55CmlmKHI9PT0xMCl7aWYoYz09PTApcmV0dXJuIGIuegp0PWIuUQpzPXQubGVuZ3RoCmlmKGM8
+PXMpcmV0dXJuIHRbYy0xXQpjLT1zCmI9Yi56CnI9Yi55fWVsc2UgaWYoYz09PTApcmV0dXJuIGIKaWYo
+ciE9PTkpdGhyb3cgSC5iKFAuaFYoIkluZGV4ZWQgYmFzZSBtdXN0IGJlIGFuIGludGVyZmFjZSB0eXBl
+IikpCnQ9Yi5RCmlmKGM8PXQubGVuZ3RoKXJldHVybiB0W2MtMV0KdGhyb3cgSC5iKFAuaFYoIkJhZCBp
+bmRleCAiK2MrIiBmb3IgIitiLncoMCkpKX0sCldlOmZ1bmN0aW9uKGEsYixjLGQsZSl7dmFyIHQscyxy
+LHEscCxvLG4sbSxsLGsKaWYoYj09PWQpcmV0dXJuITAKaWYoIUguQTgoZCkpaWYoIShkPT09dS5fKSl0
+PWQ9PT11LksKZWxzZSB0PSEwCmVsc2UgdD0hMAppZih0KXJldHVybiEwCnM9Yi55CmlmKHM9PT00KXJl
+dHVybiEwCmlmKEguQTgoYikpcmV0dXJuITEKaWYoYi55IT09MSl0PWI9PT11LlB8fGI9PT11LlQKZWxz
+ZSB0PSEwCmlmKHQpcmV0dXJuITAKcj1zPT09MTMKaWYocilpZihILldlKGEsY1tiLnpdLGMsZCxlKSly
+ZXR1cm4hMApxPWQueQppZihzPT09NilyZXR1cm4gSC5XZShhLGIueixjLGQsZSkKaWYocT09PTYpe3Q9
+ZC56CnJldHVybiBILldlKGEsYixjLHQsZSl9aWYocz09PTgpe2lmKCFILldlKGEsYi56LGMsZCxlKSly
+ZXR1cm4hMQpyZXR1cm4gSC5XZShhLEgueFooYSxiKSxjLGQsZSl9aWYocz09PTcpe3Q9SC5XZShhLGIu
+eixjLGQsZSkKcmV0dXJuIHR9aWYocT09PTgpe2lmKEguV2UoYSxiLGMsZC56LGUpKXJldHVybiEwCnJl
+dHVybiBILldlKGEsYixjLEgueFooYSxkKSxlKX1pZihxPT09Nyl7dD1ILldlKGEsYixjLGQueixlKQpy
+ZXR1cm4gdH1pZihyKXJldHVybiExCnQ9cyE9PTExCmlmKCghdHx8cz09PTEyKSYmZD09PXUuWSlyZXR1
+cm4hMAppZihxPT09MTIpe2lmKGI9PT11LnYpcmV0dXJuITAKaWYocyE9PTEyKXJldHVybiExCnA9Yi5R
+Cm89ZC5RCm49cC5sZW5ndGgKaWYobiE9PW8ubGVuZ3RoKXJldHVybiExCmM9Yz09bnVsbD9wOnAuY29u
+Y2F0KGMpCmU9ZT09bnVsbD9vOm8uY29uY2F0KGUpCmZvcihtPTA7bTxuOysrbSl7bD1wW21dCms9b1tt
+XQppZighSC5XZShhLGwsYyxrLGUpfHwhSC5XZShhLGssZSxsLGMpKXJldHVybiExfXJldHVybiBILmJP
+KGEsYi56LGMsZC56LGUpfWlmKHE9PT0xMSl7aWYoYj09PXUudilyZXR1cm4hMAppZih0KXJldHVybiEx
+CnJldHVybiBILmJPKGEsYixjLGQsZSl9aWYocz09PTkpe2lmKHEhPT05KXJldHVybiExCnJldHVybiBI
+LnBHKGEsYixjLGQsZSl9cmV0dXJuITF9LApiTzpmdW5jdGlvbihhMixhMyxhNCxhNSxhNil7dmFyIHQs
+cyxyLHEscCxvLG4sbSxsLGssaixpLGgsZyxmLGUsZCxjLGIsYSxhMCxhMQppZighSC5XZShhMixhMy56
+LGE0LGE1LnosYTYpKXJldHVybiExCnQ9YTMuUQpzPWE1LlEKcj10LmEKcT1zLmEKcD1yLmxlbmd0aApv
+PXEubGVuZ3RoCmlmKHA+bylyZXR1cm4hMQpuPW8tcAptPXQuYgpsPXMuYgprPW0ubGVuZ3RoCmo9bC5s
+ZW5ndGgKaWYocCtrPG8railyZXR1cm4hMQpmb3IoaT0wO2k8cDsrK2kpe2g9cltpXQppZighSC5XZShh
+MixxW2ldLGE2LGgsYTQpKXJldHVybiExfWZvcihpPTA7aTxuOysraSl7aD1tW2ldCmlmKCFILldlKGEy
+LHFbcCtpXSxhNixoLGE0KSlyZXR1cm4hMX1mb3IoaT0wO2k8ajsrK2kpe2g9bVtuK2ldCmlmKCFILldl
+KGEyLGxbaV0sYTYsaCxhNCkpcmV0dXJuITF9Zz10LmMKZj1zLmMKZT1nLmxlbmd0aApkPWYubGVuZ3Ro
+CmZvcihjPTAsYj0wO2I8ZDtiKz0zKXthPWZbYl0KZm9yKDshMDspe2lmKGM+PWUpcmV0dXJuITEKYTA9
+Z1tjXQpjKz0zCmlmKGE8YTApcmV0dXJuITEKYTE9Z1tjLTJdCmlmKGEwPGEpe2lmKGExKXJldHVybiEx
+CmNvbnRpbnVlfWg9ZltiKzFdCmlmKGExJiYhaClyZXR1cm4hMQpoPWdbYy0xXQppZighSC5XZShhMixm
+W2IrMl0sYTYsaCxhNCkpcmV0dXJuITEKYnJlYWt9fWZvcig7YzxlOyl7aWYoZ1tjKzFdKXJldHVybiEx
+CmMrPTN9cmV0dXJuITB9LApwRzpmdW5jdGlvbihhLGIsYyxkLGUpe3ZhciB0LHMscixxLHAsbyxuLG0s
+bD1iLnosaz1kLnoKaWYobD09PWspe3Q9Yi5RCnM9ZC5RCnI9dC5sZW5ndGgKZm9yKHE9MDtxPHI7Kytx
+KXtwPXRbcV0Kbz1zW3FdCmlmKCFILldlKGEscCxjLG8sZSkpcmV0dXJuITF9cmV0dXJuITB9aWYoZD09
+PXUuSylyZXR1cm4hMApuPUguUW8oYSxsKQppZihuPT1udWxsKXJldHVybiExCm09bltrXQppZihtPT1u
+dWxsKXJldHVybiExCnI9bS5sZW5ndGgKcz1kLlEKZm9yKHE9MDtxPHI7KytxKWlmKCFILldlKGEsSC5j
+RShhLGIsbVtxXSksYyxzW3FdLGUpKXJldHVybiExCnJldHVybiEwfSwKbFI6ZnVuY3Rpb24oYSl7dmFy
+IHQscz1hLnkKaWYoIShhPT09dS5QfHxhPT09dS5UKSlpZighSC5BOChhKSlpZihzIT09NylpZighKHM9
+PT02JiZILmxSKGEueikpKXQ9cz09PTgmJkgubFIoYS56KQplbHNlIHQ9ITAKZWxzZSB0PSEwCmVsc2Ug
+dD0hMAplbHNlIHQ9ITAKcmV0dXJuIHR9LApjYzpmdW5jdGlvbihhKXt2YXIgdAppZighSC5BOChhKSlp
+ZighKGE9PT11Ll8pKXQ9YT09PXUuSwplbHNlIHQ9ITAKZWxzZSB0PSEwCnJldHVybiB0fSwKQTg6ZnVu
+Y3Rpb24oYSl7dmFyIHQ9YS55CnJldHVybiB0PT09Mnx8dD09PTN8fHQ9PT00fHx0PT09NXx8YT09PXUu
+V30sCkl4OmZ1bmN0aW9uKGEsYil7dmFyIHQscyxyPU9iamVjdC5rZXlzKGIpLHE9ci5sZW5ndGgKZm9y
+KHQ9MDt0PHE7Kyt0KXtzPXJbdF0KYVtzXT1iW3NdfX0sCkpjOmZ1bmN0aW9uIEpjKGEsYil7dmFyIF89
+dGhpcwpfLmE9YQpfLmI9YgpfLng9Xy5yPV8uYz1udWxsCl8ueT0wCl8uY3k9Xy5jeD1fLmNoPV8uUT1f
+Lno9bnVsbH0sCkc6ZnVuY3Rpb24gRygpe3RoaXMuYz10aGlzLmI9dGhpcy5hPW51bGx9LAp1OTpmdW5j
+dGlvbiB1OSgpe30sCmlNOmZ1bmN0aW9uIGlNKGEpe3RoaXMuYT1hfSwKUjk6ZnVuY3Rpb24oYSl7cmV0
+dXJuIHUudy5iKGEpfHx1LkIuYihhKXx8dS5kei5iKGEpfHx1LkkuYihhKXx8dS5BLmIoYSl8fHUuZzQu
+YihhKXx8dS5nMi5iKGEpfSwKSmc6ZnVuY3Rpb24oYSl7cmV0dXJuIHYubWFuZ2xlZEdsb2JhbE5hbWVz
+W2FdfX0sSj17ClF1OmZ1bmN0aW9uKGEsYixjLGQpe3JldHVybntpOmEscDpiLGU6Yyx4OmR9fSwKa3M6
+ZnVuY3Rpb24oYSl7dmFyIHQscyxyLHEscD1hW3YuZGlzcGF0Y2hQcm9wZXJ0eU5hbWVdCmlmKHA9PW51
+bGwpaWYoJC5Cdj09bnVsbCl7SC5YRCgpCnA9YVt2LmRpc3BhdGNoUHJvcGVydHlOYW1lXX1pZihwIT1u
+dWxsKXt0PXAucAppZighMT09PXQpcmV0dXJuIHAuaQppZighMD09PXQpcmV0dXJuIGEKcz1PYmplY3Qu
+Z2V0UHJvdG90eXBlT2YoYSkKaWYodD09PXMpcmV0dXJuIHAuaQppZihwLmU9PT1zKXRocm93IEguYihQ
+LlNZKCJSZXR1cm4gaW50ZXJjZXB0b3IgZm9yICIrSC5Faih0KGEscCkpKSl9cj1hLmNvbnN0cnVjdG9y
+CnE9cj09bnVsbD9udWxsOnJbSi5SUCgpXQppZihxIT1udWxsKXJldHVybiBxCnE9SC53MyhhKQppZihx
+IT1udWxsKXJldHVybiBxCmlmKHR5cGVvZiBhPT0iZnVuY3Rpb24iKXJldHVybiBDLkRHCnQ9T2JqZWN0
+LmdldFByb3RvdHlwZU9mKGEpCmlmKHQ9PW51bGwpcmV0dXJuIEMuWlEKaWYodD09PU9iamVjdC5wcm90
+b3R5cGUpcmV0dXJuIEMuWlEKaWYodHlwZW9mIHI9PSJmdW5jdGlvbiIpe09iamVjdC5kZWZpbmVQcm9w
+ZXJ0eShyLEouUlAoKSx7dmFsdWU6Qy52QixlbnVtZXJhYmxlOmZhbHNlLHdyaXRhYmxlOnRydWUsY29u
+ZmlndXJhYmxlOnRydWV9KQpyZXR1cm4gQy52Qn1yZXR1cm4gQy52Qn0sClJQOmZ1bmN0aW9uKCl7dmFy
+IHQ9JC56bQpyZXR1cm4gdD09bnVsbD8kLnptPXYuZ2V0SXNvbGF0ZVRhZygiXyRkYXJ0X2pzIik6dH0s
+ClFpOmZ1bmN0aW9uKGEsYil7aWYoYTwwfHxhPjQyOTQ5NjcyOTUpdGhyb3cgSC5iKFAuVEUoYSwwLDQy
+OTQ5NjcyOTUsImxlbmd0aCIsbnVsbCkpCnJldHVybiBKLnB5KG5ldyBBcnJheShhKSxiKX0sCktoOmZ1
+bmN0aW9uKGEsYil7aWYoYTwwKXRocm93IEguYihQLnhZKCJMZW5ndGggbXVzdCBiZSBhIG5vbi1uZWdh
+dGl2ZSBpbnRlZ2VyOiAiK2EpKQpyZXR1cm4gSC5WTShuZXcgQXJyYXkoYSksYi5DKCJqZDwwPiIpKX0s
+CnB5OmZ1bmN0aW9uKGEsYil7cmV0dXJuIEouRXAoSC5WTShhLGIuQygiamQ8MD4iKSksYil9LApFcDpm
+dW5jdGlvbihhLGIpe2EuZml4ZWQkbGVuZ3RoPUFycmF5CnJldHVybiBhfSwKekM6ZnVuY3Rpb24oYSl7
+YS5maXhlZCRsZW5ndGg9QXJyYXkKYS5pbW11dGFibGUkbGlzdD1BcnJheQpyZXR1cm4gYX0sCkdhOmZ1
+bmN0aW9uKGEpe2lmKGE8MjU2KXN3aXRjaChhKXtjYXNlIDk6Y2FzZSAxMDpjYXNlIDExOmNhc2UgMTI6
+Y2FzZSAxMzpjYXNlIDMyOmNhc2UgMTMzOmNhc2UgMTYwOnJldHVybiEwCmRlZmF1bHQ6cmV0dXJuITF9
+c3dpdGNoKGEpe2Nhc2UgNTc2MDpjYXNlIDgxOTI6Y2FzZSA4MTkzOmNhc2UgODE5NDpjYXNlIDgxOTU6
+Y2FzZSA4MTk2OmNhc2UgODE5NzpjYXNlIDgxOTg6Y2FzZSA4MTk5OmNhc2UgODIwMDpjYXNlIDgyMDE6
+Y2FzZSA4MjAyOmNhc2UgODIzMjpjYXNlIDgyMzM6Y2FzZSA4MjM5OmNhc2UgODI4NzpjYXNlIDEyMjg4
+OmNhc2UgNjUyNzk6cmV0dXJuITAKZGVmYXVsdDpyZXR1cm4hMX19LAptbTpmdW5jdGlvbihhLGIpe3Zh
+ciB0LHMKZm9yKHQ9YS5sZW5ndGg7Yjx0Oyl7cz1DLnhCLlcoYSxiKQppZihzIT09MzImJnMhPT0xMyYm
+IUouR2EocykpYnJlYWs7KytifXJldHVybiBifSwKYzE6ZnVuY3Rpb24oYSxiKXt2YXIgdCxzCmZvcig7
+Yj4wO2I9dCl7dD1iLTEKcz1DLnhCLm0oYSx0KQppZihzIT09MzImJnMhPT0xMyYmIUouR2EocykpYnJl
+YWt9cmV0dXJuIGJ9LApUSjpmdW5jdGlvbihhKXtpZih0eXBlb2YgYT09Im51bWJlciIpcmV0dXJuIEou
+cUkucHJvdG90eXBlCmlmKHR5cGVvZiBhPT0ic3RyaW5nIilyZXR1cm4gSi5Eci5wcm90b3R5cGUKaWYo
+YT09bnVsbClyZXR1cm4gYQppZihhLmNvbnN0cnVjdG9yPT1BcnJheSlyZXR1cm4gSi5qZC5wcm90b3R5
+cGUKaWYodHlwZW9mIGEhPSJvYmplY3QiKXtpZih0eXBlb2YgYT09ImZ1bmN0aW9uIilyZXR1cm4gSi5j
+NS5wcm90b3R5cGUKcmV0dXJuIGF9aWYoYSBpbnN0YW5jZW9mIFAuTWgpcmV0dXJuIGEKcmV0dXJuIEou
+a3MoYSl9LApVNjpmdW5jdGlvbihhKXtpZih0eXBlb2YgYT09InN0cmluZyIpcmV0dXJuIEouRHIucHJv
+dG90eXBlCmlmKGE9PW51bGwpcmV0dXJuIGEKaWYoYS5jb25zdHJ1Y3Rvcj09QXJyYXkpcmV0dXJuIEou
+amQucHJvdG90eXBlCmlmKHR5cGVvZiBhIT0ib2JqZWN0Iil7aWYodHlwZW9mIGE9PSJmdW5jdGlvbiIp
+cmV0dXJuIEouYzUucHJvdG90eXBlCnJldHVybiBhfWlmKGEgaW5zdGFuY2VvZiBQLk1oKXJldHVybiBh
+CnJldHVybiBKLmtzKGEpfSwKWUU6ZnVuY3Rpb24oYSl7aWYoYT09bnVsbClyZXR1cm4gYQppZih0eXBl
+b2YgYSE9Im9iamVjdCIpe2lmKHR5cGVvZiBhPT0iZnVuY3Rpb24iKXJldHVybiBKLmM1LnByb3RvdHlw
+ZQpyZXR1cm4gYX1pZihhIGluc3RhbmNlb2YgUC5NaClyZXR1cm4gYQpyZXR1cm4gSi5rcyhhKX0sCmlh
+OmZ1bmN0aW9uKGEpe2lmKHR5cGVvZiBhPT0ibnVtYmVyIil7aWYoTWF0aC5mbG9vcihhKT09YSlyZXR1
+cm4gSi5iVS5wcm90b3R5cGUKcmV0dXJuIEouVkEucHJvdG90eXBlfWlmKHR5cGVvZiBhPT0ic3RyaW5n
+IilyZXR1cm4gSi5Eci5wcm90b3R5cGUKaWYoYT09bnVsbClyZXR1cm4gSi53ZS5wcm90b3R5cGUKaWYo
+dHlwZW9mIGE9PSJib29sZWFuIilyZXR1cm4gSi55RS5wcm90b3R5cGUKaWYoYS5jb25zdHJ1Y3Rvcj09
+QXJyYXkpcmV0dXJuIEouamQucHJvdG90eXBlCmlmKHR5cGVvZiBhIT0ib2JqZWN0Iil7aWYodHlwZW9m
+IGE9PSJmdW5jdGlvbiIpcmV0dXJuIEouYzUucHJvdG90eXBlCnJldHVybiBhfWlmKGEgaW5zdGFuY2Vv
+ZiBQLk1oKXJldHVybiBhCnJldHVybiBKLmtzKGEpfSwKclk6ZnVuY3Rpb24oYSl7aWYodHlwZW9mIGE9
+PSJzdHJpbmciKXJldHVybiBKLkRyLnByb3RvdHlwZQppZihhPT1udWxsKXJldHVybiBhCmlmKCEoYSBp
+bnN0YW5jZW9mIFAuTWgpKXJldHVybiBKLmtkLnByb3RvdHlwZQpyZXR1cm4gYX0sCncxOmZ1bmN0aW9u
+KGEpe2lmKGE9PW51bGwpcmV0dXJuIGEKaWYoYS5jb25zdHJ1Y3Rvcj09QXJyYXkpcmV0dXJuIEouamQu
+cHJvdG90eXBlCmlmKHR5cGVvZiBhIT0ib2JqZWN0Iil7aWYodHlwZW9mIGE9PSJmdW5jdGlvbiIpcmV0
+dXJuIEouYzUucHJvdG90eXBlCnJldHVybiBhfWlmKGEgaW5zdGFuY2VvZiBQLk1oKXJldHVybiBhCnJl
+dHVybiBKLmtzKGEpfSwKQ006ZnVuY3Rpb24oYSxiLGMsZCl7cmV0dXJuIEouWUUoYSkuZHUoYSxiLGMs
+ZCl9LApFaDpmdW5jdGlvbihhLGIsYyl7cmV0dXJuIEouWUUoYSkubUsoYSxiLGMpfSwKRkw6ZnVuY3Rp
+b24oYSxiKXtyZXR1cm4gSi5yWShhKS5kZChhLGIpfSwKR0E6ZnVuY3Rpb24oYSxiKXtyZXR1cm4gSi53
+MShhKS5FKGEsYil9LApHcjpmdW5jdGlvbihhKXtyZXR1cm4gSi5ZRShhKS5nbVcoYSl9LApIbTpmdW5j
+dGlvbihhKXtyZXR1cm4gSi5VNihhKS5nQShhKX0sCklUOmZ1bmN0aW9uKGEpe3JldHVybiBKLncxKGEp
+LmdreihhKX0sCkp5OmZ1bmN0aW9uKGEsYil7cmV0dXJuIEouaWEoYSkuZTcoYSxiKX0sCktWOmZ1bmN0
+aW9uKGEsYil7cmV0dXJuIEouclkoYSkuRyhhLGIpfSwKTHQ6ZnVuY3Rpb24oYSl7cmV0dXJuIEouWUUo
+YSkud2coYSl9LApNMTpmdW5jdGlvbihhLGIsYyl7cmV0dXJuIEoudzEoYSkuRTIoYSxiLGMpfSwKTXU6
+ZnVuY3Rpb24oYSxiKXtyZXR1cm4gSi5ZRShhKS5zUChhLGIpfSwKUXo6ZnVuY3Rpb24oYSxiKXtyZXR1
+cm4gSi5yWShhKS5XKGEsYil9LApSTTpmdW5jdGlvbihhLGIpe2lmKGE9PW51bGwpcmV0dXJuIGI9PW51
+bGwKaWYodHlwZW9mIGEhPSJvYmplY3QiKXJldHVybiBiIT1udWxsJiZhPT09YgpyZXR1cm4gSi5pYShh
+KS5ETihhLGIpfSwKVDA6ZnVuY3Rpb24oYSl7cmV0dXJuIEouclkoYSkuYlMoYSl9LAphNjpmdW5jdGlv
+bihhLGIpe3JldHVybiBKLnJZKGEpLm0oYSxiKX0sCmJUOmZ1bmN0aW9uKGEpe3JldHVybiBKLllFKGEp
+LkQ0KGEpfSwKYmI6ZnVuY3Rpb24oYSxiKXtpZih0eXBlb2YgYT09Im51bWJlciImJnR5cGVvZiBiPT0i
+bnVtYmVyIilyZXR1cm4gYStiCnJldHVybiBKLlRKKGEpLmgoYSxiKX0sCmNIOmZ1bmN0aW9uKGEpe3Jl
+dHVybiBKLnJZKGEpLmhjKGEpfSwKZFI6ZnVuY3Rpb24oYSl7cmV0dXJuIEouWUUoYSkuZ1AoYSl9LApk
+WjpmdW5jdGlvbihhLGIsYyxkKXtyZXR1cm4gSi5ZRShhKS5PbihhLGIsYyxkKX0sCmRnOmZ1bmN0aW9u
+KGEsYixjLGQpe3JldHVybiBKLnJZKGEpLmk3KGEsYixjLGQpfSwKZGg6ZnVuY3Rpb24oYSl7cmV0dXJu
+IEouWUUoYSkuRkYoYSl9LApkcjpmdW5jdGlvbihhLGIpe3JldHVybiBKLllFKGEpLnNhNChhLGIpfSwK
+aGY6ZnVuY3Rpb24oYSl7cmV0dXJuIEouaWEoYSkuZ2lPKGEpfSwKaWc6ZnVuY3Rpb24oYSl7cmV0dXJu
+IEouWUUoYSkuZ1FnKGEpfSwKajpmdW5jdGlvbihhKXtyZXR1cm4gSi5pYShhKS53KGEpfSwKbDU6ZnVu
+Y3Rpb24oYSxiKXtyZXR1cm4gSi5ZRShhKS5zaGYoYSxiKX0sCmxkOmZ1bmN0aW9uKGEsYixjKXtyZXR1
+cm4gSi5yWShhKS5OaihhLGIsYyl9LApxMDpmdW5jdGlvbihhLGIsYyl7cmV0dXJuIEouclkoYSkuUWko
+YSxiLGMpfSwKcUY6ZnVuY3Rpb24oYSl7cmV0dXJuIEouWUUoYSkuZ1ZsKGEpfSwKdEg6ZnVuY3Rpb24o
+YSxiLGMpe3JldHVybiBKLllFKGEpLnBrKGEsYixjKX0sCnVVOmZ1bmN0aW9uKGEpe3JldHVybiBKLlU2
+KGEpLmdsMChhKX0sCndmOmZ1bmN0aW9uKGEsYil7cmV0dXJuIEouWUUoYSkuc1JOKGEsYil9LAp4OTpm
+dW5jdGlvbihhLGIpe2lmKHR5cGVvZiBiPT09Im51bWJlciIpaWYoYS5jb25zdHJ1Y3Rvcj09QXJyYXl8
+fHR5cGVvZiBhPT0ic3RyaW5nInx8SC53VihhLGFbdi5kaXNwYXRjaFByb3BlcnR5TmFtZV0pKWlmKGI+
+Pj4wPT09YiYmYjxhLmxlbmd0aClyZXR1cm4gYVtiXQpyZXR1cm4gSi5VNihhKS5xKGEsYil9LAp6bDpm
+dW5jdGlvbihhLGIpe3JldHVybiBKLlU2KGEpLnRnKGEsYil9LAp2QjpmdW5jdGlvbiB2Qigpe30sCnlF
+OmZ1bmN0aW9uIHlFKCl7fSwKd2U6ZnVuY3Rpb24gd2UoKXt9LApNRjpmdW5jdGlvbiBNRigpe30sCmlD
+OmZ1bmN0aW9uIGlDKCl7fSwKa2Q6ZnVuY3Rpb24ga2QoKXt9LApjNTpmdW5jdGlvbiBjNSgpe30sCmpk
+OmZ1bmN0aW9uIGpkKGEpe3RoaXMuJHRpPWF9LApQbzpmdW5jdGlvbiBQbyhhKXt0aGlzLiR0aT1hfSwK
+bTE6ZnVuY3Rpb24gbTEoYSxiLGMpe3ZhciBfPXRoaXMKXy5hPWEKXy5iPWIKXy5jPTAKXy5kPW51bGwK
+Xy4kdGk9Y30sCnFJOmZ1bmN0aW9uIHFJKCl7fSwKYlU6ZnVuY3Rpb24gYlUoKXt9LApWQTpmdW5jdGlv
+biBWQSgpe30sCkRyOmZ1bmN0aW9uIERyKCl7fX0sUD17Ck9qOmZ1bmN0aW9uKCl7dmFyIHQscyxyPXt9
+CmlmKHNlbGYuc2NoZWR1bGVJbW1lZGlhdGUhPW51bGwpcmV0dXJuIFAuRVgoKQppZihzZWxmLk11dGF0
+aW9uT2JzZXJ2ZXIhPW51bGwmJnNlbGYuZG9jdW1lbnQhPW51bGwpe3Q9c2VsZi5kb2N1bWVudC5jcmVh
+dGVFbGVtZW50KCJkaXYiKQpzPXNlbGYuZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgic3BhbiIpCnIuYT1u
+dWxsCm5ldyBzZWxmLk11dGF0aW9uT2JzZXJ2ZXIoSC50UihuZXcgUC50aChyKSwxKSkub2JzZXJ2ZSh0
+LHtjaGlsZExpc3Q6dHJ1ZX0pCnJldHVybiBuZXcgUC5oYShyLHQscyl9ZWxzZSBpZihzZWxmLnNldElt
+bWVkaWF0ZSE9bnVsbClyZXR1cm4gUC55dCgpCnJldHVybiBQLnFXKCl9LApaVjpmdW5jdGlvbihhKXtz
+ZWxmLnNjaGVkdWxlSW1tZWRpYXRlKEgudFIobmV3IFAuVnModS5NLmEoYSkpLDApKX0sCm9BOmZ1bmN0
+aW9uKGEpe3NlbGYuc2V0SW1tZWRpYXRlKEgudFIobmV3IFAuRnQodS5NLmEoYSkpLDApKX0sCkJ6OmZ1
+bmN0aW9uKGEpe3UuTS5hKGEpClAuUU4oMCxhKX0sClFOOmZ1bmN0aW9uKGEsYil7dmFyIHQ9bmV3IFAu
+VzMoKQp0LkNZKGEsYikKcmV0dXJuIHR9LApGWDpmdW5jdGlvbihhKXtyZXR1cm4gbmV3IFAuaWgobmV3
+IFAudnMoJC5YMyxhLkMoInZzPDA+IikpLGEuQygiaWg8MD4iKSl9LApESTpmdW5jdGlvbihhLGIpe2Eu
+JDIoMCxudWxsKQpiLmI9ITAKcmV0dXJuIGIuYX0sCmpROmZ1bmN0aW9uKGEsYil7UC5KZShhLGIpfSwK
+eUM6ZnVuY3Rpb24oYSxiKXtiLmFNKDAsYSl9LApmMzpmdW5jdGlvbihhLGIpe2IudzAoSC5SdShhKSxI
+LnRzKGEpKX0sCkplOmZ1bmN0aW9uKGEsYil7dmFyIHQscyxyPW5ldyBQLldNKGIpLHE9bmV3IFAuU1go
+YikKaWYoYSBpbnN0YW5jZW9mIFAudnMpYS5RZChyLHEsdS56KQplbHNle3Q9dS56CmlmKHUuZC5iKGEp
+KWEuU3EocixxLHQpCmVsc2V7cz1uZXcgUC52cygkLlgzLHUuYykKcy5hPTQKcy5jPWEKcy5RZChyLHEs
+dCl9fX0sCmx6OmZ1bmN0aW9uKGEpe3ZhciB0PWZ1bmN0aW9uKGIsYyl7cmV0dXJuIGZ1bmN0aW9uKGQs
+ZSl7d2hpbGUodHJ1ZSl0cnl7YihkLGUpCmJyZWFrfWNhdGNoKHMpe2U9cwpkPWN9fX0oYSwxKQpyZXR1
+cm4gJC5YMy5MaihuZXcgUC5Hcyh0KSx1LlAsdS5TLHUueil9LApHUTpmdW5jdGlvbihhKXtyZXR1cm4g
+bmV3IFAuRnkoYSwxKX0sClRoOmZ1bmN0aW9uKCl7cmV0dXJuIEMud1F9LApZbTpmdW5jdGlvbihhKXty
+ZXR1cm4gbmV3IFAuRnkoYSwzKX0sCmwwOmZ1bmN0aW9uKGEsYil7cmV0dXJuIG5ldyBQLnE0KGEsYi5D
+KCJxNDwwPiIpKX0sCmszOmZ1bmN0aW9uKGEsYil7dmFyIHQscyxyCmIuYT0xCnRyeXthLlNxKG5ldyBQ
+LnBWKGIpLG5ldyBQLlU3KGIpLHUuUCl9Y2F0Y2gocil7dD1ILlJ1KHIpCnM9SC50cyhyKQpQLnJiKG5l
+dyBQLnZyKGIsdCxzKSl9fSwKQTk6ZnVuY3Rpb24oYSxiKXt2YXIgdCxzLHIKZm9yKHQ9dS5jO3M9YS5h
+LHM9PT0yOylhPXQuYShhLmMpCmlmKHM+PTQpe3I9Yi5haCgpCmIuYT1hLmEKYi5jPWEuYwpQLkhaKGIs
+cil9ZWxzZXtyPXUuRi5hKGIuYykKYi5hPTIKYi5jPWEKYS5qUShyKX19LApIWjpmdW5jdGlvbihhLGEw
+KXt2YXIgdCxzLHIscSxwLG8sbixtLGwsayxqLGksaCxnLGYsZSxkPW51bGwsYz17fSxiPWMuYT1hCmZv
+cih0PXUubixzPXUuRixyPXUuZDshMDspe3E9e30KcD1iLmE9PT04CmlmKGEwPT1udWxsKXtpZihwKXtv
+PXQuYShiLmMpClAuTDIoZCxkLGIuYixvLmEsby5iKX1yZXR1cm59cS5hPWEwCm49YTAuYQpmb3IoYj1h
+MDtuIT1udWxsO2I9bixuPW0pe2IuYT1udWxsClAuSFooYy5hLGIpCnEuYT1uCm09bi5hfWw9Yy5hCms9
+bC5jCnEuYj1wCnEuYz1rCmo9IXAKaWYoail7aT1iLmMKaT0oaSYxKSE9PTB8fChpJjE1KT09PTh9ZWxz
+ZSBpPSEwCmlmKGkpe2g9Yi5iLmIKaWYocCl7aT1sLmI9PT1oCmk9IShpfHxpKX1lbHNlIGk9ITEKaWYo
+aSl7dC5hKGspClAuTDIoZCxkLGwuYixrLmEsay5iKQpyZXR1cm59Zz0kLlgzCmlmKGchPT1oKSQuWDM9
+aAplbHNlIGc9ZApiPWIuYwppZigoYiYxNSk9PT04KW5ldyBQLlJUKHEsYyxwKS4kMCgpCmVsc2UgaWYo
+ail7aWYoKGImMSkhPT0wKW5ldyBQLnJxKHEsaykuJDAoKX1lbHNlIGlmKChiJjIpIT09MCluZXcgUC5S
+VyhjLHEpLiQwKCkKaWYoZyE9bnVsbCkkLlgzPWcKYj1xLmMKaWYoci5iKGIpKXtmPXEuYS5iCmlmKGIu
+YT49NCl7ZT1zLmEoZi5jKQpmLmM9bnVsbAphMD1mLk44KGUpCmYuYT1iLmEKZi5jPWIuYwpjLmE9Ygpj
+b250aW51ZX1lbHNlIFAuQTkoYixmKQpyZXR1cm59fWY9cS5hLmIKZT1zLmEoZi5jKQpmLmM9bnVsbAph
+MD1mLk44KGUpCmI9cS5iCmw9cS5jCmlmKCFiKXtmLiR0aS5jLmEobCkKZi5hPTQKZi5jPWx9ZWxzZXt0
+LmEobCkKZi5hPTgKZi5jPWx9Yy5hPWYKYj1mfX0sClZIOmZ1bmN0aW9uKGEsYil7dmFyIHQKaWYodS5h
+Zy5iKGEpKXJldHVybiBiLkxqKGEsdS56LHUuSyx1LmwpCnQ9dS5iSQppZih0LmIoYSkpcmV0dXJuIHQu
+YShhKQp0aHJvdyBILmIoUC5MMyhhLCJvbkVycm9yIiwiRXJyb3IgaGFuZGxlciBtdXN0IGFjY2VwdCBv
+bmUgT2JqZWN0IG9yIG9uZSBPYmplY3QgYW5kIGEgU3RhY2tUcmFjZSBhcyBhcmd1bWVudHMsIGFuZCBy
+ZXR1cm4gYSBhIHZhbGlkIHJlc3VsdCIpKX0sCnB1OmZ1bmN0aW9uKCl7dmFyIHQscwpmb3IodD0kLlM2
+O3QhPW51bGw7dD0kLlM2KXskLm1nPW51bGwKcz10LmIKJC5TNj1zCmlmKHM9PW51bGwpJC5rOD1udWxs
+CnQuYS4kMCgpfX0sCmVOOmZ1bmN0aW9uKCl7JC5VRD0hMAp0cnl7UC5wdSgpfWZpbmFsbHl7JC5tZz1u
+dWxsCiQuVUQ9ITEKaWYoJC5TNiE9bnVsbCkkLnV0KCkuJDEoUC5WOSgpKX19LAplVzpmdW5jdGlvbihh
+KXt2YXIgdD1uZXcgUC5PTShhKSxzPSQuazgKaWYocz09bnVsbCl7JC5TNj0kLms4PXQKaWYoISQuVUQp
+JC51dCgpLiQxKFAuVjkoKSl9ZWxzZSAkLms4PXMuYj10fSwKclI6ZnVuY3Rpb24oYSl7dmFyIHQscyxy
+LHE9JC5TNgppZihxPT1udWxsKXtQLmVXKGEpCiQubWc9JC5rOApyZXR1cm59dD1uZXcgUC5PTShhKQpz
+PSQubWcKaWYocz09bnVsbCl7dC5iPXEKJC5TNj0kLm1nPXR9ZWxzZXtyPXMuYgp0LmI9cgokLm1nPXMu
+Yj10CmlmKHI9PW51bGwpJC5rOD10fX0sCnJiOmZ1bmN0aW9uKGEpe3ZhciB0PW51bGwscz0kLlgzCmlm
+KEMuTlU9PT1zKXtQLlRrKHQsdCxDLk5VLGEpCnJldHVybn1QLlRrKHQsdCxzLHUuTS5hKHMuR1koYSkp
+KX0sClF3OmZ1bmN0aW9uKGEsYil7UC5VSShhLCJzdHJlYW0iLGIuQygicWg8MD4iKSkKcmV0dXJuIG5l
+dyBQLnhJKGIuQygieEk8MD4iKSl9LApUbDpmdW5jdGlvbihhLGIpe3ZhciB0PWI9PW51bGw/UC52MChh
+KTpiClAuVUkoYSwiZXJyb3IiLHUuSykKcmV0dXJuIG5ldyBQLkN3KGEsdCl9LAp2MDpmdW5jdGlvbihh
+KXt2YXIgdAppZih1Lm0uYihhKSl7dD1hLmdJSSgpCmlmKHQhPW51bGwpcmV0dXJuIHR9cmV0dXJuIEMu
+cGR9LApMMjpmdW5jdGlvbihhLGIsYyxkLGUpe1AuclIobmV3IFAucEsoZCxlKSl9LApUODpmdW5jdGlv
+bihhLGIsYyxkLGUpe3ZhciB0LHM9JC5YMwppZihzPT09YylyZXR1cm4gZC4kMCgpCiQuWDM9Ywp0PXMK
+dHJ5e3M9ZC4kMCgpCnJldHVybiBzfWZpbmFsbHl7JC5YMz10fX0sCnl2OmZ1bmN0aW9uKGEsYixjLGQs
+ZSxmLGcpe3ZhciB0LHM9JC5YMwppZihzPT09YylyZXR1cm4gZC4kMShlKQokLlgzPWMKdD1zCnRyeXtz
+PWQuJDEoZSkKcmV0dXJuIHN9ZmluYWxseXskLlgzPXR9fSwKUXg6ZnVuY3Rpb24oYSxiLGMsZCxlLGYs
+ZyxoLGkpe3ZhciB0LHM9JC5YMwppZihzPT09YylyZXR1cm4gZC4kMihlLGYpCiQuWDM9Ywp0PXMKdHJ5
+e3M9ZC4kMihlLGYpCnJldHVybiBzfWZpbmFsbHl7JC5YMz10fX0sClRrOmZ1bmN0aW9uKGEsYixjLGQp
+e3ZhciB0CnUuTS5hKGQpCnQ9Qy5OVSE9PWMKaWYodClkPSEoIXR8fCExKT9jLkdZKGQpOmMuUlQoZCx1
+LkgpClAuZVcoZCl9LAp0aDpmdW5jdGlvbiB0aChhKXt0aGlzLmE9YX0sCmhhOmZ1bmN0aW9uIGhhKGEs
+YixjKXt0aGlzLmE9YQp0aGlzLmI9Ygp0aGlzLmM9Y30sClZzOmZ1bmN0aW9uIFZzKGEpe3RoaXMuYT1h
+fSwKRnQ6ZnVuY3Rpb24gRnQoYSl7dGhpcy5hPWF9LApXMzpmdW5jdGlvbiBXMygpe30sCnlIOmZ1bmN0
+aW9uIHlIKGEsYil7dGhpcy5hPWEKdGhpcy5iPWJ9LAppaDpmdW5jdGlvbiBpaChhLGIpe3RoaXMuYT1h
+CnRoaXMuYj0hMQp0aGlzLiR0aT1ifSwKV006ZnVuY3Rpb24gV00oYSl7dGhpcy5hPWF9LApTWDpmdW5j
+dGlvbiBTWChhKXt0aGlzLmE9YX0sCkdzOmZ1bmN0aW9uIEdzKGEpe3RoaXMuYT1hfSwKRnk6ZnVuY3Rp
+b24gRnkoYSxiKXt0aGlzLmE9YQp0aGlzLmI9Yn0sCkdWOmZ1bmN0aW9uIEdWKGEsYil7dmFyIF89dGhp
+cwpfLmE9YQpfLmQ9Xy5jPV8uYj1udWxsCl8uJHRpPWJ9LApxNDpmdW5jdGlvbiBxNChhLGIpe3RoaXMu
+YT1hCnRoaXMuJHRpPWJ9LApiODpmdW5jdGlvbiBiOCgpe30sClBmOmZ1bmN0aW9uIFBmKCl7fSwKWmY6
+ZnVuY3Rpb24gWmYoYSxiKXt0aGlzLmE9YQp0aGlzLiR0aT1ifSwKRmU6ZnVuY3Rpb24gRmUoYSxiLGMs
+ZCxlKXt2YXIgXz10aGlzCl8uYT1udWxsCl8uYj1hCl8uYz1iCl8uZD1jCl8uZT1kCl8uJHRpPWV9LAp2
+czpmdW5jdGlvbiB2cyhhLGIpe3ZhciBfPXRoaXMKXy5hPTAKXy5iPWEKXy5jPW51bGwKXy4kdGk9Yn0s
+CmRhOmZ1bmN0aW9uIGRhKGEsYil7dGhpcy5hPWEKdGhpcy5iPWJ9LApvUTpmdW5jdGlvbiBvUShhLGIp
+e3RoaXMuYT1hCnRoaXMuYj1ifSwKcFY6ZnVuY3Rpb24gcFYoYSl7dGhpcy5hPWF9LApVNzpmdW5jdGlv
+biBVNyhhKXt0aGlzLmE9YX0sCnZyOmZ1bmN0aW9uIHZyKGEsYixjKXt0aGlzLmE9YQp0aGlzLmI9Ygp0
+aGlzLmM9Y30sCnJ0OmZ1bmN0aW9uIHJ0KGEsYil7dGhpcy5hPWEKdGhpcy5iPWJ9LApLRjpmdW5jdGlv
+biBLRihhLGIpe3RoaXMuYT1hCnRoaXMuYj1ifSwKWkw6ZnVuY3Rpb24gWkwoYSxiLGMpe3RoaXMuYT1h
+CnRoaXMuYj1iCnRoaXMuYz1jfSwKUlQ6ZnVuY3Rpb24gUlQoYSxiLGMpe3RoaXMuYT1hCnRoaXMuYj1i
+CnRoaXMuYz1jfSwKalo6ZnVuY3Rpb24galooYSl7dGhpcy5hPWF9LApycTpmdW5jdGlvbiBycShhLGIp
+e3RoaXMuYT1hCnRoaXMuYj1ifSwKUlc6ZnVuY3Rpb24gUlcoYSxiKXt0aGlzLmE9YQp0aGlzLmI9Yn0s
+Ck9NOmZ1bmN0aW9uIE9NKGEpe3RoaXMuYT1hCnRoaXMuYj1udWxsfSwKcWg6ZnVuY3Rpb24gcWgoKXt9
+LApCNTpmdW5jdGlvbiBCNShhLGIpe3RoaXMuYT1hCnRoaXMuYj1ifSwKdU86ZnVuY3Rpb24gdU8oYSxi
+KXt0aGlzLmE9YQp0aGlzLmI9Yn0sCk1POmZ1bmN0aW9uIE1PKCl7fSwKa1Q6ZnVuY3Rpb24ga1QoKXt9
+LAp4STpmdW5jdGlvbiB4SShhKXt0aGlzLiR0aT1hfSwKQ3c6ZnVuY3Rpb24gQ3coYSxiKXt0aGlzLmE9
+YQp0aGlzLmI9Yn0sCm0wOmZ1bmN0aW9uIG0wKCl7fSwKcEs6ZnVuY3Rpb24gcEsoYSxiKXt0aGlzLmE9
+YQp0aGlzLmI9Yn0sCkppOmZ1bmN0aW9uIEppKCl7fSwKaGo6ZnVuY3Rpb24gaGooYSxiLGMpe3RoaXMu
+YT1hCnRoaXMuYj1iCnRoaXMuYz1jfSwKVnA6ZnVuY3Rpb24gVnAoYSxiKXt0aGlzLmE9YQp0aGlzLmI9
+Yn0sCk9SOmZ1bmN0aW9uIE9SKGEsYixjKXt0aGlzLmE9YQp0aGlzLmI9Ygp0aGlzLmM9Y30sCkVGOmZ1
+bmN0aW9uKGEsYixjKXtyZXR1cm4gYi5DKCJAPDA+IikuS3EoYykuQygiRm88MSwyPiIpLmEoSC5CNyhh
+LG5ldyBILk41KGIuQygiQDwwPiIpLktxKGMpLkMoIk41PDEsMj4iKSkpKX0sCkZsOmZ1bmN0aW9uKGEs
+Yil7cmV0dXJuIG5ldyBILk41KGEuQygiQDwwPiIpLktxKGIpLkMoIk41PDEsMj4iKSl9LApMczpmdW5j
+dGlvbihhKXtyZXR1cm4gbmV3IFAuYjYoYS5DKCJiNjwwPiIpKX0sClQyOmZ1bmN0aW9uKCl7dmFyIHQ9
+T2JqZWN0LmNyZWF0ZShudWxsKQp0WyI8bm9uLWlkZW50aWZpZXIta2V5PiJdPXQKZGVsZXRlIHRbIjxu
+b24taWRlbnRpZmllci1rZXk+Il0KcmV0dXJuIHR9LApyajpmdW5jdGlvbihhLGIsYyl7dmFyIHQ9bmV3
+IFAubG0oYSxiLGMuQygibG08MD4iKSkKdC5jPWEuZQpyZXR1cm4gdH0sCkVQOmZ1bmN0aW9uKGEsYixj
+KXt2YXIgdCxzCmlmKFAuaEIoYSkpe2lmKGI9PT0iKCImJmM9PT0iKSIpcmV0dXJuIiguLi4pIgpyZXR1
+cm4gYisiLi4uIitjfXQ9SC5WTShbXSx1LnMpCkMuTm0uaSgkLnhnLGEpCnRyeXtQLlZyKGEsdCl9Zmlu
+YWxseXtpZigwPj0kLnhnLmxlbmd0aClyZXR1cm4gSC5PSCgkLnhnLC0xKQokLnhnLnBvcCgpfXM9UC52
+ZyhiLHUucC5hKHQpLCIsICIpK2MKcmV0dXJuIHMuY2hhckNvZGVBdCgwKT09MD9zOnN9LApXRTpmdW5j
+dGlvbihhLGIsYyl7dmFyIHQscwppZihQLmhCKGEpKXJldHVybiBiKyIuLi4iK2MKdD1uZXcgUC5Sbihi
+KQpDLk5tLmkoJC54ZyxhKQp0cnl7cz10CnMuYT1QLnZnKHMuYSxhLCIsICIpfWZpbmFsbHl7aWYoMD49
+JC54Zy5sZW5ndGgpcmV0dXJuIEguT0goJC54ZywtMSkKJC54Zy5wb3AoKX10LmErPWMKcz10LmEKcmV0
+dXJuIHMuY2hhckNvZGVBdCgwKT09MD9zOnN9LApoQjpmdW5jdGlvbihhKXt2YXIgdCxzCmZvcih0PSQu
+eGcubGVuZ3RoLHM9MDtzPHQ7KytzKWlmKGE9PT0kLnhnW3NdKXJldHVybiEwCnJldHVybiExfSwKVnI6
+ZnVuY3Rpb24oYSxiKXt2YXIgdCxzLHIscSxwLG8sbixtPWEuZ2t6KGEpLGw9MCxrPTAKd2hpbGUoITAp
+e2lmKCEobDw4MHx8azwzKSlicmVhawppZighbS5GKCkpcmV0dXJuCnQ9SC5FaihtLmdsKCkpCkMuTm0u
+aShiLHQpCmwrPXQubGVuZ3RoKzI7KytrfWlmKCFtLkYoKSl7aWYoazw9NSlyZXR1cm4KaWYoMD49Yi5s
+ZW5ndGgpcmV0dXJuIEguT0goYiwtMSkKcz1iLnBvcCgpCmlmKDA+PWIubGVuZ3RoKXJldHVybiBILk9I
+KGIsLTEpCnI9Yi5wb3AoKX1lbHNle3E9bS5nbCgpOysrawppZighbS5GKCkpe2lmKGs8PTQpe0MuTm0u
+aShiLEguRWoocSkpCnJldHVybn1zPUguRWoocSkKaWYoMD49Yi5sZW5ndGgpcmV0dXJuIEguT0goYiwt
MSkKcj1iLnBvcCgpCmwrPXMubGVuZ3RoKzJ9ZWxzZXtwPW0uZ2woKTsrK2sKZm9yKDttLkYoKTtxPXAs
cD1vKXtvPW0uZ2woKTsrK2sKaWYoaz4xMDApe3doaWxlKCEwKXtpZighKGw+NzUmJms+MykpYnJlYWsK
-aWYoMD49Yi5sZW5ndGgpcmV0dXJuIEguayhiLC0xKQpsLT1iLnBvcCgpLmxlbmd0aCsyOy0ta31DLk5t
-LmkoYiwiLi4uIikKcmV0dXJufX1yPUguZChxKQpzPUguZChwKQpsKz1zLmxlbmd0aCtyLmxlbmd0aCs0
-fX1pZihrPmIubGVuZ3RoKzIpe2wrPTUKbj0iLi4uIn1lbHNlIG49bnVsbAp3aGlsZSghMCl7aWYoIShs
-PjgwJiZiLmxlbmd0aD4zKSlicmVhawppZigwPj1iLmxlbmd0aClyZXR1cm4gSC5rKGIsLTEpCmwtPWIu
-cG9wKCkubGVuZ3RoKzIKaWYobj09bnVsbCl7bCs9NQpuPSIuLi4ifX1pZihuIT1udWxsKUMuTm0uaShi
-LG4pCkMuTm0uaShiLHIpCkMuTm0uaShiLHMpfSwKdE06ZnVuY3Rpb24oYSxiKXt2YXIgdCxzLHI9UC5M
-cyhiKQpmb3IodD1hLmxlbmd0aCxzPTA7czxhLmxlbmd0aDthLmxlbmd0aD09PXR8fCgwLEgubGspKGEp
-LCsrcylyLmkoMCxiLmEoYVtzXSkpCnJldHVybiByfSwKbk86ZnVuY3Rpb24oYSl7dmFyIHQscz17fQpp
-ZihQLmhCKGEpKXJldHVybiJ7Li4ufSIKdD1uZXcgUC5SbigiIikKdHJ5e0MuTm0uaSgkLnhnLGEpCnQu
-YSs9InsiCnMuYT0hMAphLksoMCxuZXcgUC5yYShzLHQpKQp0LmErPSJ9In1maW5hbGx5e2lmKDA+PSQu
-eGcubGVuZ3RoKXJldHVybiBILmsoJC54ZywtMSkKJC54Zy5wb3AoKX1zPXQuYQpyZXR1cm4gcy5jaGFy
-Q29kZUF0KDApPT0wP3M6c30sCmI2OmZ1bmN0aW9uIGI2KGEpe3ZhciBfPXRoaXMKXy5hPTAKXy5mPV8u
-ZT1fLmQ9Xy5jPV8uYj1udWxsCl8ucj0wCl8uJHRpPWF9LApibjpmdW5jdGlvbiBibihhKXt0aGlzLmE9
-YQp0aGlzLmM9dGhpcy5iPW51bGx9LApsbTpmdW5jdGlvbiBsbShhLGIsYyl7dmFyIF89dGhpcwpfLmE9
-YQpfLmI9YgpfLmQ9Xy5jPW51bGwKXy4kdGk9Y30sCm1XOmZ1bmN0aW9uIG1XKCl7fSwKdXk6ZnVuY3Rp
-b24gdXkoKXt9LApsRDpmdW5jdGlvbiBsRCgpe30sCmlsOmZ1bmN0aW9uIGlsKCl7fSwKcmE6ZnVuY3Rp
-b24gcmEoYSxiKXt0aGlzLmE9YQp0aGlzLmI9Yn0sCllrOmZ1bmN0aW9uIFlrKCl7fSwKeVE6ZnVuY3Rp
-b24geVEoYSl7dGhpcy5hPWF9LApLUDpmdW5jdGlvbiBLUCgpe30sClBuOmZ1bmN0aW9uIFBuKCl7fSwK
-R2o6ZnVuY3Rpb24gR2ooYSxiKXt0aGlzLmE9YQp0aGlzLiR0aT1ifSwKTWE6ZnVuY3Rpb24gTWEoKXt9
-LApWajpmdW5jdGlvbiBWaigpe30sClh2OmZ1bmN0aW9uIFh2KCl7fSwKblk6ZnVuY3Rpb24gblkoKXt9
-LApUQzpmdW5jdGlvbiBUQygpe30sClJVOmZ1bmN0aW9uIFJVKCl7fSwKQlM6ZnVuY3Rpb24oYSxiKXt2
-YXIgdCxzLHIscQppZih0eXBlb2YgYSE9InN0cmluZyIpdGhyb3cgSC5iKEguSShhKSkKdD1udWxsCnRy
-eXt0PUpTT04ucGFyc2UoYSl9Y2F0Y2gocil7cz1ILlJ1KHIpCnE9UC5ycihTdHJpbmcocyksbnVsbCxu
-dWxsKQp0aHJvdyBILmIocSl9cT1QLlFlKHQpCnJldHVybiBxfSwKUWU6ZnVuY3Rpb24oYSl7dmFyIHQK
-aWYoYT09bnVsbClyZXR1cm4gbnVsbAppZih0eXBlb2YgYSE9Im9iamVjdCIpcmV0dXJuIGEKaWYoT2Jq
-ZWN0LmdldFByb3RvdHlwZU9mKGEpIT09QXJyYXkucHJvdG90eXBlKXJldHVybiBuZXcgUC51dyhhLE9i
-amVjdC5jcmVhdGUobnVsbCkpCmZvcih0PTA7dDxhLmxlbmd0aDsrK3QpYVt0XT1QLlFlKGFbdF0pCnJl
-dHVybiBhfSwKa3k6ZnVuY3Rpb24oYSxiLGMsZCl7aWYoYiBpbnN0YW5jZW9mIFVpbnQ4QXJyYXkpcmV0
-dXJuIFAuUlAoITEsYixjLGQpCnJldHVybiBudWxsfSwKUlA6ZnVuY3Rpb24oYSxiLGMsZCl7dmFyIHQs
-cyxyPSQudEwoKQppZihyPT1udWxsKXJldHVybiBudWxsCnQ9MD09PWMKaWYodCYmITApcmV0dXJuIFAu
-T1EocixiKQpzPWIubGVuZ3RoCmQ9UC5qQihjLGQscykKaWYodCYmZD09PXMpcmV0dXJuIFAuT1Eocixi
-KQpyZXR1cm4gUC5PUShyLGIuc3ViYXJyYXkoYyxkKSl9LApPUTpmdW5jdGlvbihhLGIpe2lmKFAuQWoo
-YikpcmV0dXJuIG51bGwKcmV0dXJuIFAuSmgoYSxiKX0sCkpoOmZ1bmN0aW9uKGEsYil7dmFyIHQscwp0
-cnl7dD1hLmRlY29kZShiKQpyZXR1cm4gdH1jYXRjaChzKXtILlJ1KHMpfXJldHVybiBudWxsfSwKQWo6
-ZnVuY3Rpb24oYSl7dmFyIHQscz1hLmxlbmd0aC0yCmZvcih0PTA7dDxzOysrdClpZihhW3RdPT09MjM3
-KWlmKChhW3QrMV0mMjI0KT09PTE2MClyZXR1cm4hMApyZXR1cm4hMX0sCldJOmZ1bmN0aW9uKCl7dmFy
-IHQscwp0cnl7dD1uZXcgVGV4dERlY29kZXIoInV0Zi04Iix7ZmF0YWw6dHJ1ZX0pCnJldHVybiB0fWNh
-dGNoKHMpe0guUnUocyl9cmV0dXJuIG51bGx9LApjUDpmdW5jdGlvbihhLGIsYyl7dmFyIHQscyxyCmZv
-cih0PUouVTYoYSkscz1iO3M8YzsrK3Mpe3I9dC5xKGEscykKaWYodHlwZW9mIHIhPT0ibnVtYmVyIily
-ZXR1cm4gci56TSgpCmlmKChyJjEyNykhPT1yKXJldHVybiBzLWJ9cmV0dXJuIGMtYn0sCnhNOmZ1bmN0
-aW9uKGEsYixjLGQsZSxmKXtpZihDLmpuLnpZKGYsNCkhPT0wKXRocm93IEguYihQLnJyKCJJbnZhbGlk
-IGJhc2U2NCBwYWRkaW5nLCBwYWRkZWQgbGVuZ3RoIG11c3QgYmUgbXVsdGlwbGUgb2YgZm91ciwgaXMg
-IitmLGEsYykpCmlmKGQrZSE9PWYpdGhyb3cgSC5iKFAucnIoIkludmFsaWQgYmFzZTY0IHBhZGRpbmcs
-ICc9JyBub3QgYXQgdGhlIGVuZCIsYSxiKSkKaWYoZT4yKXRocm93IEguYihQLnJyKCJJbnZhbGlkIGJh
-c2U2NCBwYWRkaW5nLCBtb3JlIHRoYW4gdHdvICc9JyBjaGFyYWN0ZXJzIixhLGIpKX0sCkd5OmZ1bmN0
-aW9uKGEsYixjKXtyZXR1cm4gbmV3IFAuVWQoYSxiKX0sCk5DOmZ1bmN0aW9uKGEpe3JldHVybiBhLkx0
-KCl9LAp1WDpmdW5jdGlvbihhLGIsYyl7dmFyIHQscz1uZXcgUC5SbigiIikscj1uZXcgUC50dShzLFtd
-LFAuQ3koKSkKci5pVShhKQp0PXMuYQpyZXR1cm4gdC5jaGFyQ29kZUF0KDApPT0wP3Q6dH0sCnV3OmZ1
-bmN0aW9uIHV3KGEsYil7dGhpcy5hPWEKdGhpcy5iPWIKdGhpcy5jPW51bGx9LAppODpmdW5jdGlvbiBp
-OChhKXt0aGlzLmE9YX0sCkNWOmZ1bmN0aW9uIENWKCl7fSwKVTg6ZnVuY3Rpb24gVTgoKXt9LApVazpm
-dW5jdGlvbiBVaygpe30sCndJOmZ1bmN0aW9uIHdJKCl7fSwKWmk6ZnVuY3Rpb24gWmkoKXt9LApVZDpm
-dW5jdGlvbiBVZChhLGIpe3RoaXMuYT1hCnRoaXMuYj1ifSwKSzg6ZnVuY3Rpb24gSzgoYSxiKXt0aGlz
-LmE9YQp0aGlzLmI9Yn0sCmJ5OmZ1bmN0aW9uIGJ5KCl7fSwKb2o6ZnVuY3Rpb24gb2ooYSl7dGhpcy5i
-PWF9LApNeDpmdW5jdGlvbiBNeChhKXt0aGlzLmE9YX0sClNoOmZ1bmN0aW9uIFNoKCl7fSwKdGk6ZnVu
-Y3Rpb24gdGkoYSxiKXt0aGlzLmE9YQp0aGlzLmI9Yn0sCnR1OmZ1bmN0aW9uIHR1KGEsYixjKXt0aGlz
-LmM9YQp0aGlzLmE9Ygp0aGlzLmI9Y30sCnU1OmZ1bmN0aW9uIHU1KCl7fSwKRTM6ZnVuY3Rpb24gRTMo
-KXt9LApSdzpmdW5jdGlvbiBSdyhhKXt0aGlzLmI9MAp0aGlzLmM9YX0sCkdZOmZ1bmN0aW9uIEdZKGEp
-e3RoaXMuYT1hfSwKYno6ZnVuY3Rpb24gYnooYSxiKXt2YXIgXz10aGlzCl8uYT1hCl8uYj1iCl8uYz0h
-MApfLmY9Xy5lPV8uZD0wfSwKUUE6ZnVuY3Rpb24oYSxiLGMpe3ZhciB0PUguSHAoYSxjKQppZih0IT1u
-dWxsKXJldHVybiB0CmlmKGIhPW51bGwpcmV0dXJuIGIuJDEoYSkKdGhyb3cgSC5iKFAucnIoYSxudWxs
-LG51bGwpKX0sCm9zOmZ1bmN0aW9uKGEpe2lmKGEgaW5zdGFuY2VvZiBILlRwKXJldHVybiBhLlooMCkK
-cmV0dXJuIkluc3RhbmNlIG9mICciK0guZChILmxoKGEpKSsiJyJ9LApPODpmdW5jdGlvbihhLGIsYyxk
-KXt2YXIgdCxzCmlmKGMpdD1ILlZNKG5ldyBBcnJheShhKSxkLkMoImpkPDA+IikpCmVsc2UgdD1KLlFp
-KGEsZCkKaWYoYSE9PTAmJiEwKWZvcihzPTA7czx0Lmxlbmd0aDsrK3MpQy5ObS5ZKHQscyxiKQpyZXR1
-cm4gdH0sCkNIOmZ1bmN0aW9uKGEsYixjKXt2YXIgdCxzPUguVk0oW10sYy5DKCJqZDwwPiIpKQpmb3Io
-dD1KLklUKGEpO3QuRigpOylDLk5tLmkocyxjLmEodC5nbCgpKSkKaWYoYilyZXR1cm4gcwpyZXR1cm4g
-Yy5DKCJ6TTwwPiIpLmEoSi5FcChzKSl9LApBRjpmdW5jdGlvbihhLGIpe3JldHVybiBiLkMoInpNPDA+
-IikuYShKLnVuKFAuQ0goYSwhMSxiKSkpfSwKSE06ZnVuY3Rpb24oYSxiLGMpe3ZhciB0CmlmKEFycmF5
-LmlzQXJyYXkoYSkpe3UudC5hKGEpCnQ9YS5sZW5ndGgKYz1QLmpCKGIsYyx0KQpyZXR1cm4gSC5lVChi
-PjB8fGM8dD9DLk5tLkQ2KGEsYixjKTphKX1pZih1LmJtLmIoYSkpcmV0dXJuIEguZncoYSxiLFAuakIo
-YixjLGEubGVuZ3RoKSkKcmV0dXJuIFAuYncoYSxiLGMpfSwKT286ZnVuY3Rpb24oYSl7cmV0dXJuIEgu
-THcoYSl9LApidzpmdW5jdGlvbihhLGIsYyl7dmFyIHQscyxyLHEscD1udWxsCmlmKGI8MCl0aHJvdyBI
-LmIoUC5URShiLDAsSi5IKGEpLHAscCkpCnQ9Yz09bnVsbAppZighdCYmYzxiKXRocm93IEguYihQLlRF
-KGMsYixKLkgoYSkscCxwKSkKcz1KLklUKGEpCmZvcihyPTA7cjxiOysrcilpZighcy5GKCkpdGhyb3cg
-SC5iKFAuVEUoYiwwLHIscCxwKSkKcT1bXQppZih0KWZvcig7cy5GKCk7KXEucHVzaChzLmdsKCkpCmVs
-c2UgZm9yKHI9YjtyPGM7KytyKXtpZighcy5GKCkpdGhyb3cgSC5iKFAuVEUoYyxiLHIscCxwKSkKcS5w
-dXNoKHMuZ2woKSl9cmV0dXJuIEguZVQocSl9LApudTpmdW5jdGlvbihhKXtyZXR1cm4gbmV3IEguVlIo
-YSxILnY0KGEsITEsITAsITEsITEsITEpKX0sCnZnOmZ1bmN0aW9uKGEsYixjKXt2YXIgdD1KLklUKGIp
-CmlmKCF0LkYoKSlyZXR1cm4gYQppZihjLmxlbmd0aD09PTApe2RvIGErPUguZCh0LmdsKCkpCndoaWxl
-KHQuRigpKX1lbHNle2ErPUguZCh0LmdsKCkpCmZvcig7dC5GKCk7KWE9YStjK0guZCh0LmdsKCkpfXJl
-dHVybiBhfSwKbHI6ZnVuY3Rpb24oYSxiLGMsZCl7cmV0dXJuIG5ldyBQLm1wKGEsYixjLGQpfSwKdW86
-ZnVuY3Rpb24oKXt2YXIgdD1ILk0wKCkKaWYodCE9bnVsbClyZXR1cm4gUC5oSyh0KQp0aHJvdyBILmIo
-UC5MNCgiJ1VyaS5iYXNlJyBpcyBub3Qgc3VwcG9ydGVkIikpfSwKZVA6ZnVuY3Rpb24oYSxiLGMsZCl7
-dmFyIHQscyxyLHEscCxvLG49IjAxMjM0NTY3ODlBQkNERUYiCmlmKGM9PT1DLnhNKXt0PSQuejQoKS5i
-CmlmKHR5cGVvZiBiIT0ic3RyaW5nIilILnZoKEguSShiKSkKdD10LnRlc3QoYil9ZWxzZSB0PSExCmlm
-KHQpcmV0dXJuIGIKSC5MaChjKS5DKCJVay5TIikuYShiKQpzPWMuZ1pFKCkuV0ooYikKZm9yKHQ9cy5s
-ZW5ndGgscj0wLHE9IiI7cjx0Oysrcil7cD1zW3JdCmlmKHA8MTI4KXtvPXA+Pj40CmlmKG8+PTgpcmV0
-dXJuIEguayhhLG8pCm89KGFbb10mMTw8KHAmMTUpKSE9PTB9ZWxzZSBvPSExCmlmKG8pcSs9SC5Mdyhw
-KQplbHNlIHE9ZCYmcD09PTMyP3ErIisiOnErIiUiK25bcD4+PjQmMTVdK25bcCYxNV19cmV0dXJuIHEu
-Y2hhckNvZGVBdCgwKT09MD9xOnF9LApaYjpmdW5jdGlvbigpe3ZhciB0LHMKaWYoSC5vVCgkLnA2KCkp
-KXJldHVybiBILnRzKG5ldyBFcnJvcigpKQp0cnl7dGhyb3cgSC5iKCIiKX1jYXRjaChzKXtILlJ1KHMp
-CnQ9SC50cyhzKQpyZXR1cm4gdH19LApHcTpmdW5jdGlvbihhKXt2YXIgdD1NYXRoLmFicyhhKSxzPWE8
-MD8iLSI6IiIKaWYodD49MTAwMClyZXR1cm4iIithCmlmKHQ+PTEwMClyZXR1cm4gcysiMCIrdAppZih0
-Pj0xMClyZXR1cm4gcysiMDAiK3QKcmV0dXJuIHMrIjAwMCIrdH0sClZ4OmZ1bmN0aW9uKGEpe2lmKGE+
-PTEwMClyZXR1cm4iIithCmlmKGE+PTEwKXJldHVybiIwIithCnJldHVybiIwMCIrYX0sCmgwOmZ1bmN0
-aW9uKGEpe2lmKGE+PTEwKXJldHVybiIiK2EKcmV0dXJuIjAiK2F9LApoOmZ1bmN0aW9uKGEpe2lmKHR5
-cGVvZiBhPT0ibnVtYmVyInx8SC5yUShhKXx8bnVsbD09YSlyZXR1cm4gSi5BYyhhKQppZih0eXBlb2Yg
-YT09InN0cmluZyIpcmV0dXJuIEpTT04uc3RyaW5naWZ5KGEpCnJldHVybiBQLm9zKGEpfSwKaFY6ZnVu
-Y3Rpb24oYSl7cmV0dXJuIG5ldyBQLkM2KGEpfSwKeFk6ZnVuY3Rpb24oYSl7cmV0dXJuIG5ldyBQLkFU
-KCExLG51bGwsbnVsbCxhKX0sCkwzOmZ1bmN0aW9uKGEsYixjKXtyZXR1cm4gbmV3IFAuQVQoITAsYSxi
-LGMpfSwKRWU6ZnVuY3Rpb24oYSl7cmV0dXJuIG5ldyBQLkFUKCExLG51bGwsYSwiTXVzdCBub3QgYmUg
-bnVsbCIpfSwKVUk6ZnVuY3Rpb24oYSxiLGMpe2lmKGE9PW51bGwpdGhyb3cgSC5iKFAuRWUoYikpCnJl
-dHVybiBhfSwKTzc6ZnVuY3Rpb24oYSxiKXtyZXR1cm4gbmV3IFAuYkoobnVsbCxudWxsLCEwLGEsYiwi
-VmFsdWUgbm90IGluIHJhbmdlIil9LApURTpmdW5jdGlvbihhLGIsYyxkLGUpe3JldHVybiBuZXcgUC5i
-SihiLGMsITAsYSxkLCJJbnZhbGlkIHZhbHVlIil9LAp3QTpmdW5jdGlvbihhLGIsYyxkKXtpZihhPGJ8
-fGE+Yyl0aHJvdyBILmIoUC5URShhLGIsYyxkLG51bGwpKQpyZXR1cm4gYX0sCmpCOmZ1bmN0aW9uKGEs
-YixjKXtpZigwPmF8fGE+Yyl0aHJvdyBILmIoUC5URShhLDAsYywic3RhcnQiLG51bGwpKQppZihiIT1u
-dWxsKXtpZihhPmJ8fGI+Yyl0aHJvdyBILmIoUC5URShiLGEsYywiZW5kIixudWxsKSkKcmV0dXJuIGJ9
-cmV0dXJuIGN9LAprMTpmdW5jdGlvbihhLGIpe2lmKHR5cGVvZiBhIT09Im51bWJlciIpcmV0dXJuIGEu
-SigpCmlmKGE8MCl0aHJvdyBILmIoUC5URShhLDAsbnVsbCxiLG51bGwpKQpyZXR1cm4gYX0sCnQ6ZnVu
-Y3Rpb24oYSxiLGMsZCxlKXt2YXIgdD1ILldZKGU9PW51bGw/Si5IKGIpOmUpCnJldHVybiBuZXcgUC5l
-WSh0LCEwLGEsYywiSW5kZXggb3V0IG9mIHJhbmdlIil9LApMNDpmdW5jdGlvbihhKXtyZXR1cm4gbmV3
-IFAudWIoYSl9LApuOmZ1bmN0aW9uKGEpe3JldHVybiBuZXcgUC5kcyhhKX0sClBWOmZ1bmN0aW9uKGEp
-e3JldHVybiBuZXcgUC5saihhKX0sCmE0OmZ1bmN0aW9uKGEpe3JldHVybiBuZXcgUC5VVihhKX0sCnJy
-OmZ1bmN0aW9uKGEsYixjKXtyZXR1cm4gbmV3IFAuYUUoYSxiLGMpfSwKZEg6ZnVuY3Rpb24oYSxiLGMp
-e3ZhciB0LHM9SC5WTShbXSxjLkMoImpkPDA+IikpCkMuTm0uc0EocyxhKQpmb3IodD0wO3Q8YTsrK3Qp
-Qy5ObS5ZKHMsdCxiLiQxKHQpKQpyZXR1cm4gc30sCmhLOmZ1bmN0aW9uKGEpe3ZhciB0LHMscixxLHAs
-byxuLG0sbCxrLGosaSxoLGcsZixlPW51bGwsZD1hLmxlbmd0aAppZihkPj01KXt0PSgoSi5ReihhLDQp
-XjU4KSozfEMueEIuVyhhLDApXjEwMHxDLnhCLlcoYSwxKV45N3xDLnhCLlcoYSwyKV4xMTZ8Qy54Qi5X
-KGEsMyleOTcpPj4+MAppZih0PT09MClyZXR1cm4gUC5LRChkPGQ/Qy54Qi5OaihhLDAsZCk6YSw1LGUp
-LmdsUigpCmVsc2UgaWYodD09PTMyKXJldHVybiBQLktEKEMueEIuTmooYSw1LGQpLDAsZSkuZ2xSKCl9
-cz1uZXcgQXJyYXkoOCkKcy5maXhlZCRsZW5ndGg9QXJyYXkKcj1ILlZNKHMsdS50KQpDLk5tLlkociww
-LDApCkMuTm0uWShyLDEsLTEpCkMuTm0uWShyLDIsLTEpCkMuTm0uWShyLDcsLTEpCkMuTm0uWShyLDMs
-MCkKQy5ObS5ZKHIsNCwwKQpDLk5tLlkociw1LGQpCkMuTm0uWShyLDYsZCkKaWYoUC5VQihhLDAsZCww
-LHIpPj0xNClDLk5tLlkociw3LGQpCnE9clsxXQppZih0eXBlb2YgcSE9PSJudW1iZXIiKXJldHVybiBx
-LnRCKCkKaWYocT49MClpZihQLlVCKGEsMCxxLDIwLHIpPT09MjApcls3XT1xCnM9clsyXQppZih0eXBl
-b2YgcyE9PSJudW1iZXIiKXJldHVybiBzLmgoKQpwPXMrMQpvPXJbM10Kbj1yWzRdCm09cls1XQpsPXJb
-Nl0KaWYodHlwZW9mIGwhPT0ibnVtYmVyIilyZXR1cm4gbC5KKCkKaWYodHlwZW9mIG0hPT0ibnVtYmVy
-IilyZXR1cm4gSC5wWShtKQppZihsPG0pbT1sCmlmKHR5cGVvZiBuIT09Im51bWJlciIpcmV0dXJuIG4u
-SigpCmlmKG48cCluPW0KZWxzZSBpZihuPD1xKW49cSsxCmlmKHR5cGVvZiBvIT09Im51bWJlciIpcmV0
-dXJuIG8uSigpCmlmKG88cClvPW4Kcz1yWzddCmlmKHR5cGVvZiBzIT09Im51bWJlciIpcmV0dXJuIHMu
-SigpCms9czwwCmlmKGspaWYocD5xKzMpe2o9ZQprPSExfWVsc2V7cz1vPjAKaWYocyYmbysxPT09bil7
-aj1lCms9ITF9ZWxzZXtpZighKG08ZCYmbT09PW4rMiYmSi5xMChhLCIuLiIsbikpKWk9bT5uKzImJkou
-cTAoYSwiLy4uIixtLTMpCmVsc2UgaT0hMAppZihpKXtqPWUKaz0hMX1lbHNle2lmKHE9PT00KWlmKEou
-cTAoYSwiZmlsZSIsMCkpe2lmKHA8PTApe2lmKCFDLnhCLlFpKGEsIi8iLG4pKXtoPSJmaWxlOi8vLyIK
-dD0zfWVsc2V7aD0iZmlsZTovLyIKdD0yfWE9aCtDLnhCLk5qKGEsbixkKQpxLT0wCnM9dC0wCm0rPXMK
-bCs9cwpkPWEubGVuZ3RoCnA9NwpvPTcKbj03fWVsc2UgaWYobj09PW0pe2c9bSsxOysrbAphPUMueEIu
-aTcoYSxuLG0sIi8iKTsrK2QKbT1nfWo9ImZpbGUifWVsc2UgaWYoQy54Qi5RaShhLCJodHRwIiwwKSl7
-aWYocyYmbyszPT09biYmQy54Qi5RaShhLCI4MCIsbysxKSl7Zj1uLTMKbS09MwpsLT0zCmE9Qy54Qi5p
-NyhhLG8sbiwiIikKZC09MwpuPWZ9aj0iaHR0cCJ9ZWxzZSBqPWUKZWxzZSBpZihxPT09NSYmSi5xMChh
-LCJodHRwcyIsMCkpe2lmKHMmJm8rND09PW4mJkoucTAoYSwiNDQzIixvKzEpKXtmPW4tNAptLT00Cmwt
-PTQKYT1KLmRnKGEsbyxuLCIiKQpkLT0zCm49Zn1qPSJodHRwcyJ9ZWxzZSBqPWUKaz0hMH19fWVsc2Ug
-aj1lCmlmKGspe3M9YS5sZW5ndGgKaWYoZDxzKXthPUoubGQoYSwwLGQpCnEtPTAKcC09MApvLT0wCm4t
-PTAKbS09MApsLT0wfXJldHVybiBuZXcgUC5VZihhLHEscCxvLG4sbSxsLGopfXJldHVybiBQLmp2KGEs
-MCxkLHEscCxvLG4sbSxsLGopfSwKTXQ6ZnVuY3Rpb24oYSl7SC5jKGEpCnJldHVybiBQLmt1KGEsMCxh
-Lmxlbmd0aCxDLnhNLCExKX0sCldYOmZ1bmN0aW9uKGEpe3ZhciB0PXUuTgpyZXR1cm4gQy5ObS5OMChI
-LlZNKGEuc3BsaXQoIiYiKSx1LnMpLFAuRmwodCx0KSxuZXcgUC5uMShDLnhNKSx1LmYpfSwKSGg6ZnVu
-Y3Rpb24oYSxiLGMpe3ZhciB0LHMscixxLHAsbyxuLG09bnVsbCxsPSJJUHY0IGFkZHJlc3Mgc2hvdWxk
-IGNvbnRhaW4gZXhhY3RseSA0IHBhcnRzIixrPSJlYWNoIHBhcnQgbXVzdCBiZSBpbiB0aGUgcmFuZ2Ug
-MC4uMjU1IixqPW5ldyBQLmNTKGEpLGk9bmV3IFVpbnQ4QXJyYXkoNCkKZm9yKHQ9aS5sZW5ndGgscz1i
-LHI9cyxxPTA7czxjOysrcyl7cD1DLnhCLm0oYSxzKQppZihwIT09NDYpe2lmKChwXjQ4KT45KWouJDIo
-ImludmFsaWQgY2hhcmFjdGVyIixzKX1lbHNle2lmKHE9PT0zKWouJDIobCxzKQpvPVAuUUEoQy54Qi5O
-aihhLHIscyksbSxtKQppZih0eXBlb2YgbyE9PSJudW1iZXIiKXJldHVybiBvLm9zKCkKaWYobz4yNTUp
-ai4kMihrLHIpCm49cSsxCmlmKHE+PXQpcmV0dXJuIEguayhpLHEpCmlbcV09bwpyPXMrMQpxPW59fWlm
-KHEhPT0zKWouJDIobCxjKQpvPVAuUUEoQy54Qi5OaihhLHIsYyksbSxtKQppZih0eXBlb2YgbyE9PSJu
-dW1iZXIiKXJldHVybiBvLm9zKCkKaWYobz4yNTUpai4kMihrLHIpCmlmKHE+PXQpcmV0dXJuIEguayhp
-LHEpCmlbcV09bwpyZXR1cm4gaX0sCmVnOmZ1bmN0aW9uKGEsYixhMCl7dmFyIHQscyxyLHEscCxvLG4s
-bSxsLGssaixpLGgsZyxmLGUsZD1uZXcgUC5WQyhhKSxjPW5ldyBQLkpUKGQsYSkKaWYoYS5sZW5ndGg8
-MilkLiQxKCJhZGRyZXNzIGlzIHRvbyBzaG9ydCIpCnQ9SC5WTShbXSx1LnQpCmZvcihzPWIscj1zLHE9
-ITEscD0hMTtzPGEwOysrcyl7bz1DLnhCLm0oYSxzKQppZihvPT09NTgpe2lmKHM9PT1iKXsrK3MKaWYo
-Qy54Qi5tKGEscykhPT01OClkLiQyKCJpbnZhbGlkIHN0YXJ0IGNvbG9uLiIscykKcj1zfWlmKHM9PT1y
-KXtpZihxKWQuJDIoIm9ubHkgb25lIHdpbGRjYXJkIGA6OmAgaXMgYWxsb3dlZCIscykKQy5ObS5pKHQs
-LTEpCnE9ITB9ZWxzZSBDLk5tLmkodCxjLiQyKHIscykpCnI9cysxfWVsc2UgaWYobz09PTQ2KXA9ITB9
-aWYodC5sZW5ndGg9PT0wKWQuJDEoInRvbyBmZXcgcGFydHMiKQpuPXI9PT1hMAptPUMuTm0uZ3JaKHQp
-CmlmKG4mJm0hPT0tMSlkLiQyKCJleHBlY3RlZCBhIHBhcnQgYWZ0ZXIgbGFzdCBgOmAiLGEwKQppZigh
-bilpZighcClDLk5tLmkodCxjLiQyKHIsYTApKQplbHNle2w9UC5IaChhLHIsYTApCkMuTm0uaSh0LChs
-WzBdPDw4fGxbMV0pPj4+MCkKQy5ObS5pKHQsKGxbMl08PDh8bFszXSk+Pj4wKX1pZihxKXtpZih0Lmxl
-bmd0aD43KWQuJDEoImFuIGFkZHJlc3Mgd2l0aCBhIHdpbGRjYXJkIG11c3QgaGF2ZSBsZXNzIHRoYW4g
-NyBwYXJ0cyIpfWVsc2UgaWYodC5sZW5ndGghPT04KWQuJDEoImFuIGFkZHJlc3Mgd2l0aG91dCBhIHdp
-bGRjYXJkIG11c3QgY29udGFpbiBleGFjdGx5IDggcGFydHMiKQprPW5ldyBVaW50OEFycmF5KDE2KQpm
-b3IobT10Lmxlbmd0aCxqPWsubGVuZ3RoLGk9OS1tLHM9MCxoPTA7czxtOysrcyl7Zz10W3NdCmlmKGc9
-PT0tMSlmb3IoZj0wO2Y8aTsrK2Ype2lmKGg8MHx8aD49ailyZXR1cm4gSC5rKGssaCkKa1toXT0wCmU9
-aCsxCmlmKGU+PWopcmV0dXJuIEguayhrLGUpCmtbZV09MApoKz0yfWVsc2V7ZT1DLmpuLndHKGcsOCkK
-aWYoaDwwfHxoPj1qKXJldHVybiBILmsoayxoKQprW2hdPWUKZT1oKzEKaWYoZT49ailyZXR1cm4gSC5r
-KGssZSkKa1tlXT1nJjI1NQpoKz0yfX1yZXR1cm4ga30sCmp2OmZ1bmN0aW9uKGEsYixjLGQsZSxmLGcs
-aCxpLGope3ZhciB0LHMscixxLHAsbyxuLG09bnVsbAppZihqPT1udWxsKWlmKGQ+YilqPVAuUGkoYSxi
-LGQpCmVsc2V7aWYoZD09PWIpUC5SMyhhLGIsIkludmFsaWQgZW1wdHkgc2NoZW1lIikKaj0iIn1pZihl
-PmIpe3Q9ZCszCnM9dDxlP1AuelIoYSx0LGUtMSk6IiIKcj1QLk9lKGEsZSxmLCExKQppZih0eXBlb2Yg
-ZiE9PSJudW1iZXIiKXJldHVybiBmLmgoKQpxPWYrMQppZih0eXBlb2YgZyE9PSJudW1iZXIiKXJldHVy
-biBILnBZKGcpCnA9cTxnP1Aud0IoUC5RQShKLmxkKGEscSxnKSxuZXcgUC5lMShhLGYpLG0pLGopOm19
-ZWxzZXtwPW0Kcj1wCnM9IiJ9bz1QLmthKGEsZyxoLG0saixyIT1udWxsKQppZih0eXBlb2YgaCE9PSJu
-dW1iZXIiKXJldHVybiBoLkooKQpuPWg8aT9QLmxlKGEsaCsxLGksbSk6bQpyZXR1cm4gbmV3IFAuRG4o
-aixzLHIscCxvLG4saTxjP1AudEcoYSxpKzEsYyk6bSl9LApLTDpmdW5jdGlvbihhLGIsYyxkLGUsZixn
-KXt2YXIgdCxzLHIscSxwLG8KZj1QLlBpKGYsMCxmPT1udWxsPzA6Zi5sZW5ndGgpCmc9UC56UihnLDAs
-Zz09bnVsbD8wOmcubGVuZ3RoKQphPVAuT2UoYSwwLGE9PW51bGw/MDphLmxlbmd0aCwhMSkKdD1QLmxl
-KG51bGwsMCwwLGUpCnM9UC50RyhudWxsLDAsMCkKZD1QLndCKGQsZikKcj1mPT09ImZpbGUiCmlmKGE9
-PW51bGwpcT1nLmxlbmd0aCE9PTB8fGQhPW51bGx8fHIKZWxzZSBxPSExCmlmKHEpYT0iIgpxPWE9PW51
-bGwKcD0hcQpiPVAua2EoYiwwLGI9PW51bGw/MDpiLmxlbmd0aCxjLGYscCkKbz1mLmxlbmd0aD09PTAK
-aWYobyYmcSYmIUMueEIubihiLCIvIikpYj1QLndGKGIsIW98fHApCmVsc2UgYj1QLnhlKGIpCnJldHVy
-biBuZXcgUC5EbihmLGcscSYmQy54Qi5uKGIsIi8vIik/IiI6YSxkLGIsdCxzKX0sCndLOmZ1bmN0aW9u
-KGEpe2lmKGE9PT0iaHR0cCIpcmV0dXJuIDgwCmlmKGE9PT0iaHR0cHMiKXJldHVybiA0NDMKcmV0dXJu
-IDB9LApSMzpmdW5jdGlvbihhLGIsYyl7dGhyb3cgSC5iKFAucnIoYyxhLGIpKX0sClhkOmZ1bmN0aW9u
-KGEsYixjLGQpe3ZhciB0LHMscixxLHAsbyxuLG0sbCxrLGosaT1udWxsLGg9Yi5sZW5ndGgKaWYoaCE9
-PTApe3I9MAp3aGlsZSghMCl7aWYoIShyPGgpKXt0PSIiCnM9MApicmVha31pZihDLnhCLlcoYixyKT09
-PTY0KXt0PUMueEIuTmooYiwwLHIpCnM9cisxCmJyZWFrfSsrcn1pZihzPGgmJkMueEIuVyhiLHMpPT09
-OTEpe2ZvcihxPXMscD0tMTtxPGg7KytxKXtvPUMueEIuVyhiLHEpCmlmKG89PT0zNyYmcDwwKXtuPUMu
-eEIuUWkoYiwiMjUiLHErMSk/cSsyOnEKcD1xCnE9bn1lbHNlIGlmKG89PT05MylicmVha31pZihxPT09
-aCl0aHJvdyBILmIoUC5ycigiSW52YWxpZCBJUHY2IGhvc3QgZW50cnkuIixiLHMpKQptPXA8MD9xOnAK
-UC5lZyhiLHMrMSxtKTsrK3EKaWYocSE9PWgmJkMueEIuVyhiLHEpIT09NTgpdGhyb3cgSC5iKFAucnIo
-IkludmFsaWQgZW5kIG9mIGF1dGhvcml0eSIsYixxKSl9ZWxzZSBxPXMKd2hpbGUoITApe2lmKCEocTxo
-KSl7bD1pCmJyZWFrfWlmKEMueEIuVyhiLHEpPT09NTgpe2s9Qy54Qi5HKGIscSsxKQpsPWsubGVuZ3Ro
-IT09MD9QLlFBKGssaSxpKTppCmJyZWFrfSsrcX1qPUMueEIuTmooYixzLHEpfWVsc2V7bD1pCmo9bAp0
-PSIifXJldHVybiBQLktMKGosaSxILlZNKGMuc3BsaXQoIi8iKSx1LnMpLGwsZCxhLHQpfSwKa0U6ZnVu
-Y3Rpb24oYSxiKXtDLk5tLksoYSxuZXcgUC5OWSghMSkpfSwKSE46ZnVuY3Rpb24oYSxiLGMpe3ZhciB0
-LHMscgpmb3IodD1ILnFDKGEsYyxudWxsLEgudDYoYSkuYyksdD1uZXcgSC5hNyh0LHQuZ0EodCksdC4k
-dGkuQygiYTc8YUwuRT4iKSk7dC5GKCk7KXtzPXQuZApyPVAubnUoJ1siKi86PD4/XFxcXHxdJykKcy50
-b1N0cmluZwppZihILm0yKHMsciwwKSl7dD1QLkw0KCJJbGxlZ2FsIGNoYXJhY3RlciBpbiBwYXRoOiAi
-K3MpCnRocm93IEguYih0KX19fSwKcmc6ZnVuY3Rpb24oYSxiKXt2YXIgdAppZighKDY1PD1hJiZhPD05
-MCkpdD05Nzw9YSYmYTw9MTIyCmVsc2UgdD0hMAppZih0KXJldHVybgp0PVAuTDQoIklsbGVnYWwgZHJp
-dmUgbGV0dGVyICIrUC5PbyhhKSkKdGhyb3cgSC5iKHQpfSwKd0I6ZnVuY3Rpb24oYSxiKXtpZihhIT1u
-dWxsJiZhPT09UC53SyhiKSlyZXR1cm4gbnVsbApyZXR1cm4gYX0sCk9lOmZ1bmN0aW9uKGEsYixjLGQp
-e3ZhciB0LHMscixxLHAsbwppZihhPT1udWxsKXJldHVybiBudWxsCmlmKGI9PT1jKXJldHVybiIiCmlm
-KEMueEIubShhLGIpPT09OTEpe2lmKHR5cGVvZiBjIT09Im51bWJlciIpcmV0dXJuIGMuSE4oKQp0PWMt
-MQppZihDLnhCLm0oYSx0KSE9PTkzKVAuUjMoYSxiLCJNaXNzaW5nIGVuZCBgXWAgdG8gbWF0Y2ggYFtg
-IGluIGhvc3QiKQpzPWIrMQpyPVAudG8oYSxzLHQpCmlmKHR5cGVvZiByIT09Im51bWJlciIpcmV0dXJu
-IHIuSigpCmlmKHI8dCl7cT1yKzEKcD1QLk9BKGEsQy54Qi5RaShhLCIyNSIscSk/ciszOnEsdCwiJTI1
-Iil9ZWxzZSBwPSIiClAuZWcoYSxzLHIpCnJldHVybiBDLnhCLk5qKGEsYixyKS50b0xvd2VyQ2FzZSgp
-K3ArIl0ifWlmKHR5cGVvZiBjIT09Im51bWJlciIpcmV0dXJuIEgucFkoYykKbz1iCmZvcig7bzxjOysr
-bylpZihDLnhCLm0oYSxvKT09PTU4KXtyPUMueEIuWFUoYSwiJSIsYikKaWYoIShyPj1iJiZyPGMpKXI9
-YwppZihyPGMpe3E9cisxCnA9UC5PQShhLEMueEIuUWkoYSwiMjUiLHEpP3IrMzpxLGMsIiUyNSIpfWVs
-c2UgcD0iIgpQLmVnKGEsYixyKQpyZXR1cm4iWyIrQy54Qi5OaihhLGIscikrcCsiXSJ9cmV0dXJuIFAu
-T0woYSxiLGMpfSwKdG86ZnVuY3Rpb24oYSxiLGMpe3ZhciB0LHM9Qy54Qi5YVShhLCIlIixiKQppZihz
-Pj1iKXtpZih0eXBlb2YgYyE9PSJudW1iZXIiKXJldHVybiBILnBZKGMpCnQ9czxjfWVsc2UgdD0hMQpy
-ZXR1cm4gdD9zOmN9LApPQTpmdW5jdGlvbihhLGIsYyxkKXt2YXIgdCxzLHIscSxwLG8sbixtLGwsaz1k
-IT09IiI/bmV3IFAuUm4oZCk6bnVsbAppZih0eXBlb2YgYyE9PSJudW1iZXIiKXJldHVybiBILnBZKGMp
-CnQ9YgpzPXQKcj0hMApmb3IoO3Q8Yzspe3E9Qy54Qi5tKGEsdCkKaWYocT09PTM3KXtwPVAucnYoYSx0
-LCEwKQpvPXA9PW51bGwKaWYobyYmcil7dCs9Mwpjb250aW51ZX1pZihrPT1udWxsKWs9bmV3IFAuUm4o
-IiIpCm49ay5hKz1DLnhCLk5qKGEscyx0KQppZihvKXA9Qy54Qi5OaihhLHQsdCszKQplbHNlIGlmKHA9
-PT0iJSIpUC5SMyhhLHQsIlpvbmVJRCBzaG91bGQgbm90IGNvbnRhaW4gJSBhbnltb3JlIikKay5hPW4r
-cAp0Kz0zCnM9dApyPSEwfWVsc2V7aWYocTwxMjcpe289cT4+PjQKaWYobz49OClyZXR1cm4gSC5rKEMu
-RjMsbykKbz0oQy5GM1tvXSYxPDwocSYxNSkpIT09MH1lbHNlIG89ITEKaWYobyl7aWYociYmNjU8PXEm
-JjkwPj1xKXtpZihrPT1udWxsKWs9bmV3IFAuUm4oIiIpCmlmKHM8dCl7ay5hKz1DLnhCLk5qKGEscyx0
-KQpzPXR9cj0hMX0rK3R9ZWxzZXtpZigocSY2NDUxMik9PT01NTI5NiYmdCsxPGMpe209Qy54Qi5tKGEs
-dCsxKQppZigobSY2NDUxMik9PT01NjMyMCl7cT02NTUzNnwocSYxMDIzKTw8MTB8bSYxMDIzCmw9Mn1l
-bHNlIGw9MX1lbHNlIGw9MQppZihrPT1udWxsKWs9bmV3IFAuUm4oIiIpCmsuYSs9Qy54Qi5OaihhLHMs
-dCkKay5hKz1QLnpYKHEpCnQrPWwKcz10fX19aWYoaz09bnVsbClyZXR1cm4gQy54Qi5OaihhLGIsYykK
-aWYoczxjKWsuYSs9Qy54Qi5OaihhLHMsYykKbz1rLmEKcmV0dXJuIG8uY2hhckNvZGVBdCgwKT09MD9v
-Om99LApPTDpmdW5jdGlvbihhLGIsYyl7dmFyIHQscyxyLHEscCxvLG4sbSxsLGssagppZih0eXBlb2Yg
-YyE9PSJudW1iZXIiKXJldHVybiBILnBZKGMpCnQ9YgpzPXQKcj1udWxsCnE9ITAKZm9yKDt0PGM7KXtw
-PUMueEIubShhLHQpCmlmKHA9PT0zNyl7bz1QLnJ2KGEsdCwhMCkKbj1vPT1udWxsCmlmKG4mJnEpe3Qr
-PTMKY29udGludWV9aWYocj09bnVsbClyPW5ldyBQLlJuKCIiKQptPUMueEIuTmooYSxzLHQpCmw9ci5h
-Kz0hcT9tLnRvTG93ZXJDYXNlKCk6bQppZihuKXtvPUMueEIuTmooYSx0LHQrMykKaz0zfWVsc2UgaWYo
-bz09PSIlIil7bz0iJTI1IgprPTF9ZWxzZSBrPTMKci5hPWwrbwp0Kz1rCnM9dApxPSEwfWVsc2V7aWYo
-cDwxMjcpe249cD4+PjQKaWYobj49OClyZXR1cm4gSC5rKEMuZWEsbikKbj0oQy5lYVtuXSYxPDwocCYx
-NSkpIT09MH1lbHNlIG49ITEKaWYobil7aWYocSYmNjU8PXAmJjkwPj1wKXtpZihyPT1udWxsKXI9bmV3
-IFAuUm4oIiIpCmlmKHM8dCl7ci5hKz1DLnhCLk5qKGEscyx0KQpzPXR9cT0hMX0rK3R9ZWxzZXtpZihw
-PD05Myl7bj1wPj4+NAppZihuPj04KXJldHVybiBILmsoQy5hayxuKQpuPShDLmFrW25dJjE8PChwJjE1
-KSkhPT0wfWVsc2Ugbj0hMQppZihuKVAuUjMoYSx0LCJJbnZhbGlkIGNoYXJhY3RlciIpCmVsc2V7aWYo
-KHAmNjQ1MTIpPT09NTUyOTYmJnQrMTxjKXtqPUMueEIubShhLHQrMSkKaWYoKGomNjQ1MTIpPT09NTYz
-MjApe3A9NjU1MzZ8KHAmMTAyMyk8PDEwfGomMTAyMwprPTJ9ZWxzZSBrPTF9ZWxzZSBrPTEKaWYocj09
-bnVsbClyPW5ldyBQLlJuKCIiKQptPUMueEIuTmooYSxzLHQpCnIuYSs9IXE/bS50b0xvd2VyQ2FzZSgp
-Om0Kci5hKz1QLnpYKHApCnQrPWsKcz10fX19fWlmKHI9PW51bGwpcmV0dXJuIEMueEIuTmooYSxiLGMp
-CmlmKHM8Yyl7bT1DLnhCLk5qKGEscyxjKQpyLmErPSFxP20udG9Mb3dlckNhc2UoKTptfW49ci5hCnJl
-dHVybiBuLmNoYXJDb2RlQXQoMCk9PTA/bjpufSwKUGk6ZnVuY3Rpb24oYSxiLGMpe3ZhciB0LHMscixx
-CmlmKGI9PT1jKXJldHVybiIiCmlmKCFQLkV0KEouclkoYSkuVyhhLGIpKSlQLlIzKGEsYiwiU2NoZW1l
-IG5vdCBzdGFydGluZyB3aXRoIGFscGhhYmV0aWMgY2hhcmFjdGVyIikKZm9yKHQ9YixzPSExO3Q8Yzsr
-K3Qpe3I9Qy54Qi5XKGEsdCkKaWYocjwxMjgpe3E9cj4+PjQKaWYocT49OClyZXR1cm4gSC5rKEMubUss
-cSkKcT0oQy5tS1txXSYxPDwociYxNSkpIT09MH1lbHNlIHE9ITEKaWYoIXEpUC5SMyhhLHQsIklsbGVn
-YWwgc2NoZW1lIGNoYXJhY3RlciIpCmlmKDY1PD1yJiZyPD05MClzPSEwfWE9Qy54Qi5OaihhLGIsYykK
-cmV0dXJuIFAuWWEocz9hLnRvTG93ZXJDYXNlKCk6YSl9LApZYTpmdW5jdGlvbihhKXtpZihhPT09Imh0
-dHAiKXJldHVybiJodHRwIgppZihhPT09ImZpbGUiKXJldHVybiJmaWxlIgppZihhPT09Imh0dHBzIily
-ZXR1cm4iaHR0cHMiCmlmKGE9PT0icGFja2FnZSIpcmV0dXJuInBhY2thZ2UiCnJldHVybiBhfSwKelI6
-ZnVuY3Rpb24oYSxiLGMpe2lmKGE9PW51bGwpcmV0dXJuIiIKcmV0dXJuIFAuUEkoYSxiLGMsQy50bywh
-MSl9LAprYTpmdW5jdGlvbihhLGIsYyxkLGUsZil7dmFyIHQscz1lPT09ImZpbGUiLHI9c3x8ZixxPWE9
-PW51bGwKaWYocSYmZD09bnVsbClyZXR1cm4gcz8iLyI6IiIKcT0hcQppZihxJiZkIT1udWxsKXRocm93
-IEguYihQLnhZKCJCb3RoIHBhdGggYW5kIHBhdGhTZWdtZW50cyBzcGVjaWZpZWQiKSkKaWYocSl0PVAu
-UEkoYSxiLGMsQy5XZCwhMCkKZWxzZXtkLnRvU3RyaW5nCnE9SC50NihkKQp0PW5ldyBILmxKKGQscS5D
-KCJxVSgxKSIpLmEobmV3IFAuUlooKSkscS5DKCJsSjwxLHFVPiIpKS5IKDAsIi8iKX1pZih0Lmxlbmd0
-aD09PTApe2lmKHMpcmV0dXJuIi8ifWVsc2UgaWYociYmIUMueEIubih0LCIvIikpdD0iLyIrdApyZXR1
-cm4gUC5Kcih0LGUsZil9LApKcjpmdW5jdGlvbihhLGIsYyl7dmFyIHQ9Yi5sZW5ndGg9PT0wCmlmKHQm
-JiFjJiYhQy54Qi5uKGEsIi8iKSlyZXR1cm4gUC53RihhLCF0fHxjKQpyZXR1cm4gUC54ZShhKX0sCmxl
-OmZ1bmN0aW9uKGEsYixjLGQpe3ZhciB0LHM9e30KaWYoYSE9bnVsbCl7aWYoZCE9bnVsbCl0aHJvdyBI
-LmIoUC54WSgiQm90aCBxdWVyeSBhbmQgcXVlcnlQYXJhbWV0ZXJzIHNwZWNpZmllZCIpKQpyZXR1cm4g
-UC5QSShhLGIsYyxDLlZDLCEwKX1pZihkPT1udWxsKXJldHVybiBudWxsCnQ9bmV3IFAuUm4oIiIpCnMu
-YT0iIgpkLksoMCxuZXcgUC55NShuZXcgUC5NRShzLHQpKSkKcz10LmEKcmV0dXJuIHMuY2hhckNvZGVB
-dCgwKT09MD9zOnN9LAp0RzpmdW5jdGlvbihhLGIsYyl7aWYoYT09bnVsbClyZXR1cm4gbnVsbApyZXR1
-cm4gUC5QSShhLGIsYyxDLlZDLCEwKX0sCnJ2OmZ1bmN0aW9uKGEsYixjKXt2YXIgdCxzLHIscSxwLG89
-YisyCmlmKG8+PWEubGVuZ3RoKXJldHVybiIlIgp0PUMueEIubShhLGIrMSkKcz1DLnhCLm0oYSxvKQpy
-PUgub28odCkKcT1ILm9vKHMpCmlmKHI8MHx8cTwwKXJldHVybiIlIgpwPXIqMTYrcQppZihwPDEyNyl7
-bz1DLmpuLndHKHAsNCkKaWYobz49OClyZXR1cm4gSC5rKEMuRjMsbykKbz0oQy5GM1tvXSYxPDwocCYx
-NSkpIT09MH1lbHNlIG89ITEKaWYobylyZXR1cm4gSC5MdyhjJiY2NTw9cCYmOTA+PXA/KHB8MzIpPj4+
-MDpwKQppZih0Pj05N3x8cz49OTcpcmV0dXJuIEMueEIuTmooYSxiLGIrMykudG9VcHBlckNhc2UoKQpy
-ZXR1cm4gbnVsbH0sCnpYOmZ1bmN0aW9uKGEpe3ZhciB0LHMscixxLHAsbyxuPSIwMTIzNDU2Nzg5QUJD
-REVGIgppZihhPDEyOCl7dD1uZXcgQXJyYXkoMykKdC5maXhlZCRsZW5ndGg9QXJyYXkKcz1ILlZNKHQs
-dS50KQpDLk5tLlkocywwLDM3KQpDLk5tLlkocywxLEMueEIuVyhuLGE+Pj40KSkKQy5ObS5ZKHMsMixD
-LnhCLlcobixhJjE1KSl9ZWxzZXtpZihhPjIwNDcpaWYoYT42NTUzNSl7cj0yNDAKcT00fWVsc2V7cj0y
-MjQKcT0zfWVsc2V7cj0xOTIKcT0yfXQ9bmV3IEFycmF5KDMqcSkKdC5maXhlZCRsZW5ndGg9QXJyYXkK
-cz1ILlZNKHQsdS50KQpmb3IocD0wOy0tcSxxPj0wO3I9MTI4KXtvPUMuam4uYmYoYSw2KnEpJjYzfHIK
-Qy5ObS5ZKHMscCwzNykKQy5ObS5ZKHMscCsxLEMueEIuVyhuLG8+Pj40KSkKQy5ObS5ZKHMscCsyLEMu
-eEIuVyhuLG8mMTUpKQpwKz0zfX1yZXR1cm4gUC5ITShzLDAsbnVsbCl9LApQSTpmdW5jdGlvbihhLGIs
-YyxkLGUpe3ZhciB0PVAuVWwoYSxiLGMsZCxlKQpyZXR1cm4gdD09bnVsbD9DLnhCLk5qKGEsYixjKTp0
-fSwKVWw6ZnVuY3Rpb24oYSxiLGMsZCxlKXt2YXIgdCxzLHIscSxwLG89bnVsbCxuPSFlLG09YixsPW0s
-az1vCndoaWxlKCEwKXtpZih0eXBlb2YgbSE9PSJudW1iZXIiKXJldHVybiBtLkooKQppZih0eXBlb2Yg
-YyE9PSJudW1iZXIiKXJldHVybiBILnBZKGMpCmlmKCEobTxjKSlicmVhawpjJDA6e3Q9Qy54Qi5tKGEs
-bSkKaWYodDwxMjcpe3M9dD4+PjQKaWYocz49OClyZXR1cm4gSC5rKGQscykKcz0oZFtzXSYxPDwodCYx
-NSkpIT09MH1lbHNlIHM9ITEKaWYocykrK20KZWxzZXtpZih0PT09Mzcpe3I9UC5ydihhLG0sITEpCmlm
-KHI9PW51bGwpe20rPTMKYnJlYWsgYyQwfWlmKCIlIj09PXIpe3I9IiUyNSIKcT0xfWVsc2UgcT0zfWVs
-c2V7aWYobilpZih0PD05Myl7cz10Pj4+NAppZihzPj04KXJldHVybiBILmsoQy5hayxzKQpzPShDLmFr
-W3NdJjE8PCh0JjE1KSkhPT0wfWVsc2Ugcz0hMQplbHNlIHM9ITEKaWYocyl7UC5SMyhhLG0sIkludmFs
-aWQgY2hhcmFjdGVyIikKcT1vCnI9cX1lbHNle2lmKCh0JjY0NTEyKT09PTU1Mjk2KXtzPW0rMQppZihz
-PGMpe3A9Qy54Qi5tKGEscykKaWYoKHAmNjQ1MTIpPT09NTYzMjApe3Q9NjU1MzZ8KHQmMTAyMyk8PDEw
-fHAmMTAyMwpxPTJ9ZWxzZSBxPTF9ZWxzZSBxPTF9ZWxzZSBxPTEKcj1QLnpYKHQpfX1pZihrPT1udWxs
-KWs9bmV3IFAuUm4oIiIpCmsuYSs9Qy54Qi5OaihhLGwsbSkKay5hKz1ILmQocikKaWYodHlwZW9mIHEh
-PT0ibnVtYmVyIilyZXR1cm4gSC5wWShxKQptKz1xCmw9bX19fWlmKGs9PW51bGwpcmV0dXJuIG8KaWYo
-dHlwZW9mIGwhPT0ibnVtYmVyIilyZXR1cm4gbC5KKCkKaWYobDxjKWsuYSs9Qy54Qi5OaihhLGwsYykK
-bj1rLmEKcmV0dXJuIG4uY2hhckNvZGVBdCgwKT09MD9uOm59LAp5QjpmdW5jdGlvbihhKXtpZihDLnhC
-Lm4oYSwiLiIpKXJldHVybiEwCnJldHVybiBDLnhCLk9ZKGEsIi8uIikhPT0tMX0sCnhlOmZ1bmN0aW9u
-KGEpe3ZhciB0LHMscixxLHAsbyxuCmlmKCFQLnlCKGEpKXJldHVybiBhCnQ9SC5WTShbXSx1LnMpCmZv
-cihzPWEuc3BsaXQoIi8iKSxyPXMubGVuZ3RoLHE9ITEscD0wO3A8cjsrK3Ape289c1twXQppZihKLlJN
-KG8sIi4uIikpe249dC5sZW5ndGgKaWYobiE9PTApe2lmKDA+PW4pcmV0dXJuIEguayh0LC0xKQp0LnBv
+aWYoMD49Yi5sZW5ndGgpcmV0dXJuIEguT0goYiwtMSkKbC09Yi5wb3AoKS5sZW5ndGgrMjstLWt9Qy5O
+bS5pKGIsIi4uLiIpCnJldHVybn19cj1ILkVqKHEpCnM9SC5FaihwKQpsKz1zLmxlbmd0aCtyLmxlbmd0
+aCs0fX1pZihrPmIubGVuZ3RoKzIpe2wrPTUKbj0iLi4uIn1lbHNlIG49bnVsbAp3aGlsZSghMCl7aWYo
+IShsPjgwJiZiLmxlbmd0aD4zKSlicmVhawppZigwPj1iLmxlbmd0aClyZXR1cm4gSC5PSChiLC0xKQps
+LT1iLnBvcCgpLmxlbmd0aCsyCmlmKG49PW51bGwpe2wrPTUKbj0iLi4uIn19aWYobiE9bnVsbClDLk5t
+LmkoYixuKQpDLk5tLmkoYixyKQpDLk5tLmkoYixzKX0sCnRNOmZ1bmN0aW9uKGEsYil7dmFyIHQscyxy
+PVAuTHMoYikKZm9yKHQ9YS5sZW5ndGgscz0wO3M8YS5sZW5ndGg7YS5sZW5ndGg9PT10fHwoMCxILmxr
+KShhKSwrK3Mpci5pKDAsYi5hKGFbc10pKQpyZXR1cm4gcn0sCm5POmZ1bmN0aW9uKGEpe3ZhciB0LHM9
+e30KaWYoUC5oQihhKSlyZXR1cm4iey4uLn0iCnQ9bmV3IFAuUm4oIiIpCnRyeXtDLk5tLmkoJC54Zyxh
+KQp0LmErPSJ7IgpzLmE9ITAKYS5LKDAsbmV3IFAucmEocyx0KSkKdC5hKz0ifSJ9ZmluYWxseXtpZigw
+Pj0kLnhnLmxlbmd0aClyZXR1cm4gSC5PSCgkLnhnLC0xKQokLnhnLnBvcCgpfXM9dC5hCnJldHVybiBz
+LmNoYXJDb2RlQXQoMCk9PTA/czpzfSwKYjY6ZnVuY3Rpb24gYjYoYSl7dmFyIF89dGhpcwpfLmE9MApf
+LmY9Xy5lPV8uZD1fLmM9Xy5iPW51bGwKXy5yPTAKXy4kdGk9YX0sCmJuOmZ1bmN0aW9uIGJuKGEpe3Ro
+aXMuYT1hCnRoaXMuYz10aGlzLmI9bnVsbH0sCmxtOmZ1bmN0aW9uIGxtKGEsYixjKXt2YXIgXz10aGlz
+Cl8uYT1hCl8uYj1iCl8uZD1fLmM9bnVsbApfLiR0aT1jfSwKbVc6ZnVuY3Rpb24gbVcoKXt9LAp1eTpm
+dW5jdGlvbiB1eSgpe30sCmxEOmZ1bmN0aW9uIGxEKCl7fSwKaWw6ZnVuY3Rpb24gaWwoKXt9LApyYTpm
+dW5jdGlvbiByYShhLGIpe3RoaXMuYT1hCnRoaXMuYj1ifSwKWWs6ZnVuY3Rpb24gWWsoKXt9LAp5UTpm
+dW5jdGlvbiB5UShhKXt0aGlzLmE9YX0sCktQOmZ1bmN0aW9uIEtQKCl7fSwKUG46ZnVuY3Rpb24gUG4o
+KXt9LApHajpmdW5jdGlvbiBHaihhLGIpe3RoaXMuYT1hCnRoaXMuJHRpPWJ9LApNYTpmdW5jdGlvbiBN
+YSgpe30sClZqOmZ1bmN0aW9uIFZqKCl7fSwKWHY6ZnVuY3Rpb24gWHYoKXt9LApuWTpmdW5jdGlvbiBu
+WSgpe30sCldZOmZ1bmN0aW9uIFdZKCl7fSwKUlU6ZnVuY3Rpb24gUlUoKXt9LApCUzpmdW5jdGlvbihh
+LGIpe3ZhciB0LHMscixxCmlmKHR5cGVvZiBhIT0ic3RyaW5nIil0aHJvdyBILmIoSC50TChhKSkKdD1u
+dWxsCnRyeXt0PUpTT04ucGFyc2UoYSl9Y2F0Y2gocil7cz1ILlJ1KHIpCnE9UC5ycihTdHJpbmcocyks
+bnVsbCxudWxsKQp0aHJvdyBILmIocSl9cT1QLlFlKHQpCnJldHVybiBxfSwKUWU6ZnVuY3Rpb24oYSl7
+dmFyIHQKaWYoYT09bnVsbClyZXR1cm4gbnVsbAppZih0eXBlb2YgYSE9Im9iamVjdCIpcmV0dXJuIGEK
+aWYoT2JqZWN0LmdldFByb3RvdHlwZU9mKGEpIT09QXJyYXkucHJvdG90eXBlKXJldHVybiBuZXcgUC51
+dyhhLE9iamVjdC5jcmVhdGUobnVsbCkpCmZvcih0PTA7dDxhLmxlbmd0aDsrK3QpYVt0XT1QLlFlKGFb
+dF0pCnJldHVybiBhfSwKa3k6ZnVuY3Rpb24oYSxiLGMsZCl7dmFyIHQscwppZihiIGluc3RhbmNlb2Yg
+VWludDhBcnJheSl7dD1iCmQ9dC5sZW5ndGgKaWYoZC1jPDE1KXJldHVybiBudWxsCnM9UC5DRyhhLHQs
+YyxkKQppZihzIT1udWxsJiZhKWlmKHMuaW5kZXhPZigiXHVmZmZkIik+PTApcmV0dXJuIG51bGwKcmV0
+dXJuIHN9cmV0dXJuIG51bGx9LApDRzpmdW5jdGlvbihhLGIsYyxkKXt2YXIgdD1hPyQuSEcoKTokLnJm
+KCkKaWYodD09bnVsbClyZXR1cm4gbnVsbAppZigwPT09YyYmZD09PWIubGVuZ3RoKXJldHVybiBQLlJi
+KHQsYikKcmV0dXJuIFAuUmIodCxiLnN1YmFycmF5KGMsUC5qQihjLGQsYi5sZW5ndGgpKSl9LApSYjpm
+dW5jdGlvbihhLGIpe3ZhciB0LHMKdHJ5e3Q9YS5kZWNvZGUoYikKcmV0dXJuIHR9Y2F0Y2gocyl7SC5S
+dShzKX1yZXR1cm4gbnVsbH0sCnhNOmZ1bmN0aW9uKGEsYixjLGQsZSxmKXtpZihDLmpuLnpZKGYsNCkh
+PT0wKXRocm93IEguYihQLnJyKCJJbnZhbGlkIGJhc2U2NCBwYWRkaW5nLCBwYWRkZWQgbGVuZ3RoIG11
+c3QgYmUgbXVsdGlwbGUgb2YgZm91ciwgaXMgIitmLGEsYykpCmlmKGQrZSE9PWYpdGhyb3cgSC5iKFAu
+cnIoIkludmFsaWQgYmFzZTY0IHBhZGRpbmcsICc9JyBub3QgYXQgdGhlIGVuZCIsYSxiKSkKaWYoZT4y
+KXRocm93IEguYihQLnJyKCJJbnZhbGlkIGJhc2U2NCBwYWRkaW5nLCBtb3JlIHRoYW4gdHdvICc9JyBj
+aGFyYWN0ZXJzIixhLGIpKX0sCkd5OmZ1bmN0aW9uKGEsYixjKXtyZXR1cm4gbmV3IFAuVWQoYSxiKX0s
+Ck5DOmZ1bmN0aW9uKGEpe3JldHVybiBhLkx0KCl9LAp1WDpmdW5jdGlvbihhLGIsYyl7dmFyIHQscz1u
+ZXcgUC5SbigiIikscj1uZXcgUC50dShzLFtdLFAuQ3koKSkKci5pVShhKQp0PXMuYQpyZXR1cm4gdC5j
+aGFyQ29kZUF0KDApPT0wP3Q6dH0sCmo0OmZ1bmN0aW9uKGEpe3N3aXRjaChhKXtjYXNlIDY1OnJldHVy
+biJNaXNzaW5nIGV4dGVuc2lvbiBieXRlIgpjYXNlIDY3OnJldHVybiJVbmV4cGVjdGVkIGV4dGVuc2lv
+biBieXRlIgpjYXNlIDY5OnJldHVybiJJbnZhbGlkIFVURi04IGJ5dGUiCmNhc2UgNzE6cmV0dXJuIk92
+ZXJsb25nIGVuY29kaW5nIgpjYXNlIDczOnJldHVybiJPdXQgb2YgdW5pY29kZSByYW5nZSIKY2FzZSA3
+NTpyZXR1cm4iRW5jb2RlZCBzdXJyb2dhdGUiCmNhc2UgNzc6cmV0dXJuIlVuZmluaXNoZWQgVVRGLTgg
+b2N0ZXQgc2VxdWVuY2UiCmRlZmF1bHQ6cmV0dXJuIiJ9fSwKank6ZnVuY3Rpb24oYSxiLGMpe3ZhciB0
+LHMscixxLHA9Yy1iLG89bmV3IFVpbnQ4QXJyYXkocCkKZm9yKHQ9by5sZW5ndGgscz1KLlU2KGEpLHI9
+MDtyPHA7KytyKXtxPXMucShhLGIrcikKaWYodHlwZW9mIHEhPT0ibnVtYmVyIilyZXR1cm4gcS56TSgp
+CmlmKChxJjQyOTQ5NjcwNDApPj4+MCE9PTApcT0yNTUKaWYocj49dClyZXR1cm4gSC5PSChvLHIpCm9b
+cl09cX1yZXR1cm4gb30sCnV3OmZ1bmN0aW9uIHV3KGEsYil7dGhpcy5hPWEKdGhpcy5iPWIKdGhpcy5j
+PW51bGx9LAppODpmdW5jdGlvbiBpOChhKXt0aGlzLmE9YX0sCnBnOmZ1bmN0aW9uIHBnKCl7fSwKYzI6
+ZnVuY3Rpb24gYzIoKXt9LApDVjpmdW5jdGlvbiBDVigpe30sClU4OmZ1bmN0aW9uIFU4KCl7fSwKVWs6
+ZnVuY3Rpb24gVWsoKXt9LAp3STpmdW5jdGlvbiB3SSgpe30sClppOmZ1bmN0aW9uIFppKCl7fSwKVWQ6
+ZnVuY3Rpb24gVWQoYSxiKXt0aGlzLmE9YQp0aGlzLmI9Yn0sCks4OmZ1bmN0aW9uIEs4KGEsYil7dGhp
+cy5hPWEKdGhpcy5iPWJ9LApieTpmdW5jdGlvbiBieSgpe30sCm9qOmZ1bmN0aW9uIG9qKGEpe3RoaXMu
+Yj1hfSwKTXg6ZnVuY3Rpb24gTXgoYSl7dGhpcy5hPWF9LApTaDpmdW5jdGlvbiBTaCgpe30sCnRpOmZ1
+bmN0aW9uIHRpKGEsYil7dGhpcy5hPWEKdGhpcy5iPWJ9LAp0dTpmdW5jdGlvbiB0dShhLGIsYyl7dGhp
+cy5jPWEKdGhpcy5hPWIKdGhpcy5iPWN9LAp1NTpmdW5jdGlvbiB1NSgpe30sCkUzOmZ1bmN0aW9uIEUz
+KCl7fSwKUnc6ZnVuY3Rpb24gUncoYSl7dGhpcy5iPTAKdGhpcy5jPWF9LApHWTpmdW5jdGlvbiBHWShh
+KXt0aGlzLmE9YX0sCmJ6OmZ1bmN0aW9uIGJ6KGEpe3RoaXMuYT1hCnRoaXMuYj0xNgp0aGlzLmM9MH0s
+ClFBOmZ1bmN0aW9uKGEsYil7dmFyIHQ9SC5IcChhLGIpCmlmKHQhPW51bGwpcmV0dXJuIHQKdGhyb3cg
+SC5iKFAucnIoYSxudWxsLG51bGwpKX0sCkY6ZnVuY3Rpb24oYSl7aWYoYSBpbnN0YW5jZW9mIEgudily
+ZXR1cm4gYS53KDApCnJldHVybiJJbnN0YW5jZSBvZiAnIitILkVqKEguTShhKSkrIicifSwKTzg6ZnVu
+Y3Rpb24oYSxiLGMsZCl7dmFyIHQscz1jP0ouS2goYSxkKTpKLlFpKGEsZCkKaWYoYSE9PTAmJmIhPW51
+bGwpZm9yKHQ9MDt0PHMubGVuZ3RoOysrdClzW3RdPWIKcmV0dXJuIHN9LApDSDpmdW5jdGlvbihhLGIs
+Yyl7dmFyIHQscz1ILlZNKFtdLGMuQygiamQ8MD4iKSkKZm9yKHQ9Si5JVChhKTt0LkYoKTspQy5ObS5p
+KHMsYy5hKHQuZ2woKSkpCmlmKGIpcmV0dXJuIHMKcmV0dXJuIEouRXAocyxjKX0sCmRIOmZ1bmN0aW9u
+KGEsYixjKXt2YXIgdCxzPUouS2goYSxjKQpmb3IodD0wO3Q8YTsrK3QpQy5ObS5ZKHMsdCxiLiQxKHQp
+KQpyZXR1cm4gc30sCkFGOmZ1bmN0aW9uKGEsYil7cmV0dXJuIEouekMoUC5DSChhLCExLGIpKX0sCkhN
+OmZ1bmN0aW9uKGEsYixjKXt2YXIgdCxzCmlmKEFycmF5LmlzQXJyYXkoYSkpe3Q9YQpzPXQubGVuZ3Ro
+CmM9UC5qQihiLGMscykKcmV0dXJuIEguZVQoYj4wfHxjPHM/dC5zbGljZShiLGMpOnQpfWlmKHUuYm0u
+YihhKSlyZXR1cm4gSC5mdyhhLGIsUC5qQihiLGMsYS5sZW5ndGgpKQpyZXR1cm4gUC5idyhhLGIsYyl9
+LApPbzpmdW5jdGlvbihhKXtyZXR1cm4gSC5MdyhhKX0sCmJ3OmZ1bmN0aW9uKGEsYixjKXt2YXIgdCxz
+LHIscSxwLG89bnVsbAppZihiPDApdGhyb3cgSC5iKFAuVEUoYiwwLGEubGVuZ3RoLG8sbykpCnQ9Yz09
+bnVsbAppZighdCYmYzxiKXRocm93IEguYihQLlRFKGMsYixhLmxlbmd0aCxvLG8pKQpzPW5ldyBILmE3
+KGEsYS5sZW5ndGgsSC56KGEpLkMoImE3PGxELkU+IikpCmZvcihyPTA7cjxiOysrcilpZighcy5GKCkp
+dGhyb3cgSC5iKFAuVEUoYiwwLHIsbyxvKSkKcT1bXQppZih0KWZvcig7cy5GKCk7KXtwPXMuZApxLnB1
+c2gocCl9ZWxzZSBmb3Iocj1iO3I8YzsrK3Ipe2lmKCFzLkYoKSl0aHJvdyBILmIoUC5URShjLGIscixv
+LG8pKQpwPXMuZApxLnB1c2gocCl9cmV0dXJuIEguZVQocSl9LApudTpmdW5jdGlvbihhKXtyZXR1cm4g
+bmV3IEguVlIoYSxILnY0KGEsITEsITAsITEsITEsITEpKX0sCnZnOmZ1bmN0aW9uKGEsYixjKXt2YXIg
+dD1KLklUKGIpCmlmKCF0LkYoKSlyZXR1cm4gYQppZihjLmxlbmd0aD09PTApe2RvIGErPUguRWoodC5n
+bCgpKQp3aGlsZSh0LkYoKSl9ZWxzZXthKz1ILkVqKHQuZ2woKSkKZm9yKDt0LkYoKTspYT1hK2MrSC5F
+aih0LmdsKCkpfXJldHVybiBhfSwKbHI6ZnVuY3Rpb24oYSxiLGMsZCl7cmV0dXJuIG5ldyBQLm1wKGEs
+YixjLGQpfSwKdW86ZnVuY3Rpb24oKXt2YXIgdD1ILk0wKCkKaWYodCE9bnVsbClyZXR1cm4gUC5oSyh0
+KQp0aHJvdyBILmIoUC5MNCgiJ1VyaS5iYXNlJyBpcyBub3Qgc3VwcG9ydGVkIikpfSwKZVA6ZnVuY3Rp
+b24oYSxiLGMsZCl7dmFyIHQscyxyLHEscCxvLG49IjAxMjM0NTY3ODlBQkNERUYiCmlmKGM9PT1DLnhN
+KXt0PSQuejQoKS5iCmlmKHR5cGVvZiBiIT0ic3RyaW5nIilILnZoKEgudEwoYikpCnQ9dC50ZXN0KGIp
+fWVsc2UgdD0hMQppZih0KXJldHVybiBiCkguTGgoYykuQygiVWsuUyIpLmEoYikKcz1jLmdaRSgpLldK
+KGIpCmZvcih0PXMubGVuZ3RoLHI9MCxxPSIiO3I8dDsrK3Ipe3A9c1tyXQppZihwPDEyOCl7bz1wPj4+
+NAppZihvPj04KXJldHVybiBILk9IKGEsbykKbz0oYVtvXSYxPDwocCYxNSkpIT09MH1lbHNlIG89ITEK
+aWYobylxKz1ILkx3KHApCmVsc2UgcT1kJiZwPT09MzI/cSsiKyI6cSsiJSIrbltwPj4+NCYxNV0rbltw
+JjE1XX1yZXR1cm4gcS5jaGFyQ29kZUF0KDApPT0wP3E6cX0sCkdxOmZ1bmN0aW9uKGEpe3ZhciB0PU1h
+dGguYWJzKGEpLHM9YTwwPyItIjoiIgppZih0Pj0xMDAwKXJldHVybiIiK2EKaWYodD49MTAwKXJldHVy
+biBzKyIwIit0CmlmKHQ+PTEwKXJldHVybiBzKyIwMCIrdApyZXR1cm4gcysiMDAwIit0fSwKVng6ZnVu
+Y3Rpb24oYSl7aWYoYT49MTAwKXJldHVybiIiK2EKaWYoYT49MTApcmV0dXJuIjAiK2EKcmV0dXJuIjAw
+IithfSwKaDA6ZnVuY3Rpb24oYSl7aWYoYT49MTApcmV0dXJuIiIrYQpyZXR1cm4iMCIrYX0sCnA6ZnVu
+Y3Rpb24oYSl7aWYodHlwZW9mIGE9PSJudW1iZXIifHxILmwoYSl8fG51bGw9PWEpcmV0dXJuIEouaihh
+KQppZih0eXBlb2YgYT09InN0cmluZyIpcmV0dXJuIEpTT04uc3RyaW5naWZ5KGEpCnJldHVybiBQLkYo
+YSl9LApoVjpmdW5jdGlvbihhKXtyZXR1cm4gbmV3IFAuQzYoYSl9LAp4WTpmdW5jdGlvbihhKXtyZXR1
+cm4gbmV3IFAudSghMSxudWxsLG51bGwsYSl9LApMMzpmdW5jdGlvbihhLGIsYyl7cmV0dXJuIG5ldyBQ
+LnUoITAsYSxiLGMpfSwKVUk6ZnVuY3Rpb24oYSxiLGMpe2lmKGE9PW51bGwpdGhyb3cgSC5iKG5ldyBQ
+LnUoITEsbnVsbCxiLCJNdXN0IG5vdCBiZSBudWxsIikpCnJldHVybiBhfSwKTzc6ZnVuY3Rpb24oYSxi
+KXtyZXR1cm4gbmV3IFAuYkoobnVsbCxudWxsLCEwLGEsYiwiVmFsdWUgbm90IGluIHJhbmdlIil9LApU
+RTpmdW5jdGlvbihhLGIsYyxkLGUpe3JldHVybiBuZXcgUC5iSihiLGMsITAsYSxkLCJJbnZhbGlkIHZh
+bHVlIil9LAp3QTpmdW5jdGlvbihhLGIsYyxkKXtpZihhPGJ8fGE+Yyl0aHJvdyBILmIoUC5URShhLGIs
+YyxkLG51bGwpKQpyZXR1cm4gYX0sCmpCOmZ1bmN0aW9uKGEsYixjKXtpZigwPmF8fGE+Yyl0aHJvdyBI
+LmIoUC5URShhLDAsYywic3RhcnQiLG51bGwpKQppZihiIT1udWxsKXtpZihhPmJ8fGI+Yyl0aHJvdyBI
+LmIoUC5URShiLGEsYywiZW5kIixudWxsKSkKcmV0dXJuIGJ9cmV0dXJuIGN9LAprMTpmdW5jdGlvbihh
+LGIpe2lmKGE8MCl0aHJvdyBILmIoUC5URShhLDAsbnVsbCxiLG51bGwpKQpyZXR1cm4gYX0sCkNmOmZ1
+bmN0aW9uKGEsYixjLGQsZSl7dmFyIHQ9SC51UChlPT1udWxsP0ouSG0oYik6ZSkKcmV0dXJuIG5ldyBQ
+LmVZKHQsITAsYSxjLCJJbmRleCBvdXQgb2YgcmFuZ2UiKX0sCkw0OmZ1bmN0aW9uKGEpe3JldHVybiBu
+ZXcgUC51YihhKX0sClNZOmZ1bmN0aW9uKGEpe3JldHVybiBuZXcgUC5kcyhhKX0sClBWOmZ1bmN0aW9u
+KGEpe3JldHVybiBuZXcgUC5saihhKX0sCmE0OmZ1bmN0aW9uKGEpe3JldHVybiBuZXcgUC5VVihhKX0s
+CnJyOmZ1bmN0aW9uKGEsYixjKXtyZXR1cm4gbmV3IFAuYUUoYSxiLGMpfSwKaEs6ZnVuY3Rpb24oYTQp
+e3ZhciB0LHMscixxLHAsbyxuLG0sbCxrLGosaSxoLGcsZixlLGQsYyxiLGEsYTAsYTEsYTI9bnVsbCxh
+Mz1hNC5sZW5ndGgKaWYoYTM+PTUpe3Q9KChKLlF6KGE0LDQpXjU4KSozfEMueEIuVyhhNCwwKV4xMDB8
+Qy54Qi5XKGE0LDEpXjk3fEMueEIuVyhhNCwyKV4xMTZ8Qy54Qi5XKGE0LDMpXjk3KT4+PjAKaWYodD09
+PTApcmV0dXJuIFAuS0QoYTM8YTM/Qy54Qi5OaihhNCwwLGEzKTphNCw1LGEyKS5nbFIoKQplbHNlIGlm
+KHQ9PT0zMilyZXR1cm4gUC5LRChDLnhCLk5qKGE0LDUsYTMpLDAsYTIpLmdsUigpfXM9UC5POCg4LDAs
+ITEsdS5TKQpDLk5tLlkocywwLDApCkMuTm0uWShzLDEsLTEpCkMuTm0uWShzLDIsLTEpCkMuTm0uWShz
+LDcsLTEpCkMuTm0uWShzLDMsMCkKQy5ObS5ZKHMsNCwwKQpDLk5tLlkocyw1LGEzKQpDLk5tLlkocyw2
+LGEzKQppZihQLlVCKGE0LDAsYTMsMCxzKT49MTQpQy5ObS5ZKHMsNyxhMykKaWYoMT49cy5sZW5ndGgp
+cmV0dXJuIEguT0gocywxKQpyPXNbMV0KaWYocj49MClpZihQLlVCKGE0LDAsciwyMCxzKT09PTIwKXtp
+Zig3Pj1zLmxlbmd0aClyZXR1cm4gSC5PSChzLDcpCnNbN109cn1xPXMubGVuZ3RoCmlmKDI+PXEpcmV0
+dXJuIEguT0gocywyKQpwPXNbMl0rMQppZigzPj1xKXJldHVybiBILk9IKHMsMykKbz1zWzNdCmlmKDQ+
+PXEpcmV0dXJuIEguT0gocyw0KQpuPXNbNF0KaWYoNT49cSlyZXR1cm4gSC5PSChzLDUpCm09c1s1XQpp
+Zig2Pj1xKXJldHVybiBILk9IKHMsNikKbD1zWzZdCmlmKGw8bSltPWwKaWYobjxwKW49bQplbHNlIGlm
+KG48PXIpbj1yKzEKaWYobzxwKW89bgppZig3Pj1xKXJldHVybiBILk9IKHMsNykKaz1zWzddPDAKaWYo
+aylpZihwPnIrMyl7aj1hMgprPSExfWVsc2V7cT1vPjAKaWYocSYmbysxPT09bil7aj1hMgprPSExfWVs
+c2V7aWYoIShtPGEzJiZtPT09bisyJiZKLnEwKGE0LCIuLiIsbikpKWk9bT5uKzImJkoucTAoYTQsIi8u
+LiIsbS0zKQplbHNlIGk9ITAKaWYoaSl7aj1hMgprPSExfWVsc2V7aWYocj09PTQpaWYoSi5xMChhNCwi
+ZmlsZSIsMCkpe2lmKHA8PTApe2lmKCFDLnhCLlFpKGE0LCIvIixuKSl7aD0iZmlsZTovLy8iCnQ9M31l
+bHNle2g9ImZpbGU6Ly8iCnQ9Mn1hND1oK0MueEIuTmooYTQsbixhMykKci09MApxPXQtMAptKz1xCmwr
+PXEKYTM9YTQubGVuZ3RoCnA9NwpvPTcKbj03fWVsc2UgaWYobj09PW0peysrbApnPW0rMQphND1DLnhC
+Lmk3KGE0LG4sbSwiLyIpOysrYTMKbT1nfWo9ImZpbGUifWVsc2UgaWYoQy54Qi5RaShhNCwiaHR0cCIs
+MCkpe2lmKHEmJm8rMz09PW4mJkMueEIuUWkoYTQsIjgwIixvKzEpKXtsLT0zCmY9bi0zCm0tPTMKYTQ9
+Qy54Qi5pNyhhNCxvLG4sIiIpCmEzLT0zCm49Zn1qPSJodHRwIn1lbHNlIGo9YTIKZWxzZSBpZihyPT09
+NSYmSi5xMChhNCwiaHR0cHMiLDApKXtpZihxJiZvKzQ9PT1uJiZKLnEwKGE0LCI0NDMiLG8rMSkpe2wt
+PTQKZj1uLTQKbS09NAphND1KLmRnKGE0LG8sbiwiIikKYTMtPTMKbj1mfWo9Imh0dHBzIn1lbHNlIGo9
+YTIKaz0hMH19fWVsc2Ugaj1hMgppZihrKXtxPWE0Lmxlbmd0aAppZihhMzxxKXthND1KLmxkKGE0LDAs
+YTMpCnItPTAKcC09MApvLT0wCm4tPTAKbS09MApsLT0wfXJldHVybiBuZXcgUC5VZihhNCxyLHAsbyxu
+LG0sbCxqKX1pZihqPT1udWxsKWlmKHI+MClqPVAuUGkoYTQsMCxyKQplbHNle2lmKHI9PT0wKVAuUjMo
+YTQsMCwiSW52YWxpZCBlbXB0eSBzY2hlbWUiKQpqPSIifWlmKHA+MCl7ZT1yKzMKZD1lPHA/UC56Uihh
+NCxlLHAtMSk6IiIKYz1QLk9lKGE0LHAsbywhMSkKcT1vKzEKaWYocTxuKXtiPUguSHAoSi5sZChhNCxx
+LG4pLGEyKQphPVAud0IoYj09bnVsbD9ILnZoKFAucnIoIkludmFsaWQgcG9ydCIsYTQscSkpOmIsail9
+ZWxzZSBhPWEyfWVsc2V7YT1hMgpjPWEKZD0iIn1hMD1QLmthKGE0LG4sbSxhMixqLGMhPW51bGwpCmEx
+PW08bD9QLmxlKGE0LG0rMSxsLGEyKTphMgpyZXR1cm4gbmV3IFAuRG4oaixkLGMsYSxhMCxhMSxsPGEz
+P1AudEcoYTQsbCsxLGEzKTphMil9LApNdDpmdW5jdGlvbihhKXtILmgoYSkKcmV0dXJuIFAua3UoYSww
+LGEubGVuZ3RoLEMueE0sITEpfSwKV1g6ZnVuY3Rpb24oYSl7dmFyIHQ9dS5OCnJldHVybiBDLk5tLk4w
+KEguVk0oYS5zcGxpdCgiJiIpLHUucyksUC5GbCh0LHQpLG5ldyBQLm4xKEMueE0pLHUuSil9LApIaDpm
+dW5jdGlvbihhLGIsYyl7dmFyIHQscyxyLHEscCxvLG4sbT0iSVB2NCBhZGRyZXNzIHNob3VsZCBjb250
+YWluIGV4YWN0bHkgNCBwYXJ0cyIsbD0iZWFjaCBwYXJ0IG11c3QgYmUgaW4gdGhlIHJhbmdlIDAuLjI1
+NSIsaz1uZXcgUC5jUyhhKSxqPW5ldyBVaW50OEFycmF5KDQpCmZvcih0PWoubGVuZ3RoLHM9YixyPXMs
+cT0wO3M8YzsrK3Mpe3A9Qy54Qi5tKGEscykKaWYocCE9PTQ2KXtpZigocF40OCk+OSlrLiQyKCJpbnZh
+bGlkIGNoYXJhY3RlciIscyl9ZWxzZXtpZihxPT09MylrLiQyKG0scykKbz1QLlFBKEMueEIuTmooYSxy
+LHMpLG51bGwpCmlmKHR5cGVvZiBvIT09Im51bWJlciIpcmV0dXJuIG8ub3MoKQppZihvPjI1NSlrLiQy
+KGwscikKbj1xKzEKaWYocT49dClyZXR1cm4gSC5PSChqLHEpCmpbcV09bwpyPXMrMQpxPW59fWlmKHEh
+PT0zKWsuJDIobSxjKQpvPVAuUUEoQy54Qi5OaihhLHIsYyksbnVsbCkKaWYodHlwZW9mIG8hPT0ibnVt
+YmVyIilyZXR1cm4gby5vcygpCmlmKG8+MjU1KWsuJDIobCxyKQppZihxPj10KXJldHVybiBILk9IKGos
+cSkKaltxXT1vCnJldHVybiBqfSwKZWc6ZnVuY3Rpb24oYSxiLGEwKXt2YXIgdCxzLHIscSxwLG8sbixt
+LGwsayxqLGksaCxnLGYsZSxkPW5ldyBQLlZDKGEpLGM9bmV3IFAuSlQoZCxhKQppZihhLmxlbmd0aDwy
+KWQuJDEoImFkZHJlc3MgaXMgdG9vIHNob3J0IikKdD1ILlZNKFtdLHUuYSkKZm9yKHM9YixyPXMscT0h
+MSxwPSExO3M8YTA7KytzKXtvPUMueEIubShhLHMpCmlmKG89PT01OCl7aWYocz09PWIpeysrcwppZihD
+LnhCLm0oYSxzKSE9PTU4KWQuJDIoImludmFsaWQgc3RhcnQgY29sb24uIixzKQpyPXN9aWYocz09PXIp
+e2lmKHEpZC4kMigib25seSBvbmUgd2lsZGNhcmQgYDo6YCBpcyBhbGxvd2VkIixzKQpDLk5tLmkodCwt
+MSkKcT0hMH1lbHNlIEMuTm0uaSh0LGMuJDIocixzKSkKcj1zKzF9ZWxzZSBpZihvPT09NDYpcD0hMH1p
+Zih0Lmxlbmd0aD09PTApZC4kMSgidG9vIGZldyBwYXJ0cyIpCm49cj09PWEwCm09Qy5ObS5ncloodCkK
+aWYobiYmbSE9PS0xKWQuJDIoImV4cGVjdGVkIGEgcGFydCBhZnRlciBsYXN0IGA6YCIsYTApCmlmKCFu
+KWlmKCFwKUMuTm0uaSh0LGMuJDIocixhMCkpCmVsc2V7bD1QLkhoKGEscixhMCkKQy5ObS5pKHQsKGxb
+MF08PDh8bFsxXSk+Pj4wKQpDLk5tLmkodCwobFsyXTw8OHxsWzNdKT4+PjApfWlmKHEpe2lmKHQubGVu
+Z3RoPjcpZC4kMSgiYW4gYWRkcmVzcyB3aXRoIGEgd2lsZGNhcmQgbXVzdCBoYXZlIGxlc3MgdGhhbiA3
+IHBhcnRzIil9ZWxzZSBpZih0Lmxlbmd0aCE9PTgpZC4kMSgiYW4gYWRkcmVzcyB3aXRob3V0IGEgd2ls
+ZGNhcmQgbXVzdCBjb250YWluIGV4YWN0bHkgOCBwYXJ0cyIpCms9bmV3IFVpbnQ4QXJyYXkoMTYpCmZv
+cihtPXQubGVuZ3RoLGo9ay5sZW5ndGgsaT05LW0scz0wLGg9MDtzPG07KytzKXtnPXRbc10KaWYoZz09
+PS0xKWZvcihmPTA7ZjxpOysrZil7aWYoaDwwfHxoPj1qKXJldHVybiBILk9IKGssaCkKa1toXT0wCmU9
+aCsxCmlmKGU+PWopcmV0dXJuIEguT0goayxlKQprW2VdPTAKaCs9Mn1lbHNle2U9Qy5qbi53RyhnLDgp
+CmlmKGg8MHx8aD49ailyZXR1cm4gSC5PSChrLGgpCmtbaF09ZQplPWgrMQppZihlPj1qKXJldHVybiBI
+Lk9IKGssZSkKa1tlXT1nJjI1NQpoKz0yfX1yZXR1cm4ga30sCktMOmZ1bmN0aW9uKGEsYixjLGQsZSxm
+LGcpe3ZhciB0LHMscixxLHAsbwpmPWY9PW51bGw/IiI6UC5QaShmLDAsZi5sZW5ndGgpCmc9UC56Uihn
+LDAsZz09bnVsbD8wOmcubGVuZ3RoKQphPVAuT2UoYSwwLGE9PW51bGw/MDphLmxlbmd0aCwhMSkKdD1Q
+LmxlKG51bGwsMCwwLGUpCnM9UC50RyhudWxsLDAsMCkKZD1QLndCKGQsZikKcj1mPT09ImZpbGUiCmlm
+KGE9PW51bGwpcT1nLmxlbmd0aCE9PTB8fGQhPW51bGx8fHIKZWxzZSBxPSExCmlmKHEpYT0iIgpxPWE9
+PW51bGwKcD0hcQpiPVAua2EoYiwwLGI9PW51bGw/MDpiLmxlbmd0aCxjLGYscCkKbz1mLmxlbmd0aD09
+PTAKaWYobyYmcSYmIUMueEIubihiLCIvIikpYj1QLndGKGIsIW98fHApCmVsc2UgYj1QLnhlKGIpCnJl
+dHVybiBuZXcgUC5EbihmLGcscSYmQy54Qi5uKGIsIi8vIik/IiI6YSxkLGIsdCxzKX0sCndLOmZ1bmN0
+aW9uKGEpe2lmKGE9PT0iaHR0cCIpcmV0dXJuIDgwCmlmKGE9PT0iaHR0cHMiKXJldHVybiA0NDMKcmV0
+dXJuIDB9LApSMzpmdW5jdGlvbihhLGIsYyl7dGhyb3cgSC5iKFAucnIoYyxhLGIpKX0sClhkOmZ1bmN0
+aW9uKGEsYixjLGQpe3ZhciB0LHMscixxLHAsbyxuLG0sbCxrLGosaT1udWxsLGg9Yi5sZW5ndGgKaWYo
+aCE9PTApe3I9MAp3aGlsZSghMCl7aWYoIShyPGgpKXt0PSIiCnM9MApicmVha31pZihDLnhCLlcoYixy
+KT09PTY0KXt0PUMueEIuTmooYiwwLHIpCnM9cisxCmJyZWFrfSsrcn1pZihzPGgmJkMueEIuVyhiLHMp
+PT09OTEpe2ZvcihxPXMscD0tMTtxPGg7KytxKXtvPUMueEIuVyhiLHEpCmlmKG89PT0zNyYmcDwwKXtu
+PUMueEIuUWkoYiwiMjUiLHErMSk/cSsyOnEKcD1xCnE9bn1lbHNlIGlmKG89PT05MylicmVha31pZihx
+PT09aCl0aHJvdyBILmIoUC5ycigiSW52YWxpZCBJUHY2IGhvc3QgZW50cnkuIixiLHMpKQptPXA8MD9x
+OnAKUC5lZyhiLHMrMSxtKTsrK3EKaWYocSE9PWgmJkMueEIuVyhiLHEpIT09NTgpdGhyb3cgSC5iKFAu
+cnIoIkludmFsaWQgZW5kIG9mIGF1dGhvcml0eSIsYixxKSl9ZWxzZSBxPXMKd2hpbGUoITApe2lmKCEo
+cTxoKSl7bD1pCmJyZWFrfWlmKEMueEIuVyhiLHEpPT09NTgpe2s9Qy54Qi5HKGIscSsxKQpsPWsubGVu
+Z3RoIT09MD9QLlFBKGssaSk6aQpicmVha30rK3F9aj1DLnhCLk5qKGIscyxxKX1lbHNle2w9aQpqPWwK
+dD0iIn1yZXR1cm4gUC5LTChqLGksSC5WTShjLnNwbGl0KCIvIiksdS5zKSxsLGQsYSx0KX0sCmtFOmZ1
+bmN0aW9uKGEsYil7dmFyIHQscyxyLHEscApmb3IodD1hLmxlbmd0aCxzPTA7czx0Oysrcyl7cj1hW3Nd
+CnIudG9TdHJpbmcKcT1KLlU2KHIpCnA9cS5nQShyKQppZigwPnApSC52aChQLlRFKDAsMCxxLmdBKHIp
+LG51bGwsbnVsbCkpCmlmKEgubTIociwiLyIsMCkpe3Q9UC5MNCgiSWxsZWdhbCBwYXRoIGNoYXJhY3Rl
+ciAiK0guRWoocikpCnRocm93IEguYih0KX19fSwKSE46ZnVuY3Rpb24oYSxiLGMpe3ZhciB0LHMscgpm
+b3IodD1ILnFDKGEsYyxudWxsLEgudDYoYSkuYyksdD1uZXcgSC5hNyh0LHQuZ0EodCksdC4kdGkuQygi
+YTc8YUwuRT4iKSk7dC5GKCk7KXtzPXQuZApyPVAubnUoJ1siKi86PD4/XFxcXHxdJykKcy50b1N0cmlu
+ZwppZihILm0yKHMsciwwKSl7dD1QLkw0KCJJbGxlZ2FsIGNoYXJhY3RlciBpbiBwYXRoOiAiK3MpCnRo
+cm93IEguYih0KX19fSwKcmc6ZnVuY3Rpb24oYSxiKXt2YXIgdAppZighKDY1PD1hJiZhPD05MCkpdD05
+Nzw9YSYmYTw9MTIyCmVsc2UgdD0hMAppZih0KXJldHVybgp0PVAuTDQoIklsbGVnYWwgZHJpdmUgbGV0
+dGVyICIrUC5PbyhhKSkKdGhyb3cgSC5iKHQpfSwKd0I6ZnVuY3Rpb24oYSxiKXtpZihhIT1udWxsJiZh
+PT09UC53SyhiKSlyZXR1cm4gbnVsbApyZXR1cm4gYX0sCk9lOmZ1bmN0aW9uKGEsYixjLGQpe3ZhciB0
+LHMscixxLHAsbwppZihhPT1udWxsKXJldHVybiBudWxsCmlmKGI9PT1jKXJldHVybiIiCmlmKEMueEIu
+bShhLGIpPT09OTEpe3Q9Yy0xCmlmKEMueEIubShhLHQpIT09OTMpUC5SMyhhLGIsIk1pc3NpbmcgZW5k
+IGBdYCB0byBtYXRjaCBgW2AgaW4gaG9zdCIpCnM9YisxCnI9UC50byhhLHMsdCkKaWYocjx0KXtxPXIr
+MQpwPVAuT0EoYSxDLnhCLlFpKGEsIjI1IixxKT9yKzM6cSx0LCIlMjUiKX1lbHNlIHA9IiIKUC5lZyhh
+LHMscikKcmV0dXJuIEMueEIuTmooYSxiLHIpLnRvTG93ZXJDYXNlKCkrcCsiXSJ9Zm9yKG89YjtvPGM7
+KytvKWlmKEMueEIubShhLG8pPT09NTgpe3I9Qy54Qi5YVShhLCIlIixiKQpyPXI+PWImJnI8Yz9yOmMK
+aWYocjxjKXtxPXIrMQpwPVAuT0EoYSxDLnhCLlFpKGEsIjI1IixxKT9yKzM6cSxjLCIlMjUiKX1lbHNl
+IHA9IiIKUC5lZyhhLGIscikKcmV0dXJuIlsiK0MueEIuTmooYSxiLHIpK3ArIl0ifXJldHVybiBQLk9M
+KGEsYixjKX0sCnRvOmZ1bmN0aW9uKGEsYixjKXt2YXIgdD1DLnhCLlhVKGEsIiUiLGIpCnJldHVybiB0
+Pj1iJiZ0PGM/dDpjfSwKT0E6ZnVuY3Rpb24oYSxiLGMsZCl7dmFyIHQscyxyLHEscCxvLG4sbSxsLGss
+aj1kIT09IiI/bmV3IFAuUm4oZCk6bnVsbApmb3IodD1iLHM9dCxyPSEwO3Q8Yzspe3E9Qy54Qi5tKGEs
+dCkKaWYocT09PTM3KXtwPVAucnYoYSx0LCEwKQpvPXA9PW51bGwKaWYobyYmcil7dCs9Mwpjb250aW51
+ZX1pZihqPT1udWxsKWo9bmV3IFAuUm4oIiIpCm49ai5hKz1DLnhCLk5qKGEscyx0KQppZihvKXA9Qy54
+Qi5OaihhLHQsdCszKQplbHNlIGlmKHA9PT0iJSIpUC5SMyhhLHQsIlpvbmVJRCBzaG91bGQgbm90IGNv
+bnRhaW4gJSBhbnltb3JlIikKai5hPW4rcAp0Kz0zCnM9dApyPSEwfWVsc2V7aWYocTwxMjcpe289cT4+
+PjQKaWYobz49OClyZXR1cm4gSC5PSChDLkYzLG8pCm89KEMuRjNbb10mMTw8KHEmMTUpKSE9PTB9ZWxz
+ZSBvPSExCmlmKG8pe2lmKHImJjY1PD1xJiY5MD49cSl7aWYoaj09bnVsbClqPW5ldyBQLlJuKCIiKQpp
+ZihzPHQpe2ouYSs9Qy54Qi5OaihhLHMsdCkKcz10fXI9ITF9Kyt0fWVsc2V7aWYoKHEmNjQ1MTIpPT09
+NTUyOTYmJnQrMTxjKXttPUMueEIubShhLHQrMSkKaWYoKG0mNjQ1MTIpPT09NTYzMjApe3E9NjU1MzZ8
+KHEmMTAyMyk8PDEwfG0mMTAyMwpsPTJ9ZWxzZSBsPTF9ZWxzZSBsPTEKaz1DLnhCLk5qKGEscyx0KQpp
+ZihqPT1udWxsKXtqPW5ldyBQLlJuKCIiKQpvPWp9ZWxzZSBvPWoKby5hKz1rCm8uYSs9UC56WChxKQp0
+Kz1sCnM9dH19fWlmKGo9PW51bGwpcmV0dXJuIEMueEIuTmooYSxiLGMpCmlmKHM8YylqLmErPUMueEIu
+TmooYSxzLGMpCm89ai5hCnJldHVybiBvLmNoYXJDb2RlQXQoMCk9PTA/bzpvfSwKT0w6ZnVuY3Rpb24o
+YSxiLGMpe3ZhciB0LHMscixxLHAsbyxuLG0sbCxrLGoKZm9yKHQ9YixzPXQscj1udWxsLHE9ITA7dDxj
+Oyl7cD1DLnhCLm0oYSx0KQppZihwPT09Mzcpe289UC5ydihhLHQsITApCm49bz09bnVsbAppZihuJiZx
+KXt0Kz0zCmNvbnRpbnVlfWlmKHI9PW51bGwpcj1uZXcgUC5SbigiIikKbT1DLnhCLk5qKGEscyx0KQps
+PXIuYSs9IXE/bS50b0xvd2VyQ2FzZSgpOm0KaWYobil7bz1DLnhCLk5qKGEsdCx0KzMpCms9M31lbHNl
+IGlmKG89PT0iJSIpe289IiUyNSIKaz0xfWVsc2Ugaz0zCnIuYT1sK28KdCs9awpzPXQKcT0hMH1lbHNl
+e2lmKHA8MTI3KXtuPXA+Pj40CmlmKG4+PTgpcmV0dXJuIEguT0goQy5lYSxuKQpuPShDLmVhW25dJjE8
+PChwJjE1KSkhPT0wfWVsc2Ugbj0hMQppZihuKXtpZihxJiY2NTw9cCYmOTA+PXApe2lmKHI9PW51bGwp
+cj1uZXcgUC5SbigiIikKaWYoczx0KXtyLmErPUMueEIuTmooYSxzLHQpCnM9dH1xPSExfSsrdH1lbHNl
+e2lmKHA8PTkzKXtuPXA+Pj40CmlmKG4+PTgpcmV0dXJuIEguT0goQy5hayxuKQpuPShDLmFrW25dJjE8
+PChwJjE1KSkhPT0wfWVsc2Ugbj0hMQppZihuKVAuUjMoYSx0LCJJbnZhbGlkIGNoYXJhY3RlciIpCmVs
+c2V7aWYoKHAmNjQ1MTIpPT09NTUyOTYmJnQrMTxjKXtqPUMueEIubShhLHQrMSkKaWYoKGomNjQ1MTIp
+PT09NTYzMjApe3A9NjU1MzZ8KHAmMTAyMyk8PDEwfGomMTAyMwprPTJ9ZWxzZSBrPTF9ZWxzZSBrPTEK
+bT1DLnhCLk5qKGEscyx0KQppZighcSltPW0udG9Mb3dlckNhc2UoKQppZihyPT1udWxsKXtyPW5ldyBQ
+LlJuKCIiKQpuPXJ9ZWxzZSBuPXIKbi5hKz1tCm4uYSs9UC56WChwKQp0Kz1rCnM9dH19fX1pZihyPT1u
+dWxsKXJldHVybiBDLnhCLk5qKGEsYixjKQppZihzPGMpe209Qy54Qi5OaihhLHMsYykKci5hKz0hcT9t
+LnRvTG93ZXJDYXNlKCk6bX1uPXIuYQpyZXR1cm4gbi5jaGFyQ29kZUF0KDApPT0wP246bn0sClBpOmZ1
+bmN0aW9uKGEsYixjKXt2YXIgdCxzLHIscQppZihiPT09YylyZXR1cm4iIgppZighUC5FdChKLnJZKGEp
+LlcoYSxiKSkpUC5SMyhhLGIsIlNjaGVtZSBub3Qgc3RhcnRpbmcgd2l0aCBhbHBoYWJldGljIGNoYXJh
+Y3RlciIpCmZvcih0PWIscz0hMTt0PGM7Kyt0KXtyPUMueEIuVyhhLHQpCmlmKHI8MTI4KXtxPXI+Pj40
+CmlmKHE+PTgpcmV0dXJuIEguT0goQy5tSyxxKQpxPShDLm1LW3FdJjE8PChyJjE1KSkhPT0wfWVsc2Ug
+cT0hMQppZighcSlQLlIzKGEsdCwiSWxsZWdhbCBzY2hlbWUgY2hhcmFjdGVyIikKaWYoNjU8PXImJnI8
+PTkwKXM9ITB9YT1DLnhCLk5qKGEsYixjKQpyZXR1cm4gUC5ZYShzP2EudG9Mb3dlckNhc2UoKTphKX0s
+CllhOmZ1bmN0aW9uKGEpe2lmKGE9PT0iaHR0cCIpcmV0dXJuImh0dHAiCmlmKGE9PT0iZmlsZSIpcmV0
+dXJuImZpbGUiCmlmKGE9PT0iaHR0cHMiKXJldHVybiJodHRwcyIKaWYoYT09PSJwYWNrYWdlIilyZXR1
+cm4icGFja2FnZSIKcmV0dXJuIGF9LAp6UjpmdW5jdGlvbihhLGIsYyl7aWYoYT09bnVsbClyZXR1cm4i
+IgpyZXR1cm4gUC5QSShhLGIsYyxDLnRvLCExKX0sCmthOmZ1bmN0aW9uKGEsYixjLGQsZSxmKXt2YXIg
+dCxzLHI9ZT09PSJmaWxlIixxPXJ8fGYKaWYoYT09bnVsbCl7aWYoZD09bnVsbClyZXR1cm4gcj8iLyI6
+IiIKdD1ILnQ2KGQpCnM9bmV3IEgubEooZCx0LkMoInFVKDEpIikuYShuZXcgUC5SWigpKSx0LkMoImxK
+PDEscVU+IikpLkgoMCwiLyIpfWVsc2UgaWYoZCE9bnVsbCl0aHJvdyBILmIoUC54WSgiQm90aCBwYXRo
+IGFuZCBwYXRoU2VnbWVudHMgc3BlY2lmaWVkIikpCmVsc2Ugcz1QLlBJKGEsYixjLEMuV2QsITApCmlm
+KHMubGVuZ3RoPT09MCl7aWYocilyZXR1cm4iLyJ9ZWxzZSBpZihxJiYhQy54Qi5uKHMsIi8iKSlzPSIv
+IitzCnJldHVybiBQLkpyKHMsZSxmKX0sCkpyOmZ1bmN0aW9uKGEsYixjKXt2YXIgdD1iLmxlbmd0aD09
+PTAKaWYodCYmIWMmJiFDLnhCLm4oYSwiLyIpKXJldHVybiBQLndGKGEsIXR8fGMpCnJldHVybiBQLnhl
+KGEpfSwKbGU6ZnVuY3Rpb24oYSxiLGMsZCl7dmFyIHQscz17fQppZihhIT1udWxsKXtpZihkIT1udWxs
+KXRocm93IEguYihQLnhZKCJCb3RoIHF1ZXJ5IGFuZCBxdWVyeVBhcmFtZXRlcnMgc3BlY2lmaWVkIikp
+CnJldHVybiBQLlBJKGEsYixjLEMuVkMsITApfWlmKGQ9PW51bGwpcmV0dXJuIG51bGwKdD1uZXcgUC5S
+bigiIikKcy5hPSIiCmQuSygwLG5ldyBQLnk1KG5ldyBQLk1FKHMsdCkpKQpzPXQuYQpyZXR1cm4gcy5j
+aGFyQ29kZUF0KDApPT0wP3M6c30sCnRHOmZ1bmN0aW9uKGEsYixjKXtpZihhPT1udWxsKXJldHVybiBu
+dWxsCnJldHVybiBQLlBJKGEsYixjLEMuVkMsITApfSwKcnY6ZnVuY3Rpb24oYSxiLGMpe3ZhciB0LHMs
+cixxLHAsbz1iKzIKaWYobz49YS5sZW5ndGgpcmV0dXJuIiUiCnQ9Qy54Qi5tKGEsYisxKQpzPUMueEIu
+bShhLG8pCnI9SC5vbyh0KQpxPUgub28ocykKaWYocjwwfHxxPDApcmV0dXJuIiUiCnA9cioxNitxCmlm
+KHA8MTI3KXtvPUMuam4ud0cocCw0KQppZihvPj04KXJldHVybiBILk9IKEMuRjMsbykKbz0oQy5GM1tv
+XSYxPDwocCYxNSkpIT09MH1lbHNlIG89ITEKaWYobylyZXR1cm4gSC5MdyhjJiY2NTw9cCYmOTA+PXA/
+KHB8MzIpPj4+MDpwKQppZih0Pj05N3x8cz49OTcpcmV0dXJuIEMueEIuTmooYSxiLGIrMykudG9VcHBl
+ckNhc2UoKQpyZXR1cm4gbnVsbH0sCnpYOmZ1bmN0aW9uKGEpe3ZhciB0LHMscixxLHAsbyxuLG0sbD0i
+MDEyMzQ1Njc4OUFCQ0RFRiIKaWYoYTwxMjgpe3Q9bmV3IFVpbnQ4QXJyYXkoMykKcz10Lmxlbmd0aApp
+ZigwPj1zKXJldHVybiBILk9IKHQsMCkKdFswXT0zNwpyPUMueEIuVyhsLGE+Pj40KQppZigxPj1zKXJl
+dHVybiBILk9IKHQsMSkKdFsxXT1yCnI9Qy54Qi5XKGwsYSYxNSkKaWYoMj49cylyZXR1cm4gSC5PSCh0
+LDIpCnRbMl09cn1lbHNle2lmKGE+MjA0NylpZihhPjY1NTM1KXtxPTI0MApwPTR9ZWxzZXtxPTIyNApw
+PTN9ZWxzZXtxPTE5MgpwPTJ9dD1uZXcgVWludDhBcnJheSgzKnApCmZvcihzPXQubGVuZ3RoLG89MDst
+LXAscD49MDtxPTEyOCl7bj1DLmpuLmJmKGEsNipwKSY2M3xxCmlmKG8+PXMpcmV0dXJuIEguT0godCxv
+KQp0W29dPTM3CnI9bysxCm09Qy54Qi5XKGwsbj4+PjQpCmlmKHI+PXMpcmV0dXJuIEguT0godCxyKQp0
+W3JdPW0KbT1vKzIKcj1DLnhCLlcobCxuJjE1KQppZihtPj1zKXJldHVybiBILk9IKHQsbSkKdFttXT1y
+Cm8rPTN9fXJldHVybiBQLkhNKHQsMCxudWxsKX0sClBJOmZ1bmN0aW9uKGEsYixjLGQsZSl7dmFyIHQ9
+UC5VbChhLGIsYyxkLGUpCnJldHVybiB0PT1udWxsP0MueEIuTmooYSxiLGMpOnR9LApVbDpmdW5jdGlv
+bihhLGIsYyxkLGUpe3ZhciB0LHMscixxLHAsbyxuLG0sbCxrPW51bGwKZm9yKHQ9IWUscz1iLHI9cyxx
+PWs7czxjOyl7cD1DLnhCLm0oYSxzKQppZihwPDEyNyl7bz1wPj4+NAppZihvPj04KXJldHVybiBILk9I
+KGQsbykKbz0oZFtvXSYxPDwocCYxNSkpIT09MH1lbHNlIG89ITEKaWYobykrK3MKZWxzZXtpZihwPT09
+Mzcpe249UC5ydihhLHMsITEpCmlmKG49PW51bGwpe3MrPTMKY29udGludWV9aWYoIiUiPT09bil7bj0i
+JTI1IgptPTF9ZWxzZSBtPTN9ZWxzZXtpZih0KWlmKHA8PTkzKXtvPXA+Pj40CmlmKG8+PTgpcmV0dXJu
+IEguT0goQy5hayxvKQpvPShDLmFrW29dJjE8PChwJjE1KSkhPT0wfWVsc2Ugbz0hMQplbHNlIG89ITEK
+aWYobyl7UC5SMyhhLHMsIkludmFsaWQgY2hhcmFjdGVyIikKbT1rCm49bX1lbHNle2lmKChwJjY0NTEy
+KT09PTU1Mjk2KXtvPXMrMQppZihvPGMpe2w9Qy54Qi5tKGEsbykKaWYoKGwmNjQ1MTIpPT09NTYzMjAp
+e3A9NjU1MzZ8KHAmMTAyMyk8PDEwfGwmMTAyMwptPTJ9ZWxzZSBtPTF9ZWxzZSBtPTF9ZWxzZSBtPTEK
+bj1QLnpYKHApfX1pZihxPT1udWxsKXtxPW5ldyBQLlJuKCIiKQpvPXF9ZWxzZSBvPXEKby5hKz1DLnhC
+Lk5qKGEscixzKQpvLmErPUguRWoobikKaWYodHlwZW9mIG0hPT0ibnVtYmVyIilyZXR1cm4gSC5wWSht
+KQpzKz1tCnI9c319aWYocT09bnVsbClyZXR1cm4gawppZihyPGMpcS5hKz1DLnhCLk5qKGEscixjKQp0
+PXEuYQpyZXR1cm4gdC5jaGFyQ29kZUF0KDApPT0wP3Q6dH0sCnlCOmZ1bmN0aW9uKGEpe2lmKEMueEIu
+bihhLCIuIikpcmV0dXJuITAKcmV0dXJuIEMueEIuT1koYSwiLy4iKSE9PS0xfSwKeGU6ZnVuY3Rpb24o
+YSl7dmFyIHQscyxyLHEscCxvLG4KaWYoIVAueUIoYSkpcmV0dXJuIGEKdD1ILlZNKFtdLHUucykKZm9y
+KHM9YS5zcGxpdCgiLyIpLHI9cy5sZW5ndGgscT0hMSxwPTA7cDxyOysrcCl7bz1zW3BdCmlmKEouUk0o
+bywiLi4iKSl7bj10Lmxlbmd0aAppZihuIT09MCl7aWYoMD49bilyZXR1cm4gSC5PSCh0LC0xKQp0LnBv
cCgpCmlmKHQubGVuZ3RoPT09MClDLk5tLmkodCwiIil9cT0hMH1lbHNlIGlmKCIuIj09PW8pcT0hMApl
bHNle0MuTm0uaSh0LG8pCnE9ITF9fWlmKHEpQy5ObS5pKHQsIiIpCnJldHVybiBDLk5tLkgodCwiLyIp
fSwKd0Y6ZnVuY3Rpb24oYSxiKXt2YXIgdCxzLHIscSxwLG8KaWYoIVAueUIoYSkpcmV0dXJuIWI/UC5D
MShhKTphCnQ9SC5WTShbXSx1LnMpCmZvcihzPWEuc3BsaXQoIi8iKSxyPXMubGVuZ3RoLHE9ITEscD0w
O3A8cjsrK3Ape289c1twXQppZigiLi4iPT09bylpZih0Lmxlbmd0aCE9PTAmJkMuTm0uZ3JaKHQpIT09
-Ii4uIil7aWYoMD49dC5sZW5ndGgpcmV0dXJuIEguayh0LC0xKQp0LnBvcCgpCnE9ITB9ZWxzZXtDLk5t
-LmkodCwiLi4iKQpxPSExfWVsc2UgaWYoIi4iPT09bylxPSEwCmVsc2V7Qy5ObS5pKHQsbykKcT0hMX19
-cz10Lmxlbmd0aAppZihzIT09MClpZihzPT09MSl7aWYoMD49cylyZXR1cm4gSC5rKHQsMCkKcz10WzBd
-Lmxlbmd0aD09PTB9ZWxzZSBzPSExCmVsc2Ugcz0hMAppZihzKXJldHVybiIuLyIKaWYocXx8Qy5ObS5n
-cloodCk9PT0iLi4iKUMuTm0uaSh0LCIiKQppZighYil7aWYoMD49dC5sZW5ndGgpcmV0dXJuIEguayh0
-LDApCkMuTm0uWSh0LDAsUC5DMSh0WzBdKSl9cmV0dXJuIEMuTm0uSCh0LCIvIil9LApDMTpmdW5jdGlv
-bihhKXt2YXIgdCxzLHIscT1hLmxlbmd0aAppZihxPj0yJiZQLkV0KEouUXooYSwwKSkpZm9yKHQ9MTt0
-PHE7Kyt0KXtzPUMueEIuVyhhLHQpCmlmKHM9PT01OClyZXR1cm4gQy54Qi5OaihhLDAsdCkrIiUzQSIr
-Qy54Qi5HKGEsdCsxKQppZihzPD0xMjcpe3I9cz4+PjQKaWYocj49OClyZXR1cm4gSC5rKEMubUsscikK
-cj0oQy5tS1tyXSYxPDwocyYxNSkpPT09MH1lbHNlIHI9ITAKaWYocilicmVha31yZXR1cm4gYX0sCm1u
-OmZ1bmN0aW9uKGEpe3ZhciB0LHMscixxPWEuZ0ZqKCkscD1xLmxlbmd0aAppZihwPjAmJkouSChxWzBd
-KT09PTImJkouYTYocVswXSwxKT09PTU4KXtpZigwPj1wKXJldHVybiBILmsocSwwKQpQLnJnKEouYTYo
-cVswXSwwKSwhMSkKUC5ITihxLCExLDEpCnQ9ITB9ZWxzZXtQLkhOKHEsITEsMCkKdD0hMX1zPWEuZ3RU
-KCkmJiF0PyJcXCI6IiIKaWYoYS5nY2ooKSl7cj1hLmdKZihhKQppZihyLmxlbmd0aCE9PTApcz1zKyJc
-XCIrcisiXFwifXM9UC52ZyhzLHEsIlxcIikKcD10JiZwPT09MT9zKyJcXCI6cwpyZXR1cm4gcC5jaGFy
-Q29kZUF0KDApPT0wP3A6cH0sCkloOmZ1bmN0aW9uKGEsYil7dmFyIHQscyxyCmZvcih0PTAscz0wO3M8
-MjsrK3Mpe3I9Qy54Qi5XKGEsYitzKQppZig0ODw9ciYmcjw9NTcpdD10KjE2K3ItNDgKZWxzZXtyfD0z
-MgppZig5Nzw9ciYmcjw9MTAyKXQ9dCoxNityLTg3CmVsc2UgdGhyb3cgSC5iKFAueFkoIkludmFsaWQg
-VVJMIGVuY29kaW5nIikpfX1yZXR1cm4gdH0sCmt1OmZ1bmN0aW9uKGEsYixjLGQsZSl7dmFyIHQscyxy
-LHEscD1KLnJZKGEpLG89Ygp3aGlsZSghMCl7aWYoIShvPGMpKXt0PSEwCmJyZWFrfXM9cC5XKGEsbykK
-aWYoczw9MTI3KWlmKHMhPT0zNylyPWUmJnM9PT00MwplbHNlIHI9ITAKZWxzZSByPSEwCmlmKHIpe3Q9
-ITEKYnJlYWt9KytvfWlmKHQpe2lmKEMueE0hPT1kKXI9ITEKZWxzZSByPSEwCmlmKHIpcmV0dXJuIHAu
-TmooYSxiLGMpCmVsc2UgcT1uZXcgSC5xaihwLk5qKGEsYixjKSl9ZWxzZXtxPUguVk0oW10sdS50KQpm
-b3Iobz1iO288YzsrK28pe3M9cC5XKGEsbykKaWYocz4xMjcpdGhyb3cgSC5iKFAueFkoIklsbGVnYWwg
-cGVyY2VudCBlbmNvZGluZyBpbiBVUkkiKSkKaWYocz09PTM3KXtpZihvKzM+YS5sZW5ndGgpdGhyb3cg
-SC5iKFAueFkoIlRydW5jYXRlZCBVUkkiKSkKQy5ObS5pKHEsUC5JaChhLG8rMSkpCm8rPTJ9ZWxzZSBp
-ZihlJiZzPT09NDMpQy5ObS5pKHEsMzIpCmVsc2UgQy5ObS5pKHEscyl9fXUuTC5hKHEpCnJldHVybiBu
-ZXcgUC5HWSghMSkuV0oocSl9LApFdDpmdW5jdGlvbihhKXt2YXIgdD1hfDMyCnJldHVybiA5Nzw9dCYm
-dDw9MTIyfSwKS0Q6ZnVuY3Rpb24oYSxiLGMpe3ZhciB0LHMscixxLHAsbyxuLG0sbD0iSW52YWxpZCBN
-SU1FIHR5cGUiLGs9SC5WTShbYi0xXSx1LnQpCmZvcih0PWEubGVuZ3RoLHM9YixyPS0xLHE9bnVsbDtz
-PHQ7KytzKXtxPUMueEIuVyhhLHMpCmlmKHE9PT00NHx8cT09PTU5KWJyZWFrCmlmKHE9PT00Nyl7aWYo
-cjwwKXtyPXMKY29udGludWV9dGhyb3cgSC5iKFAucnIobCxhLHMpKX19aWYocjwwJiZzPmIpdGhyb3cg
-SC5iKFAucnIobCxhLHMpKQpmb3IoO3EhPT00NDspe0MuTm0uaShrLHMpOysrcwpmb3IocD0tMTtzPHQ7
-KytzKXtxPUMueEIuVyhhLHMpCmlmKHE9PT02MSl7aWYocDwwKXA9c31lbHNlIGlmKHE9PT01OXx8cT09
-PTQ0KWJyZWFrfWlmKHA+PTApQy5ObS5pKGsscCkKZWxzZXtvPUMuTm0uZ3JaKGspCmlmKHEhPT00NHx8
-cyE9PW8rN3x8IUMueEIuUWkoYSwiYmFzZTY0IixvKzEpKXRocm93IEguYihQLnJyKCJFeHBlY3Rpbmcg
-Jz0nIixhLHMpKQpicmVha319Qy5ObS5pKGsscykKbj1zKzEKaWYoKGsubGVuZ3RoJjEpPT09MSlhPUMu
-aDkueXIoYSxuLHQpCmVsc2V7bT1QLlVsKGEsbix0LEMuVkMsITApCmlmKG0hPW51bGwpYT1DLnhCLmk3
-KGEsbix0LG0pfXJldHVybiBuZXcgUC5QRShhLGssYyl9LApLTjpmdW5jdGlvbigpe3ZhciB0PSIwMTIz
-NDU2Nzg5QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVphYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ei0u
-X34hJCYnKCkqKyw7PSIscz0iLiIscj0iOiIscT0iLyIscD0iPyIsbz0iIyIsbj11LmdjLG09UC5kSCgy
-MixuZXcgUC5xMygpLG4pLGw9bmV3IFAueUkobSksaz1uZXcgUC5jNigpLGo9bmV3IFAucWQoKSxpPW4u
-YShsLiQyKDAsMjI1KSkKay4kMyhpLHQsMSkKay4kMyhpLHMsMTQpCmsuJDMoaSxyLDM0KQprLiQzKGks
-cSwzKQprLiQzKGkscCwxNzIpCmsuJDMoaSxvLDIwNSkKaT1uLmEobC4kMigxNCwyMjUpKQprLiQzKGks
-dCwxKQprLiQzKGkscywxNSkKay4kMyhpLHIsMzQpCmsuJDMoaSxxLDIzNCkKay4kMyhpLHAsMTcyKQpr
-LiQzKGksbywyMDUpCmk9bi5hKGwuJDIoMTUsMjI1KSkKay4kMyhpLHQsMSkKay4kMyhpLCIlIiwyMjUp
-CmsuJDMoaSxyLDM0KQprLiQzKGkscSw5KQprLiQzKGkscCwxNzIpCmsuJDMoaSxvLDIwNSkKaT1uLmEo
-bC4kMigxLDIyNSkpCmsuJDMoaSx0LDEpCmsuJDMoaSxyLDM0KQprLiQzKGkscSwxMCkKay4kMyhpLHAs
-MTcyKQprLiQzKGksbywyMDUpCmk9bi5hKGwuJDIoMiwyMzUpKQprLiQzKGksdCwxMzkpCmsuJDMoaSxx
-LDEzMSkKay4kMyhpLHMsMTQ2KQprLiQzKGkscCwxNzIpCmsuJDMoaSxvLDIwNSkKaT1uLmEobC4kMigz
-LDIzNSkpCmsuJDMoaSx0LDExKQprLiQzKGkscSw2OCkKay4kMyhpLHMsMTgpCmsuJDMoaSxwLDE3MikK
-ay4kMyhpLG8sMjA1KQppPW4uYShsLiQyKDQsMjI5KSkKay4kMyhpLHQsNSkKai4kMyhpLCJBWiIsMjI5
-KQprLiQzKGksciwxMDIpCmsuJDMoaSwiQCIsNjgpCmsuJDMoaSwiWyIsMjMyKQprLiQzKGkscSwxMzgp
-CmsuJDMoaSxwLDE3MikKay4kMyhpLG8sMjA1KQppPW4uYShsLiQyKDUsMjI5KSkKay4kMyhpLHQsNSkK
-ai4kMyhpLCJBWiIsMjI5KQprLiQzKGksciwxMDIpCmsuJDMoaSwiQCIsNjgpCmsuJDMoaSxxLDEzOCkK
-ay4kMyhpLHAsMTcyKQprLiQzKGksbywyMDUpCmk9bi5hKGwuJDIoNiwyMzEpKQpqLiQzKGksIjE5Iiw3
-KQprLiQzKGksIkAiLDY4KQprLiQzKGkscSwxMzgpCmsuJDMoaSxwLDE3MikKay4kMyhpLG8sMjA1KQpp
-PW4uYShsLiQyKDcsMjMxKSkKai4kMyhpLCIwOSIsNykKay4kMyhpLCJAIiw2OCkKay4kMyhpLHEsMTM4
-KQprLiQzKGkscCwxNzIpCmsuJDMoaSxvLDIwNSkKay4kMyhuLmEobC4kMig4LDgpKSwiXSIsNSkKaT1u
-LmEobC4kMig5LDIzNSkpCmsuJDMoaSx0LDExKQprLiQzKGkscywxNikKay4kMyhpLHEsMjM0KQprLiQz
-KGkscCwxNzIpCmsuJDMoaSxvLDIwNSkKaT1uLmEobC4kMigxNiwyMzUpKQprLiQzKGksdCwxMSkKay4k
-MyhpLHMsMTcpCmsuJDMoaSxxLDIzNCkKay4kMyhpLHAsMTcyKQprLiQzKGksbywyMDUpCmk9bi5hKGwu
-JDIoMTcsMjM1KSkKay4kMyhpLHQsMTEpCmsuJDMoaSxxLDkpCmsuJDMoaSxwLDE3MikKay4kMyhpLG8s
-MjA1KQppPW4uYShsLiQyKDEwLDIzNSkpCmsuJDMoaSx0LDExKQprLiQzKGkscywxOCkKay4kMyhpLHEs
-MjM0KQprLiQzKGkscCwxNzIpCmsuJDMoaSxvLDIwNSkKaT1uLmEobC4kMigxOCwyMzUpKQprLiQzKGks
-dCwxMSkKay4kMyhpLHMsMTkpCmsuJDMoaSxxLDIzNCkKay4kMyhpLHAsMTcyKQprLiQzKGksbywyMDUp
-Cmk9bi5hKGwuJDIoMTksMjM1KSkKay4kMyhpLHQsMTEpCmsuJDMoaSxxLDIzNCkKay4kMyhpLHAsMTcy
-KQprLiQzKGksbywyMDUpCmk9bi5hKGwuJDIoMTEsMjM1KSkKay4kMyhpLHQsMTEpCmsuJDMoaSxxLDEw
-KQprLiQzKGkscCwxNzIpCmsuJDMoaSxvLDIwNSkKaT1uLmEobC4kMigxMiwyMzYpKQprLiQzKGksdCwx
-MikKay4kMyhpLHAsMTIpCmsuJDMoaSxvLDIwNSkKaT1uLmEobC4kMigxMywyMzcpKQprLiQzKGksdCwx
-MykKay4kMyhpLHAsMTMpCmouJDMobi5hKGwuJDIoMjAsMjQ1KSksImF6IiwyMSkKbD1uLmEobC4kMigy
-MSwyNDUpKQpqLiQzKGwsImF6IiwyMSkKai4kMyhsLCIwOSIsMjEpCmsuJDMobCwiKy0uIiwyMSkKcmV0
-dXJuIG19LApVQjpmdW5jdGlvbihhLGIsYyxkLGUpe3ZhciB0LHMscixxLHAsbz0kLnZaKCkKZm9yKHQ9
-Si5yWShhKSxzPWI7czxjOysrcyl7aWYoZDwwfHxkPj1vLmxlbmd0aClyZXR1cm4gSC5rKG8sZCkKcj1v
-W2RdCnE9dC5XKGEscyleOTYKaWYocT45NSlxPTMxCmlmKHE+PXIubGVuZ3RoKXJldHVybiBILmsocixx
-KQpwPXJbcV0KZD1wJjMxCkMuTm0uWShlLHA+Pj41LHMpfXJldHVybiBkfSwKV0Y6ZnVuY3Rpb24gV0Yo
-YSxiKXt0aGlzLmE9YQp0aGlzLmI9Yn0sCmEyOmZ1bmN0aW9uIGEyKCl7fSwKaVA6ZnVuY3Rpb24gaVAo
-YSxiKXt0aGlzLmE9YQp0aGlzLmI9Yn0sCkNQOmZ1bmN0aW9uIENQKCl7fSwKWFM6ZnVuY3Rpb24gWFMo
-KXt9LApDNjpmdW5jdGlvbiBDNihhKXt0aGlzLmE9YX0sCkxLOmZ1bmN0aW9uIExLKCl7fSwKQVQ6ZnVu
-Y3Rpb24gQVQoYSxiLGMsZCl7dmFyIF89dGhpcwpfLmE9YQpfLmI9YgpfLmM9YwpfLmQ9ZH0sCmJKOmZ1
-bmN0aW9uIGJKKGEsYixjLGQsZSxmKXt2YXIgXz10aGlzCl8uZT1hCl8uZj1iCl8uYT1jCl8uYj1kCl8u
-Yz1lCl8uZD1mfSwKZVk6ZnVuY3Rpb24gZVkoYSxiLGMsZCxlKXt2YXIgXz10aGlzCl8uZj1hCl8uYT1i
-Cl8uYj1jCl8uYz1kCl8uZD1lfSwKbXA6ZnVuY3Rpb24gbXAoYSxiLGMsZCl7dmFyIF89dGhpcwpfLmE9
-YQpfLmI9YgpfLmM9YwpfLmQ9ZH0sCnViOmZ1bmN0aW9uIHViKGEpe3RoaXMuYT1hfSwKZHM6ZnVuY3Rp
-b24gZHMoYSl7dGhpcy5hPWF9LApsajpmdW5jdGlvbiBsaihhKXt0aGlzLmE9YX0sClVWOmZ1bmN0aW9u
-IFVWKGEpe3RoaXMuYT1hfSwKazU6ZnVuY3Rpb24gazUoKXt9LApLWTpmdW5jdGlvbiBLWSgpe30sCnQ3
-OmZ1bmN0aW9uIHQ3KGEpe3RoaXMuYT1hfSwKQ0Q6ZnVuY3Rpb24gQ0QoYSl7dGhpcy5hPWF9LAphRTpm
+Ii4uIil7aWYoMD49dC5sZW5ndGgpcmV0dXJuIEguT0godCwtMSkKdC5wb3AoKQpxPSEwfWVsc2V7Qy5O
+bS5pKHQsIi4uIikKcT0hMX1lbHNlIGlmKCIuIj09PW8pcT0hMAplbHNle0MuTm0uaSh0LG8pCnE9ITF9
+fXM9dC5sZW5ndGgKaWYocyE9PTApaWYocz09PTEpe2lmKDA+PXMpcmV0dXJuIEguT0godCwwKQpzPXRb
+MF0ubGVuZ3RoPT09MH1lbHNlIHM9ITEKZWxzZSBzPSEwCmlmKHMpcmV0dXJuIi4vIgppZihxfHxDLk5t
+LmdyWih0KT09PSIuLiIpQy5ObS5pKHQsIiIpCmlmKCFiKXtpZigwPj10Lmxlbmd0aClyZXR1cm4gSC5P
+SCh0LDApCkMuTm0uWSh0LDAsUC5DMSh0WzBdKSl9cmV0dXJuIEMuTm0uSCh0LCIvIil9LApDMTpmdW5j
+dGlvbihhKXt2YXIgdCxzLHIscT1hLmxlbmd0aAppZihxPj0yJiZQLkV0KEouUXooYSwwKSkpZm9yKHQ9
+MTt0PHE7Kyt0KXtzPUMueEIuVyhhLHQpCmlmKHM9PT01OClyZXR1cm4gQy54Qi5OaihhLDAsdCkrIiUz
+QSIrQy54Qi5HKGEsdCsxKQppZihzPD0xMjcpe3I9cz4+PjQKaWYocj49OClyZXR1cm4gSC5PSChDLm1L
+LHIpCnI9KEMubUtbcl0mMTw8KHMmMTUpKT09PTB9ZWxzZSByPSEwCmlmKHIpYnJlYWt9cmV0dXJuIGF9
+LAptbjpmdW5jdGlvbihhKXt2YXIgdCxzLHIscT1hLmdGaigpLHA9cS5sZW5ndGgKaWYocD4wJiZKLkht
+KHFbMF0pPT09MiYmSi5hNihxWzBdLDEpPT09NTgpe2lmKDA+PXApcmV0dXJuIEguT0gocSwwKQpQLnJn
+KEouYTYocVswXSwwKSwhMSkKUC5ITihxLCExLDEpCnQ9ITB9ZWxzZXtQLkhOKHEsITEsMCkKdD0hMX1z
+PWEuZ3RUKCkmJiF0PyJcXCI6IiIKaWYoYS5nY2ooKSl7cj1hLmdKZihhKQppZihyLmxlbmd0aCE9PTAp
+cz1zKyJcXCIrcisiXFwifXM9UC52ZyhzLHEsIlxcIikKcD10JiZwPT09MT9zKyJcXCI6cwpyZXR1cm4g
+cC5jaGFyQ29kZUF0KDApPT0wP3A6cH0sCkloOmZ1bmN0aW9uKGEsYil7dmFyIHQscyxyCmZvcih0PTAs
+cz0wO3M8MjsrK3Mpe3I9Qy54Qi5XKGEsYitzKQppZig0ODw9ciYmcjw9NTcpdD10KjE2K3ItNDgKZWxz
+ZXtyfD0zMgppZig5Nzw9ciYmcjw9MTAyKXQ9dCoxNityLTg3CmVsc2UgdGhyb3cgSC5iKFAueFkoIklu
+dmFsaWQgVVJMIGVuY29kaW5nIikpfX1yZXR1cm4gdH0sCmt1OmZ1bmN0aW9uKGEsYixjLGQsZSl7dmFy
+IHQscyxyLHEscD1KLnJZKGEpLG89Ygp3aGlsZSghMCl7aWYoIShvPGMpKXt0PSEwCmJyZWFrfXM9cC5X
+KGEsbykKaWYoczw9MTI3KWlmKHMhPT0zNylyPWUmJnM9PT00MwplbHNlIHI9ITAKZWxzZSByPSEwCmlm
+KHIpe3Q9ITEKYnJlYWt9KytvfWlmKHQpe2lmKEMueE0hPT1kKXI9ITEKZWxzZSByPSEwCmlmKHIpcmV0
+dXJuIHAuTmooYSxiLGMpCmVsc2UgcT1uZXcgSC5xaihwLk5qKGEsYixjKSl9ZWxzZXtxPUguVk0oW10s
+dS5hKQpmb3Iobz1iO288YzsrK28pe3M9cC5XKGEsbykKaWYocz4xMjcpdGhyb3cgSC5iKFAueFkoIkls
+bGVnYWwgcGVyY2VudCBlbmNvZGluZyBpbiBVUkkiKSkKaWYocz09PTM3KXtpZihvKzM+YS5sZW5ndGgp
+dGhyb3cgSC5iKFAueFkoIlRydW5jYXRlZCBVUkkiKSkKQy5ObS5pKHEsUC5JaChhLG8rMSkpCm8rPTJ9
+ZWxzZSBpZihlJiZzPT09NDMpQy5ObS5pKHEsMzIpCmVsc2UgQy5ObS5pKHEscyl9fXUuTC5hKHEpCnJl
+dHVybiBDLm9FLldKKHEpfSwKRXQ6ZnVuY3Rpb24oYSl7dmFyIHQ9YXwzMgpyZXR1cm4gOTc8PXQmJnQ8
+PTEyMn0sCktEOmZ1bmN0aW9uKGEsYixjKXt2YXIgdCxzLHIscSxwLG8sbixtLGw9IkludmFsaWQgTUlN
+RSB0eXBlIixrPUguVk0oW2ItMV0sdS5hKQpmb3IodD1hLmxlbmd0aCxzPWIscj0tMSxxPW51bGw7czx0
+Oysrcyl7cT1DLnhCLlcoYSxzKQppZihxPT09NDR8fHE9PT01OSlicmVhawppZihxPT09NDcpe2lmKHI8
+MCl7cj1zCmNvbnRpbnVlfXRocm93IEguYihQLnJyKGwsYSxzKSl9fWlmKHI8MCYmcz5iKXRocm93IEgu
+YihQLnJyKGwsYSxzKSkKZm9yKDtxIT09NDQ7KXtDLk5tLmkoayxzKTsrK3MKZm9yKHA9LTE7czx0Oysr
+cyl7cT1DLnhCLlcoYSxzKQppZihxPT09NjEpe2lmKHA8MClwPXN9ZWxzZSBpZihxPT09NTl8fHE9PT00
+NClicmVha31pZihwPj0wKUMuTm0uaShrLHApCmVsc2V7bz1DLk5tLmdyWihrKQppZihxIT09NDR8fHMh
+PT1vKzd8fCFDLnhCLlFpKGEsImJhc2U2NCIsbysxKSl0aHJvdyBILmIoUC5ycigiRXhwZWN0aW5nICc9
+JyIsYSxzKSkKYnJlYWt9fUMuTm0uaShrLHMpCm49cysxCmlmKChrLmxlbmd0aCYxKT09PTEpYT1DLmg5
+LnlyKGEsbix0KQplbHNle209UC5VbChhLG4sdCxDLlZDLCEwKQppZihtIT1udWxsKWE9Qy54Qi5pNyhh
+LG4sdCxtKX1yZXR1cm4gbmV3IFAuUEUoYSxrLGMpfSwKS046ZnVuY3Rpb24oKXt2YXIgdD0iMDEyMzQ1
+Njc4OUFCQ0RFRkdISUpLTE1OT1BRUlNUVVZXWFlaYWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXotLl9+
+ISQmJygpKissOz0iLHM9Ii4iLHI9IjoiLHE9Ii8iLHA9Ij8iLG89IiMiLG49UC5kSCgyMixuZXcgUC5x
+MygpLHUuZ2MpLG09bmV3IFAueUkobiksbD1uZXcgUC5jNigpLGs9bmV3IFAucWQoKSxqPW0uJDIoMCwy
+MjUpCmwuJDMoaix0LDEpCmwuJDMoaixzLDE0KQpsLiQzKGosciwzNCkKbC4kMyhqLHEsMykKbC4kMyhq
+LHAsMTcyKQpsLiQzKGosbywyMDUpCmo9bS4kMigxNCwyMjUpCmwuJDMoaix0LDEpCmwuJDMoaixzLDE1
+KQpsLiQzKGosciwzNCkKbC4kMyhqLHEsMjM0KQpsLiQzKGoscCwxNzIpCmwuJDMoaixvLDIwNSkKaj1t
+LiQyKDE1LDIyNSkKbC4kMyhqLHQsMSkKbC4kMyhqLCIlIiwyMjUpCmwuJDMoaixyLDM0KQpsLiQzKGos
+cSw5KQpsLiQzKGoscCwxNzIpCmwuJDMoaixvLDIwNSkKaj1tLiQyKDEsMjI1KQpsLiQzKGosdCwxKQps
+LiQzKGosciwzNCkKbC4kMyhqLHEsMTApCmwuJDMoaixwLDE3MikKbC4kMyhqLG8sMjA1KQpqPW0uJDIo
+MiwyMzUpCmwuJDMoaix0LDEzOSkKbC4kMyhqLHEsMTMxKQpsLiQzKGoscywxNDYpCmwuJDMoaixwLDE3
+MikKbC4kMyhqLG8sMjA1KQpqPW0uJDIoMywyMzUpCmwuJDMoaix0LDExKQpsLiQzKGoscSw2OCkKbC4k
+MyhqLHMsMTgpCmwuJDMoaixwLDE3MikKbC4kMyhqLG8sMjA1KQpqPW0uJDIoNCwyMjkpCmwuJDMoaix0
+LDUpCmsuJDMoaiwiQVoiLDIyOSkKbC4kMyhqLHIsMTAyKQpsLiQzKGosIkAiLDY4KQpsLiQzKGosIlsi
+LDIzMikKbC4kMyhqLHEsMTM4KQpsLiQzKGoscCwxNzIpCmwuJDMoaixvLDIwNSkKaj1tLiQyKDUsMjI5
+KQpsLiQzKGosdCw1KQprLiQzKGosIkFaIiwyMjkpCmwuJDMoaixyLDEwMikKbC4kMyhqLCJAIiw2OCkK
+bC4kMyhqLHEsMTM4KQpsLiQzKGoscCwxNzIpCmwuJDMoaixvLDIwNSkKaj1tLiQyKDYsMjMxKQprLiQz
+KGosIjE5Iiw3KQpsLiQzKGosIkAiLDY4KQpsLiQzKGoscSwxMzgpCmwuJDMoaixwLDE3MikKbC4kMyhq
+LG8sMjA1KQpqPW0uJDIoNywyMzEpCmsuJDMoaiwiMDkiLDcpCmwuJDMoaiwiQCIsNjgpCmwuJDMoaixx
+LDEzOCkKbC4kMyhqLHAsMTcyKQpsLiQzKGosbywyMDUpCmwuJDMobS4kMig4LDgpLCJdIiw1KQpqPW0u
+JDIoOSwyMzUpCmwuJDMoaix0LDExKQpsLiQzKGoscywxNikKbC4kMyhqLHEsMjM0KQpsLiQzKGoscCwx
+NzIpCmwuJDMoaixvLDIwNSkKaj1tLiQyKDE2LDIzNSkKbC4kMyhqLHQsMTEpCmwuJDMoaixzLDE3KQps
+LiQzKGoscSwyMzQpCmwuJDMoaixwLDE3MikKbC4kMyhqLG8sMjA1KQpqPW0uJDIoMTcsMjM1KQpsLiQz
+KGosdCwxMSkKbC4kMyhqLHEsOSkKbC4kMyhqLHAsMTcyKQpsLiQzKGosbywyMDUpCmo9bS4kMigxMCwy
+MzUpCmwuJDMoaix0LDExKQpsLiQzKGoscywxOCkKbC4kMyhqLHEsMjM0KQpsLiQzKGoscCwxNzIpCmwu
+JDMoaixvLDIwNSkKaj1tLiQyKDE4LDIzNSkKbC4kMyhqLHQsMTEpCmwuJDMoaixzLDE5KQpsLiQzKGos
+cSwyMzQpCmwuJDMoaixwLDE3MikKbC4kMyhqLG8sMjA1KQpqPW0uJDIoMTksMjM1KQpsLiQzKGosdCwx
+MSkKbC4kMyhqLHEsMjM0KQpsLiQzKGoscCwxNzIpCmwuJDMoaixvLDIwNSkKaj1tLiQyKDExLDIzNSkK
+bC4kMyhqLHQsMTEpCmwuJDMoaixxLDEwKQpsLiQzKGoscCwxNzIpCmwuJDMoaixvLDIwNSkKaj1tLiQy
+KDEyLDIzNikKbC4kMyhqLHQsMTIpCmwuJDMoaixwLDEyKQpsLiQzKGosbywyMDUpCmo9bS4kMigxMywy
+MzcpCmwuJDMoaix0LDEzKQpsLiQzKGoscCwxMykKay4kMyhtLiQyKDIwLDI0NSksImF6IiwyMSkKaj1t
+LiQyKDIxLDI0NSkKay4kMyhqLCJheiIsMjEpCmsuJDMoaiwiMDkiLDIxKQpsLiQzKGosIistLiIsMjEp
+CnJldHVybiBufSwKVUI6ZnVuY3Rpb24oYSxiLGMsZCxlKXt2YXIgdCxzLHIscSxwLG89JC52WigpCmZv
+cih0PUouclkoYSkscz1iO3M8YzsrK3Mpe2lmKGQ8MHx8ZD49by5sZW5ndGgpcmV0dXJuIEguT0gobyxk
+KQpyPW9bZF0KcT10LlcoYSxzKV45NgppZihxPjk1KXE9MzEKaWYocT49ci5sZW5ndGgpcmV0dXJuIEgu
+T0gocixxKQpwPXJbcV0KZD1wJjMxCkMuTm0uWShlLHA+Pj41LHMpfXJldHVybiBkfSwKV0Y6ZnVuY3Rp
+b24gV0YoYSxiKXt0aGlzLmE9YQp0aGlzLmI9Yn0sCmEyOmZ1bmN0aW9uIGEyKCl7fSwKaVA6ZnVuY3Rp
+b24gaVAoYSxiKXt0aGlzLmE9YQp0aGlzLmI9Yn0sCkNQOmZ1bmN0aW9uIENQKCl7fSwKWFM6ZnVuY3Rp
+b24gWFMoKXt9LApDNjpmdW5jdGlvbiBDNihhKXt0aGlzLmE9YX0sCm46ZnVuY3Rpb24gbigpe30sCnU6
+ZnVuY3Rpb24gdShhLGIsYyxkKXt2YXIgXz10aGlzCl8uYT1hCl8uYj1iCl8uYz1jCl8uZD1kfSwKYko6
+ZnVuY3Rpb24gYkooYSxiLGMsZCxlLGYpe3ZhciBfPXRoaXMKXy5lPWEKXy5mPWIKXy5hPWMKXy5iPWQK
+Xy5jPWUKXy5kPWZ9LAplWTpmdW5jdGlvbiBlWShhLGIsYyxkLGUpe3ZhciBfPXRoaXMKXy5mPWEKXy5h
+PWIKXy5iPWMKXy5jPWQKXy5kPWV9LAptcDpmdW5jdGlvbiBtcChhLGIsYyxkKXt2YXIgXz10aGlzCl8u
+YT1hCl8uYj1iCl8uYz1jCl8uZD1kfSwKdWI6ZnVuY3Rpb24gdWIoYSl7dGhpcy5hPWF9LApkczpmdW5j
+dGlvbiBkcyhhKXt0aGlzLmE9YX0sCmxqOmZ1bmN0aW9uIGxqKGEpe3RoaXMuYT1hfSwKVVY6ZnVuY3Rp
+b24gVVYoYSl7dGhpcy5hPWF9LAprNTpmdW5jdGlvbiBrNSgpe30sCktZOmZ1bmN0aW9uIEtZKCl7fSwK
+YzpmdW5jdGlvbiBjKGEpe3RoaXMuYT1hfSwKQ0Q6ZnVuY3Rpb24gQ0QoYSl7dGhpcy5hPWF9LAphRTpm
dW5jdGlvbiBhRShhLGIsYyl7dGhpcy5hPWEKdGhpcy5iPWIKdGhpcy5jPWN9LApFSDpmdW5jdGlvbiBF
SCgpe30sCklmOmZ1bmN0aW9uIElmKCl7fSwKY1g6ZnVuY3Rpb24gY1goKXt9LApBbjpmdW5jdGlvbiBB
bigpe30sCnpNOmZ1bmN0aW9uIHpNKCl7fSwKWjA6ZnVuY3Rpb24gWjAoKXt9LApOMzpmdW5jdGlvbiBO
@@ -2065,2596 +8987,2596 @@
OmZ1bmN0aW9uIGNTKGEpe3RoaXMuYT1hfSwKVkM6ZnVuY3Rpb24gVkMoYSl7dGhpcy5hPWF9LApKVDpm
dW5jdGlvbiBKVChhLGIpe3RoaXMuYT1hCnRoaXMuYj1ifSwKRG46ZnVuY3Rpb24gRG4oYSxiLGMsZCxl
LGYsZyl7dmFyIF89dGhpcwpfLmE9YQpfLmI9YgpfLmM9YwpfLmQ9ZApfLmU9ZQpfLmY9ZgpfLnI9Zwpf
-LlE9Xy56PV8ueT1fLng9bnVsbH0sCmUxOmZ1bmN0aW9uIGUxKGEsYil7dGhpcy5hPWEKdGhpcy5iPWJ9
-LApOWTpmdW5jdGlvbiBOWShhKXt0aGlzLmE9YX0sClJaOmZ1bmN0aW9uIFJaKCl7fSwKTUU6ZnVuY3Rp
-b24gTUUoYSxiKXt0aGlzLmE9YQp0aGlzLmI9Yn0sCnk1OmZ1bmN0aW9uIHk1KGEpe3RoaXMuYT1hfSwK
-UEU6ZnVuY3Rpb24gUEUoYSxiLGMpe3RoaXMuYT1hCnRoaXMuYj1iCnRoaXMuYz1jfSwKcTM6ZnVuY3Rp
-b24gcTMoKXt9LAp5STpmdW5jdGlvbiB5SShhKXt0aGlzLmE9YX0sCmM2OmZ1bmN0aW9uIGM2KCl7fSwK
-cWQ6ZnVuY3Rpb24gcWQoKXt9LApVZjpmdW5jdGlvbiBVZihhLGIsYyxkLGUsZixnLGgpe3ZhciBfPXRo
-aXMKXy5hPWEKXy5iPWIKXy5jPWMKXy5kPWQKXy5lPWUKXy5mPWYKXy5yPWcKXy54PWgKXy55PW51bGx9
-LApxZTpmdW5jdGlvbiBxZShhLGIsYyxkLGUsZixnKXt2YXIgXz10aGlzCl8uYT1hCl8uYj1iCl8uYz1j
-Cl8uZD1kCl8uZT1lCl8uZj1mCl8ucj1nCl8uUT1fLno9Xy55PV8ueD1udWxsfSwKaUo6ZnVuY3Rpb24g
-aUooKXt9LApqZzpmdW5jdGlvbiBqZyhhLGIpe3RoaXMuYT1hCnRoaXMuYj1ifSwKVGE6ZnVuY3Rpb24g
-VGEoYSxiKXt0aGlzLmE9YQp0aGlzLmI9Yn0sCkJmOmZ1bmN0aW9uIEJmKGEsYil7dGhpcy5hPWEKdGhp
-cy5iPWJ9LApBczpmdW5jdGlvbiBBcygpe30sCkdFOmZ1bmN0aW9uIEdFKGEpe3RoaXMuYT1hfSwKTjc6
-ZnVuY3Rpb24gTjcoYSxiKXt0aGlzLmE9YQp0aGlzLmI9Yn0sCnVROmZ1bmN0aW9uIHVRKCl7fSwKaEY6
-ZnVuY3Rpb24gaEYoKXt9LApSNDpmdW5jdGlvbihhLGIsYyxkKXt2YXIgdCxzLHIKSC5FOShiKQp1Lmou
-YShkKQppZihILm9UKGIpKXt0PVtjXQpDLk5tLkZWKHQsZCkKZD10fXM9dS56CnI9UC5DSChKLk0xKGQs
-UC53MCgpLHMpLCEwLHMpCnUuWi5hKGEpCnJldHVybiBQLndZKEguRWsoYSxyLG51bGwpKX0sCkRtOmZ1
-bmN0aW9uKGEsYixjKXt2YXIgdAp0cnl7aWYoT2JqZWN0LmlzRXh0ZW5zaWJsZShhKSYmIU9iamVjdC5w
-cm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbChhLGIpKXtPYmplY3QuZGVmaW5lUHJvcGVydHkoYSxi
-LHt2YWx1ZTpjfSkKcmV0dXJuITB9fWNhdGNoKHQpe0guUnUodCl9cmV0dXJuITF9LApPbTpmdW5jdGlv
-bihhLGIpe2lmKE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbChhLGIpKXJldHVybiBh
-W2JdCnJldHVybiBudWxsfSwKd1k6ZnVuY3Rpb24oYSl7aWYoYT09bnVsbHx8dHlwZW9mIGE9PSJzdHJp
-bmcifHx0eXBlb2YgYT09Im51bWJlciJ8fEguclEoYSkpcmV0dXJuIGEKaWYoYSBpbnN0YW5jZW9mIFAu
-RTQpcmV0dXJuIGEuYQppZihILlI5KGEpKXJldHVybiBhCmlmKHUudS5iKGEpKXJldHVybiBhCmlmKGEg
-aW5zdGFuY2VvZiBQLmlQKXJldHVybiBILm8yKGEpCmlmKHUuWi5iKGEpKXJldHVybiBQLmhFKGEsIiRk
-YXJ0X2pzRnVuY3Rpb24iLG5ldyBQLlBDKCkpCnJldHVybiBQLmhFKGEsIl8kZGFydF9qc09iamVjdCIs
-bmV3IFAubXQoJC5rSSgpKSl9LApoRTpmdW5jdGlvbihhLGIsYyl7dmFyIHQ9UC5PbShhLGIpCmlmKHQ9
-PW51bGwpe3Q9Yy4kMShhKQpQLkRtKGEsYix0KX1yZXR1cm4gdH0sCkw3OmZ1bmN0aW9uKGEpe3ZhciB0
-LHMKaWYoYT09bnVsbHx8dHlwZW9mIGE9PSJzdHJpbmcifHx0eXBlb2YgYT09Im51bWJlciJ8fHR5cGVv
-ZiBhPT0iYm9vbGVhbiIpcmV0dXJuIGEKZWxzZSBpZihhIGluc3RhbmNlb2YgT2JqZWN0JiZILlI5KGEp
-KXJldHVybiBhCmVsc2UgaWYoYSBpbnN0YW5jZW9mIE9iamVjdCYmdS51LmIoYSkpcmV0dXJuIGEKZWxz
-ZSBpZihhIGluc3RhbmNlb2YgRGF0ZSl7dD1ILldZKGEuZ2V0VGltZSgpKQppZihNYXRoLmFicyh0KTw9
-ODY0ZTEzKXM9ITEKZWxzZSBzPSEwCmlmKHMpSC52aChQLnhZKCJEYXRlVGltZSBpcyBvdXRzaWRlIHZh
-bGlkIHJhbmdlOiAiK3QpKQpQLlVJKCExLCJpc1V0YyIsdS55KQpyZXR1cm4gbmV3IFAuaVAodCwhMSl9
-ZWxzZSBpZihhLmNvbnN0cnVjdG9yPT09JC5rSSgpKXJldHVybiBhLm8KZWxzZSByZXR1cm4gUC5ORChh
-KX0sCk5EOmZ1bmN0aW9uKGEpe2lmKHR5cGVvZiBhPT0iZnVuY3Rpb24iKXJldHVybiBQLmlRKGEsJC53
-USgpLG5ldyBQLk56KCkpCmlmKGEgaW5zdGFuY2VvZiBBcnJheSlyZXR1cm4gUC5pUShhLCQuQ3IoKSxu
-ZXcgUC5RUygpKQpyZXR1cm4gUC5pUShhLCQuQ3IoKSxuZXcgUC5ucCgpKX0sCmlROmZ1bmN0aW9uKGEs
-YixjKXt2YXIgdD1QLk9tKGEsYikKaWYodD09bnVsbHx8IShhIGluc3RhbmNlb2YgT2JqZWN0KSl7dD1j
-LiQxKGEpClAuRG0oYSxiLHQpfXJldHVybiB0fSwKUEM6ZnVuY3Rpb24gUEMoKXt9LAptdDpmdW5jdGlv
-biBtdChhKXt0aGlzLmE9YX0sCk56OmZ1bmN0aW9uIE56KCl7fSwKUVM6ZnVuY3Rpb24gUVMoKXt9LApu
-cDpmdW5jdGlvbiBucCgpe30sCkU0OmZ1bmN0aW9uIEU0KGEpe3RoaXMuYT1hfSwKcjc6ZnVuY3Rpb24g
-cjcoYSl7dGhpcy5hPWF9LApUejpmdW5jdGlvbiBUeihhLGIpe3RoaXMuYT1hCnRoaXMuJHRpPWJ9LApj
-bzpmdW5jdGlvbiBjbygpe30sCm5kOmZ1bmN0aW9uIG5kKCl7fSwKS2U6ZnVuY3Rpb24gS2UoYSl7dGhp
-cy5hPWF9LApkNTpmdW5jdGlvbiBkNSgpe30sCm42OmZ1bmN0aW9uIG42KCl7fX0sVz17CngzOmZ1bmN0
-aW9uKCl7cmV0dXJuIHdpbmRvd30sClpyOmZ1bmN0aW9uKCl7cmV0dXJuIGRvY3VtZW50fSwKSjY6ZnVu
-Y3Rpb24oYSl7dmFyIHQ9ZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgiYSIpCmlmKGEhPW51bGwpdC5ocmVm
-PWEKcmV0dXJuIHR9LApVOTpmdW5jdGlvbihhLGIsYyl7dmFyIHQ9ZG9jdW1lbnQuYm9keSxzPSh0JiZD
-LlJZKS5yNih0LGEsYixjKQpzLnRvU3RyaW5nCnQ9dS5hYwp0PW5ldyBILlU1KG5ldyBXLmU3KHMpLHQu
-QygiYTIobEQuRSkiKS5hKG5ldyBXLkN2KCkpLHQuQygiVTU8bEQuRT4iKSkKcmV0dXJuIHUuaC5hKHQu
-Z3I4KHQpKX0sCnJTOmZ1bmN0aW9uKGEpe3ZhciB0LHMscj0iZWxlbWVudCB0YWcgdW5hdmFpbGFibGUi
-CnRyeXt0PUouUkUoYSkKaWYodHlwZW9mIHQuZ25zKGEpPT0ic3RyaW5nIilyPXQuZ25zKGEpfWNhdGNo
-KHMpe0guUnUocyl9cmV0dXJuIHJ9LApDMDpmdW5jdGlvbihhLGIpe2E9NTM2ODcwOTExJmErYgphPTUz
-Njg3MDkxMSZhKygoNTI0Mjg3JmEpPDwxMCkKcmV0dXJuIGFeYT4+PjZ9LApyRTpmdW5jdGlvbihhLGIs
-YyxkKXt2YXIgdD1XLkMwKFcuQzAoVy5DMChXLkMwKDAsYSksYiksYyksZCkscz01MzY4NzA5MTEmdCso
-KDY3MTA4ODYzJnQpPDwzKQpzXj1zPj4+MTEKcmV0dXJuIDUzNjg3MDkxMSZzKygoMTYzODMmcyk8PDE1
-KX0sClROOmZ1bmN0aW9uKGEsYil7dmFyIHQscyxyPWEuY2xhc3NMaXN0CmZvcih0PWIubGVuZ3RoLHM9
-MDtzPGIubGVuZ3RoO2IubGVuZ3RoPT09dHx8KDAsSC5saykoYiksKytzKXIuYWRkKGJbc10pfSwKSkU6
-ZnVuY3Rpb24oYSxiLGMsZCxlKXt2YXIgdD1XLmFGKG5ldyBXLnZOKGMpLHUuQikKaWYodCE9bnVsbCYm
-ITApSi5kWihhLGIsdCwhMSkKcmV0dXJuIG5ldyBXLnhDKGEsYix0LCExLGUuQygieEM8MD4iKSl9LApU
-dzpmdW5jdGlvbihhKXt2YXIgdD1XLko2KG51bGwpLHM9d2luZG93LmxvY2F0aW9uCnQ9bmV3IFcuSlEo
-bmV3IFcubWsodCxzKSkKdC5DWShhKQpyZXR1cm4gdH0sCnFEOmZ1bmN0aW9uKGEsYixjLGQpe3UuaC5h
-KGEpCkguYyhiKQpILmMoYykKdS5PLmEoZCkKcmV0dXJuITB9LApRVzpmdW5jdGlvbihhLGIsYyxkKXt2
-YXIgdCxzLHIKdS5oLmEoYSkKSC5jKGIpCkguYyhjKQp0PXUuTy5hKGQpLmEKcz10LmEKcy5ocmVmPWMK
-cj1zLmhvc3RuYW1lCnQ9dC5iCmlmKCEocj09dC5ob3N0bmFtZSYmcy5wb3J0PT10LnBvcnQmJnMucHJv
-dG9jb2w9PXQucHJvdG9jb2wpKWlmKHI9PT0iIilpZihzLnBvcnQ9PT0iIil7dD1zLnByb3RvY29sCnQ9
-dD09PSI6Inx8dD09PSIifWVsc2UgdD0hMQplbHNlIHQ9ITEKZWxzZSB0PSEwCnJldHVybiB0fSwKQmw6
-ZnVuY3Rpb24oKXt2YXIgdD11Lk4scz1QLnRNKEMuUXgsdCkscj11LmRHLmEobmV3IFcuSUEoKSkscT1I
-LlZNKFsiVEVNUExBVEUiXSx1LnMpCnQ9bmV3IFcuY3QocyxQLkxzKHQpLFAuTHModCksUC5Mcyh0KSxu
-dWxsKQp0LkNZKG51bGwsbmV3IEgubEooQy5ReCxyLHUuZHYpLHEsbnVsbCkKcmV0dXJuIHR9LApQdjpm
-dW5jdGlvbihhKXtpZihhPT1udWxsKXJldHVybiBudWxsCnJldHVybiBXLlAxKGEpfSwKcWM6ZnVuY3Rp
-b24oYSl7dmFyIHQKaWYoYT09bnVsbClyZXR1cm4gbnVsbAppZigicG9zdE1lc3NhZ2UiIGluIGEpe3Q9
-Vy5QMShhKQppZih1LnIuYih0KSlyZXR1cm4gdApyZXR1cm4gbnVsbH1lbHNlIHJldHVybiB1LnIuYShh
-KX0sClAxOmZ1bmN0aW9uKGEpe2lmKGE9PT13aW5kb3cpcmV0dXJuIHUuY2kuYShhKQplbHNlIHJldHVy
-biBuZXcgVy5kVyhhKX0sCkhIOmZ1bmN0aW9uKGEpe2lmKGE9PT13aW5kb3cubG9jYXRpb24pcmV0dXJu
-IGEKZWxzZSByZXR1cm4gbmV3IFcuRmIoKX0sCmFGOmZ1bmN0aW9uKGEsYil7dmFyIHQ9JC5YMwppZih0
-PT09Qy5OVSlyZXR1cm4gYQpyZXR1cm4gdC5QeShhLGIpfSwKcUU6ZnVuY3Rpb24gcUUoKXt9LApHaDpm
-dW5jdGlvbiBHaCgpe30sCmZZOmZ1bmN0aW9uIGZZKCl7fSwKbkI6ZnVuY3Rpb24gbkIoKXt9LApBejpm
-dW5jdGlvbiBBeigpe30sClFQOmZ1bmN0aW9uIFFQKCl7fSwKbng6ZnVuY3Rpb24gbngoKXt9LApvSjpm
-dW5jdGlvbiBvSigpe30sCmlkOmZ1bmN0aW9uIGlkKCl7fSwKUUY6ZnVuY3Rpb24gUUYoKXt9LApOaDpm
-dW5jdGlvbiBOaCgpe30sCklCOmZ1bmN0aW9uIElCKCl7fSwKbjc6ZnVuY3Rpb24gbjcoKXt9LAp3ejpm
-dW5jdGlvbiB3eihhLGIpe3RoaXMuYT1hCnRoaXMuJHRpPWJ9LApjdjpmdW5jdGlvbiBjdigpe30sCkN2
-OmZ1bmN0aW9uIEN2KCl7fSwKZWE6ZnVuY3Rpb24gZWEoKXt9LApEMDpmdW5jdGlvbiBEMCgpe30sCmhI
-OmZ1bmN0aW9uIGhIKCl7fSwKaDQ6ZnVuY3Rpb24gaDQoKXt9LApicjpmdW5jdGlvbiBicigpe30sClZi
-OmZ1bmN0aW9uIFZiKCl7fSwKZko6ZnVuY3Rpb24gZkooKXt9LAp3YTpmdW5jdGlvbiB3YSgpe30sClNn
-OmZ1bmN0aW9uIFNnKCl7fSwKdTg6ZnVuY3Rpb24gdTgoKXt9LApPSzpmdW5jdGlvbiBPSygpe30sCmU3
-OmZ1bmN0aW9uIGU3KGEpe3RoaXMuYT1hfSwKdUg6ZnVuY3Rpb24gdUgoKXt9LApCSDpmdW5jdGlvbiBC
-SCgpe30sClNOOmZ1bmN0aW9uIFNOKCl7fSwKZXc6ZnVuY3Rpb24gZXcoKXt9LApscDpmdW5jdGlvbiBs
-cCgpe30sClRiOmZ1bmN0aW9uIFRiKCl7fSwKSXY6ZnVuY3Rpb24gSXYoKXt9LApXUDpmdW5jdGlvbiBX
-UCgpe30sCnlZOmZ1bmN0aW9uIHlZKCl7fSwKdzY6ZnVuY3Rpb24gdzYoKXt9LApLNTpmdW5jdGlvbiBL
-NSgpe30sCkNtOmZ1bmN0aW9uIENtKCl7fSwKQ1E6ZnVuY3Rpb24gQ1EoKXt9LAp3NDpmdW5jdGlvbiB3
-NCgpe30sCnJoOmZ1bmN0aW9uIHJoKCl7fSwKY2Y6ZnVuY3Rpb24gY2YoKXt9LAppNzpmdW5jdGlvbiBp
-NyhhKXt0aGlzLmE9YX0sClN5OmZ1bmN0aW9uIFN5KGEpe3RoaXMuYT1hfSwKS1M6ZnVuY3Rpb24gS1Mo
-YSxiKXt0aGlzLmE9YQp0aGlzLmI9Yn0sCkEzOmZ1bmN0aW9uIEEzKGEsYil7dGhpcy5hPWEKdGhpcy5i
-PWJ9LApJNDpmdW5jdGlvbiBJNChhKXt0aGlzLmE9YX0sCkZrOmZ1bmN0aW9uIEZrKGEsYil7dGhpcy5h
-PWEKdGhpcy4kdGk9Yn0sClJPOmZ1bmN0aW9uIFJPKGEsYixjLGQpe3ZhciBfPXRoaXMKXy5hPWEKXy5i
-PWIKXy5jPWMKXy4kdGk9ZH0sCmV1OmZ1bmN0aW9uIGV1KGEsYixjLGQpe3ZhciBfPXRoaXMKXy5hPWEK
-Xy5iPWIKXy5jPWMKXy4kdGk9ZH0sCnhDOmZ1bmN0aW9uIHhDKGEsYixjLGQsZSl7dmFyIF89dGhpcwpf
-LmI9YQpfLmM9YgpfLmQ9YwpfLmU9ZApfLiR0aT1lfSwKdk46ZnVuY3Rpb24gdk4oYSl7dGhpcy5hPWF9
-LApKUTpmdW5jdGlvbiBKUShhKXt0aGlzLmE9YX0sCkdtOmZ1bmN0aW9uIEdtKCl7fSwKdkQ6ZnVuY3Rp
-b24gdkQoYSl7dGhpcy5hPWF9LApVdjpmdW5jdGlvbiBVdihhKXt0aGlzLmE9YX0sCkVnOmZ1bmN0aW9u
-IEVnKGEsYixjKXt0aGlzLmE9YQp0aGlzLmI9Ygp0aGlzLmM9Y30sCm02OmZ1bmN0aW9uIG02KCl7fSwK
-RW86ZnVuY3Rpb24gRW8oKXt9LApXazpmdW5jdGlvbiBXaygpe30sCmN0OmZ1bmN0aW9uIGN0KGEsYixj
-LGQsZSl7dmFyIF89dGhpcwpfLmU9YQpfLmE9YgpfLmI9YwpfLmM9ZApfLmQ9ZX0sCklBOmZ1bmN0aW9u
-IElBKCl7fSwKT3c6ZnVuY3Rpb24gT3coKXt9LApXOTpmdW5jdGlvbiBXOShhLGIsYyl7dmFyIF89dGhp
-cwpfLmE9YQpfLmI9YgpfLmM9LTEKXy5kPW51bGwKXy4kdGk9Y30sCmRXOmZ1bmN0aW9uIGRXKGEpe3Ro
-aXMuYT1hfSwKRmI6ZnVuY3Rpb24gRmIoKXt9LAprRjpmdW5jdGlvbiBrRigpe30sCm1rOmZ1bmN0aW9u
-IG1rKGEsYil7dGhpcy5hPWEKdGhpcy5iPWJ9LApLbzpmdW5jdGlvbiBLbyhhKXt0aGlzLmE9YQp0aGlz
-LmI9ITF9LApmbTpmdW5jdGlvbiBmbShhKXt0aGlzLmE9YX0sCkxlOmZ1bmN0aW9uIExlKCl7fSwKSzc6
-ZnVuY3Rpb24gSzcoKXt9LApyQjpmdW5jdGlvbiByQigpe30sClhXOmZ1bmN0aW9uIFhXKCl7fSwKb2E6
-ZnVuY3Rpb24gb2EoKXt9fSxNPXsKT1g6ZnVuY3Rpb24oYSl7c3dpdGNoKGEpe2Nhc2UgQy5BZDpyZXR1
-cm4iQWRkIC8qPyovIGhpbnQiCmNhc2UgQy5uZTpyZXR1cm4iQWRkIC8qISovIGhpbnQiCmNhc2UgQy53
-VjpyZXR1cm4iUmVtb3ZlIC8qPyovIGhpbnQiCmNhc2UgQy5mUjpyZXR1cm4iUmVtb3ZlIC8qISovIGhp
-bnQiCmNhc2UgQy5teTpyZXR1cm4iQ2hhbmdlIHRvIC8qPyovIGhpbnQiCmNhc2UgQy5yeDpyZXR1cm4i
-Q2hhbmdlIHRvIC8qISovIGhpbnQifXJldHVybiBudWxsfSwKSDc6ZnVuY3Rpb24gSDcoYSxiKXt0aGlz
-LmE9YQp0aGlzLmI9Yn0sCllGOmZ1bmN0aW9uKGEsYil7dmFyIHQscyxyLHEscCxvLG4KZm9yKHQ9Yi5s
-ZW5ndGgscz0xO3M8dDsrK3Mpe2lmKGJbc109PW51bGx8fGJbcy0xXSE9bnVsbCljb250aW51ZQpmb3Io
-O3Q+PTE7dD1yKXtyPXQtMQppZihiW3JdIT1udWxsKWJyZWFrfXE9bmV3IFAuUm4oIiIpCnA9YSsiKCIK
-cS5hPXAKbz1ILnFDKGIsMCx0LEgudDYoYikuYykKbj1vLiR0aQpuPXArbmV3IEgubEoobyxuLkMoInFV
-KGFMLkUpIikuYShuZXcgTS5ObygpKSxuLkMoImxKPGFMLkUscVU+IikpLkgoMCwiLCAiKQpxLmE9bgpx
-LmE9bisoIik6IHBhcnQgIisocy0xKSsiIHdhcyBudWxsLCBidXQgcGFydCAiK3MrIiB3YXMgbm90LiIp
-CnRocm93IEguYihQLnhZKHEuWigwKSkpfX0sCmxJOmZ1bmN0aW9uIGxJKGEpe3RoaXMuYT1hfSwKTWk6
-ZnVuY3Rpb24gTWkoKXt9LApxNzpmdW5jdGlvbiBxNygpe30sCk5vOmZ1bmN0aW9uIE5vKCl7fX0sVT17
-Cm56OmZ1bmN0aW9uKGEpe3ZhciB0PUguV1koYS5xKDAsIm5vZGVJZCIpKQpyZXR1cm4gbmV3IFUuTEwo
-Qy5ObS5IdChDLnJrLG5ldyBVLk1EKGEpKSx0KX0sCkxMOmZ1bmN0aW9uIExMKGEsYil7dGhpcy5hPWEK
-dGhpcy5iPWJ9LApNRDpmdW5jdGlvbiBNRChhKXt0aGlzLmE9YX0sCmpmOmZ1bmN0aW9uKGEpe3ZhciB0
-LHMscixxCmlmKGE9PW51bGwpdD1udWxsCmVsc2V7dD1ILlZNKFtdLHUuZkEpCmZvcihzPUouSVQodS5S
-LmEoYSkpO3MuRigpOyl7cj1zLmdsKCkKcT1KLlU2KHIpCkMuTm0uaSh0LG5ldyBVLlNlKEguYyhxLnEo
-ciwiZGVzY3JpcHRpb24iKSksSC5jKHEucShyLCJocmVmIikpKSl9fXJldHVybiB0fSwKTmQ6ZnVuY3Rp
-b24oYSl7dmFyIHQscwppZihhPT1udWxsKXQ9bnVsbAplbHNle3Q9SC5WTShbXSx1LmhoKQpmb3Iocz1K
-LklUKHUuUi5hKGEpKTtzLkYoKTspQy5ObS5pKHQsVS5KaihzLmdsKCkpKX1yZXR1cm4gdH0sCkpqOmZ1
-bmN0aW9uKGEpe3ZhciB0PUouVTYoYSkscz1ILmModC5xKGEsImRlc2NyaXB0aW9uIikpLHI9SC5WTShb
-XSx1LmFKKQpmb3IodD1KLklUKHUuUi5hKHQucShhLCJlbnRyaWVzIikpKTt0LkYoKTspQy5ObS5pKHIs
-VS5Saih0LmdsKCkpKQpyZXR1cm4gbmV3IFUueUQocyxyKX0sClJqOmZ1bmN0aW9uKGEpe3ZhciB0LHM9
-Si5VNihhKSxyPUguYyhzLnEoYSwiZGVzY3JpcHRpb24iKSkscT1ILmMocy5xKGEsImZ1bmN0aW9uIikp
-LHA9cy5xKGEsImxpbmsiKQppZihwPT1udWxsKXA9bnVsbAplbHNle3Q9Si5VNihwKQpwPW5ldyBVLk1s
-KEguYyh0LnEocCwiaHJlZiIpKSxILldZKHQucShwLCJsaW5lIikpLEguYyh0LnEocCwicGF0aCIpKSl9
-cz11LmouYShzLnEoYSwiaGludEFjdGlvbnMiKSkKcz1zPT1udWxsP251bGw6Si5NMShzLG5ldyBVLmFO
-KCksdS5FKQpzPXM9PW51bGw/bnVsbDpzLmJyKDApCnJldHVybiBuZXcgVS53YihyLHEscCxzPT1udWxs
-P0MuZG46cyl9LApkMjpmdW5jdGlvbiBkMihhLGIsYyxkLGUsZil7dmFyIF89dGhpcwpfLmE9YQpfLmI9
-YgpfLmM9YwpfLmQ9ZApfLmU9ZQpfLmY9Zn0sClNlOmZ1bmN0aW9uIFNlKGEsYil7dGhpcy5hPWEKdGhp
-cy5iPWJ9LApNbDpmdW5jdGlvbiBNbChhLGIsYyl7dGhpcy5hPWEKdGhpcy5iPWIKdGhpcy5jPWN9LAp5
-RDpmdW5jdGlvbiB5RChhLGIpe3RoaXMuYT1hCnRoaXMuYj1ifSwKd2I6ZnVuY3Rpb24gd2IoYSxiLGMs
-ZCl7dmFyIF89dGhpcwpfLmE9YQpfLmI9YgpfLmM9YwpfLmQ9ZH0sCmFOOmZ1bmN0aW9uIGFOKCl7fSwK
-YjA6ZnVuY3Rpb24gYjAoKXt9fSxCPXsKWWY6ZnVuY3Rpb24oYSl7dmFyIHQscyxyLHEscCxvLG4sbSxs
-PUguYyhhLnEoMCwicmVnaW9ucyIpKSxrPUguYyhhLnEoMCwibmF2aWdhdGlvbkNvbnRlbnQiKSksaj1I
-LmMoYS5xKDAsInNvdXJjZUNvZGUiKSksaT1QLkZsKHUuTix1LmY0KQpmb3IodD11LlMuYShhLnEoMCwi
-ZWRpdHMiKSksdD10LmdQdSh0KSx0PXQuZ2t6KHQpLHM9dS5SLHI9dS5naTt0LkYoKTspe3E9dC5nbCgp
-CnA9cS5hCm89SC5WTShbXSxyKQpmb3IocT1KLklUKHMuYShxLmIpKTtxLkYoKTspe249cS5nbCgpCm09
-Si5VNihuKQpDLk5tLmkobyxuZXcgQi5qOChILldZKG0ucShuLCJsaW5lIikpLEguYyhtLnEobiwiZXhw
-bGFuYXRpb24iKSksSC5XWShtLnEobiwib2Zmc2V0IikpKSl9aS5ZKDAscCxvKX1yZXR1cm4gbmV3IEIu
-cXAobCxrLGosaSl9LApqODpmdW5jdGlvbiBqOChhLGIsYyl7dGhpcy5hPWEKdGhpcy5iPWIKdGhpcy5j
-PWN9LApxcDpmdW5jdGlvbiBxcChhLGIsYyxkKXt2YXIgXz10aGlzCl8uYT1hCl8uYj1iCl8uYz1jCl8u
-ZD1kfSwKZnY6ZnVuY3Rpb24gZnYoKXt9LApPUzpmdW5jdGlvbihhKXt2YXIgdAppZighKGE+PTY1JiZh
-PD05MCkpdD1hPj05NyYmYTw9MTIyCmVsc2UgdD0hMApyZXR1cm4gdH0sCll1OmZ1bmN0aW9uKGEsYil7
-dmFyIHQ9YS5sZW5ndGgscz1iKzIKaWYodDxzKXJldHVybiExCmlmKCFCLk9TKEMueEIubShhLGIpKSly
-ZXR1cm4hMQppZihDLnhCLm0oYSxiKzEpIT09NTgpcmV0dXJuITEKaWYodD09PXMpcmV0dXJuITAKcmV0
-dXJuIEMueEIubShhLHMpPT09NDd9fSxUPXttUTpmdW5jdGlvbiBtUSgpe319LEw9ewpJcTpmdW5jdGlv
-bigpe0MuQlouQihkb2N1bWVudCwiRE9NQ29udGVudExvYWRlZCIsbmV3IEwuZSgpKQpDLm9sLkIod2lu
-ZG93LCJwb3BzdGF0ZSIsbmV3IEwuTCgpKX0sCmt6OmZ1bmN0aW9uKGEpe3ZhciB0LHM9dS5oLmEoYS5w
-YXJlbnROb2RlKS5xdWVyeVNlbGVjdG9yKCI6c2NvcGUgPiB1bCIpLHI9cy5zdHlsZSxxPSIiK0MuQ0Qu
-elEocy5vZmZzZXRIZWlnaHQpKjIrInB4IgpyLm1heEhlaWdodD1xCnI9Si5xRihhKQpxPXIuJHRpCnQ9
-cS5DKCJ+KDEpIikuYShuZXcgTC5XeChzLGEpKQp1Lk0uYShudWxsKQpXLkpFKHIuYSxyLmIsdCwhMSxx
-LmMpfSwKeVg6ZnVuY3Rpb24oYSxiKXt2YXIgdCxzLHIscSxwLG89InF1ZXJ5U2VsZWN0b3JBbGwiLG49
-ZG9jdW1lbnQucXVlcnlTZWxlY3RvcihhKSxtPXUuaApuLnRvU3RyaW5nCkguRGgobSxtLCJUIixvKQp0
-PXUuVApzPW5ldyBXLnd6KG4ucXVlcnlTZWxlY3RvckFsbCgiLm5hdi1saW5rIiksdCkKcy5LKHMsbmV3
-IEwuQU8oYikpCkguRGgobSxtLCJUIixvKQpyPW5ldyBXLnd6KG4ucXVlcnlTZWxlY3RvckFsbCgiLnJl
-Z2lvbiIpLHQpCmlmKHIuZ0EocikhPT0wKXtxPW4ucXVlcnlTZWxlY3RvcigidGFibGVbZGF0YS1wYXRo
-XSIpCnEudG9TdHJpbmcKci5LKHIsbmV3IEwuSG8ocS5nZXRBdHRyaWJ1dGUoImRhdGEtIituZXcgVy5T
-eShuZXcgVy5pNyhxKSkuTygicGF0aCIpKSkpfUguRGgobSxtLCJUIixvKQpwPW5ldyBXLnd6KG4ucXVl
-cnlTZWxlY3RvckFsbCgiLmFkZC1oaW50LWxpbmsiKSx0KQpwLksocCxuZXcgTC5JQygpKX0sClE2OmZ1
-bmN0aW9uKGEsYixjKXt2YXIgdD1uZXcgWE1MSHR0cFJlcXVlc3QoKQpDLkR0LmVvKHQsIkdFVCIsTC5R
-NChhLGIpLCEwKQp0LnNldFJlcXVlc3RIZWFkZXIoIkNvbnRlbnQtVHlwZSIsImFwcGxpY2F0aW9uL2pz
-b247IGNoYXJzZXQ9VVRGLTgiKQpyZXR1cm4gTC5MVSh0LG51bGwsYyl9LAp0eTpmdW5jdGlvbihhLGIp
-e3ZhciB0PW5ldyBYTUxIdHRwUmVxdWVzdCgpLHM9dS5OCkMuRHQuZW8odCwiUE9TVCIsTC5RNChhLFAu
-RmwocyxzKSksITApCnQuc2V0UmVxdWVzdEhlYWRlcigiQ29udGVudC1UeXBlIiwiYXBwbGljYXRpb24v
-anNvbjsgY2hhcnNldD1VVEYtOCIpCnJldHVybiBMLkxVKHQsYix1LlMpfSwKTFU6ZnVuY3Rpb24oYSxi
-LGMpe3JldHVybiBMLlRnKGEsYixjLGMpfSwKVGc6ZnVuY3Rpb24oYSxiLGMsZCl7dmFyIHQ9MCxzPVAu
-RlgoZCkscixxPTIscCxvPVtdLG4sbSxsLGssaixpLGgsZwp2YXIgJGFzeW5jJExVPVAubHooZnVuY3Rp
-b24oZSxmKXtpZihlPT09MSl7cD1mCnQ9cX13aGlsZSh0cnVlKXN3aXRjaCh0KXtjYXNlIDA6aj1uZXcg
-UC5aZihuZXcgUC52cygkLlgzLHUuYW8pLHUuYmopCmk9dS5hbgpoPWkuYShuZXcgTC5mQyhqLGEpKQp1
-Lk0uYShudWxsKQptPXUuRgpXLkpFKGEsImxvYWQiLGgsITEsbSkKVy5KRShhLCJlcnJvciIsaS5hKGou
-Z1lKKCkpLCExLG0pCmEuc2VuZChiPT1udWxsP251bGw6Qy5DdC5PQihiLG51bGwpKQpxPTQKdD03CnJl
-dHVybiBQLmpRKGouYSwkYXN5bmMkTFUpCmNhc2UgNzpxPTIKdD02CmJyZWFrCmNhc2UgNDpxPTMKZz1w
-CkguUnUoZykKbj1ILnRzKGcpCmk9UC5UbCgiRXJyb3IgcmVhY2hpbmcgbWlncmF0aW9uIHByZXZpZXcg
-c2VydmVyLiIsbikKdGhyb3cgSC5iKGkpCnQ9NgpicmVhawpjYXNlIDM6dD0yCmJyZWFrCmNhc2UgNjpr
-PUMuQ3QucFcoMCxhLnJlc3BvbnNlVGV4dCxudWxsKQppZihhLnN0YXR1cz09PTIwMCl7cj1jLmEoaykK
-dD0xCmJyZWFrfWVsc2UgdGhyb3cgSC5iKGspCmNhc2UgMTpyZXR1cm4gUC55QyhyLHMpCmNhc2UgMjpy
-ZXR1cm4gUC5mMyhwLHMpfX0pCnJldHVybiBQLkRJKCRhc3luYyRMVSxzKX0sCmFLOmZ1bmN0aW9uKGEp
-e3ZhciB0PVAuaEsoYSkuZ2hZKCkucSgwLCJsaW5lIikKcmV0dXJuIHQ9PW51bGw/bnVsbDpILkhwKHQs
-bnVsbCl9LApHNjpmdW5jdGlvbihhKXt2YXIgdD1QLmhLKGEpLmdoWSgpLnEoMCwib2Zmc2V0IikKcmV0
-dXJuIHQ9PW51bGw/bnVsbDpILkhwKHQsbnVsbCl9LAppNjpmdW5jdGlvbihhKXtyZXR1cm4gTC5uVyh1
-LlYuYShhKSl9LApuVzpmdW5jdGlvbihhKXt2YXIgdD0wLHM9UC5GWCh1LnopLHI9MSxxLHA9W10sbyxu
-LG0sbCxrCnZhciAkYXN5bmMkaTY9UC5seihmdW5jdGlvbihiLGMpe2lmKGI9PT0xKXtxPWMKdD1yfXdo
-aWxlKHRydWUpc3dpdGNoKHQpe2Nhc2UgMDpsPXUuaC5hKFcucWMoYS5jdXJyZW50VGFyZ2V0KSkuZ2V0
-QXR0cmlidXRlKCJocmVmIikKYS5wcmV2ZW50RGVmYXVsdCgpCnI9Mwp0PTYKcmV0dXJuIFAualEoTC50
-eShsLG51bGwpLCRhc3luYyRpNikKY2FzZSA2OnUuYV8uYShKLkdyKFcuUHYoZG9jdW1lbnQuZGVmYXVs
-dFZpZXcpKSkucmVsb2FkKCkKcj0xCnQ9NQpicmVhawpjYXNlIDM6cj0yCms9cQpvPUguUnUoaykKbj1I
-LnRzKGspCkwuQzIoIkNvdWxkIG5vdCBhZGQvcmVtb3ZlIGhpbnQiLG8sbikKdD01CmJyZWFrCmNhc2Ug
-Mjp0PTEKYnJlYWsKY2FzZSA1OnJldHVybiBQLnlDKG51bGwscykKY2FzZSAxOnJldHVybiBQLmYzKHEs
-cyl9fSkKcmV0dXJuIFAuREkoJGFzeW5jJGk2LHMpfSwKQzI6ZnVuY3Rpb24oYSxiLGMpe3ZhciB0LHMs
-cj0iZXhjZXB0aW9uIixxPSJzdGFja1RyYWNlIixwPXUuUy5iKGIpJiZKLlJNKGIucSgwLCJzdWNjZXNz
-IiksITEpJiZILm9UKGIueDQocikpJiZILm9UKGIueDQocSkpLG89Si5pYShiKQppZihwKXt0PUguYyhv
-LnEoYixyKSkKYz1vLnEoYixxKX1lbHNlIHQ9by5aKGIpCnA9ZG9jdW1lbnQKcz1wLnF1ZXJ5U2VsZWN0
-b3IoIi5wb3B1cC1wYW5lIikKcy5xdWVyeVNlbGVjdG9yKCJoMiIpLmlubmVyVGV4dD1hCnMucXVlcnlT
-ZWxlY3RvcigicCIpLmlubmVyVGV4dD10CnMucXVlcnlTZWxlY3RvcigicHJlIikuaW5uZXJUZXh0PUou
-QWMoYykKbz11Lk4KdS5icS5hKHMucXVlcnlTZWxlY3RvcigiYS5ib3R0b20iKSkuaHJlZj1QLlhkKCJo
-dHRwcyIsImdpdGh1Yi5jb20iLCJkYXJ0LWxhbmcvc2RrL2lzc3Vlcy9uZXciLFAuRUYoWyJ0aXRsZSIs
-IkN1c3RvbWVyLXJlcG9ydGVkIGlzc3VlIHdpdGggTk5CRCBtaWdyYXRpb24gdG9vbDogIithLCJsYWJl
-bHMiLCJhcmVhLWFuYWx5emVyLGFuYWx5emVyLW5uYmQtbWlncmF0aW9uLHR5cGUtYnVnIiwiYm9keSIs
-YSsiXG5cbkVycm9yOiAiK0guZCh0KSsiXG5cblBsZWFzZSBmaWxsIGluIHRoZSBmb2xsb3dpbmc6XG5c
-bioqTmFtZSBvZiBwYWNrYWdlIGJlaW5nIG1pZ3JhdGVkIChpZiBwdWJsaWMpKio6XG4qKldoYXQgSSB3
-YXMgZG9pbmcgd2hlbiB0aGlzIGlzc3VlIG9jY3VycmVkKio6XG4qKklzIGl0IHBvc3NpYmxlIHRvIHdv
-cmsgYXJvdW5kIHRoaXMgaXNzdWUqKjpcbioqSGFzIHRoaXMgaXNzdWUgaGFwcGVuZWQgYmVmb3JlLCBh
-bmQgaWYgc28sIGhvdyBvZnRlbioqOlxuKipEYXJ0IFNESyB2ZXJzaW9uKio6ICIrSC5kKHAuZ2V0RWxl
-bWVudEJ5SWQoInNkay12ZXJzaW9uIikudGV4dENvbnRlbnQpKyJcbioqQWRkaXRpb25hbCBkZXRhaWxz
-Kio6XG5cblRoYW5rcyBmb3IgZmlsaW5nIVxuXG5TdGFja3RyYWNlOiBfYXV0byBwb3B1bGF0ZWQgYnkg
-bWlncmF0aW9uIHByZXZpZXcgdG9vbC5fXG5cbmBgYFxuIitILmQoYykrIlxuYGBgXG4iXSxvLG8pKS5a
-KDApCm89cy5zdHlsZQpvLmRpc3BsYXk9ImluaXRpYWwiCnA9YSsiOiAiK0guZChiKQp3aW5kb3cKaWYo
-dHlwZW9mIGNvbnNvbGUhPSJ1bmRlZmluZWQiKXdpbmRvdy5jb25zb2xlLmVycm9yKHApCndpbmRvdwpw
-PUguZChjKQppZih0eXBlb2YgY29uc29sZSE9InVuZGVmaW5lZCIpd2luZG93LmNvbnNvbGUuZXJyb3Io
-cCl9LAp0MjpmdW5jdGlvbihhLGIsYyl7dmFyIHQscyxyLHEscD17fSxvPXUuaC5hKFcucWMoYS5jdXJy
-ZW50VGFyZ2V0KSkKYS5wcmV2ZW50RGVmYXVsdCgpCnQ9cC5hPW8uZ2V0QXR0cmlidXRlKCJocmVmIikK
-cz1KLnpsKHQsIj8iKT9wLmE9Qy54Qi5Oaih0LDAsQy54Qi5PWSh0LCI/IikpOnQKcj1MLkc2KHQpCnE9
-TC5hSyh0KQppZihyIT1udWxsKUwuYWYocyxyLHEsYixuZXcgTC5uVChwLHIscSkpCmVsc2UgTC5hZihz
-LG51bGwsbnVsbCxiLG5ldyBMLkJaKHApKX0sCnZVOmZ1bmN0aW9uKCl7dmFyIHQ9ZG9jdW1lbnQscz11
-LmgKSC5EaChzLHMsIlQiLCJxdWVyeVNlbGVjdG9yQWxsIikKdD1uZXcgVy53eih0LnF1ZXJ5U2VsZWN0
-b3JBbGwoIi5jb2RlIiksdS5UKQp0LksodCxuZXcgTC5HSCgpKX0sCmhYOmZ1bmN0aW9uKGEsYixjKXty
-ZXR1cm4gTC5ZdyhhLGIsYyl9LApZdzpmdW5jdGlvbihhLGIsYyl7dmFyIHQ9MCxzPVAuRlgodS56KSxy
-PTEscSxwPVtdLG8sbixtLGwsayxqLGksaAp2YXIgJGFzeW5jJGhYPVAubHooZnVuY3Rpb24oZCxlKXtp
-ZihkPT09MSl7cT1lCnQ9cn13aGlsZSh0cnVlKXN3aXRjaCh0KXtjYXNlIDA6cj0zCms9dS5OCnQ9Ngpy
-ZXR1cm4gUC5qUShMLlE2KGEsUC5FRihbInJlZ2lvbiIsInJlZ2lvbiIsIm9mZnNldCIsSC5kKGIpXSxr
-LGspLHUuUyksJGFzeW5jJGhYKQpjYXNlIDY6bz1lCms9bwpqPUouVTYoaykKbj1uZXcgVS5kMihVLmpm
-KGoucShrLCJlZGl0cyIpKSxILmMoai5xKGssImV4cGxhbmF0aW9uIikpLEguV1koai5xKGssImxpbmUi
-KSksSC5jKGoucShrLCJkaXNwbGF5UGF0aCIpKSxILmMoai5xKGssInVyaVBhdGgiKSksVS5OZChqLnEo
-aywidHJhY2VzIikpKQpMLlQxKG4pCkwuRnIoYSxiLGMpCkwueVgoIi5lZGl0LXBhbmVsIC5wYW5lbC1j
-b250ZW50IiwhMSkKcj0xCnQ9NQpicmVhawpjYXNlIDM6cj0yCmg9cQptPUguUnUoaCkKbD1ILnRzKGgp
-CkwuQzIoIkNvdWxkIG5vdCBsb2FkIGVkaXQgZGV0YWlscyIsbSxsKQp0PTUKYnJlYWsKY2FzZSAyOnQ9
-MQpicmVhawpjYXNlIDU6cmV0dXJuIFAueUMobnVsbCxzKQpjYXNlIDE6cmV0dXJuIFAuZjMocSxzKX19
-KQpyZXR1cm4gUC5ESSgkYXN5bmMkaFgscyl9LApHNzpmdW5jdGlvbihhLGIsYyxkLGUpe3JldHVybiBM
-Lkw1KGEsYixjLGQsZSl9LApMNTpmdW5jdGlvbihhLGIsYyxkLGUpe3ZhciB0PTAscz1QLkZYKHUueiks
-cixxPTIscCxvPVtdLG4sbSxsLGssaixpLGgKdmFyICRhc3luYyRHNz1QLmx6KGZ1bmN0aW9uKGYsZyl7
-aWYoZj09PTEpe3A9Zwp0PXF9d2hpbGUodHJ1ZSlzd2l0Y2godCl7Y2FzZSAwOmlmKCFKLnJZKGEpLlRj
-KGEsIi5kYXJ0Iikpe0wuQkUoYSxuZXcgQi5xcCgiIiwiIiwiIixDLkNNKSxkKQpMLkJYKGEsbnVsbCkK
-aWYoZSE9bnVsbCllLiQwKCkKdD0xCmJyZWFrfXE9NApqPXUuTgp0PTcKcmV0dXJuIFAualEoTC5RNihh
-LFAuRUYoWyJpbmxpbmUiLCJ0cnVlIl0saixqKSx1LlMpLCRhc3luYyRHNykKY2FzZSA3Om49ZwpMLkJF
-KGEsQi5ZZihuKSxkKQpMLmZHKGIsYykKbT1DLnhCLnRnKGEsIj8iKT9DLnhCLk5qKGEsMCxDLnhCLk9Z
-KGEsIj8iKSk6YQpMLkJYKG0sYikKaWYoZSE9bnVsbCllLiQwKCkKcT0yCnQ9NgpicmVhawpjYXNlIDQ6
-cT0zCmg9cApsPUguUnUoaCkKaz1ILnRzKGgpCkwuQzIoIkNvdWxkIG5vdCBsb2FkIGRhcnQgZmlsZSAi
-K2EsbCxrKQp0PTYKYnJlYWsKY2FzZSAzOnQ9MgpicmVhawpjYXNlIDY6Y2FzZSAxOnJldHVybiBQLnlD
-KHIscykKY2FzZSAyOnJldHVybiBQLmYzKHAscyl9fSkKcmV0dXJuIFAuREkoJGFzeW5jJEc3LHMpfSwK
-R2U6ZnVuY3Rpb24oKXt2YXIgdD0wLHM9UC5GWCh1LnopLHI9MSxxLHA9W10sbyxuLG0sbCxrLGosaQp2
-YXIgJGFzeW5jJEdlPVAubHooZnVuY3Rpb24oYSxiKXtpZihhPT09MSl7cT1iCnQ9cn13aGlsZSh0cnVl
-KXN3aXRjaCh0KXtjYXNlIDA6aj0iL19wcmV2aWV3L25hdmlnYXRpb25UcmVlLmpzb24iCnI9Mwp0PTYK
-cmV0dXJuIFAualEoTC5RNihqLEMuV08sdS5ldyksJGFzeW5jJEdlKQpjYXNlIDY6bz1iCm49ZG9jdW1l
-bnQucXVlcnlTZWxlY3RvcigiLm5hdi10cmVlIikKSi5sNShuLCIiKQpMLnRYKG4sTC5tSyhvKSkKcj0x
-CnQ9NQpicmVhawpjYXNlIDM6cj0yCmk9cQptPUguUnUoaSkKbD1ILnRzKGkpCkwuQzIoIkNvdWxkIG5v
-dCBsb2FkIG5hdmlnYXRpb24gdHJlZSIsbSxsKQp0PTUKYnJlYWsKY2FzZSAyOnQ9MQpicmVhawpjYXNl
-IDU6cmV0dXJuIFAueUMobnVsbCxzKQpjYXNlIDE6cmV0dXJuIFAuZjMocSxzKX19KQpyZXR1cm4gUC5E
-SSgkYXN5bmMkR2Uscyl9LApxTzpmdW5jdGlvbihhKXt2YXIgdCxzPWEuZ2V0Qm91bmRpbmdDbGllbnRS
-ZWN0KCkscj1DLkNELnpRKCQuZmkoKS5vZmZzZXRIZWlnaHQpLHE9d2luZG93LmlubmVySGVpZ2h0LHA9
-Qy5DRC56USgkLkRXKCkub2Zmc2V0SGVpZ2h0KQppZih0eXBlb2YgcSE9PSJudW1iZXIiKXJldHVybiBx
-LkhOKCkKdD1zLmJvdHRvbQppZih0eXBlb2YgdCE9PSJudW1iZXIiKXJldHVybiB0Lm9zKCkKaWYodD5x
-LShwKzE0KSlKLmRoKGEpCmVsc2V7cT1zLnRvcAppZih0eXBlb2YgcSE9PSJudW1iZXIiKXJldHVybiBx
-LkooKQppZihxPHIrMTQpSi5kaChhKX19LApmRzpmdW5jdGlvbihhLGIpe3ZhciB0LHMscixxLHAKaWYo
-YSE9bnVsbCl7dD1kb2N1bWVudApzPXQuZ2V0RWxlbWVudEJ5SWQoIm8iK0guZChhKSkKcj10LnF1ZXJ5
-U2VsZWN0b3IoIi5saW5lLSIrSC5kKGIpKQppZihzIT1udWxsKXtMLnFPKHMpCkouZFIocykuaSgwLCJ0
-YXJnZXQiKX1lbHNlIGlmKHIhPW51bGwpTC5xTyhyLnBhcmVudEVsZW1lbnQpCmlmKHIhPW51bGwpSi5k
-Uih1LmguYShyLnBhcmVudE5vZGUpKS5pKDAsImhpZ2hsaWdodCIpfWVsc2V7dD1kb2N1bWVudApxPXUu
-aApILkRoKHEscSwiVCIsInF1ZXJ5U2VsZWN0b3JBbGwiKQp0PXQucXVlcnlTZWxlY3RvckFsbCgiLmxp
-bmUtbm8iKQpwPW5ldyBXLnd6KHQsdS5UKQppZihwLmdBKHApPT09MClyZXR1cm4KTC5xTyhxLmEoQy50
-NS5ndEgodCkpKX19LAphZjpmdW5jdGlvbihhLGIsYyxkLGUpe3ZhciB0LHMscj1MLkc2KHdpbmRvdy5s
-b2NhdGlvbi5ocmVmKSxxPUwuYUsod2luZG93LmxvY2F0aW9uLmhyZWYpCmlmKHIhPW51bGwpe3Q9ZG9j
-dW1lbnQuZ2V0RWxlbWVudEJ5SWQoIm8iK0guZChyKSkKaWYodCE9bnVsbClKLmRSKHQpLlIoMCwidGFy
-Z2V0Iil9aWYocSE9bnVsbCl7cz1kb2N1bWVudC5xdWVyeVNlbGVjdG9yKCIubGluZS0iK0guZChxKSkK
-aWYocyE9bnVsbClKLmRSKHMucGFyZW50RWxlbWVudCkuUigwLCJoaWdobGlnaHQiKX1pZihhPT13aW5k
-b3cubG9jYXRpb24ucGF0aG5hbWUpe0wuZkcoYixjKQplLiQwKCl9ZWxzZSBMLkc3KGEsYixjLGQsZSl9
-LApRNDpmdW5jdGlvbihhLGIpe3ZhciB0LHMscj1QLmhLKGEpLHE9dS5OCnE9UC5GbChxLHEpCmZvcih0
-PXIuZ2hZKCksdD10LmdQdSh0KSx0PXQuZ2t6KHQpO3QuRigpOyl7cz10LmdsKCkKcS5ZKDAscy5hLHMu
-Yil9Zm9yKHQ9Yi5nUHUoYiksdD10Lmdreih0KTt0LkYoKTspe3M9dC5nbCgpCnEuWSgwLHMuYSxzLmIp
-fXEuWSgwLCJhdXRoVG9rZW4iLCQuVUUoKSkKcmV0dXJuIHIubm0oMCxxKS5aKDApfSwKVDE6ZnVuY3Rp
-b24oYSl7dmFyIHQscyxyLHEscCxvLG4sbSxsLGs9JC5oTCgpCkoubDUoaywiIikKaWYoYT09bnVsbCl7
-dD1kb2N1bWVudC5jcmVhdGVFbGVtZW50KCJwIikKdC50ZXh0Q29udGVudD0iU2VlIGRldGFpbHMgYWJv
-dXQgYSBwcm9wb3NlZCBlZGl0LiIKQy5MdC5zUCh0LEguVk0oWyJwbGFjZWhvbGRlciJdLHUucykpCmsu
-YXBwZW5kQ2hpbGQodCkKQy5MdC5GRih0KQpyZXR1cm59cz1hLmQKcj0kLm5VKCkKcT1yLnpmKHMpCnA9
-YS5iCm89ZG9jdW1lbnQKbj1yLkhQKHMsSi5UMChvLnF1ZXJ5U2VsZWN0b3IoIi5yb290IikudGV4dENv
-bnRlbnQpKQptPWEuYwpsPW8uY3JlYXRlRWxlbWVudCgicCIpCmsuYXBwZW5kQ2hpbGQobCkKbC5hcHBl
-bmRDaGlsZChvLmNyZWF0ZVRleHROb2RlKEguZChwKSsiIGF0ICIpKQpyPXUuTgpyPVcuSjYoTC5RNChh
-LmUsUC5FRihbImxpbmUiLEouQWMobSldLHIscikpKQpyLmFwcGVuZENoaWxkKG8uY3JlYXRlVGV4dE5v
-ZGUoSC5kKG4pKyI6IitILmQobSkrIi4iKSkKbC5hcHBlbmRDaGlsZChyKQpKLmRoKGwpCkwuQ0MoYSxr
-LHEpCkwuRnooYSxrKX0sCkxIOmZ1bmN0aW9uKGEsYixjKXt2YXIgdCxzLHIscSxwLG8sbixtLGwsayxq
-LGksaCxnPSQueVAoKQpKLmw1KGcsIiIpCmlmKGIuZ0EoYik9PT0wKXt0PWRvY3VtZW50CnM9dC5jcmVh
-dGVFbGVtZW50KCJwIikKZy5hcHBlbmRDaGlsZChzKQpzLmFwcGVuZENoaWxkKHQuY3JlYXRlVGV4dE5v
-ZGUoIk5vIHByb3Bvc2VkIGVkaXRzIikpfWVsc2UgZm9yKGc9Yi5nUHUoYiksZz1nLmdreihnKSx0PXUu
-USxyPXQuQygifigxKSIpLHE9dS5NLHQ9dC5jO2cuRigpOyl7cD1nLmdsKCkKbz1kb2N1bWVudApzPW8u
-Y3JlYXRlRWxlbWVudCgicCIpCm49JC55UCgpCm4uYXBwZW5kQ2hpbGQocykKcy5hcHBlbmRDaGlsZChv
-LmNyZWF0ZVRleHROb2RlKEguZChwLmEpKyI6IikpCm09by5jcmVhdGVFbGVtZW50KCJ1bCIpCm4uYXBw
-ZW5kQ2hpbGQobSkKZm9yKHA9Si5JVChwLmIpO3AuRigpOyl7bj1wLmdsKCkKbD1vLmNyZWF0ZUVsZW1l
-bnQoImxpIikKbS5hcHBlbmRDaGlsZChsKQpKLmRSKGwpLmkoMCwiZWRpdCIpCms9by5jcmVhdGVFbGVt
-ZW50KCJhIikKbC5hcHBlbmRDaGlsZChrKQprLmNsYXNzTGlzdC5hZGQoImVkaXQtbGluayIpCmo9bi5j
-Cmk9SC5kKGopCmsuc2V0QXR0cmlidXRlKCJkYXRhLSIrbmV3IFcuU3kobmV3IFcuaTcoaykpLk8oIm9m
-ZnNldCIpLGkpCmg9bi5hCmk9SC5kKGgpCmsuc2V0QXR0cmlidXRlKCJkYXRhLSIrbmV3IFcuU3kobmV3
-IFcuaTcoaykpLk8oImxpbmUiKSxpKQprLmFwcGVuZENoaWxkKG8uY3JlYXRlVGV4dE5vZGUoImxpbmUg
-IitILmQoaCkpKQppPXIuYShuZXcgTC5FRShqLGgsYSkpCnEuYShudWxsKQpXLkpFKGssImNsaWNrIixp
-LCExLHQpCmwuYXBwZW5kQ2hpbGQoby5jcmVhdGVUZXh0Tm9kZSgiOiAiK0guZChuLmIpKSl9fWlmKGMp
-TC5UMShudWxsKX0sCkZyOmZ1bmN0aW9uKGEsYixjKXt2YXIgdCxzLHI9d2luZG93LmxvY2F0aW9uLHE9
-UC5oSygociYmQy5FeCkuZ0RyKHIpK0guZChhKSkKcj11Lk4Kcj1QLkZsKHIscikKaWYoYiE9bnVsbCly
-LlkoMCwib2Zmc2V0IixILmQoYikpCmlmKGMhPW51bGwpci5ZKDAsImxpbmUiLEguZChjKSkKci5ZKDAs
-ImF1dGhUb2tlbiIsJC5VRSgpKQpxPXEubm0oMCxyKQpyPXdpbmRvdy5oaXN0b3J5CnQ9dS56CnM9cS5a
-KDApCnIudG9TdHJpbmcKci5wdXNoU3RhdGUobmV3IFAuQmYoW10sW10pLlB2KFAuRmwodCx0KSksIiIs
-cyl9LApFbjpmdW5jdGlvbihhKXt2YXIgdD1KLm0oZG9jdW1lbnQucXVlcnlTZWxlY3RvcigiLnJvb3Qi
-KS50ZXh0Q29udGVudCwiLyIpCmlmKEMueEIubihhLHQpKXJldHVybiBDLnhCLkcoYSx0Lmxlbmd0aCkK
-ZWxzZSByZXR1cm4gYX0sCkJYOmZ1bmN0aW9uKGEsYil7dmFyIHQscyxyPXt9CnIuYT1hCmE9TC5Fbihh
-KQpyLmE9YQokLkQ5KCkudGV4dENvbnRlbnQ9YQp0PWRvY3VtZW50CnM9dS5oCkguRGgocyxzLCJUIiwi
-cXVlcnlTZWxlY3RvckFsbCIpCnQ9bmV3IFcud3oodC5xdWVyeVNlbGVjdG9yQWxsKCIubmF2LXBhbmVs
-IC5uYXYtbGluayIpLHUuVCkKdC5LKHQsbmV3IEwuVlMocikpfSwKQkU6ZnVuY3Rpb24oYSxiLGMpe3Zh
-ciB0PSIucmVnaW9ucyIscz1kb2N1bWVudCxyPXMucXVlcnlTZWxlY3Rvcih0KSxxPXMucXVlcnlTZWxl
-Y3RvcigiLmNvZGUiKQpKLnRIKHIsYi5hLCQuS0coKSkKSi50SChxLGIuYiwkLktHKCkpCkwuTEgoYSxi
-LmQsYykKTC52VSgpCkwueVgoIi5jb2RlIiwhMCkKTC55WCh0LCEwKX0sCnRYOmZ1bmN0aW9uKGEsYil7
-dmFyIHQscyxyLHEscCxvLG4sbSxsLGssaixpLGgsZz1kb2N1bWVudCxmPWcuY3JlYXRlRWxlbWVudCgi
-dWwiKQphLmFwcGVuZENoaWxkKGYpCmZvcih0PWIubGVuZ3RoLHM9dS5NLHI9MDtyPGIubGVuZ3RoO2Iu
-bGVuZ3RoPT09dHx8KDAsSC5saykoYiksKytyKXtxPWJbcl0KcD1nLmNyZWF0ZUVsZW1lbnQoImxpIikK
-Zi5hcHBlbmRDaGlsZChwKQpvPUouUkUocCkKaWYocS5hPT09Qy5ZMil7by5nUChwKS5pKDAsImRpciIp
-Cm49Zy5jcmVhdGVFbGVtZW50KCJzcGFuIikKcC5hcHBlbmRDaGlsZChuKQpvPUouUkUobikKby5nUChu
-KS5pKDAsImFycm93IikKby5zaGYobiwiJiN4MjVCQzsiKQptPWcuY3JlYXRlRWxlbWVudCgic3BhbiIp
-CnAuYXBwZW5kQ2hpbGQobSkKSi5sNShtLCImI3gxRjRDMTsiKQpwLmFwcGVuZENoaWxkKGcuY3JlYXRl
-VGV4dE5vZGUocS5iKSkKTC50WChwLHEuYykKTC5reihuKX1lbHNle28uc2hmKHAsIiYjeDFGNEM0OyIp
-Cmw9Zy5jcmVhdGVFbGVtZW50KCJhIikKcC5hcHBlbmRDaGlsZChsKQpvPUouUkUobCkKby5nUChsKS5p
-KDAsIm5hdi1saW5rIikKbC5zZXRBdHRyaWJ1dGUoImRhdGEtIituZXcgVy5TeShuZXcgVy5pNyhsKSku
-TygibmFtZSIpLHEuZCkKbC5zZXRBdHRyaWJ1dGUoImhyZWYiLHEuZSkKbC5hcHBlbmRDaGlsZChnLmNy
-ZWF0ZVRleHROb2RlKHEuYikpCm89by5nVmwobCkKaz1vLiR0aQpqPWsuQygifigxKSIpLmEobmV3IEwu
-VEQoKSkKcy5hKG51bGwpClcuSkUoby5hLG8uYixqLCExLGsuYykKaT1xLmYKaWYodHlwZW9mIGkhPT0i
-bnVtYmVyIilyZXR1cm4gaS5vcygpCmlmKGk+MCl7aD1nLmNyZWF0ZUVsZW1lbnQoInNwYW4iKQpwLmFw
-cGVuZENoaWxkKGgpCkouZFIoaCkuaSgwLCJlZGl0LWNvdW50IikKbz0iIitpKyIgIgppZihpPT09MSlr
-PSJlZGl0IgplbHNlIGs9ImVkaXRzIgpoLnNldEF0dHJpYnV0ZSgidGl0bGUiLG8raykKaC5hcHBlbmRD
-aGlsZChnLmNyZWF0ZVRleHROb2RlKEMuam4uWihpKSkpfX19fSwKRno6ZnVuY3Rpb24oYSxiKXt2YXIg
-dCxzLHIscSxwLG8sbixtLGwsayxqLGk9YS5hCmlmKGk9PW51bGwpcmV0dXJuCnQ9ZG9jdW1lbnQKcz10
-LmNyZWF0ZUVsZW1lbnQoInAiKQpyPWIuYXBwZW5kQ2hpbGQocykKcz10LmNyZWF0ZUVsZW1lbnQoInNw
-YW4iKQpxPXUucwpKLk11KHMsSC5WTShbInR5cGUtZGVzY3JpcHRpb24iXSxxKSkKcy5hcHBlbmRDaGls
-ZCh0LmNyZWF0ZVRleHROb2RlKCJBY3Rpb25zIikpCnIuYXBwZW5kQ2hpbGQocykKci5hcHBlbmRDaGls
-ZCh0LmNyZWF0ZVRleHROb2RlKCI6IikpCnA9dC5jcmVhdGVFbGVtZW50KCJwIikKYi5hcHBlbmRDaGls
-ZChwKQpmb3Iocz1pLmxlbmd0aCxvPXUuWCxuPTA7bjxpLmxlbmd0aDtpLmxlbmd0aD09PXN8fCgwLEgu
-bGspKGkpLCsrbil7bT1pW25dCmw9dC5jcmVhdGVFbGVtZW50KCJhIikKcC5hcHBlbmRDaGlsZChsKQps
-LmFwcGVuZENoaWxkKHQuY3JlYXRlVGV4dE5vZGUobS5hKSkKbC5zZXRBdHRyaWJ1dGUoImhyZWYiLG0u
-YikKaz1vLmEoSC5WTShbImFkZC1oaW50LWxpbmsiLCJiZWZvcmUtYXBwbHkiLCJidXR0b24iXSxxKSkK
-aj1KLmRSKGwpCmouVjEoMCkKai5GVigwLGspfX0sCkNDOmZ1bmN0aW9uKGE4LGE5LGIwKXt2YXIgdCxz
-LHIscSxwLG8sbixtLGwsayxqLGksaCxnLGYsZSxkLGMsYixhLGEwLGExLGEyLGEzLGE0LGE1LGE2LGE3
-CmZvcih0PWE4LmYscz10Lmxlbmd0aCxyPXUucyxxPXUuWCxwPXUuUSxvPXAuQygifigxKSIpLG49dS5N
-LHA9cC5jLG09MDttPHQubGVuZ3RoO3QubGVuZ3RoPT09c3x8KDAsSC5saykodCksKyttKXtsPXRbbV0K
-az1kb2N1bWVudApqPWsuY3JlYXRlRWxlbWVudCgicCIpCmk9cS5hKEguVk0oWyJ0cmFjZSJdLHIpKQpo
-PUouZFIoaikKaC5WMSgwKQpoLkZWKDAsaSkKZz1hOS5hcHBlbmRDaGlsZChqKQpqPWsuY3JlYXRlRWxl
-bWVudCgic3BhbiIpCmk9cS5hKEguVk0oWyJ0eXBlLWRlc2NyaXB0aW9uIl0scikpCmg9Si5kUihqKQpo
-LlYxKDApCmguRlYoMCxpKQpqLmFwcGVuZENoaWxkKGsuY3JlYXRlVGV4dE5vZGUobC5hKSkKZy5hcHBl
-bmRDaGlsZChqKQpnLmFwcGVuZENoaWxkKGsuY3JlYXRlVGV4dE5vZGUoIjoiKSkKaj1rLmNyZWF0ZUVs
-ZW1lbnQoInVsIikKaT1xLmEoSC5WTShbInRyYWNlIl0scikpCmg9Si5kUihqKQpoLlYxKDApCmguRlYo
-MCxpKQpmPWcuYXBwZW5kQ2hpbGQoaikKZm9yKGo9bC5iLGk9ai5sZW5ndGgsZT0wO2U8ai5sZW5ndGg7
-ai5sZW5ndGg9PT1pfHwoMCxILmxrKShqKSwrK2Upe2Q9altlXQpjPWsuY3JlYXRlRWxlbWVudCgibGki
-KQpmLmFwcGVuZENoaWxkKGMpCmI9ay5jcmVhdGVFbGVtZW50KCJzcGFuIikKYT1xLmEoSC5WTShbImZ1
-bmN0aW9uIl0scikpCmg9Si5kUihiKQpoLlYxKDApCmguRlYoMCxhKQphPWQuYgpMLmtEKGIsYT09bnVs
-bD8idW5rbm93biI6YSkKYy5hcHBlbmRDaGlsZChiKQphMD1kLmMKaWYoYTAhPW51bGwpe2MuYXBwZW5k
-Q2hpbGQoay5jcmVhdGVUZXh0Tm9kZSgiICgiKSkKYTE9YTAuYgphMj1rLmNyZWF0ZUVsZW1lbnQoImEi
-KQphMi5hcHBlbmRDaGlsZChrLmNyZWF0ZVRleHROb2RlKEguZChhMC5jKSsiOiIrSC5kKGExKSkpCmEy
-LnNldEF0dHJpYnV0ZSgiaHJlZiIsYTAuYSkKYTIuY2xhc3NMaXN0LmFkZCgibmF2LWxpbmsiKQpjLmFw
-cGVuZENoaWxkKGEyKQpjLmFwcGVuZENoaWxkKGsuY3JlYXRlVGV4dE5vZGUoIikiKSl9Yy5hcHBlbmRD
-aGlsZChrLmNyZWF0ZVRleHROb2RlKCI6ICIpKQpiPWQuYQpMLmtEKGMsYj09bnVsbD8idW5rbm93biI6
-YikKYj1kLmQKaWYoYi5sZW5ndGghPT0wKXthPWsuY3JlYXRlRWxlbWVudCgicCIpCmEzPXEuYShILlZN
-KFsiZHJhd2VyIiwiYmVmb3JlLWFwcGx5Il0scikpCmg9Si5kUihhKQpoLlYxKDApCmguRlYoMCxhMykK
-YTQ9Yy5hcHBlbmRDaGlsZChhKQpmb3IoYT1iLmxlbmd0aCxhNT0wO2E1PGIubGVuZ3RoO2IubGVuZ3Ro
-PT09YXx8KDAsSC5saykoYiksKythNSl7YTY9YlthNV0KYTM9ay5jcmVhdGVFbGVtZW50KCJidXR0b24i
-KQphNz1vLmEobmV3IEwuQVMoYTYsYTApKQpuLmEobnVsbCkKVy5KRShhMywiY2xpY2siLGE3LCExLHAp
-CmEzLmFwcGVuZENoaWxkKGsuY3JlYXRlVGV4dE5vZGUoTS5PWChhNi5hKSkpCmE0LmFwcGVuZENoaWxk
-KGEzKX19fX19LAprRDpmdW5jdGlvbihhLGIpe3ZhciB0LHMscj1ILlZNKGIuc3BsaXQoIi4iKSx1LnMp
-LHE9Qy5ObS5ndEgocikscD1kb2N1bWVudAphLmFwcGVuZENoaWxkKHAuY3JlYXRlVGV4dE5vZGUocSkp
-CmZvcihxPUgucUMociwxLG51bGwsdS5OKSxxPW5ldyBILmE3KHEscS5nQShxKSxxLiR0aS5DKCJhNzxh
-TC5FPiIpKSx0PUouUkUoYSk7cS5GKCk7KXtzPXEuZAp0Lm56KGEsImJlZm9yZWVuZCIsIiYjODIwMzsu
-IixudWxsLG51bGwpCmEuYXBwZW5kQ2hpbGQocC5jcmVhdGVUZXh0Tm9kZShzKSl9fSwKZTpmdW5jdGlv
-biBlKCl7fSwKVlc6ZnVuY3Rpb24gVlcoYSxiLGMpe3RoaXMuYT1hCnRoaXMuYj1iCnRoaXMuYz1jfSwK
-b1o6ZnVuY3Rpb24gb1ooKXt9LApqcjpmdW5jdGlvbiBqcigpe30sCnFsOmZ1bmN0aW9uIHFsKCl7fSwK
-eTg6ZnVuY3Rpb24geTgoKXt9LApIaTpmdW5jdGlvbiBIaSgpe30sCkJUOmZ1bmN0aW9uIEJUKCl7fSwK
-TDpmdW5jdGlvbiBMKCl7fSwKV3g6ZnVuY3Rpb24gV3goYSxiKXt0aGlzLmE9YQp0aGlzLmI9Yn0sCkFP
-OmZ1bmN0aW9uIEFPKGEpe3RoaXMuYT1hfSwKZE46ZnVuY3Rpb24gZE4oYSl7dGhpcy5hPWF9LApIbzpm
-dW5jdGlvbiBIbyhhKXt0aGlzLmE9YX0sCnh6OmZ1bmN0aW9uIHh6KGEsYil7dGhpcy5hPWEKdGhpcy5i
-PWJ9LApJQzpmdW5jdGlvbiBJQygpe30sCmZDOmZ1bmN0aW9uIGZDKGEsYil7dGhpcy5hPWEKdGhpcy5i
-PWJ9LApuVDpmdW5jdGlvbiBuVChhLGIsYyl7dGhpcy5hPWEKdGhpcy5iPWIKdGhpcy5jPWN9LApCWjpm
-dW5jdGlvbiBCWihhKXt0aGlzLmE9YX0sCkdIOmZ1bmN0aW9uIEdIKCl7fSwKRUU6ZnVuY3Rpb24gRUUo
-YSxiLGMpe3RoaXMuYT1hCnRoaXMuYj1iCnRoaXMuYz1jfSwKUUw6ZnVuY3Rpb24gUUwoYSxiKXt0aGlz
-LmE9YQp0aGlzLmI9Yn0sClZTOmZ1bmN0aW9uIFZTKGEpe3RoaXMuYT1hfSwKVEQ6ZnVuY3Rpb24gVEQo
-KXt9LApBUzpmdW5jdGlvbiBBUyhhLGIpe3RoaXMuYT1hCnRoaXMuYj1ifSwKWEE6ZnVuY3Rpb24gWEEo
-KXt9LAptSzpmdW5jdGlvbihhKXt2YXIgdCxzLHIscSxwLG8sbj1ILlZNKFtdLHUuZmgpCmZvcih0PUou
-SVQodS5SLmEoYSkpO3QuRigpOyl7cz10LmdsKCkKcj1KLlU2KHMpCnE9TC5wMihILmMoci5xKHMsInR5
-cGUiKSkpCnA9SC5jKHIucShzLCJuYW1lIikpCm89ci5xKHMsInN1YnRyZWUiKQpvPW89PW51bGw/bnVs
-bDpMLm1LKG8pCkMuTm0uaShuLG5ldyBMLlpaKHEscCxvLEguYyhyLnEocywicGF0aCIpKSxILmMoci5x
-KHMsImhyZWYiKSksSC5XWShyLnEocywiZWRpdENvdW50IikpKSl9cmV0dXJuIG59LApWRDpmdW5jdGlv
-bihhKXt2YXIgdCxzLHI9SC5WTShbXSx1LkopCmZvcih0PWEubGVuZ3RoLHM9MDtzPGEubGVuZ3RoO2Eu
-bGVuZ3RoPT09dHx8KDAsSC5saykoYSksKytzKUMuTm0uaShyLGFbc10uTHQoKSkKcmV0dXJuIHJ9LApw
-MjpmdW5jdGlvbihhKXtzd2l0Y2goYSl7Y2FzZSJkaXJlY3RvcnkiOnJldHVybiBDLlkyCmNhc2UiZmls
-ZSI6cmV0dXJuIEMucmYKZGVmYXVsdDp0aHJvdyBILmIoUC5QVigiVW5yZWNvZ25pemVkIG5hdmlnYXRp
-b24gdHJlZSBub2RlIHR5cGU6ICIrSC5kKGEpKSl9fSwKdnk6ZnVuY3Rpb24oYSl7c3dpdGNoKGEpe2Nh
-c2UgQy5ZMjpyZXR1cm4iZGlyZWN0b3J5IgpjYXNlIEMucmY6cmV0dXJuImZpbGUifXRocm93IEguYihQ
-LlBWKCJVbnJlY29nbml6ZWQgbmF2aWdhdGlvbiB0cmVlIG5vZGUgdHlwZTogIithLlooMCkpKX0sClpa
-OmZ1bmN0aW9uIFpaKGEsYixjLGQsZSxmKXt2YXIgXz10aGlzCl8uYT1hCl8uYj1iCl8uYz1jCl8uZD1k
-Cl8uZT1lCl8uZj1mfSwKTzk6ZnVuY3Rpb24gTzkoYSl7dGhpcy5iPWF9LApJVjpmdW5jdGlvbiBJVihh
-LGIsYyxkKXt2YXIgXz10aGlzCl8uZD1hCl8uZT1iCl8uZj1jCl8ucj1kfX0sWD17CkNMOmZ1bmN0aW9u
-KGEsYil7dmFyIHQscyxyLHEscCxvPWIueFooYSkKYi5oSyhhKQppZihvIT1udWxsKWE9Si5LVihhLG8u
-bGVuZ3RoKQp0PXUucwpzPUguVk0oW10sdCkKcj1ILlZNKFtdLHQpCnQ9YS5sZW5ndGgKaWYodCE9PTAm
-JmIucjQoQy54Qi5XKGEsMCkpKXtpZigwPj10KXJldHVybiBILmsoYSwwKQpDLk5tLmkocixhWzBdKQpx
-PTF9ZWxzZXtDLk5tLmkociwiIikKcT0wfWZvcihwPXE7cDx0OysrcClpZihiLnI0KEMueEIuVyhhLHAp
-KSl7Qy5ObS5pKHMsQy54Qi5OaihhLHEscCkpCkMuTm0uaShyLGFbcF0pCnE9cCsxfWlmKHE8dCl7Qy5O
-bS5pKHMsQy54Qi5HKGEscSkpCkMuTm0uaShyLCIiKX1yZXR1cm4gbmV3IFguV0QoYixvLHMscil9LApX
-RDpmdW5jdGlvbiBXRChhLGIsYyxkKXt2YXIgXz10aGlzCl8uYT1hCl8uYj1iCl8uZD1jCl8uZT1kfSwK
-STc6ZnVuY3Rpb24oYSl7cmV0dXJuIG5ldyBYLmR2KGEpfSwKZHY6ZnVuY3Rpb24gZHYoYSl7dGhpcy5h
-PWF9fSxPPXsKUmg6ZnVuY3Rpb24oKXt2YXIgdCxzPW51bGwKaWYoUC51bygpLmdGaSgpIT09ImZpbGUi
-KXJldHVybiAkLkViKCkKdD1QLnVvKCkKaWYoIUMueEIuVGModC5nSWkodCksIi8iKSlyZXR1cm4gJC5F
-YigpCmlmKFAuS0wocywiYS9iIixzLHMscyxzLHMpLnQ0KCk9PT0iYVxcYiIpcmV0dXJuICQuS2soKQpy
-ZXR1cm4gJC5iRCgpfSwKekw6ZnVuY3Rpb24gekwoKXt9fSxFPXtPRjpmdW5jdGlvbiBPRihhLGIsYyl7
-dGhpcy5kPWEKdGhpcy5lPWIKdGhpcy5mPWN9fSxGPXtydTpmdW5jdGlvbiBydShhLGIsYyxkKXt2YXIg
-Xz10aGlzCl8uZD1hCl8uZT1iCl8uZj1jCl8ucj1kfX0sRD17ClJYOmZ1bmN0aW9uKCl7dmFyIHQscyxy
-LHEscD1udWxsCnRyeXtwPVAudW8oKX1jYXRjaCh0KXtpZih1Lmc4LmIoSC5SdSh0KSkpe3M9JC5GZgpp
-ZihzIT1udWxsKXJldHVybiBzCnRocm93IHR9ZWxzZSB0aHJvdyB0fWlmKEouUk0ocCwkLkk2KSlyZXR1
-cm4gJC5GZgokLkk2PXAKaWYoJC5IaygpPT0kLkViKCkpcz0kLkZmPXAuWkkoIi4iKS5aKDApCmVsc2V7
-cj1wLnQ0KCkKcT1yLmxlbmd0aC0xCnM9JC5GZj1xPT09MD9yOkMueEIuTmoociwwLHEpfXJldHVybiBz
-fX0KdmFyIHc9W0MsSCxKLFAsVyxNLFUsQixULEwsWCxPLEUsRixEXQpodW5rSGVscGVycy5zZXRGdW5j
-dGlvbk5hbWVzSWZOZWNlc3NhcnkodykKdmFyICQ9e30KSC5GSy5wcm90b3R5cGU9e30KSi52Qi5wcm90
-b3R5cGU9ewpETjpmdW5jdGlvbihhLGIpe3JldHVybiBhPT09Yn0sCmdpTzpmdW5jdGlvbihhKXtyZXR1
-cm4gSC5lUShhKX0sClo6ZnVuY3Rpb24oYSl7cmV0dXJuIkluc3RhbmNlIG9mICciK0guZChILmxoKGEp
-KSsiJyJ9LAplNzpmdW5jdGlvbihhLGIpe3Uuby5hKGIpCnRocm93IEguYihQLmxyKGEsYi5nV2EoKSxi
-LmduZCgpLGIuZ1ZtKCkpKX19CkoueUUucHJvdG90eXBlPXsKWjpmdW5jdGlvbihhKXtyZXR1cm4gU3Ry
-aW5nKGEpfSwKZ2lPOmZ1bmN0aW9uKGEpe3JldHVybiBhPzUxOTAxODoyMTgxNTl9LAokaWEyOjF9Ckou
-WUUucHJvdG90eXBlPXsKRE46ZnVuY3Rpb24oYSxiKXtyZXR1cm4gbnVsbD09Yn0sClo6ZnVuY3Rpb24o
-YSl7cmV0dXJuIm51bGwifSwKZ2lPOmZ1bmN0aW9uKGEpe3JldHVybiAwfSwKZTc6ZnVuY3Rpb24oYSxi
-KXtyZXR1cm4gdGhpcy5TaihhLHUuby5hKGIpKX0sCiRpYzg6MX0KSi5NRi5wcm90b3R5cGU9ewpnaU86
-ZnVuY3Rpb24oYSl7cmV0dXJuIDB9LApaOmZ1bmN0aW9uKGEpe3JldHVybiBTdHJpbmcoYSl9LAokaXZt
-OjF9CkouaUMucHJvdG90eXBlPXt9Ckoua2QucHJvdG90eXBlPXt9CkouYzUucHJvdG90eXBlPXsKWjpm
-dW5jdGlvbihhKXt2YXIgdD1hWyQud1EoKV0KaWYodD09bnVsbClyZXR1cm4gdGhpcy50KGEpCnJldHVy
-biJKYXZhU2NyaXB0IGZ1bmN0aW9uIGZvciAiK0guZChKLkFjKHQpKX0sCiRTOmZ1bmN0aW9uKCl7cmV0
-dXJue2Z1bmM6MSxvcHQ6WywsLCwsLCwsLCwsLCwsLCxdfX0sCiRpRUg6MX0KSi5qZC5wcm90b3R5cGU9
-ewppOmZ1bmN0aW9uKGEsYil7SC50NihhKS5jLmEoYikKaWYoISFhLmZpeGVkJGxlbmd0aClILnZoKFAu
-TDQoImFkZCIpKQphLnB1c2goYil9LApXNDpmdW5jdGlvbihhLGIpe3ZhciB0CmlmKCEhYS5maXhlZCRs
-ZW5ndGgpSC52aChQLkw0KCJyZW1vdmVBdCIpKQp0PWEubGVuZ3RoCmlmKGI+PXQpdGhyb3cgSC5iKFAu
-TzcoYixudWxsKSkKcmV0dXJuIGEuc3BsaWNlKGIsMSlbMF19LApVRzpmdW5jdGlvbihhLGIsYyl7dmFy
-IHQscyxyCkgudDYoYSkuQygiY1g8MT4iKS5hKGMpCmlmKCEhYS5maXhlZCRsZW5ndGgpSC52aChQLkw0
-KCJpbnNlcnRBbGwiKSkKdD1hLmxlbmd0aApQLndBKGIsMCx0LCJpbmRleCIpCnM9Yy5sZW5ndGgKdGhp
-cy5zQShhLHQrcykKcj1iK3MKdGhpcy5ZVyhhLHIsYS5sZW5ndGgsYSxiKQp0aGlzLnZnKGEsYixyLGMp
-fSwKbXY6ZnVuY3Rpb24oYSl7aWYoISFhLmZpeGVkJGxlbmd0aClILnZoKFAuTDQoInJlbW92ZUxhc3Qi
-KSkKaWYoYS5sZW5ndGg9PT0wKXRocm93IEguYihILkhZKGEsLTEpKQpyZXR1cm4gYS5wb3AoKX0sCkZW
-OmZ1bmN0aW9uKGEsYil7dmFyIHQKSC50NihhKS5DKCJjWDwxPiIpLmEoYikKaWYoISFhLmZpeGVkJGxl
-bmd0aClILnZoKFAuTDQoImFkZEFsbCIpKQpmb3IodD1KLklUKGIpO3QuRigpOylhLnB1c2godC5nbCgp
-KX0sCks6ZnVuY3Rpb24oYSxiKXt2YXIgdCxzCkgudDYoYSkuQygifigxKSIpLmEoYikKdD1hLmxlbmd0
-aApmb3Iocz0wO3M8dDsrK3Mpe2IuJDEoYVtzXSkKaWYoYS5sZW5ndGghPT10KXRocm93IEguYihQLmE0
-KGEpKX19LApFMjpmdW5jdGlvbihhLGIsYyl7dmFyIHQ9SC50NihhKQpyZXR1cm4gbmV3IEgubEooYSx0
-LktxKGMpLkMoIjEoMikiKS5hKGIpLHQuQygiQDwxPiIpLktxKGMpLkMoImxKPDEsMj4iKSl9LApIOmZ1
-bmN0aW9uKGEsYil7dmFyIHQscz1uZXcgQXJyYXkoYS5sZW5ndGgpCnMuZml4ZWQkbGVuZ3RoPUFycmF5
-CmZvcih0PTA7dDxhLmxlbmd0aDsrK3QpdGhpcy5ZKHMsdCxILmQoYVt0XSkpCnJldHVybiBzLmpvaW4o
-Yil9LApOMDpmdW5jdGlvbihhLGIsYyxkKXt2YXIgdCxzLHIKZC5hKGIpCkgudDYoYSkuS3EoZCkuQygi
-MSgxLDIpIikuYShjKQp0PWEubGVuZ3RoCmZvcihzPWIscj0wO3I8dDsrK3Ipe3M9Yy4kMihzLGFbcl0p
-CmlmKGEubGVuZ3RoIT09dCl0aHJvdyBILmIoUC5hNChhKSl9cmV0dXJuIHN9LApIdDpmdW5jdGlvbihh
-LGIpe3ZhciB0LHMscixxLHAsbz1ILnQ2KGEpCm8uQygiYTIoMSkiKS5hKGIpCm8uQygiMSgpIikuYShu
-dWxsKQp0PWEubGVuZ3RoCmZvcihzPW51bGwscj0hMSxxPTA7cTx0OysrcSl7cD1hW3FdCmlmKEgub1Qo
-Yi4kMShwKSkpe2lmKHIpdGhyb3cgSC5iKEguZFUoKSkKcz1wCnI9ITB9aWYodCE9PWEubGVuZ3RoKXRo
-cm93IEguYihQLmE0KGEpKX1pZihyKXJldHVybiBzCnRocm93IEguYihILldwKCkpfSwKRTpmdW5jdGlv
-bihhLGIpe2lmKGI8MHx8Yj49YS5sZW5ndGgpcmV0dXJuIEguayhhLGIpCnJldHVybiBhW2JdfSwKRDY6
-ZnVuY3Rpb24oYSxiLGMpe2lmKGI8MHx8Yj5hLmxlbmd0aCl0aHJvdyBILmIoUC5URShiLDAsYS5sZW5n
-dGgsInN0YXJ0IixudWxsKSkKaWYoYzxifHxjPmEubGVuZ3RoKXRocm93IEguYihQLlRFKGMsYixhLmxl
-bmd0aCwiZW5kIixudWxsKSkKaWYoYj09PWMpcmV0dXJuIEguVk0oW10sSC50NihhKSkKcmV0dXJuIEgu
-Vk0oYS5zbGljZShiLGMpLEgudDYoYSkpfSwKZ3RIOmZ1bmN0aW9uKGEpe2lmKGEubGVuZ3RoPjApcmV0
-dXJuIGFbMF0KdGhyb3cgSC5iKEguV3AoKSl9LApnclo6ZnVuY3Rpb24oYSl7dmFyIHQ9YS5sZW5ndGgK
-aWYodD4wKXJldHVybiBhW3QtMV0KdGhyb3cgSC5iKEguV3AoKSl9LApZVzpmdW5jdGlvbihhLGIsYyxk
-LGUpe3ZhciB0LHMscj1ILnQ2KGEpCnIuQygiY1g8MT4iKS5hKGQpCmlmKCEhYS5pbW11dGFibGUkbGlz
-dClILnZoKFAuTDQoInNldFJhbmdlIikpClAuakIoYixjLGEubGVuZ3RoKQp0PWMtYgppZih0PT09MCly
-ZXR1cm4KUC5rMShlLCJza2lwQ291bnQiKQpyLkMoInpNPDE+IikuYShkKQpyPUouVTYoZCkKaWYoZSt0
-PnIuZ0EoZCkpdGhyb3cgSC5iKEguYXIoKSkKaWYoZTxiKWZvcihzPXQtMTtzPj0wOy0tcylhW2Irc109
-ci5xKGQsZStzKQplbHNlIGZvcihzPTA7czx0OysrcylhW2Irc109ci5xKGQsZStzKX0sCnZnOmZ1bmN0
-aW9uKGEsYixjLGQpe3JldHVybiB0aGlzLllXKGEsYixjLGQsMCl9LApWcjpmdW5jdGlvbihhLGIpe3Zh
-ciB0LHMKSC50NihhKS5DKCJhMigxKSIpLmEoYikKdD1hLmxlbmd0aApmb3Iocz0wO3M8dDsrK3Mpe2lm
-KEgub1QoYi4kMShhW3NdKSkpcmV0dXJuITAKaWYoYS5sZW5ndGghPT10KXRocm93IEguYihQLmE0KGEp
-KX1yZXR1cm4hMX0sCnRnOmZ1bmN0aW9uKGEsYil7dmFyIHQKZm9yKHQ9MDt0PGEubGVuZ3RoOysrdClp
-ZihKLlJNKGFbdF0sYikpcmV0dXJuITAKcmV0dXJuITF9LApnbDA6ZnVuY3Rpb24oYSl7cmV0dXJuIGEu
-bGVuZ3RoPT09MH0sCmdvcjpmdW5jdGlvbihhKXtyZXR1cm4gYS5sZW5ndGghPT0wfSwKWjpmdW5jdGlv
-bihhKXtyZXR1cm4gUC5XRShhLCJbIiwiXSIpfSwKZ2t6OmZ1bmN0aW9uKGEpe3JldHVybiBuZXcgSi5t
-MShhLGEubGVuZ3RoLEgudDYoYSkuQygibTE8MT4iKSl9LApnaU86ZnVuY3Rpb24oYSl7cmV0dXJuIEgu
-ZVEoYSl9LApnQTpmdW5jdGlvbihhKXtyZXR1cm4gYS5sZW5ndGh9LApzQTpmdW5jdGlvbihhLGIpe2lm
-KCEhYS5maXhlZCRsZW5ndGgpSC52aChQLkw0KCJzZXQgbGVuZ3RoIikpCmlmKGI8MCl0aHJvdyBILmIo
-UC5URShiLDAsbnVsbCwibmV3TGVuZ3RoIixudWxsKSkKYS5sZW5ndGg9Yn0sCnE6ZnVuY3Rpb24oYSxi
-KXtILldZKGIpCmlmKGI+PWEubGVuZ3RofHxiPDApdGhyb3cgSC5iKEguSFkoYSxiKSkKcmV0dXJuIGFb
-Yl19LApZOmZ1bmN0aW9uKGEsYixjKXtILnQ2KGEpLmMuYShjKQppZighIWEuaW1tdXRhYmxlJGxpc3Qp
-SC52aChQLkw0KCJpbmRleGVkIHNldCIpKQppZihiPj1hLmxlbmd0aHx8YjwwKXRocm93IEguYihILkhZ
-KGEsYikpCmFbYl09Y30sCiRpYlE6MSwKJGljWDoxLAokaXpNOjF9CkouUG8ucHJvdG90eXBlPXt9Ckou
-bTEucHJvdG90eXBlPXsKZ2w6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5kfSwKRjpmdW5jdGlvbigpe3Zh
-ciB0LHM9dGhpcyxyPXMuYSxxPXIubGVuZ3RoCmlmKHMuYiE9PXEpdGhyb3cgSC5iKEgubGsocikpCnQ9
-cy5jCmlmKHQ+PXEpe3Muc00obnVsbCkKcmV0dXJuITF9cy5zTShyW3RdKTsrK3MuYwpyZXR1cm4hMH0s
-CnNNOmZ1bmN0aW9uKGEpe3RoaXMuZD10aGlzLiR0aS5jLmEoYSl9LAokaUFuOjF9CkoucUkucHJvdG90
-eXBlPXsKeXU6ZnVuY3Rpb24oYSl7dmFyIHQKaWYoYT49LTIxNDc0ODM2NDgmJmE8PTIxNDc0ODM2NDcp
-cmV0dXJuIGF8MAppZihpc0Zpbml0ZShhKSl7dD1hPDA/TWF0aC5jZWlsKGEpOk1hdGguZmxvb3IoYSkK
-cmV0dXJuIHQrMH10aHJvdyBILmIoUC5MNCgiIithKyIudG9JbnQoKSIpKX0sCnpROmZ1bmN0aW9uKGEp
-e2lmKGE+MCl7aWYoYSE9PTEvMClyZXR1cm4gTWF0aC5yb3VuZChhKX1lbHNlIGlmKGE+LTEvMClyZXR1
-cm4gMC1NYXRoLnJvdW5kKDAtYSkKdGhyb3cgSC5iKFAuTDQoIiIrYSsiLnJvdW5kKCkiKSl9LApXWjpm
-dW5jdGlvbihhLGIpe3ZhciB0LHMscixxCmlmKGI8Mnx8Yj4zNil0aHJvdyBILmIoUC5URShiLDIsMzYs
-InJhZGl4IixudWxsKSkKdD1hLnRvU3RyaW5nKGIpCmlmKEMueEIubSh0LHQubGVuZ3RoLTEpIT09NDEp
-cmV0dXJuIHQKcz0vXihbXGRhLXpdKykoPzpcLihbXGRhLXpdKykpP1woZVwrKFxkKylcKSQvLmV4ZWMo
-dCkKaWYocz09bnVsbClILnZoKFAuTDQoIlVuZXhwZWN0ZWQgdG9TdHJpbmcgcmVzdWx0OiAiK3QpKQpy
-PXMubGVuZ3RoCmlmKDE+PXIpcmV0dXJuIEguayhzLDEpCnQ9c1sxXQppZigzPj1yKXJldHVybiBILmso
-cywzKQpxPStzWzNdCnI9c1syXQppZihyIT1udWxsKXt0Kz1yCnEtPXIubGVuZ3RofXJldHVybiB0K0Mu
-eEIuSXgoIjAiLHEpfSwKWjpmdW5jdGlvbihhKXtpZihhPT09MCYmMS9hPDApcmV0dXJuIi0wLjAiCmVs
-c2UgcmV0dXJuIiIrYX0sCmdpTzpmdW5jdGlvbihhKXt2YXIgdCxzLHIscSxwPWF8MAppZihhPT09cCly
-ZXR1cm4gNTM2ODcwOTExJnAKdD1NYXRoLmFicyhhKQpzPU1hdGgubG9nKHQpLzAuNjkzMTQ3MTgwNTU5
-OTQ1M3wwCnI9TWF0aC5wb3coMixzKQpxPXQ8MT90L3I6ci90CnJldHVybiA1MzY4NzA5MTEmKChxKjkw
-MDcxOTkyNTQ3NDA5OTJ8MCkrKHEqMzU0MjI0MzE4MTE3NjUyMXwwKSkqNTk5MTk3K3MqMTI1OX0sCnpZ
-OmZ1bmN0aW9uKGEsYil7dmFyIHQ9YSViCmlmKHQ9PT0wKXJldHVybiAwCmlmKHQ+MClyZXR1cm4gdApp
-ZihiPDApcmV0dXJuIHQtYgplbHNlIHJldHVybiB0K2J9LAp3RzpmdW5jdGlvbihhLGIpe3ZhciB0Cmlm
-KGE+MCl0PXRoaXMucDMoYSxiKQplbHNle3Q9Yj4zMT8zMTpiCnQ9YT4+dD4+PjB9cmV0dXJuIHR9LApi
-ZjpmdW5jdGlvbihhLGIpe2lmKGI8MCl0aHJvdyBILmIoSC5JKGIpKQpyZXR1cm4gdGhpcy5wMyhhLGIp
-fSwKcDM6ZnVuY3Rpb24oYSxiKXtyZXR1cm4gYj4zMT8wOmE+Pj5ifSwKJGlDUDoxLAokaWxmOjF9Ckou
-YlUucHJvdG90eXBlPXskaUlmOjF9CkouVkEucHJvdG90eXBlPXt9CkouRHIucHJvdG90eXBlPXsKbTpm
-dW5jdGlvbihhLGIpe2lmKGI8MCl0aHJvdyBILmIoSC5IWShhLGIpKQppZihiPj1hLmxlbmd0aClILnZo
-KEguSFkoYSxiKSkKcmV0dXJuIGEuY2hhckNvZGVBdChiKX0sClc6ZnVuY3Rpb24oYSxiKXtpZihiPj1h
-Lmxlbmd0aCl0aHJvdyBILmIoSC5IWShhLGIpKQpyZXR1cm4gYS5jaGFyQ29kZUF0KGIpfSwKZGQ6ZnVu
-Y3Rpb24oYSxiKXtyZXR1cm4gbmV3IEguTkYoYixhLDApfSwKaDpmdW5jdGlvbihhLGIpe2lmKHR5cGVv
-ZiBiIT0ic3RyaW5nIil0aHJvdyBILmIoUC5MMyhiLG51bGwsbnVsbCkpCnJldHVybiBhK2J9LApUYzpm
-dW5jdGlvbihhLGIpe3ZhciB0PWIubGVuZ3RoLHM9YS5sZW5ndGgKaWYodD5zKXJldHVybiExCnJldHVy
-biBiPT09dGhpcy5HKGEscy10KX0sCmk3OmZ1bmN0aW9uKGEsYixjLGQpe3ZhciB0LHMKYz1QLmpCKGIs
-YyxhLmxlbmd0aCkKdD1hLnN1YnN0cmluZygwLGIpCnM9YS5zdWJzdHJpbmcoYykKcmV0dXJuIHQrZCtz
-fSwKUWk6ZnVuY3Rpb24oYSxiLGMpe3ZhciB0CmlmKCFILm9rKGMpKUgudmgoSC5JKGMpKQppZih0eXBl
-b2YgYyE9PSJudW1iZXIiKXJldHVybiBjLkooKQppZihjPDB8fGM+YS5sZW5ndGgpdGhyb3cgSC5iKFAu
-VEUoYywwLGEubGVuZ3RoLG51bGwsbnVsbCkpCnQ9YytiLmxlbmd0aAppZih0PmEubGVuZ3RoKXJldHVy
-biExCnJldHVybiBiPT09YS5zdWJzdHJpbmcoYyx0KX0sCm46ZnVuY3Rpb24oYSxiKXtyZXR1cm4gdGhp
-cy5RaShhLGIsMCl9LApOajpmdW5jdGlvbihhLGIsYyl7aWYoIUgub2soYikpSC52aChILkkoYikpCmlm
-KGM9PW51bGwpYz1hLmxlbmd0aAppZih0eXBlb2YgYiE9PSJudW1iZXIiKXJldHVybiBiLkooKQppZihi
-PDApdGhyb3cgSC5iKFAuTzcoYixudWxsKSkKaWYoYj5jKXRocm93IEguYihQLk83KGIsbnVsbCkpCmlm
-KGM+YS5sZW5ndGgpdGhyb3cgSC5iKFAuTzcoYyxudWxsKSkKcmV0dXJuIGEuc3Vic3RyaW5nKGIsYyl9
-LApHOmZ1bmN0aW9uKGEsYil7cmV0dXJuIHRoaXMuTmooYSxiLG51bGwpfSwKaGM6ZnVuY3Rpb24oYSl7
-cmV0dXJuIGEudG9Mb3dlckNhc2UoKX0sCmJTOmZ1bmN0aW9uKGEpe3ZhciB0LHMscixxPWEudHJpbSgp
-LHA9cS5sZW5ndGgKaWYocD09PTApcmV0dXJuIHEKaWYodGhpcy5XKHEsMCk9PT0xMzMpe3Q9Si5tbShx
-LDEpCmlmKHQ9PT1wKXJldHVybiIifWVsc2UgdD0wCnM9cC0xCnI9dGhpcy5tKHEscyk9PT0xMzM/Si5j
-MShxLHMpOnAKaWYodD09PTAmJnI9PT1wKXJldHVybiBxCnJldHVybiBxLnN1YnN0cmluZyh0LHIpfSwK
-SXg6ZnVuY3Rpb24oYSxiKXt2YXIgdCxzCmlmKDA+PWIpcmV0dXJuIiIKaWYoYj09PTF8fGEubGVuZ3Ro
-PT09MClyZXR1cm4gYQppZihiIT09Yj4+PjApdGhyb3cgSC5iKEMuRXEpCmZvcih0PWEscz0iIjshMDsp
-e2lmKChiJjEpPT09MSlzPXQrcwpiPWI+Pj4xCmlmKGI9PT0wKWJyZWFrCnQrPXR9cmV0dXJuIHN9LApY
-VTpmdW5jdGlvbihhLGIsYyl7dmFyIHQKaWYoYzwwfHxjPmEubGVuZ3RoKXRocm93IEguYihQLlRFKGMs
-MCxhLmxlbmd0aCxudWxsLG51bGwpKQp0PWEuaW5kZXhPZihiLGMpCnJldHVybiB0fSwKT1k6ZnVuY3Rp
-b24oYSxiKXtyZXR1cm4gdGhpcy5YVShhLGIsMCl9LApQazpmdW5jdGlvbihhLGIsYyl7dmFyIHQscwpp
-ZihjPT1udWxsKWM9YS5sZW5ndGgKZWxzZSBpZihjPDB8fGM+YS5sZW5ndGgpdGhyb3cgSC5iKFAuVEUo
-YywwLGEubGVuZ3RoLG51bGwsbnVsbCkpCnQ9Yi5sZW5ndGgKcz1hLmxlbmd0aAppZihjK3Q+cyljPXMt
-dApyZXR1cm4gYS5sYXN0SW5kZXhPZihiLGMpfSwKY246ZnVuY3Rpb24oYSxiKXtyZXR1cm4gdGhpcy5Q
-ayhhLGIsbnVsbCl9LApJczpmdW5jdGlvbihhLGIsYyl7dmFyIHQ9YS5sZW5ndGgKaWYoYz50KXRocm93
-IEguYihQLlRFKGMsMCx0LG51bGwsbnVsbCkpCnJldHVybiBILm0yKGEsYixjKX0sCnRnOmZ1bmN0aW9u
-KGEsYil7cmV0dXJuIHRoaXMuSXMoYSxiLDApfSwKWjpmdW5jdGlvbihhKXtyZXR1cm4gYX0sCmdpTzpm
-dW5jdGlvbihhKXt2YXIgdCxzLHIKZm9yKHQ9YS5sZW5ndGgscz0wLHI9MDtyPHQ7KytyKXtzPTUzNjg3
-MDkxMSZzK2EuY2hhckNvZGVBdChyKQpzPTUzNjg3MDkxMSZzKygoNTI0Mjg3JnMpPDwxMCkKc149cz4+
-Nn1zPTUzNjg3MDkxMSZzKygoNjcxMDg4NjMmcyk8PDMpCnNePXM+PjExCnJldHVybiA1MzY4NzA5MTEm
-cysoKDE2MzgzJnMpPDwxNSl9LApnQTpmdW5jdGlvbihhKXtyZXR1cm4gYS5sZW5ndGh9LApxOmZ1bmN0
-aW9uKGEsYil7SC5XWShiKQppZihiPj1hLmxlbmd0aHx8ITEpdGhyb3cgSC5iKEguSFkoYSxiKSkKcmV0
-dXJuIGFbYl19LAokaXZYOjEsCiRpcVU6MX0KSC5xai5wcm90b3R5cGU9ewpnQTpmdW5jdGlvbihhKXty
-ZXR1cm4gdGhpcy5hLmxlbmd0aH0sCnE6ZnVuY3Rpb24oYSxiKXtyZXR1cm4gQy54Qi5tKHRoaXMuYSxI
-LldZKGIpKX19CkguYlEucHJvdG90eXBlPXt9CkguYUwucHJvdG90eXBlPXsKZ2t6OmZ1bmN0aW9uKGEp
-e3ZhciB0PXRoaXMKcmV0dXJuIG5ldyBILmE3KHQsdC5nQSh0KSxILkxoKHQpLkMoImE3PGFMLkU+Iikp
-fSwKZ2wwOmZ1bmN0aW9uKGEpe3JldHVybiB0aGlzLmdBKHRoaXMpPT09MH0sCkg6ZnVuY3Rpb24oYSxi
-KXt2YXIgdCxzLHIscT10aGlzLHA9cS5nQShxKQppZihiLmxlbmd0aCE9PTApe2lmKHA9PT0wKXJldHVy
-biIiCnQ9SC5kKHEuRSgwLDApKQppZihwIT09cS5nQShxKSl0aHJvdyBILmIoUC5hNChxKSkKZm9yKHM9
-dCxyPTE7cjxwOysrcil7cz1zK2IrSC5kKHEuRSgwLHIpKQppZihwIT09cS5nQShxKSl0aHJvdyBILmIo
-UC5hNChxKSl9cmV0dXJuIHMuY2hhckNvZGVBdCgwKT09MD9zOnN9ZWxzZXtmb3Iocj0wLHM9IiI7cjxw
-Oysrcil7cys9SC5kKHEuRSgwLHIpKQppZihwIT09cS5nQShxKSl0aHJvdyBILmIoUC5hNChxKSl9cmV0
-dXJuIHMuY2hhckNvZGVBdCgwKT09MD9zOnN9fSwKZXY6ZnVuY3Rpb24oYSxiKXtyZXR1cm4gdGhpcy5H
-RygwLEguTGgodGhpcykuQygiYTIoYUwuRSkiKS5hKGIpKX0sCkUyOmZ1bmN0aW9uKGEsYixjKXt2YXIg
-dD1ILkxoKHRoaXMpCnJldHVybiBuZXcgSC5sSih0aGlzLHQuS3EoYykuQygiMShhTC5FKSIpLmEoYiks
-dC5DKCJAPGFMLkU+IikuS3EoYykuQygibEo8MSwyPiIpKX0sCnR0OmZ1bmN0aW9uKGEsYil7dmFyIHQs
-cz10aGlzLHI9SC5WTShbXSxILkxoKHMpLkMoImpkPGFMLkU+IikpCkMuTm0uc0EocixzLmdBKHMpKQpm
-b3IodD0wO3Q8cy5nQShzKTsrK3QpQy5ObS5ZKHIsdCxzLkUoMCx0KSkKcmV0dXJuIHJ9LApicjpmdW5j
-dGlvbihhKXtyZXR1cm4gdGhpcy50dChhLCEwKX19CkgubkgucHJvdG90eXBlPXsKZ1VEOmZ1bmN0aW9u
-KCl7dmFyIHQ9Si5IKHRoaXMuYSkscz10aGlzLmMKaWYocz09bnVsbHx8cz50KXJldHVybiB0CnJldHVy
-biBzfSwKZ0FzOmZ1bmN0aW9uKCl7dmFyIHQ9Si5IKHRoaXMuYSkscz10aGlzLmIKaWYocz50KXJldHVy
-biB0CnJldHVybiBzfSwKZ0E6ZnVuY3Rpb24oYSl7dmFyIHQscz1KLkgodGhpcy5hKSxyPXRoaXMuYgpp
-ZihyPj1zKXJldHVybiAwCnQ9dGhpcy5jCmlmKHQ9PW51bGx8fHQ+PXMpcmV0dXJuIHMtcgppZih0eXBl
-b2YgdCE9PSJudW1iZXIiKXJldHVybiB0LkhOKCkKcmV0dXJuIHQtcn0sCkU6ZnVuY3Rpb24oYSxiKXt2
-YXIgdCxzPXRoaXMscj1zLmdBcygpK2IKaWYoYj49MCl7dD1zLmdVRCgpCmlmKHR5cGVvZiB0IT09Im51
-bWJlciIpcmV0dXJuIEgucFkodCkKdD1yPj10fWVsc2UgdD0hMAppZih0KXRocm93IEguYihQLnQoYixz
-LCJpbmRleCIsbnVsbCxudWxsKSkKcmV0dXJuIEouR0Eocy5hLHIpfX0KSC5hNy5wcm90b3R5cGU9ewpn
-bDpmdW5jdGlvbigpe3JldHVybiB0aGlzLmR9LApGOmZ1bmN0aW9uKCl7dmFyIHQscz10aGlzLHI9cy5h
-LHE9Si5VNihyKSxwPXEuZ0EocikKaWYocy5iIT09cCl0aHJvdyBILmIoUC5hNChyKSkKdD1zLmMKaWYo
-dD49cCl7cy5zSShudWxsKQpyZXR1cm4hMX1zLnNJKHEuRShyLHQpKTsrK3MuYwpyZXR1cm4hMH0sCnNJ
-OmZ1bmN0aW9uKGEpe3RoaXMuZD10aGlzLiR0aS5jLmEoYSl9LAokaUFuOjF9CkguaTEucHJvdG90eXBl
-PXsKZ2t6OmZ1bmN0aW9uKGEpe3ZhciB0PUguTGgodGhpcykKcmV0dXJuIG5ldyBILk1IKEouSVQodGhp
-cy5hKSx0aGlzLmIsdC5DKCJAPDE+IikuS3EodC5RWzFdKS5DKCJNSDwxLDI+IikpfSwKZ0E6ZnVuY3Rp
-b24oYSl7cmV0dXJuIEouSCh0aGlzLmEpfX0KSC54eS5wcm90b3R5cGU9eyRpYlE6MX0KSC5NSC5wcm90
-b3R5cGU9ewpGOmZ1bmN0aW9uKCl7dmFyIHQ9dGhpcyxzPXQuYgppZihzLkYoKSl7dC5zSSh0LmMuJDEo
-cy5nbCgpKSkKcmV0dXJuITB9dC5zSShudWxsKQpyZXR1cm4hMX0sCmdsOmZ1bmN0aW9uKCl7cmV0dXJu
-IHRoaXMuYX0sCnNJOmZ1bmN0aW9uKGEpe3RoaXMuYT10aGlzLiR0aS5RWzFdLmEoYSl9fQpILmxKLnBy
-b3RvdHlwZT17CmdBOmZ1bmN0aW9uKGEpe3JldHVybiBKLkgodGhpcy5hKX0sCkU6ZnVuY3Rpb24oYSxi
-KXtyZXR1cm4gdGhpcy5iLiQxKEouR0EodGhpcy5hLGIpKX19CkguVTUucHJvdG90eXBlPXsKZ2t6OmZ1
-bmN0aW9uKGEpe3JldHVybiBuZXcgSC52RyhKLklUKHRoaXMuYSksdGhpcy5iLHRoaXMuJHRpLkMoInZH
-PDE+IikpfX0KSC52Ry5wcm90b3R5cGU9ewpGOmZ1bmN0aW9uKCl7dmFyIHQscwpmb3IodD10aGlzLmEs
-cz10aGlzLmI7dC5GKCk7KWlmKEgub1Qocy4kMSh0LmdsKCkpKSlyZXR1cm4hMApyZXR1cm4hMX0sCmds
-OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuYS5nbCgpfX0KSC5TVS5wcm90b3R5cGU9e30KSC5SZS5wcm90
-b3R5cGU9ewpZOmZ1bmN0aW9uKGEsYixjKXtILkxoKHRoaXMpLkMoIlJlLkUiKS5hKGMpCnRocm93IEgu
-YihQLkw0KCJDYW5ub3QgbW9kaWZ5IGFuIHVubW9kaWZpYWJsZSBsaXN0IikpfX0KSC53Mi5wcm90b3R5
-cGU9e30KSC53di5wcm90b3R5cGU9ewpnaU86ZnVuY3Rpb24oYSl7dmFyIHQ9dGhpcy5faGFzaENvZGUK
-aWYodCE9bnVsbClyZXR1cm4gdAp0PTUzNjg3MDkxMSY2NjQ1OTcqSi5oZih0aGlzLmEpCnRoaXMuX2hh
-c2hDb2RlPXQKcmV0dXJuIHR9LApaOmZ1bmN0aW9uKGEpe3JldHVybidTeW1ib2woIicrSC5kKHRoaXMu
-YSkrJyIpJ30sCkROOmZ1bmN0aW9uKGEsYil7aWYoYj09bnVsbClyZXR1cm4hMQpyZXR1cm4gYiBpbnN0
-YW5jZW9mIEgud3YmJnRoaXMuYT09Yi5hfSwKJGlHRDoxfQpILlBELnByb3RvdHlwZT17fQpILldVLnBy
-b3RvdHlwZT17CmdsMDpmdW5jdGlvbihhKXtyZXR1cm4gdGhpcy5nQSh0aGlzKT09PTB9LApaOmZ1bmN0
-aW9uKGEpe3JldHVybiBQLm5PKHRoaXMpfSwKWTpmdW5jdGlvbihhLGIsYyl7dmFyIHQ9SC5MaCh0aGlz
-KQp0LmMuYShiKQp0LlFbMV0uYShjKQpyZXR1cm4gSC5kYygpfSwKZ1B1OmZ1bmN0aW9uKGEpe3JldHVy
-biB0aGlzLnE0KGEsSC5MaCh0aGlzKS5DKCJOMzwxLDI+IikpfSwKcTQ6ZnVuY3Rpb24oYSxiKXt2YXIg
-dD10aGlzCnJldHVybiBQLmwwKGZ1bmN0aW9uKCl7dmFyIHM9YQp2YXIgcj0wLHE9MSxwLG8sbixtCnJl
-dHVybiBmdW5jdGlvbiAkYXN5bmMkZ1B1KGMsZCl7aWYoYz09PTEpe3A9ZApyPXF9d2hpbGUodHJ1ZSlz
-d2l0Y2gocil7Y2FzZSAwOm89dC5nVigpLG89by5na3oobyksbj1ILkxoKHQpLG49bi5DKCJAPDE+Iiku
-S3Eobi5RWzFdKS5DKCJOMzwxLDI+IikKY2FzZSAyOmlmKCFvLkYoKSl7cj0zCmJyZWFrfW09by5nbCgp
-CnI9NApyZXR1cm4gbmV3IFAuTjMobSx0LnEoMCxtKSxuKQpjYXNlIDQ6cj0yCmJyZWFrCmNhc2UgMzpy
-ZXR1cm4gUC5UaCgpCmNhc2UgMTpyZXR1cm4gUC5ZbShwKX19fSxiKX0sCiRpWjA6MX0KSC5MUC5wcm90
-b3R5cGU9ewpnQTpmdW5jdGlvbihhKXtyZXR1cm4gdGhpcy5hfSwKeDQ6ZnVuY3Rpb24oYSl7aWYodHlw
-ZW9mIGEhPSJzdHJpbmciKXJldHVybiExCmlmKCJfX3Byb3RvX18iPT09YSlyZXR1cm4hMQpyZXR1cm4g
-dGhpcy5iLmhhc093blByb3BlcnR5KGEpfSwKcTpmdW5jdGlvbihhLGIpe2lmKCF0aGlzLng0KGIpKXJl
-dHVybiBudWxsCnJldHVybiB0aGlzLkQoYil9LApEOmZ1bmN0aW9uKGEpe3JldHVybiB0aGlzLmJbSC5j
-KGEpXX0sCks6ZnVuY3Rpb24oYSxiKXt2YXIgdCxzLHIscSxwPUguTGgodGhpcykKcC5DKCJ+KDEsMiki
-KS5hKGIpCnQ9dGhpcy5jCmZvcihzPXQubGVuZ3RoLHA9cC5RWzFdLHI9MDtyPHM7KytyKXtxPXRbcl0K
-Yi4kMihxLHAuYSh0aGlzLkQocSkpKX19LApnVjpmdW5jdGlvbigpe3JldHVybiBuZXcgSC5YUih0aGlz
-LEguTGgodGhpcykuQygiWFI8MT4iKSl9fQpILlhSLnByb3RvdHlwZT17CmdrejpmdW5jdGlvbihhKXt2
-YXIgdD10aGlzLmEuYwpyZXR1cm4gbmV3IEoubTEodCx0Lmxlbmd0aCxILnQ2KHQpLkMoIm0xPDE+Iikp
-fSwKZ0E6ZnVuY3Rpb24oYSl7cmV0dXJuIHRoaXMuYS5jLmxlbmd0aH19CkguTEkucHJvdG90eXBlPXsK
-Z1dhOmZ1bmN0aW9uKCl7dmFyIHQ9dGhpcy5hCnJldHVybiB0fSwKZ25kOmZ1bmN0aW9uKCl7dmFyIHQs
-cyxyLHEscD10aGlzCmlmKHAuYz09PTEpcmV0dXJuIEMuaFUKdD1wLmQKcz10Lmxlbmd0aC1wLmUubGVu
-Z3RoLXAuZgppZihzPT09MClyZXR1cm4gQy5oVQpyPVtdCmZvcihxPTA7cTxzOysrcSl7aWYocT49dC5s
-ZW5ndGgpcmV0dXJuIEguayh0LHEpCnIucHVzaCh0W3FdKX1yZXR1cm4gSi51bihyKX0sCmdWbTpmdW5j
-dGlvbigpe3ZhciB0LHMscixxLHAsbyxuLG0sbD10aGlzCmlmKGwuYyE9PTApcmV0dXJuIEMuRHgKdD1s
-LmUKcz10Lmxlbmd0aApyPWwuZApxPXIubGVuZ3RoLXMtbC5mCmlmKHM9PT0wKXJldHVybiBDLkR4CnA9
-bmV3IEguTjUodS5lbykKZm9yKG89MDtvPHM7KytvKXtpZihvPj10Lmxlbmd0aClyZXR1cm4gSC5rKHQs
-bykKbj10W29dCm09cStvCmlmKG08MHx8bT49ci5sZW5ndGgpcmV0dXJuIEguayhyLG0pCnAuWSgwLG5l
-dyBILnd2KG4pLHJbbV0pfXJldHVybiBuZXcgSC5QRChwLHUuZ0YpfSwKJGl2UToxfQpILkNqLnByb3Rv
-dHlwZT17CiQyOmZ1bmN0aW9uKGEsYil7dmFyIHQKSC5jKGEpCnQ9dGhpcy5hCnQuYj10LmIrIiQiK0gu
-ZChhKQpDLk5tLmkodGhpcy5iLGEpCkMuTm0uaSh0aGlzLmMsYik7Kyt0LmF9LAokUzoxMn0KSC5mOS5w
-cm90b3R5cGU9ewpxUzpmdW5jdGlvbihhKXt2YXIgdCxzLHI9dGhpcyxxPW5ldyBSZWdFeHAoci5hKS5l
-eGVjKGEpCmlmKHE9PW51bGwpcmV0dXJuIG51bGwKdD1PYmplY3QuY3JlYXRlKG51bGwpCnM9ci5iCmlm
-KHMhPT0tMSl0LmFyZ3VtZW50cz1xW3MrMV0Kcz1yLmMKaWYocyE9PS0xKXQuYXJndW1lbnRzRXhwcj1x
-W3MrMV0Kcz1yLmQKaWYocyE9PS0xKXQuZXhwcj1xW3MrMV0Kcz1yLmUKaWYocyE9PS0xKXQubWV0aG9k
-PXFbcysxXQpzPXIuZgppZihzIT09LTEpdC5yZWNlaXZlcj1xW3MrMV0KcmV0dXJuIHR9fQpILlcwLnBy
-b3RvdHlwZT17Clo6ZnVuY3Rpb24oYSl7dmFyIHQ9dGhpcy5iCmlmKHQ9PW51bGwpcmV0dXJuIk5vU3Vj
-aE1ldGhvZEVycm9yOiAiK0guZCh0aGlzLmEpCnJldHVybiJOb1N1Y2hNZXRob2RFcnJvcjogbWV0aG9k
-IG5vdCBmb3VuZDogJyIrdCsiJyBvbiBudWxsIn19CkguYXoucHJvdG90eXBlPXsKWjpmdW5jdGlvbihh
-KXt2YXIgdCxzPXRoaXMscj0iTm9TdWNoTWV0aG9kRXJyb3I6IG1ldGhvZCBub3QgZm91bmQ6ICciLHE9
-cy5iCmlmKHE9PW51bGwpcmV0dXJuIk5vU3VjaE1ldGhvZEVycm9yOiAiK0guZChzLmEpCnQ9cy5jCmlm
-KHQ9PW51bGwpcmV0dXJuIHIrcSsiJyAoIitILmQocy5hKSsiKSIKcmV0dXJuIHIrcSsiJyBvbiAnIit0
-KyInICgiK0guZChzLmEpKyIpIn19CkgudlYucHJvdG90eXBlPXsKWjpmdW5jdGlvbihhKXt2YXIgdD10
-aGlzLmEKcmV0dXJuIHQubGVuZ3RoPT09MD8iRXJyb3IiOiJFcnJvcjogIit0fX0KSC5icS5wcm90b3R5
-cGU9e30KSC5BbS5wcm90b3R5cGU9ewokMTpmdW5jdGlvbihhKXtpZih1LlcuYihhKSlpZihhLiR0aHJv
-d25Kc0Vycm9yPT1udWxsKWEuJHRocm93bkpzRXJyb3I9dGhpcy5hCnJldHVybiBhfSwKJFM6MX0KSC5Y
-Ty5wcm90b3R5cGU9ewpaOmZ1bmN0aW9uKGEpe3ZhciB0LHM9dGhpcy5iCmlmKHMhPW51bGwpcmV0dXJu
-IHMKcz10aGlzLmEKdD1zIT09bnVsbCYmdHlwZW9mIHM9PT0ib2JqZWN0Ij9zLnN0YWNrOm51bGwKcmV0
-dXJuIHRoaXMuYj10PT1udWxsPyIiOnR9LAokaUd6OjF9CkguVHAucHJvdG90eXBlPXsKWjpmdW5jdGlv
-bihhKXt2YXIgdD10aGlzLmNvbnN0cnVjdG9yLHM9dD09bnVsbD9udWxsOnQubmFtZQpyZXR1cm4iQ2xv
-c3VyZSAnIitILk5RKHM9PW51bGw/InVua25vd24iOnMpKyInIn0sCiRpRUg6MSwKZ1FsOmZ1bmN0aW9u
-KCl7cmV0dXJuIHRoaXN9LAokQzoiJDEiLAokUjoxLAokRDpudWxsfQpILmxjLnByb3RvdHlwZT17fQpI
-Lnp4LnByb3RvdHlwZT17Clo6ZnVuY3Rpb24oYSl7dmFyIHQ9dGhpcy4kc3RhdGljX25hbWUKaWYodD09
-bnVsbClyZXR1cm4iQ2xvc3VyZSBvZiB1bmtub3duIHN0YXRpYyBtZXRob2QiCnJldHVybiJDbG9zdXJl
-ICciK0guTlEodCkrIicifX0KSC5qeS5wcm90b3R5cGU9ewpETjpmdW5jdGlvbihhLGIpe3ZhciB0PXRo
-aXMKaWYoYj09bnVsbClyZXR1cm4hMQppZih0PT09YilyZXR1cm4hMAppZighKGIgaW5zdGFuY2VvZiBI
-Lmp5KSlyZXR1cm4hMQpyZXR1cm4gdC5hPT09Yi5hJiZ0LmI9PT1iLmImJnQuYz09PWIuY30sCmdpTzpm
-dW5jdGlvbihhKXt2YXIgdCxzPXRoaXMuYwppZihzPT1udWxsKXQ9SC5lUSh0aGlzLmEpCmVsc2UgdD10
-eXBlb2YgcyE9PSJvYmplY3QiP0ouaGYocyk6SC5lUShzKQpyZXR1cm4odF5ILmVRKHRoaXMuYikpPj4+
-MH0sClo6ZnVuY3Rpb24oYSl7dmFyIHQ9dGhpcy5jCmlmKHQ9PW51bGwpdD10aGlzLmEKcmV0dXJuIkNs
-b3N1cmUgJyIrSC5kKHRoaXMuZCkrIicgb2YgIisoIkluc3RhbmNlIG9mICciK0guZChILmxoKHQpKSsi
-JyIpfX0KSC5FcS5wcm90b3R5cGU9ewpaOmZ1bmN0aW9uKGEpe3JldHVybiJSdW50aW1lRXJyb3I6ICIr
-SC5kKHRoaXMuYSl9fQpILmtZLnByb3RvdHlwZT17Clo6ZnVuY3Rpb24oYSl7cmV0dXJuIkFzc2VydGlv
-biBmYWlsZWQ6ICIrUC5oKHRoaXMuYSl9fQpILk41LnByb3RvdHlwZT17CmdBOmZ1bmN0aW9uKGEpe3Jl
-dHVybiB0aGlzLmF9LApnbDA6ZnVuY3Rpb24oYSl7cmV0dXJuIHRoaXMuYT09PTB9LApnVjpmdW5jdGlv
-bigpe3JldHVybiBuZXcgSC5pNSh0aGlzLEguTGgodGhpcykuQygiaTU8MT4iKSl9LAp4NDpmdW5jdGlv
-bihhKXt2YXIgdCxzCmlmKHR5cGVvZiBhPT0ic3RyaW5nIil7dD10aGlzLmIKaWYodD09bnVsbClyZXR1
-cm4hMQpyZXR1cm4gdGhpcy5YdSh0LGEpfWVsc2V7cz10aGlzLkNYKGEpCnJldHVybiBzfX0sCkNYOmZ1
-bmN0aW9uKGEpe3ZhciB0PXRoaXMuZAppZih0PT1udWxsKXJldHVybiExCnJldHVybiB0aGlzLkZoKHRo
-aXMuQnQodCxKLmhmKGEpJjB4M2ZmZmZmZiksYSk+PTB9LApxOmZ1bmN0aW9uKGEsYil7dmFyIHQscyxy
-LHEscD10aGlzLG89bnVsbAppZih0eXBlb2YgYj09InN0cmluZyIpe3Q9cC5iCmlmKHQ9PW51bGwpcmV0
-dXJuIG8Kcz1wLmoyKHQsYikKcj1zPT1udWxsP286cy5iCnJldHVybiByfWVsc2UgaWYodHlwZW9mIGI9
-PSJudW1iZXIiJiYoYiYweDNmZmZmZmYpPT09Yil7cT1wLmMKaWYocT09bnVsbClyZXR1cm4gbwpzPXAu
-ajIocSxiKQpyPXM9PW51bGw/bzpzLmIKcmV0dXJuIHJ9ZWxzZSByZXR1cm4gcC5hYShiKX0sCmFhOmZ1
-bmN0aW9uKGEpe3ZhciB0LHMscj10aGlzLmQKaWYocj09bnVsbClyZXR1cm4gbnVsbAp0PXRoaXMuQnQo
-cixKLmhmKGEpJjB4M2ZmZmZmZikKcz10aGlzLkZoKHQsYSkKaWYoczwwKXJldHVybiBudWxsCnJldHVy
-biB0W3NdLmJ9LApZOmZ1bmN0aW9uKGEsYixjKXt2YXIgdCxzLHIscSxwLG8sbj10aGlzLG09SC5MaChu
-KQptLmMuYShiKQptLlFbMV0uYShjKQppZih0eXBlb2YgYj09InN0cmluZyIpe3Q9bi5iCm4uRUgodD09
-bnVsbD9uLmI9bi56SygpOnQsYixjKX1lbHNlIGlmKHR5cGVvZiBiPT0ibnVtYmVyIiYmKGImMHgzZmZm
-ZmZmKT09PWIpe3M9bi5jCm4uRUgocz09bnVsbD9uLmM9bi56SygpOnMsYixjKX1lbHNle3I9bi5kCmlm
-KHI9PW51bGwpcj1uLmQ9bi56SygpCnE9Si5oZihiKSYweDNmZmZmZmYKcD1uLkJ0KHIscSkKaWYocD09
-bnVsbCluLkVJKHIscSxbbi5IbihiLGMpXSkKZWxzZXtvPW4uRmgocCxiKQppZihvPj0wKXBbb10uYj1j
-CmVsc2UgcC5wdXNoKG4uSG4oYixjKSl9fX0sCks6ZnVuY3Rpb24oYSxiKXt2YXIgdCxzLHI9dGhpcwpI
-LkxoKHIpLkMoIn4oMSwyKSIpLmEoYikKdD1yLmUKcz1yLnIKZm9yKDt0IT1udWxsOyl7Yi4kMih0LmEs
-dC5iKQppZihzIT09ci5yKXRocm93IEguYihQLmE0KHIpKQp0PXQuY319LApFSDpmdW5jdGlvbihhLGIs
-Yyl7dmFyIHQscz10aGlzLHI9SC5MaChzKQpyLmMuYShiKQpyLlFbMV0uYShjKQp0PXMuajIoYSxiKQpp
-Zih0PT1udWxsKXMuRUkoYSxiLHMuSG4oYixjKSkKZWxzZSB0LmI9Y30sCmtzOmZ1bmN0aW9uKCl7dGhp
-cy5yPXRoaXMucisxJjY3MTA4ODYzfSwKSG46ZnVuY3Rpb24oYSxiKXt2YXIgdCxzPXRoaXMscj1ILkxo
-KHMpLHE9bmV3IEguZGIoci5jLmEoYSksci5RWzFdLmEoYikpCmlmKHMuZT09bnVsbClzLmU9cy5mPXEK
-ZWxzZXt0PXMuZgpxLmQ9dApzLmY9dC5jPXF9KytzLmEKcy5rcygpCnJldHVybiBxfSwKRmg6ZnVuY3Rp
-b24oYSxiKXt2YXIgdCxzCmlmKGE9PW51bGwpcmV0dXJuLTEKdD1hLmxlbmd0aApmb3Iocz0wO3M8dDsr
-K3MpaWYoSi5STShhW3NdLmEsYikpcmV0dXJuIHMKcmV0dXJuLTF9LApaOmZ1bmN0aW9uKGEpe3JldHVy
-biBQLm5PKHRoaXMpfSwKajI6ZnVuY3Rpb24oYSxiKXtyZXR1cm4gYVtiXX0sCkJ0OmZ1bmN0aW9uKGEs
-Yil7cmV0dXJuIGFbYl19LApFSTpmdW5jdGlvbihhLGIsYyl7YVtiXT1jfSwKcm46ZnVuY3Rpb24oYSxi
-KXtkZWxldGUgYVtiXX0sClh1OmZ1bmN0aW9uKGEsYil7cmV0dXJuIHRoaXMuajIoYSxiKSE9bnVsbH0s
-CnpLOmZ1bmN0aW9uKCl7dmFyIHQ9Ijxub24taWRlbnRpZmllci1rZXk+IixzPU9iamVjdC5jcmVhdGUo
-bnVsbCkKdGhpcy5FSShzLHQscykKdGhpcy5ybihzLHQpCnJldHVybiBzfSwKJGlGbzoxfQpILmRiLnBy
-b3RvdHlwZT17fQpILmk1LnByb3RvdHlwZT17CmdBOmZ1bmN0aW9uKGEpe3JldHVybiB0aGlzLmEuYX0s
-CmdsMDpmdW5jdGlvbihhKXtyZXR1cm4gdGhpcy5hLmE9PT0wfSwKZ2t6OmZ1bmN0aW9uKGEpe3ZhciB0
-PXRoaXMuYSxzPW5ldyBILk42KHQsdC5yLHRoaXMuJHRpLkMoIk42PDE+IikpCnMuYz10LmUKcmV0dXJu
-IHN9LAp0ZzpmdW5jdGlvbihhLGIpe3JldHVybiB0aGlzLmEueDQoYil9fQpILk42LnByb3RvdHlwZT17
-CmdsOmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuZH0sCkY6ZnVuY3Rpb24oKXt2YXIgdD10aGlzLHM9dC5h
-CmlmKHQuYiE9PXMucil0aHJvdyBILmIoUC5hNChzKSkKZWxzZXtzPXQuYwppZihzPT1udWxsKXt0LnNx
-WShudWxsKQpyZXR1cm4hMX1lbHNle3Quc3FZKHMuYSkKdC5jPXQuYy5jCnJldHVybiEwfX19LApzcVk6
-ZnVuY3Rpb24oYSl7dGhpcy5kPXRoaXMuJHRpLmMuYShhKX0sCiRpQW46MX0KSC5yLnByb3RvdHlwZT17
-CiQxOmZ1bmN0aW9uKGEpe3JldHVybiB0aGlzLmEoYSl9LAokUzoxfQpILmRDLnByb3RvdHlwZT17CiQy
-OmZ1bmN0aW9uKGEsYil7cmV0dXJuIHRoaXMuYShhLGIpfSwKJFM6NDR9Ckgud04ucHJvdG90eXBlPXsK
-JDE6ZnVuY3Rpb24oYSl7cmV0dXJuIHRoaXMuYShILmMoYSkpfSwKJFM6NDB9CkguVlIucHJvdG90eXBl
-PXsKWjpmdW5jdGlvbihhKXtyZXR1cm4iUmVnRXhwLyIrdGhpcy5hKyIvIit0aGlzLmIuZmxhZ3N9LApn
-SGM6ZnVuY3Rpb24oKXt2YXIgdD10aGlzLHM9dC5jCmlmKHMhPW51bGwpcmV0dXJuIHMKcz10LmIKcmV0
-dXJuIHQuYz1ILnY0KHQuYSxzLm11bHRpbGluZSwhcy5pZ25vcmVDYXNlLHMudW5pY29kZSxzLmRvdEFs
-bCwhMCl9LApkZDpmdW5jdGlvbihhLGIpe3JldHVybiBuZXcgSC5LVyh0aGlzLGIsMCl9LApVWjpmdW5j
-dGlvbihhLGIpe3ZhciB0LHM9dGhpcy5nSGMoKQpzLmxhc3RJbmRleD1iCnQ9cy5leGVjKGEpCmlmKHQ9
-PW51bGwpcmV0dXJuIG51bGwKcmV0dXJuIG5ldyBILkVLKHQpfSwKJGl2WDoxLAokaXdMOjF9CkguRUsu
-cHJvdG90eXBlPXsKcTpmdW5jdGlvbihhLGIpe3ZhciB0CkguV1koYikKdD10aGlzLmIKaWYoYj49dC5s
-ZW5ndGgpcmV0dXJuIEguayh0LGIpCnJldHVybiB0W2JdfSwKJGlPZDoxLAokaWliOjF9CkguS1cucHJv
-dG90eXBlPXsKZ2t6OmZ1bmN0aW9uKGEpe3JldHVybiBuZXcgSC5QYih0aGlzLmEsdGhpcy5iLHRoaXMu
-Yyl9fQpILlBiLnByb3RvdHlwZT17CmdsOmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuZH0sCkY6ZnVuY3Rp
-b24oKXt2YXIgdCxzLHIscSxwPXRoaXMsbz1wLmIKaWYobz09bnVsbClyZXR1cm4hMQp0PXAuYwppZih0
-PD1vLmxlbmd0aCl7cz1wLmEKcj1zLlVaKG8sdCkKaWYociE9bnVsbCl7cC5kPXIKbz1yLmIKdD1vLmlu
-ZGV4CnE9dCtvWzBdLmxlbmd0aAppZih0PT09cSl7aWYocy5iLnVuaWNvZGUpe289cC5jCnQ9bysxCnM9
-cC5iCmlmKHQ8cy5sZW5ndGgpe289Si5yWShzKS5tKHMsbykKaWYobz49NTUyOTYmJm88PTU2MzE5KXtv
-PUMueEIubShzLHQpCm89bz49NTYzMjAmJm88PTU3MzQzfWVsc2Ugbz0hMX1lbHNlIG89ITF9ZWxzZSBv
-PSExCnE9KG8/cSsxOnEpKzF9cC5jPXEKcmV0dXJuITB9fXAuYj1wLmQ9bnVsbApyZXR1cm4hMX0sCiRp
-QW46MX0KSC50US5wcm90b3R5cGU9ewpxOmZ1bmN0aW9uKGEsYil7SC5XWShiKQppZihiIT09MClILnZo
-KFAuTzcoYixudWxsKSkKcmV0dXJuIHRoaXMuY30sCiRpT2Q6MX0KSC5ORi5wcm90b3R5cGU9ewpna3o6
-ZnVuY3Rpb24oYSl7cmV0dXJuIG5ldyBILlNkKHRoaXMuYSx0aGlzLmIsdGhpcy5jKX19CkguU2QucHJv
-dG90eXBlPXsKRjpmdW5jdGlvbigpe3ZhciB0LHMscj10aGlzLHE9ci5jLHA9ci5iLG89cC5sZW5ndGgs
-bj1yLmEsbT1uLmxlbmd0aAppZihxK28+bSl7ci5kPW51bGwKcmV0dXJuITF9dD1uLmluZGV4T2YocCxx
-KQppZih0PDApe3IuYz1tKzEKci5kPW51bGwKcmV0dXJuITF9cz10K28Kci5kPW5ldyBILnRRKHQscCkK
-ci5jPXM9PT1yLmM/cysxOnMKcmV0dXJuITB9LApnbDpmdW5jdGlvbigpe3JldHVybiB0aGlzLmR9LAok
-aUFuOjF9CkguZUgucHJvdG90eXBlPXskaWVIOjEsJGllcToxfQpILkxaLnByb3RvdHlwZT17CmdBOmZ1
-bmN0aW9uKGEpe3JldHVybiBhLmxlbmd0aH0sCiRpWGo6MX0KSC5EZy5wcm90b3R5cGU9ewpxOmZ1bmN0
-aW9uKGEsYil7SC5XWShiKQpILm9kKGIsYSxhLmxlbmd0aCkKcmV0dXJuIGFbYl19LApZOmZ1bmN0aW9u
-KGEsYixjKXtILmRqKGMpCkgub2QoYixhLGEubGVuZ3RoKQphW2JdPWN9LAokaWJROjEsCiRpY1g6MSwK
-JGl6TToxfQpILlBnLnByb3RvdHlwZT17Clk6ZnVuY3Rpb24oYSxiLGMpe0guV1koYykKSC5vZChiLGEs
-YS5sZW5ndGgpCmFbYl09Y30sCiRpYlE6MSwKJGljWDoxLAokaXpNOjF9CkgueGoucHJvdG90eXBlPXsK
-cTpmdW5jdGlvbihhLGIpe0guV1koYikKSC5vZChiLGEsYS5sZW5ndGgpCnJldHVybiBhW2JdfX0KSC5k
-RS5wcm90b3R5cGU9ewpxOmZ1bmN0aW9uKGEsYil7SC5XWShiKQpILm9kKGIsYSxhLmxlbmd0aCkKcmV0
-dXJuIGFbYl19fQpILlpBLnByb3RvdHlwZT17CnE6ZnVuY3Rpb24oYSxiKXtILldZKGIpCkgub2QoYixh
-LGEubGVuZ3RoKQpyZXR1cm4gYVtiXX19Ckgud2YucHJvdG90eXBlPXsKcTpmdW5jdGlvbihhLGIpe0gu
-V1koYikKSC5vZChiLGEsYS5sZW5ndGgpCnJldHVybiBhW2JdfX0KSC5QcS5wcm90b3R5cGU9ewpxOmZ1
-bmN0aW9uKGEsYil7SC5XWShiKQpILm9kKGIsYSxhLmxlbmd0aCkKcmV0dXJuIGFbYl19fQpILmVFLnBy
-b3RvdHlwZT17CmdBOmZ1bmN0aW9uKGEpe3JldHVybiBhLmxlbmd0aH0sCnE6ZnVuY3Rpb24oYSxiKXtI
-LldZKGIpCkgub2QoYixhLGEubGVuZ3RoKQpyZXR1cm4gYVtiXX19CkguVjYucHJvdG90eXBlPXsKZ0E6
-ZnVuY3Rpb24oYSl7cmV0dXJuIGEubGVuZ3RofSwKcTpmdW5jdGlvbihhLGIpe0guV1koYikKSC5vZChi
-LGEsYS5sZW5ndGgpCnJldHVybiBhW2JdfSwKJGlWNjoxLAokaW42OjF9CkguUkcucHJvdG90eXBlPXt9
-CkguVlAucHJvdG90eXBlPXt9CkguV0IucHJvdG90eXBlPXt9CkguWkcucHJvdG90eXBlPXt9CkguSmMu
-cHJvdG90eXBlPXsKQzpmdW5jdGlvbihhKXtyZXR1cm4gSC5jRSh2LnR5cGVVbml2ZXJzZSx0aGlzLGEp
-fSwKS3E6ZnVuY3Rpb24oYSl7cmV0dXJuIEgudjUodi50eXBlVW5pdmVyc2UsdGhpcyxhKX19CkguRVQu
-cHJvdG90eXBlPXt9CkgudTkucHJvdG90eXBlPXsKWjpmdW5jdGlvbihhKXtyZXR1cm4gdGhpcy5hfX0K
-SC54LnByb3RvdHlwZT17fQpQLnRoLnByb3RvdHlwZT17CiQxOmZ1bmN0aW9uKGEpe3ZhciB0PXRoaXMu
-YSxzPXQuYQp0LmE9bnVsbApzLiQwKCl9LAokUzoxMH0KUC5oYS5wcm90b3R5cGU9ewokMTpmdW5jdGlv
-bihhKXt2YXIgdCxzCnRoaXMuYS5hPXUuTS5hKGEpCnQ9dGhpcy5iCnM9dGhpcy5jCnQuZmlyc3RDaGls
-ZD90LnJlbW92ZUNoaWxkKHMpOnQuYXBwZW5kQ2hpbGQocyl9LAokUzoyMH0KUC5Wcy5wcm90b3R5cGU9
-ewokMDpmdW5jdGlvbigpe3RoaXMuYS4kMCgpfSwKJEM6IiQwIiwKJFI6MCwKJFM6MH0KUC5GdC5wcm90
-b3R5cGU9ewokMDpmdW5jdGlvbigpe3RoaXMuYS4kMCgpfSwKJEM6IiQwIiwKJFI6MCwKJFM6MH0KUC5X
-My5wcm90b3R5cGU9ewpDWTpmdW5jdGlvbihhLGIpe2lmKHNlbGYuc2V0VGltZW91dCE9bnVsbClzZWxm
-LnNldFRpbWVvdXQoSC50UihuZXcgUC55SCh0aGlzLGIpLDApLGEpCmVsc2UgdGhyb3cgSC5iKFAuTDQo
-ImBzZXRUaW1lb3V0KClgIG5vdCBmb3VuZC4iKSl9fQpQLnlILnByb3RvdHlwZT17CiQwOmZ1bmN0aW9u
-KCl7dGhpcy5iLiQwKCl9LAokQzoiJDAiLAokUjowLAokUzoyfQpQLmloLnByb3RvdHlwZT17CmFNOmZ1
-bmN0aW9uKGEsYil7dmFyIHQscyxyPXRoaXMuJHRpCnIuQygiMS8iKS5hKGIpCnQ9IXRoaXMuYnx8ci5D
-KCJiODwxPiIpLmIoYikKcz10aGlzLmEKaWYodClzLlhmKGIpCmVsc2Ugcy5YMihyLmMuYShiKSl9LAp3
-MDpmdW5jdGlvbihhLGIpe3ZhciB0CmlmKGI9PW51bGwpYj1QLnYwKGEpCnQ9dGhpcy5hCmlmKHRoaXMu
-Yil0LlpMKGEsYikKZWxzZSB0Lk5rKGEsYil9fQpQLldNLnByb3RvdHlwZT17CiQxOmZ1bmN0aW9uKGEp
-e3JldHVybiB0aGlzLmEuJDIoMCxhKX0sCiRTOjQ5fQpQLlNYLnByb3RvdHlwZT17CiQyOmZ1bmN0aW9u
-KGEsYil7dGhpcy5hLiQyKDEsbmV3IEguYnEoYSx1LmwuYShiKSkpfSwKJEM6IiQyIiwKJFI6MiwKJFM6
-MjF9ClAuR3MucHJvdG90eXBlPXsKJDI6ZnVuY3Rpb24oYSxiKXt0aGlzLmEoSC5XWShhKSxiKX0sCiRT
-OjI2fQpQLkZ5LnByb3RvdHlwZT17Clo6ZnVuY3Rpb24oYSl7cmV0dXJuIkl0ZXJhdGlvbk1hcmtlcigi
-K3RoaXMuYisiLCAiK0guZCh0aGlzLmEpKyIpIn19ClAuR1YucHJvdG90eXBlPXsKZ2w6ZnVuY3Rpb24o
-KXt2YXIgdD10aGlzLmMKaWYodD09bnVsbClyZXR1cm4gdGhpcy5iCnJldHVybiB0aGlzLiR0aS5jLmEo
-dC5nbCgpKX0sCkY6ZnVuY3Rpb24oKXt2YXIgdCxzLHIscSxwPXRoaXMKZm9yKDshMDspe3Q9cC5jCmlm
-KHQhPW51bGwpaWYodC5GKCkpcmV0dXJuITAKZWxzZSBwLmM9bnVsbApzPWZ1bmN0aW9uKGEsYixjKXt2
-YXIgbyxuPWIKd2hpbGUodHJ1ZSl0cnl7cmV0dXJuIGEobixvKX1jYXRjaChtKXtvPW0Kbj1jfX0ocC5h
-LDAsMSkKaWYocyBpbnN0YW5jZW9mIFAuRnkpe3I9cy5iCmlmKHI9PT0yKXt0PXAuZAppZih0PT1udWxs
-fHx0Lmxlbmd0aD09PTApe3Auc0VDKG51bGwpCnJldHVybiExfWlmKDA+PXQubGVuZ3RoKXJldHVybiBI
-LmsodCwtMSkKcC5hPXQucG9wKCkKY29udGludWV9ZWxzZXt0PXMuYQppZihyPT09Myl0aHJvdyB0CmVs
-c2V7cT1KLklUKHQpCmlmKHEgaW5zdGFuY2VvZiBQLkdWKXt0PXAuZAppZih0PT1udWxsKXQ9cC5kPVtd
-CkMuTm0uaSh0LHAuYSkKcC5hPXEuYQpjb250aW51ZX1lbHNle3AuYz1xCmNvbnRpbnVlfX19fWVsc2V7
-cC5zRUMocykKcmV0dXJuITB9fXJldHVybiExfSwKc0VDOmZ1bmN0aW9uKGEpe3RoaXMuYj10aGlzLiR0
-aS5jLmEoYSl9LAokaUFuOjF9ClAucTQucHJvdG90eXBlPXsKZ2t6OmZ1bmN0aW9uKGEpe3JldHVybiBu
-ZXcgUC5HVih0aGlzLmEoKSx0aGlzLiR0aS5DKCJHVjwxPiIpKX19ClAuYjgucHJvdG90eXBlPXt9ClAu
-UGYucHJvdG90eXBlPXsKdzA6ZnVuY3Rpb24oYSxiKXt2YXIgdApQLlVJKGEsImVycm9yIix1LkspCnQ9
-dGhpcy5hCmlmKHQuYSE9PTApdGhyb3cgSC5iKFAuUFYoIkZ1dHVyZSBhbHJlYWR5IGNvbXBsZXRlZCIp
-KQp0Lk5rKGEsYj09bnVsbD9QLnYwKGEpOmIpfSwKcG06ZnVuY3Rpb24oYSl7cmV0dXJuIHRoaXMudzAo
-YSxudWxsKX19ClAuWmYucHJvdG90eXBlPXsKYU06ZnVuY3Rpb24oYSxiKXt2YXIgdAp0aGlzLiR0aS5D
-KCIxLyIpLmEoYikKdD10aGlzLmEKaWYodC5hIT09MCl0aHJvdyBILmIoUC5QVigiRnV0dXJlIGFscmVh
-ZHkgY29tcGxldGVkIikpCnQuWGYoYil9fQpQLkZlLnByb3RvdHlwZT17CkhSOmZ1bmN0aW9uKGEpe2lm
-KCh0aGlzLmMmMTUpIT09NilyZXR1cm4hMApyZXR1cm4gdGhpcy5iLmIuYnYodS5hbC5hKHRoaXMuZCks
-YS5hLHUueSx1LkspfSwKS3c6ZnVuY3Rpb24oYSl7dmFyIHQ9dGhpcy5lLHM9dS56LHI9dS5LLHE9dGhp
-cy4kdGkuQygiMi8iKSxwPXRoaXMuYi5iCmlmKHUuYWcuYih0KSlyZXR1cm4gcS5hKHAucnAodCxhLmEs
-YS5iLHMscix1LmwpKQplbHNlIHJldHVybiBxLmEocC5idih1LmJJLmEodCksYS5hLHMscikpfX0KUC52
-cy5wcm90b3R5cGU9ewpTcTpmdW5jdGlvbihhLGIsYyl7dmFyIHQscyxyLHE9dGhpcy4kdGkKcS5LcShj
-KS5DKCIxLygyKSIpLmEoYSkKdD0kLlgzCmlmKHQhPT1DLk5VKXtjLkMoIkA8MC8+IikuS3EocS5jKS5D
-KCIxKDIpIikuYShhKQppZihiIT1udWxsKWI9UC5WSChiLHQpfXM9bmV3IFAudnMoJC5YMyxjLkMoInZz
-PDA+IikpCnI9Yj09bnVsbD8xOjMKdGhpcy54ZihuZXcgUC5GZShzLHIsYSxiLHEuQygiQDwxPiIpLktx
-KGMpLkMoIkZlPDEsMj4iKSkpCnJldHVybiBzfSwKVzc6ZnVuY3Rpb24oYSxiKXtyZXR1cm4gdGhpcy5T
-cShhLG51bGwsYil9LApRZDpmdW5jdGlvbihhLGIsYyl7dmFyIHQscz10aGlzLiR0aQpzLktxKGMpLkMo
-IjEvKDIpIikuYShhKQp0PW5ldyBQLnZzKCQuWDMsYy5DKCJ2czwwPiIpKQp0aGlzLnhmKG5ldyBQLkZl
-KHQsMTksYSxiLHMuQygiQDwxPiIpLktxKGMpLkMoIkZlPDEsMj4iKSkpCnJldHVybiB0fSwKeGY6ZnVu
-Y3Rpb24oYSl7dmFyIHQscz10aGlzLHI9cy5hCmlmKHI8PTEpe2EuYT11LnguYShzLmMpCnMuYz1hfWVs
-c2V7aWYocj09PTIpe3Q9dS5fLmEocy5jKQpyPXQuYQppZihyPDQpe3QueGYoYSkKcmV0dXJufXMuYT1y
-CnMuYz10LmN9UC5UayhudWxsLG51bGwscy5iLHUuTS5hKG5ldyBQLmRhKHMsYSkpKX19LApqUTpmdW5j
-dGlvbihhKXt2YXIgdCxzLHIscSxwLG89dGhpcyxuPXt9Cm4uYT1hCmlmKGE9PW51bGwpcmV0dXJuCnQ9
-by5hCmlmKHQ8PTEpe3M9dS54LmEoby5jKQpyPW8uYz1hCmlmKHMhPW51bGwpe2Zvcig7cT1yLmEscSE9
-bnVsbDtyPXEpO3IuYT1zfX1lbHNle2lmKHQ9PT0yKXtwPXUuXy5hKG8uYykKdD1wLmEKaWYodDw0KXtw
-LmpRKGEpCnJldHVybn1vLmE9dApvLmM9cC5jfW4uYT1vLk44KGEpClAuVGsobnVsbCxudWxsLG8uYix1
-Lk0uYShuZXcgUC5vUShuLG8pKSl9fSwKYWg6ZnVuY3Rpb24oKXt2YXIgdD11LnguYSh0aGlzLmMpCnRo
-aXMuYz1udWxsCnJldHVybiB0aGlzLk44KHQpfSwKTjg6ZnVuY3Rpb24oYSl7dmFyIHQscyxyCmZvcih0
-PWEscz1udWxsO3QhPW51bGw7cz10LHQ9cil7cj10LmEKdC5hPXN9cmV0dXJuIHN9LApISDpmdW5jdGlv
-bihhKXt2YXIgdCxzPXRoaXMscj1zLiR0aQpyLkMoIjEvIikuYShhKQppZihyLkMoImI4PDE+IikuYihh
-KSlpZihyLmIoYSkpUC5BOShhLHMpCmVsc2UgUC5rMyhhLHMpCmVsc2V7dD1zLmFoKCkKci5jLmEoYSkK
-cy5hPTQKcy5jPWEKUC5IWihzLHQpfX0sClgyOmZ1bmN0aW9uKGEpe3ZhciB0LHM9dGhpcwpzLiR0aS5j
-LmEoYSkKdD1zLmFoKCkKcy5hPTQKcy5jPWEKUC5IWihzLHQpfSwKWkw6ZnVuY3Rpb24oYSxiKXt2YXIg
-dCxzLHI9dGhpcwp1LmwuYShiKQp0PXIuYWgoKQpzPVAuVGwoYSxiKQpyLmE9OApyLmM9cwpQLkhaKHIs
-dCl9LApYZjpmdW5jdGlvbihhKXt2YXIgdD10aGlzLHM9dC4kdGkKcy5DKCIxLyIpLmEoYSkKaWYocy5D
-KCJiODwxPiIpLmIoYSkpe3QuY1UoYSkKcmV0dXJufXQuYT0xClAuVGsobnVsbCxudWxsLHQuYix1Lk0u
-YShuZXcgUC5ySCh0LGEpKSl9LApjVTpmdW5jdGlvbihhKXt2YXIgdD10aGlzLHM9dC4kdGkKcy5DKCJi
-ODwxPiIpLmEoYSkKaWYocy5iKGEpKXtpZihhLmE9PT04KXt0LmE9MQpQLlRrKG51bGwsbnVsbCx0LmIs
-dS5NLmEobmV3IFAuS0YodCxhKSkpfWVsc2UgUC5BOShhLHQpCnJldHVybn1QLmszKGEsdCl9LApOazpm
-dW5jdGlvbihhLGIpe3RoaXMuYT0xClAuVGsobnVsbCxudWxsLHRoaXMuYix1Lk0uYShuZXcgUC5aTCh0
-aGlzLGEsYikpKX0sCiRpYjg6MX0KUC5kYS5wcm90b3R5cGU9ewokMDpmdW5jdGlvbigpe1AuSFoodGhp
-cy5hLHRoaXMuYil9LAokUzowfQpQLm9RLnByb3RvdHlwZT17CiQwOmZ1bmN0aW9uKCl7UC5IWih0aGlz
-LmIsdGhpcy5hLmEpfSwKJFM6MH0KUC5wVi5wcm90b3R5cGU9ewokMTpmdW5jdGlvbihhKXt2YXIgdD10
-aGlzLmEKdC5hPTAKdC5ISChhKX0sCiRTOjEwfQpQLlU3LnByb3RvdHlwZT17CiQyOmZ1bmN0aW9uKGEs
-Yil7dS5sLmEoYikKdGhpcy5hLlpMKGEsYil9LAokMTpmdW5jdGlvbihhKXtyZXR1cm4gdGhpcy4kMihh
-LG51bGwpfSwKJEM6IiQyIiwKJEQ6ZnVuY3Rpb24oKXtyZXR1cm5bbnVsbF19LAokUzoyOX0KUC52ci5w
-cm90b3R5cGU9ewokMDpmdW5jdGlvbigpe3RoaXMuYS5aTCh0aGlzLmIsdGhpcy5jKX0sCiRTOjB9ClAu
-ckgucHJvdG90eXBlPXsKJDA6ZnVuY3Rpb24oKXt2YXIgdD10aGlzLmEKdC5YMih0LiR0aS5jLmEodGhp
-cy5iKSl9LAokUzowfQpQLktGLnByb3RvdHlwZT17CiQwOmZ1bmN0aW9uKCl7UC5BOSh0aGlzLmIsdGhp
-cy5hKX0sCiRTOjB9ClAuWkwucHJvdG90eXBlPXsKJDA6ZnVuY3Rpb24oKXt0aGlzLmEuWkwodGhpcy5i
-LHRoaXMuYyl9LAokUzowfQpQLlJULnByb3RvdHlwZT17CiQwOmZ1bmN0aW9uKCl7dmFyIHQscyxyLHEs
-cCxvLG49dGhpcyxtPW51bGwKdHJ5e3I9bi5jCm09ci5iLmIuenoodS5mTy5hKHIuZCksdS56KX1jYXRj
-aChxKXt0PUguUnUocSkKcz1ILnRzKHEpCmlmKG4uZCl7cj11Lm4uYShuLmEuYS5jKS5hCnA9dApwPXI9
-PW51bGw/cD09bnVsbDpyPT09cApyPXB9ZWxzZSByPSExCnA9bi5iCmlmKHIpcC5iPXUubi5hKG4uYS5h
-LmMpCmVsc2UgcC5iPVAuVGwodCxzKQpwLmE9ITAKcmV0dXJufWlmKHUuYy5iKG0pKXtpZihtIGluc3Rh
-bmNlb2YgUC52cyYmbS5hPj00KXtpZihtLmE9PT04KXtyPW4uYgpyLmI9dS5uLmEobS5jKQpyLmE9ITB9
-cmV0dXJufW89bi5hLmEKcj1uLmIKci5iPW0uVzcobmV3IFAualoobyksdS56KQpyLmE9ITF9fSwKJFM6
-Mn0KUC5qWi5wcm90b3R5cGU9ewokMTpmdW5jdGlvbihhKXtyZXR1cm4gdGhpcy5hfSwKJFM6Mzl9ClAu
-cnEucHJvdG90eXBlPXsKJDA6ZnVuY3Rpb24oKXt2YXIgdCxzLHIscSxwLG8sbixtPXRoaXMKdHJ5e3I9
-bS5iCnE9ci4kdGkKcD1xLmMKbz1wLmEobS5jKQptLmEuYj1yLmIuYi5idihxLkMoIjIvKDEpIikuYShy
-LmQpLG8scS5DKCIyLyIpLHApfWNhdGNoKG4pe3Q9SC5SdShuKQpzPUgudHMobikKcj1tLmEKci5iPVAu
-VGwodCxzKQpyLmE9ITB9fSwKJFM6Mn0KUC5SVy5wcm90b3R5cGU9ewokMDpmdW5jdGlvbigpe3ZhciB0
-LHMscixxLHAsbyxuLG0sbD10aGlzCnRyeXt0PXUubi5hKGwuYS5hLmMpCnE9bC5jCmlmKEgub1QocS5I
-Uih0KSkmJnEuZSE9bnVsbCl7cD1sLmIKcC5iPXEuS3codCkKcC5hPSExfX1jYXRjaChvKXtzPUguUnUo
-bykKcj1ILnRzKG8pCnE9dS5uLmEobC5hLmEuYykKcD1xLmEKbj1zCm09bC5iCmlmKHA9PW51bGw/bj09
-bnVsbDpwPT09biltLmI9cQplbHNlIG0uYj1QLlRsKHMscikKbS5hPSEwfX0sCiRTOjJ9ClAuT00ucHJv
-dG90eXBlPXt9ClAucWgucHJvdG90eXBlPXsKZ0E6ZnVuY3Rpb24oYSl7dmFyIHQscyxyPXRoaXMscT17
-fSxwPW5ldyBQLnZzKCQuWDMsdS5mSikKcS5hPTAKdD1ILkxoKHIpCnM9dC5DKCJ+KDEpIikuYShuZXcg
-UC5CNShxLHIpKQp1Lk0uYShuZXcgUC51TyhxLHApKQpXLkpFKHIuYSxyLmIscywhMSx0LmMpCnJldHVy
-biBwfX0KUC5CNS5wcm90b3R5cGU9ewokMTpmdW5jdGlvbihhKXtILkxoKHRoaXMuYikuYy5hKGEpOysr
-dGhpcy5hLmF9LAokUzpmdW5jdGlvbigpe3JldHVybiBILkxoKHRoaXMuYikuQygiYzgoMSkiKX19ClAu
-dU8ucHJvdG90eXBlPXsKJDA6ZnVuY3Rpb24oKXt0aGlzLmIuSEgodGhpcy5hLmEpfSwKJFM6MH0KUC5N
-Ty5wcm90b3R5cGU9e30KUC5rVC5wcm90b3R5cGU9e30KUC54SS5wcm90b3R5cGU9e30KUC5PSC5wcm90
-b3R5cGU9ewpaOmZ1bmN0aW9uKGEpe3JldHVybiBILmQodGhpcy5hKX0sCiRpWFM6MSwKZ0lJOmZ1bmN0
-aW9uKCl7cmV0dXJuIHRoaXMuYn19ClAubTAucHJvdG90eXBlPXskaUpCOjF9ClAucEsucHJvdG90eXBl
-PXsKJDA6ZnVuY3Rpb24oKXt2YXIgdCxzPXRoaXMuYSxyPXMuYgppZihyPT1udWxsKXRocm93IEguYihz
-LmEpCnQ9SC5iKHMuYSkKdC5zdGFjaz1yLlooMCkKdGhyb3cgdH0sCiRTOjB9ClAuSmkucHJvdG90eXBl
-PXsKYkg6ZnVuY3Rpb24oYSl7dmFyIHQscyxyLHE9bnVsbAp1Lk0uYShhKQp0cnl7aWYoQy5OVT09PSQu
-WDMpe2EuJDAoKQpyZXR1cm59UC5UOChxLHEsdGhpcyxhLHUuSCl9Y2F0Y2gocil7dD1ILlJ1KHIpCnM9
-SC50cyhyKQpQLkwyKHEscSx0aGlzLHQsdS5sLmEocykpfX0sCkRsOmZ1bmN0aW9uKGEsYixjKXt2YXIg
-dCxzLHIscT1udWxsCmMuQygifigwKSIpLmEoYSkKYy5hKGIpCnRyeXtpZihDLk5VPT09JC5YMyl7YS4k
-MShiKQpyZXR1cm59UC55dihxLHEsdGhpcyxhLGIsdS5ILGMpfWNhdGNoKHIpe3Q9SC5SdShyKQpzPUgu
-dHMocikKUC5MMihxLHEsdGhpcyx0LHUubC5hKHMpKX19LApSVDpmdW5jdGlvbihhLGIpe3JldHVybiBu
-ZXcgUC5oaih0aGlzLGIuQygiMCgpIikuYShhKSxiKX0sCkdZOmZ1bmN0aW9uKGEpe3JldHVybiBuZXcg
-UC5WcCh0aGlzLHUuTS5hKGEpKX0sClB5OmZ1bmN0aW9uKGEsYil7cmV0dXJuIG5ldyBQLk9SKHRoaXMs
-Yi5DKCJ+KDApIikuYShhKSxiKX0sCnE6ZnVuY3Rpb24oYSxiKXtyZXR1cm4gbnVsbH0sCnp6OmZ1bmN0
-aW9uKGEsYil7Yi5DKCIwKCkiKS5hKGEpCmlmKCQuWDM9PT1DLk5VKXJldHVybiBhLiQwKCkKcmV0dXJu
-IFAuVDgobnVsbCxudWxsLHRoaXMsYSxiKX0sCmJ2OmZ1bmN0aW9uKGEsYixjLGQpe2MuQygiQDwwPiIp
-LktxKGQpLkMoIjEoMikiKS5hKGEpCmQuYShiKQppZigkLlgzPT09Qy5OVSlyZXR1cm4gYS4kMShiKQpy
-ZXR1cm4gUC55dihudWxsLG51bGwsdGhpcyxhLGIsYyxkKX0sCnJwOmZ1bmN0aW9uKGEsYixjLGQsZSxm
-KXtkLkMoIkA8MD4iKS5LcShlKS5LcShmKS5DKCIxKDIsMykiKS5hKGEpCmUuYShiKQpmLmEoYykKaWYo
-JC5YMz09PUMuTlUpcmV0dXJuIGEuJDIoYixjKQpyZXR1cm4gUC5ReChudWxsLG51bGwsdGhpcyxhLGIs
-YyxkLGUsZil9LApMajpmdW5jdGlvbihhLGIsYyxkKXtyZXR1cm4gYi5DKCJAPDA+IikuS3EoYykuS3Eo
-ZCkuQygiMSgyLDMpIikuYShhKX19ClAuaGoucHJvdG90eXBlPXsKJDA6ZnVuY3Rpb24oKXtyZXR1cm4g
-dGhpcy5hLnp6KHRoaXMuYix0aGlzLmMpfSwKJFM6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5jLkMoIjAo
-KSIpfX0KUC5WcC5wcm90b3R5cGU9ewokMDpmdW5jdGlvbigpe3JldHVybiB0aGlzLmEuYkgodGhpcy5i
-KX0sCiRTOjJ9ClAuT1IucHJvdG90eXBlPXsKJDE6ZnVuY3Rpb24oYSl7dmFyIHQ9dGhpcy5jCnJldHVy
-biB0aGlzLmEuRGwodGhpcy5iLHQuYShhKSx0KX0sCiRTOmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuYy5D
-KCJ+KDApIil9fQpQLmI2LnByb3RvdHlwZT17CmdrejpmdW5jdGlvbihhKXt2YXIgdD10aGlzLHM9bmV3
-IFAubG0odCx0LnIsSC5MaCh0KS5DKCJsbTwxPiIpKQpzLmM9dC5lCnJldHVybiBzfSwKZ0E6ZnVuY3Rp
-b24oYSl7cmV0dXJuIHRoaXMuYX0sCnRnOmZ1bmN0aW9uKGEsYil7dmFyIHQscwppZih0eXBlb2YgYj09
-InN0cmluZyImJmIhPT0iX19wcm90b19fIil7dD10aGlzLmIKaWYodD09bnVsbClyZXR1cm4hMQpyZXR1
-cm4gdS5ELmEodFtiXSkhPW51bGx9ZWxzZXtzPXRoaXMuUFIoYikKcmV0dXJuIHN9fSwKUFI6ZnVuY3Rp
-b24oYSl7dmFyIHQ9dGhpcy5kCmlmKHQ9PW51bGwpcmV0dXJuITEKcmV0dXJuIHRoaXMuREYodFt0aGlz
-Lk4oYSldLGEpPj0wfSwKaTpmdW5jdGlvbihhLGIpe3ZhciB0LHMscj10aGlzCkguTGgocikuYy5hKGIp
-CmlmKHR5cGVvZiBiPT0ic3RyaW5nIiYmYiE9PSJfX3Byb3RvX18iKXt0PXIuYgpyZXR1cm4gci5iUSh0
-PT1udWxsP3IuYj1QLlQyKCk6dCxiKX1lbHNlIGlmKHR5cGVvZiBiPT0ibnVtYmVyIiYmKGImMTA3Mzc0
-MTgyMyk9PT1iKXtzPXIuYwpyZXR1cm4gci5iUShzPT1udWxsP3IuYz1QLlQyKCk6cyxiKX1lbHNlIHJl
-dHVybiByLkI3KGIpfSwKQjc6ZnVuY3Rpb24oYSl7dmFyIHQscyxyLHE9dGhpcwpILkxoKHEpLmMuYShh
-KQp0PXEuZAppZih0PT1udWxsKXQ9cS5kPVAuVDIoKQpzPXEuTihhKQpyPXRbc10KaWYocj09bnVsbCl0
-W3NdPVtxLnlvKGEpXQplbHNle2lmKHEuREYocixhKT49MClyZXR1cm4hMQpyLnB1c2gocS55byhhKSl9
-cmV0dXJuITB9LApSOmZ1bmN0aW9uKGEsYil7dmFyIHQ9dGhpcwppZih0eXBlb2YgYj09InN0cmluZyIm
-JmIhPT0iX19wcm90b19fIilyZXR1cm4gdC5INCh0LmIsYikKZWxzZSBpZih0eXBlb2YgYj09Im51bWJl
-ciImJihiJjEwNzM3NDE4MjMpPT09YilyZXR1cm4gdC5INCh0LmMsYikKZWxzZSByZXR1cm4gdC5xZyhi
-KX0sCnFnOmZ1bmN0aW9uKGEpe3ZhciB0LHMscixxLHA9dGhpcyxvPXAuZAppZihvPT1udWxsKXJldHVy
-biExCnQ9cC5OKGEpCnM9b1t0XQpyPXAuREYocyxhKQppZihyPDApcmV0dXJuITEKcT1zLnNwbGljZShy
-LDEpWzBdCmlmKDA9PT1zLmxlbmd0aClkZWxldGUgb1t0XQpwLkdTKHEpCnJldHVybiEwfSwKYlE6ZnVu
-Y3Rpb24oYSxiKXtILkxoKHRoaXMpLmMuYShiKQppZih1LkQuYShhW2JdKSE9bnVsbClyZXR1cm4hMQph
-W2JdPXRoaXMueW8oYikKcmV0dXJuITB9LApINDpmdW5jdGlvbihhLGIpe3ZhciB0CmlmKGE9PW51bGwp
-cmV0dXJuITEKdD11LkQuYShhW2JdKQppZih0PT1udWxsKXJldHVybiExCnRoaXMuR1ModCkKZGVsZXRl
-IGFbYl0KcmV0dXJuITB9LApTOmZ1bmN0aW9uKCl7dGhpcy5yPTEwNzM3NDE4MjMmdGhpcy5yKzF9LAp5
-bzpmdW5jdGlvbihhKXt2YXIgdCxzPXRoaXMscj1uZXcgUC5ibihILkxoKHMpLmMuYShhKSkKaWYocy5l
-PT1udWxsKXMuZT1zLmY9cgplbHNle3Q9cy5mCnIuYz10CnMuZj10LmI9cn0rK3MuYQpzLlMoKQpyZXR1
-cm4gcn0sCkdTOmZ1bmN0aW9uKGEpe3ZhciB0PXRoaXMscz1hLmMscj1hLmIKaWYocz09bnVsbCl0LmU9
-cgplbHNlIHMuYj1yCmlmKHI9PW51bGwpdC5mPXMKZWxzZSByLmM9czstLXQuYQp0LlMoKX0sCk46ZnVu
-Y3Rpb24oYSl7cmV0dXJuIEouaGYoYSkmMTA3Mzc0MTgyM30sCkRGOmZ1bmN0aW9uKGEsYil7dmFyIHQs
-cwppZihhPT1udWxsKXJldHVybi0xCnQ9YS5sZW5ndGgKZm9yKHM9MDtzPHQ7KytzKWlmKEouUk0oYVtz
-XS5hLGIpKXJldHVybiBzCnJldHVybi0xfX0KUC5ibi5wcm90b3R5cGU9e30KUC5sbS5wcm90b3R5cGU9
-ewpnbDpmdW5jdGlvbigpe3JldHVybiB0aGlzLmR9LApGOmZ1bmN0aW9uKCl7dmFyIHQ9dGhpcyxzPXQu
-YQppZih0LmIhPT1zLnIpdGhyb3cgSC5iKFAuYTQocykpCmVsc2V7cz10LmMKaWYocz09bnVsbCl7dC5z
-aihudWxsKQpyZXR1cm4hMX1lbHNle3Quc2oodC4kdGkuYy5hKHMuYSkpCnQuYz10LmMuYgpyZXR1cm4h
-MH19fSwKc2o6ZnVuY3Rpb24oYSl7dGhpcy5kPXRoaXMuJHRpLmMuYShhKX0sCiRpQW46MX0KUC5tVy5w
-cm90b3R5cGU9e30KUC51eS5wcm90b3R5cGU9eyRpYlE6MSwkaWNYOjEsJGl6TToxfQpQLmxELnByb3Rv
-dHlwZT17CmdrejpmdW5jdGlvbihhKXtyZXR1cm4gbmV3IEguYTcoYSx0aGlzLmdBKGEpLEgucShhKS5D
-KCJhNzxsRC5FPiIpKX0sCkU6ZnVuY3Rpb24oYSxiKXtyZXR1cm4gdGhpcy5xKGEsYil9LApLOmZ1bmN0
-aW9uKGEsYil7dmFyIHQscwpILnEoYSkuQygifihsRC5FKSIpLmEoYikKdD10aGlzLmdBKGEpCmZvcihz
-PTA7czx0Oysrcyl7Yi4kMSh0aGlzLnEoYSxzKSkKaWYodCE9PXRoaXMuZ0EoYSkpdGhyb3cgSC5iKFAu
-YTQoYSkpfX0sCmdvcjpmdW5jdGlvbihhKXtyZXR1cm4gdGhpcy5nQShhKSE9PTB9LApFMjpmdW5jdGlv
-bihhLGIsYyl7dmFyIHQ9SC5xKGEpCnJldHVybiBuZXcgSC5sSihhLHQuS3EoYykuQygiMShsRC5FKSIp
-LmEoYiksdC5DKCJAPGxELkU+IikuS3EoYykuQygibEo8MSwyPiIpKX0sCmR1OmZ1bmN0aW9uKGEsYixj
-LGQpe3ZhciB0CkgucShhKS5DKCJsRC5FIikuYShkKQpQLmpCKGIsYyx0aGlzLmdBKGEpKQpmb3IodD1i
-O3Q8YzsrK3QpdGhpcy5ZKGEsdCxkKX0sClo6ZnVuY3Rpb24oYSl7cmV0dXJuIFAuV0UoYSwiWyIsIl0i
-KX19ClAuaWwucHJvdG90eXBlPXt9ClAucmEucHJvdG90eXBlPXsKJDI6ZnVuY3Rpb24oYSxiKXt2YXIg
-dCxzPXRoaXMuYQppZighcy5hKXRoaXMuYi5hKz0iLCAiCnMuYT0hMQpzPXRoaXMuYgp0PXMuYSs9SC5k
-KGEpCnMuYT10KyI6ICIKcy5hKz1ILmQoYil9LAokUzo0fQpQLllrLnByb3RvdHlwZT17Cks6ZnVuY3Rp
-b24oYSxiKXt2YXIgdCxzCkguTGgodGhpcykuQygifihZay5LLFlrLlYpIikuYShiKQpmb3IodD1KLklU
-KHRoaXMuZ1YoKSk7dC5GKCk7KXtzPXQuZ2woKQpiLiQyKHMsdGhpcy5xKDAscykpfX0sCmdQdTpmdW5j
-dGlvbihhKXtyZXR1cm4gSi5NMSh0aGlzLmdWKCksbmV3IFAueVEodGhpcyksSC5MaCh0aGlzKS5DKCJO
-MzxZay5LLFlrLlY+IikpfSwKeDQ6ZnVuY3Rpb24oYSl7cmV0dXJuIEouemwodGhpcy5nVigpLGEpfSwK
-Z0E6ZnVuY3Rpb24oYSl7cmV0dXJuIEouSCh0aGlzLmdWKCkpfSwKZ2wwOmZ1bmN0aW9uKGEpe3JldHVy
-biBKLkNpKHRoaXMuZ1YoKSl9LApaOmZ1bmN0aW9uKGEpe3JldHVybiBQLm5PKHRoaXMpfSwKJGlaMDox
-fQpQLnlRLnByb3RvdHlwZT17CiQxOmZ1bmN0aW9uKGEpe3ZhciB0PXRoaXMuYSxzPUguTGgodCkKcy5D
-KCJZay5LIikuYShhKQpyZXR1cm4gbmV3IFAuTjMoYSx0LnEoMCxhKSxzLkMoIkA8WWsuSz4iKS5LcShz
-LkMoIllrLlYiKSkuQygiTjM8MSwyPiIpKX0sCiRTOmZ1bmN0aW9uKCl7cmV0dXJuIEguTGgodGhpcy5h
-KS5DKCJOMzxZay5LLFlrLlY+KFlrLkspIil9fQpQLktQLnByb3RvdHlwZT17Clk6ZnVuY3Rpb24oYSxi
-LGMpe3ZhciB0PUguTGgodGhpcykKdC5jLmEoYikKdC5RWzFdLmEoYykKdGhyb3cgSC5iKFAuTDQoIkNh
-bm5vdCBtb2RpZnkgdW5tb2RpZmlhYmxlIG1hcCIpKX19ClAuUG4ucHJvdG90eXBlPXsKcTpmdW5jdGlv
-bihhLGIpe3JldHVybiB0aGlzLmEucSgwLGIpfSwKWTpmdW5jdGlvbihhLGIsYyl7dmFyIHQ9SC5MaCh0
-aGlzKQp0aGlzLmEuWSgwLHQuYy5hKGIpLHQuUVsxXS5hKGMpKX0sCng0OmZ1bmN0aW9uKGEpe3JldHVy
-biB0aGlzLmEueDQoYSl9LApLOmZ1bmN0aW9uKGEsYil7dGhpcy5hLksoMCxILkxoKHRoaXMpLkMoIn4o
-MSwyKSIpLmEoYikpfSwKZ2wwOmZ1bmN0aW9uKGEpe3ZhciB0PXRoaXMuYQpyZXR1cm4gdC5nbDAodCl9
-LApnQTpmdW5jdGlvbihhKXt2YXIgdD10aGlzLmEKcmV0dXJuIHQuZ0EodCl9LApaOmZ1bmN0aW9uKGEp
-e3JldHVybiBKLkFjKHRoaXMuYSl9LApnUHU6ZnVuY3Rpb24oYSl7dmFyIHQ9dGhpcy5hCnJldHVybiB0
-LmdQdSh0KX0sCiRpWjA6MX0KUC5Hai5wcm90b3R5cGU9e30KUC5NYS5wcm90b3R5cGU9ewpaOmZ1bmN0
-aW9uKGEpe3JldHVybiBQLldFKHRoaXMsInsiLCJ9Iil9fQpQLlZqLnByb3RvdHlwZT17JGliUToxLCRp
-Y1g6MSwkaXh1OjF9ClAuWHYucHJvdG90eXBlPXsKRlY6ZnVuY3Rpb24oYSxiKXt2YXIgdApmb3IodD1K
-LklUKEguTGgodGhpcykuQygiY1g8MT4iKS5hKGIpKTt0LkYoKTspdGhpcy5pKDAsdC5nbCgpKX0sClo6
-ZnVuY3Rpb24oYSl7cmV0dXJuIFAuV0UodGhpcywieyIsIn0iKX0sCkg6ZnVuY3Rpb24oYSxiKXt2YXIg
-dCxzPVAucmoodGhpcyx0aGlzLnIsSC5MaCh0aGlzKS5jKQppZighcy5GKCkpcmV0dXJuIiIKaWYoYj09
-PSIiKXt0PSIiCmRvIHQrPUguZChzLmQpCndoaWxlKHMuRigpKX1lbHNle3Q9SC5kKHMuZCkKZm9yKDtz
-LkYoKTspdD10K2IrSC5kKHMuZCl9cmV0dXJuIHQuY2hhckNvZGVBdCgwKT09MD90OnR9LAokaWJROjEs
-CiRpY1g6MSwKJGl4dToxfQpQLm5ZLnByb3RvdHlwZT17fQpQLlRDLnByb3RvdHlwZT17fQpQLlJVLnBy
-b3RvdHlwZT17fQpQLnV3LnByb3RvdHlwZT17CnE6ZnVuY3Rpb24oYSxiKXt2YXIgdCxzPXRoaXMuYgpp
-ZihzPT1udWxsKXJldHVybiB0aGlzLmMucSgwLGIpCmVsc2UgaWYodHlwZW9mIGIhPSJzdHJpbmciKXJl
-dHVybiBudWxsCmVsc2V7dD1zW2JdCnJldHVybiB0eXBlb2YgdD09InVuZGVmaW5lZCI/dGhpcy5mYihi
-KTp0fX0sCmdBOmZ1bmN0aW9uKGEpe3JldHVybiB0aGlzLmI9PW51bGw/dGhpcy5jLmE6dGhpcy5DZigp
-Lmxlbmd0aH0sCmdsMDpmdW5jdGlvbihhKXtyZXR1cm4gdGhpcy5nQSh0aGlzKT09PTB9LApnVjpmdW5j
-dGlvbigpe2lmKHRoaXMuYj09bnVsbCl7dmFyIHQ9dGhpcy5jCnJldHVybiBuZXcgSC5pNSh0LEguTGgo
-dCkuQygiaTU8MT4iKSl9cmV0dXJuIG5ldyBQLmk4KHRoaXMpfSwKWTpmdW5jdGlvbihhLGIsYyl7dmFy
-IHQscyxyPXRoaXMKaWYoci5iPT1udWxsKXIuYy5ZKDAsYixjKQplbHNlIGlmKHIueDQoYikpe3Q9ci5i
-CnRbYl09YwpzPXIuYQppZihzPT1udWxsP3QhPW51bGw6cyE9PXQpc1tiXT1udWxsfWVsc2Ugci5YSygp
-LlkoMCxiLGMpfSwKeDQ6ZnVuY3Rpb24oYSl7aWYodGhpcy5iPT1udWxsKXJldHVybiB0aGlzLmMueDQo
-YSkKcmV0dXJuIE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbCh0aGlzLmEsYSl9LApL
-OmZ1bmN0aW9uKGEsYil7dmFyIHQscyxyLHEscD10aGlzCnUuY0EuYShiKQppZihwLmI9PW51bGwpcmV0
-dXJuIHAuYy5LKDAsYikKdD1wLkNmKCkKZm9yKHM9MDtzPHQubGVuZ3RoOysrcyl7cj10W3NdCnE9cC5i
-W3JdCmlmKHR5cGVvZiBxPT0idW5kZWZpbmVkIil7cT1QLlFlKHAuYVtyXSkKcC5iW3JdPXF9Yi4kMihy
-LHEpCmlmKHQhPT1wLmMpdGhyb3cgSC5iKFAuYTQocCkpfX0sCkNmOmZ1bmN0aW9uKCl7dmFyIHQ9dS5q
-LmEodGhpcy5jKQppZih0PT1udWxsKXQ9dGhpcy5jPUguVk0oT2JqZWN0LmtleXModGhpcy5hKSx1LnMp
-CnJldHVybiB0fSwKWEs6ZnVuY3Rpb24oKXt2YXIgdCxzLHIscSxwLG89dGhpcwppZihvLmI9PW51bGwp
-cmV0dXJuIG8uYwp0PVAuRmwodS5OLHUueikKcz1vLkNmKCkKZm9yKHI9MDtxPXMubGVuZ3RoLHI8cTsr
-K3Ipe3A9c1tyXQp0LlkoMCxwLG8ucSgwLHApKX1pZihxPT09MClDLk5tLmkocyxudWxsKQplbHNlIEMu
-Tm0uc0EocywwKQpvLmE9by5iPW51bGwKcmV0dXJuIG8uYz10fSwKZmI6ZnVuY3Rpb24oYSl7dmFyIHQK
-aWYoIU9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbCh0aGlzLmEsYSkpcmV0dXJuIG51
-bGwKdD1QLlFlKHRoaXMuYVthXSkKcmV0dXJuIHRoaXMuYlthXT10fX0KUC5pOC5wcm90b3R5cGU9ewpn
-QTpmdW5jdGlvbihhKXt2YXIgdD10aGlzLmEKcmV0dXJuIHQuZ0EodCl9LApFOmZ1bmN0aW9uKGEsYil7
-dmFyIHQ9dGhpcy5hCmlmKHQuYj09bnVsbCl0PXQuZ1YoKS5FKDAsYikKZWxzZXt0PXQuQ2YoKQppZihi
-PDB8fGI+PXQubGVuZ3RoKXJldHVybiBILmsodCxiKQp0PXRbYl19cmV0dXJuIHR9LApna3o6ZnVuY3Rp
-b24oYSl7dmFyIHQ9dGhpcy5hCmlmKHQuYj09bnVsbCl7dD10LmdWKCkKdD10Lmdreih0KX1lbHNle3Q9
-dC5DZigpCnQ9bmV3IEoubTEodCx0Lmxlbmd0aCxILnQ2KHQpLkMoIm0xPDE+IikpfXJldHVybiB0fSwK
-dGc6ZnVuY3Rpb24oYSxiKXtyZXR1cm4gdGhpcy5hLng0KGIpfX0KUC5DVi5wcm90b3R5cGU9ewp5cjpm
+LlE9Xy56PV8ueT1fLng9bnVsbH0sClJaOmZ1bmN0aW9uIFJaKCl7fSwKTUU6ZnVuY3Rpb24gTUUoYSxi
+KXt0aGlzLmE9YQp0aGlzLmI9Yn0sCnk1OmZ1bmN0aW9uIHk1KGEpe3RoaXMuYT1hfSwKUEU6ZnVuY3Rp
+b24gUEUoYSxiLGMpe3RoaXMuYT1hCnRoaXMuYj1iCnRoaXMuYz1jfSwKcTM6ZnVuY3Rpb24gcTMoKXt9
+LAp5STpmdW5jdGlvbiB5SShhKXt0aGlzLmE9YX0sCmM2OmZ1bmN0aW9uIGM2KCl7fSwKcWQ6ZnVuY3Rp
+b24gcWQoKXt9LApVZjpmdW5jdGlvbiBVZihhLGIsYyxkLGUsZixnLGgpe3ZhciBfPXRoaXMKXy5hPWEK
+Xy5iPWIKXy5jPWMKXy5kPWQKXy5lPWUKXy5mPWYKXy5yPWcKXy54PWgKXy55PW51bGx9LApxZTpmdW5j
+dGlvbiBxZShhLGIsYyxkLGUsZixnKXt2YXIgXz10aGlzCl8uYT1hCl8uYj1iCl8uYz1jCl8uZD1kCl8u
+ZT1lCl8uZj1mCl8ucj1nCl8uUT1fLno9Xy55PV8ueD1udWxsfSwKaUo6ZnVuY3Rpb24gaUooKXt9LApq
+ZzpmdW5jdGlvbiBqZyhhLGIpe3RoaXMuYT1hCnRoaXMuYj1ifSwKVGE6ZnVuY3Rpb24gVGEoYSxiKXt0
+aGlzLmE9YQp0aGlzLmI9Yn0sCkJmOmZ1bmN0aW9uIEJmKGEsYil7dGhpcy5hPWEKdGhpcy5iPWJ9LApB
+czpmdW5jdGlvbiBBcygpe30sCkdFOmZ1bmN0aW9uIEdFKGEpe3RoaXMuYT1hfSwKTjc6ZnVuY3Rpb24g
+TjcoYSxiKXt0aGlzLmE9YQp0aGlzLmI9Yn0sCnVROmZ1bmN0aW9uIHVRKCl7fSwKaEY6ZnVuY3Rpb24g
+aEYoKXt9LApSNDpmdW5jdGlvbihhLGIsYyxkKXt2YXIgdCxzLHIKSC55OChiKQp1LmIuYShkKQppZihI
+Lm9UKGIpKXt0PVtjXQpDLk5tLkZWKHQsZCkKZD10fXM9dS56CnI9UC5DSChKLk0xKGQsUC53MCgpLHMp
+LCEwLHMpCnUuWS5hKGEpCnJldHVybiBQLndZKEguRWsoYSxyLG51bGwpKX0sCkRtOmZ1bmN0aW9uKGEs
+YixjKXt2YXIgdAp0cnl7aWYoT2JqZWN0LmlzRXh0ZW5zaWJsZShhKSYmIU9iamVjdC5wcm90b3R5cGUu
+aGFzT3duUHJvcGVydHkuY2FsbChhLGIpKXtPYmplY3QuZGVmaW5lUHJvcGVydHkoYSxiLHt2YWx1ZTpj
+fSkKcmV0dXJuITB9fWNhdGNoKHQpe0guUnUodCl9cmV0dXJuITF9LApPbTpmdW5jdGlvbihhLGIpe2lm
+KE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbChhLGIpKXJldHVybiBhW2JdCnJldHVy
+biBudWxsfSwKd1k6ZnVuY3Rpb24oYSl7aWYoYT09bnVsbHx8dHlwZW9mIGE9PSJzdHJpbmcifHx0eXBl
+b2YgYT09Im51bWJlciJ8fEgubChhKSlyZXR1cm4gYQppZihhIGluc3RhbmNlb2YgUC5FNClyZXR1cm4g
+YS5hCmlmKEguUjkoYSkpcmV0dXJuIGEKaWYodS54LmIoYSkpcmV0dXJuIGEKaWYoYSBpbnN0YW5jZW9m
+IFAuaVApcmV0dXJuIEgubzIoYSkKaWYodS5ZLmIoYSkpcmV0dXJuIFAuaEUoYSwiJGRhcnRfanNGdW5j
+dGlvbiIsbmV3IFAuUEMoKSkKcmV0dXJuIFAuaEUoYSwiXyRkYXJ0X2pzT2JqZWN0IixuZXcgUC5tdCgk
+LmtJKCkpKX0sCmhFOmZ1bmN0aW9uKGEsYixjKXt2YXIgdD1QLk9tKGEsYikKaWYodD09bnVsbCl7dD1j
+LiQxKGEpClAuRG0oYSxiLHQpfXJldHVybiB0fSwKZFU6ZnVuY3Rpb24oYSl7dmFyIHQscwppZihhPT1u
+dWxsfHx0eXBlb2YgYT09InN0cmluZyJ8fHR5cGVvZiBhPT0ibnVtYmVyInx8dHlwZW9mIGE9PSJib29s
+ZWFuIilyZXR1cm4gYQplbHNlIGlmKGEgaW5zdGFuY2VvZiBPYmplY3QmJkguUjkoYSkpcmV0dXJuIGEK
+ZWxzZSBpZihhIGluc3RhbmNlb2YgT2JqZWN0JiZ1LnguYihhKSlyZXR1cm4gYQplbHNlIGlmKGEgaW5z
+dGFuY2VvZiBEYXRlKXt0PUgudVAoYS5nZXRUaW1lKCkpCmlmKE1hdGguYWJzKHQpPD04NjRlMTMpcz0h
+MQplbHNlIHM9ITAKaWYocylILnZoKFAueFkoIkRhdGVUaW1lIGlzIG91dHNpZGUgdmFsaWQgcmFuZ2U6
+ICIrdCkpClAuVUkoITEsImlzVXRjIix1LnkpCnJldHVybiBuZXcgUC5pUCh0LCExKX1lbHNlIGlmKGEu
+Y29uc3RydWN0b3I9PT0kLmtJKCkpcmV0dXJuIGEubwplbHNlIHJldHVybiBQLk5EKGEpfSwKTkQ6ZnVu
+Y3Rpb24oYSl7aWYodHlwZW9mIGE9PSJmdW5jdGlvbiIpcmV0dXJuIFAuaVEoYSwkLncoKSxuZXcgUC5O
+eigpKQppZihhIGluc3RhbmNlb2YgQXJyYXkpcmV0dXJuIFAuaVEoYSwkLlI4KCksbmV3IFAuUVMoKSkK
+cmV0dXJuIFAuaVEoYSwkLlI4KCksbmV3IFAubnAoKSl9LAppUTpmdW5jdGlvbihhLGIsYyl7dmFyIHQ9
+UC5PbShhLGIpCmlmKHQ9PW51bGx8fCEoYSBpbnN0YW5jZW9mIE9iamVjdCkpe3Q9Yy4kMShhKQpQLkRt
+KGEsYix0KX1yZXR1cm4gdH0sClBDOmZ1bmN0aW9uIFBDKCl7fSwKbXQ6ZnVuY3Rpb24gbXQoYSl7dGhp
+cy5hPWF9LApOejpmdW5jdGlvbiBOeigpe30sClFTOmZ1bmN0aW9uIFFTKCl7fSwKbnA6ZnVuY3Rpb24g
+bnAoKXt9LApFNDpmdW5jdGlvbiBFNChhKXt0aGlzLmE9YX0sCnI3OmZ1bmN0aW9uIHI3KGEpe3RoaXMu
+YT1hfSwKVHo6ZnVuY3Rpb24gVHooYSxiKXt0aGlzLmE9YQp0aGlzLiR0aT1ifSwKY286ZnVuY3Rpb24g
+Y28oKXt9LApiQjpmdW5jdGlvbiBiQigpe30sCktlOmZ1bmN0aW9uIEtlKGEpe3RoaXMuYT1hfSwKZDU6
+ZnVuY3Rpb24gZDUoKXt9LApuNjpmdW5jdGlvbiBuNigpe319LFc9ewp4MzpmdW5jdGlvbigpe3JldHVy
+biB3aW5kb3d9LApacjpmdW5jdGlvbigpe3JldHVybiBkb2N1bWVudH0sCko2OmZ1bmN0aW9uKGEpe3Zh
+ciB0PWRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoImEiKQppZihhIT1udWxsKXQuaHJlZj1hCnJldHVybiB0
+fSwKVTk6ZnVuY3Rpb24oYSxiLGMpe3ZhciB0LHM9ZG9jdW1lbnQuYm9keQpzLnRvU3RyaW5nCnQ9Qy5S
+WS5yNihzLGEsYixjKQp0LnRvU3RyaW5nCnM9dS5hYwpzPW5ldyBILlU1KG5ldyBXLmU3KHQpLHMuQygi
+YTIobEQuRSkiKS5hKG5ldyBXLkN2KCkpLHMuQygiVTU8bEQuRT4iKSkKcmV0dXJuIHUuaC5hKHMuZ3I4
+KHMpKX0sCnJTOmZ1bmN0aW9uKGEpe3ZhciB0LHMscj0iZWxlbWVudCB0YWcgdW5hdmFpbGFibGUiCnRy
+eXt0PUouWUUoYSkKaWYodHlwZW9mIHQuZ25zKGEpPT0ic3RyaW5nIilyPXQuZ25zKGEpfWNhdGNoKHMp
+e0guUnUocyl9cmV0dXJuIHJ9LApDMDpmdW5jdGlvbihhLGIpe2E9NTM2ODcwOTExJmErYgphPTUzNjg3
+MDkxMSZhKygoNTI0Mjg3JmEpPDwxMCkKcmV0dXJuIGFeYT4+PjZ9LApyRTpmdW5jdGlvbihhLGIsYyxk
+KXt2YXIgdD1XLkMwKFcuQzAoVy5DMChXLkMwKDAsYSksYiksYyksZCkscz01MzY4NzA5MTEmdCsoKDY3
+MTA4ODYzJnQpPDwzKQpzXj1zPj4+MTEKcmV0dXJuIDUzNjg3MDkxMSZzKygoMTYzODMmcyk8PDE1KX0s
+ClROOmZ1bmN0aW9uKGEsYil7dmFyIHQscyxyPWEuY2xhc3NMaXN0CmZvcih0PWIubGVuZ3RoLHM9MDtz
+PGIubGVuZ3RoO2IubGVuZ3RoPT09dHx8KDAsSC5saykoYiksKytzKXIuYWRkKGJbc10pfSwKSkU6ZnVu
+Y3Rpb24oYSxiLGMsZCxlKXt2YXIgdD1XLmFGKG5ldyBXLnZOKGMpLHUuQikKaWYodCE9bnVsbCYmITAp
+Si5kWihhLGIsdCwhMSkKcmV0dXJuIG5ldyBXLnhDKGEsYix0LCExLGUuQygieEM8MD4iKSl9LApUdzpm
+dW5jdGlvbihhKXt2YXIgdD1XLko2KG51bGwpLHM9d2luZG93LmxvY2F0aW9uCnQ9bmV3IFcuSlEobmV3
+IFcubWsodCxzKSkKdC5DWShhKQpyZXR1cm4gdH0sCnFEOmZ1bmN0aW9uKGEsYixjLGQpe3UuaC5hKGEp
+CkguaChiKQpILmgoYykKdS5jci5hKGQpCnJldHVybiEwfSwKUVc6ZnVuY3Rpb24oYSxiLGMsZCl7dmFy
+IHQscyxyCnUuaC5hKGEpCkguaChiKQpILmgoYykKdD11LmNyLmEoZCkuYQpzPXQuYQpzLmhyZWY9Ywpy
+PXMuaG9zdG5hbWUKdD10LmIKaWYoIShyPT10Lmhvc3RuYW1lJiZzLnBvcnQ9PXQucG9ydCYmcy5wcm90
+b2NvbD09dC5wcm90b2NvbCkpaWYocj09PSIiKWlmKHMucG9ydD09PSIiKXt0PXMucHJvdG9jb2wKdD10
+PT09IjoifHx0PT09IiJ9ZWxzZSB0PSExCmVsc2UgdD0hMQplbHNlIHQ9ITAKcmV0dXJuIHR9LApCbDpm
+dW5jdGlvbigpe3ZhciB0PXUuTixzPVAudE0oQy5ReCx0KSxyPXUuZDAuYShuZXcgVy5JQSgpKSxxPUgu
+Vk0oWyJURU1QTEFURSJdLHUucykKdD1uZXcgVy5jdChzLFAuTHModCksUC5Mcyh0KSxQLkxzKHQpLG51
+bGwpCnQuQ1kobnVsbCxuZXcgSC5sSihDLlF4LHIsdS5maikscSxudWxsKQpyZXR1cm4gdH0sClB2OmZ1
+bmN0aW9uKGEpe2lmKGE9PW51bGwpcmV0dXJuIG51bGwKcmV0dXJuIFcuUDEoYSl9LApxYzpmdW5jdGlv
+bihhKXt2YXIgdAppZihhPT1udWxsKXJldHVybiBudWxsCmlmKCJwb3N0TWVzc2FnZSIgaW4gYSl7dD1X
+LlAxKGEpCmlmKHUuYVMuYih0KSlyZXR1cm4gdApyZXR1cm4gbnVsbH1lbHNlIHJldHVybiB1LmNoLmEo
+YSl9LApQMTpmdW5jdGlvbihhKXtpZihhPT09d2luZG93KXJldHVybiB1LmNpLmEoYSkKZWxzZSByZXR1
+cm4gbmV3IFcuZFcoYSl9LApISDpmdW5jdGlvbihhKXtpZihhPT09d2luZG93LmxvY2F0aW9uKXJldHVy
+biBhCmVsc2UgcmV0dXJuIG5ldyBXLkZiKCl9LAphRjpmdW5jdGlvbihhLGIpe3ZhciB0PSQuWDMKaWYo
+dD09PUMuTlUpcmV0dXJuIGEKcmV0dXJuIHQuUHkoYSxiKX0sCnFFOmZ1bmN0aW9uIHFFKCl7fSwKR2g6
+ZnVuY3Rpb24gR2goKXt9LApmWTpmdW5jdGlvbiBmWSgpe30sCm5COmZ1bmN0aW9uIG5CKCl7fSwKQXo6
+ZnVuY3Rpb24gQXooKXt9LApRUDpmdW5jdGlvbiBRUCgpe30sCm54OmZ1bmN0aW9uIG54KCl7fSwKb0o6
+ZnVuY3Rpb24gb0ooKXt9LAppZDpmdW5jdGlvbiBpZCgpe30sClFGOmZ1bmN0aW9uIFFGKCl7fSwKTmg6
+ZnVuY3Rpb24gTmgoKXt9LAphZTpmdW5jdGlvbiBhZSgpe30sCklCOmZ1bmN0aW9uIElCKCl7fSwKbjc6
+ZnVuY3Rpb24gbjcoKXt9LAp3ejpmdW5jdGlvbiB3eihhLGIpe3RoaXMuYT1hCnRoaXMuJHRpPWJ9LApj
+djpmdW5jdGlvbiBjdigpe30sCkN2OmZ1bmN0aW9uIEN2KCl7fSwKZWE6ZnVuY3Rpb24gZWEoKXt9LApE
+MDpmdW5jdGlvbiBEMCgpe30sCmhIOmZ1bmN0aW9uIGhIKCl7fSwKaDQ6ZnVuY3Rpb24gaDQoKXt9LApi
+cjpmdW5jdGlvbiBicigpe30sClZiOmZ1bmN0aW9uIFZiKCl7fSwKZko6ZnVuY3Rpb24gZkooKXt9LAp3
+YTpmdW5jdGlvbiB3YSgpe30sClNnOmZ1bmN0aW9uIFNnKCl7fSwKdTg6ZnVuY3Rpb24gdTgoKXt9LApB
+ajpmdW5jdGlvbiBBaigpe30sCmU3OmZ1bmN0aW9uIGU3KGEpe3RoaXMuYT1hfSwKdUg6ZnVuY3Rpb24g
+dUgoKXt9LApCSDpmdW5jdGlvbiBCSCgpe30sClNOOmZ1bmN0aW9uIFNOKCl7fSwKZXc6ZnVuY3Rpb24g
+ZXcoKXt9LApscDpmdW5jdGlvbiBscCgpe30sClRiOmZ1bmN0aW9uIFRiKCl7fSwKSXY6ZnVuY3Rpb24g
+SXYoKXt9LApXUDpmdW5jdGlvbiBXUCgpe30sCnlZOmZ1bmN0aW9uIHlZKCl7fSwKdzY6ZnVuY3Rpb24g
+dzYoKXt9LApLNTpmdW5jdGlvbiBLNSgpe30sCkNtOmZ1bmN0aW9uIENtKCl7fSwKQ1E6ZnVuY3Rpb24g
+Q1EoKXt9LAp3NDpmdW5jdGlvbiB3NCgpe30sCnJoOmZ1bmN0aW9uIHJoKCl7fSwKY2Y6ZnVuY3Rpb24g
+Y2YoKXt9LAppNzpmdW5jdGlvbiBpNyhhKXt0aGlzLmE9YX0sClN5OmZ1bmN0aW9uIFN5KGEpe3RoaXMu
+YT1hfSwKS1M6ZnVuY3Rpb24gS1MoYSxiKXt0aGlzLmE9YQp0aGlzLmI9Yn0sCkEzOmZ1bmN0aW9uIEEz
+KGEsYil7dGhpcy5hPWEKdGhpcy5iPWJ9LApJNDpmdW5jdGlvbiBJNChhKXt0aGlzLmE9YX0sCkZrOmZ1
+bmN0aW9uIEZrKGEsYil7dGhpcy5hPWEKdGhpcy4kdGk9Yn0sClJPOmZ1bmN0aW9uIFJPKGEsYixjLGQp
+e3ZhciBfPXRoaXMKXy5hPWEKXy5iPWIKXy5jPWMKXy4kdGk9ZH0sCmV1OmZ1bmN0aW9uIGV1KGEsYixj
+LGQpe3ZhciBfPXRoaXMKXy5hPWEKXy5iPWIKXy5jPWMKXy4kdGk9ZH0sCnhDOmZ1bmN0aW9uIHhDKGEs
+YixjLGQsZSl7dmFyIF89dGhpcwpfLmI9YQpfLmM9YgpfLmQ9YwpfLmU9ZApfLiR0aT1lfSwKdk46ZnVu
+Y3Rpb24gdk4oYSl7dGhpcy5hPWF9LApKUTpmdW5jdGlvbiBKUShhKXt0aGlzLmE9YX0sCkdtOmZ1bmN0
+aW9uIEdtKCl7fSwKdkQ6ZnVuY3Rpb24gdkQoYSl7dGhpcy5hPWF9LApVdjpmdW5jdGlvbiBVdihhKXt0
+aGlzLmE9YX0sCkVnOmZ1bmN0aW9uIEVnKGEsYixjKXt0aGlzLmE9YQp0aGlzLmI9Ygp0aGlzLmM9Y30s
+Cm02OmZ1bmN0aW9uIG02KCl7fSwKRW86ZnVuY3Rpb24gRW8oKXt9LApXazpmdW5jdGlvbiBXaygpe30s
+CmN0OmZ1bmN0aW9uIGN0KGEsYixjLGQsZSl7dmFyIF89dGhpcwpfLmU9YQpfLmE9YgpfLmI9YwpfLmM9
+ZApfLmQ9ZX0sCklBOmZ1bmN0aW9uIElBKCl7fSwKT3c6ZnVuY3Rpb24gT3coKXt9LApXOTpmdW5jdGlv
+biBXOShhLGIsYyl7dmFyIF89dGhpcwpfLmE9YQpfLmI9YgpfLmM9LTEKXy5kPW51bGwKXy4kdGk9Y30s
+CmRXOmZ1bmN0aW9uIGRXKGEpe3RoaXMuYT1hfSwKRmI6ZnVuY3Rpb24gRmIoKXt9LAprRjpmdW5jdGlv
+biBrRigpe30sCm1rOmZ1bmN0aW9uIG1rKGEsYil7dGhpcy5hPWEKdGhpcy5iPWJ9LApLbzpmdW5jdGlv
+biBLbyhhKXt0aGlzLmE9YQp0aGlzLmI9ITF9LApmbTpmdW5jdGlvbiBmbShhKXt0aGlzLmE9YX0sCkxl
+OmZ1bmN0aW9uIExlKCl7fSwKSzc6ZnVuY3Rpb24gSzcoKXt9LApyQjpmdW5jdGlvbiByQigpe30sClhX
+OmZ1bmN0aW9uIFhXKCl7fSwKb2E6ZnVuY3Rpb24gb2EoKXt9fSxNPXsKT1g6ZnVuY3Rpb24oYSl7c3dp
+dGNoKGEpe2Nhc2UgQy5BZDpyZXR1cm4iQWRkIC8qPyovIGhpbnQiCmNhc2UgQy5uZTpyZXR1cm4iQWRk
+IC8qISovIGhpbnQiCmNhc2UgQy53VjpyZXR1cm4iUmVtb3ZlIC8qPyovIGhpbnQiCmNhc2UgQy5mUjpy
+ZXR1cm4iUmVtb3ZlIC8qISovIGhpbnQiCmNhc2UgQy5teTpyZXR1cm4iQ2hhbmdlIHRvIC8qPyovIGhp
+bnQiCmNhc2UgQy5yeDpyZXR1cm4iQ2hhbmdlIHRvIC8qISovIGhpbnQifXJldHVybiBudWxsfSwKSDc6
+ZnVuY3Rpb24gSDcoYSxiKXt0aGlzLmE9YQp0aGlzLmI9Yn0sCllGOmZ1bmN0aW9uKGEsYil7dmFyIHQs
+cyxyLHEscCxvLG4KZm9yKHQ9Yi5sZW5ndGgscz0xO3M8dDsrK3Mpe2lmKGJbc109PW51bGx8fGJbcy0x
+XSE9bnVsbCljb250aW51ZQpmb3IoO3Q+PTE7dD1yKXtyPXQtMQppZihiW3JdIT1udWxsKWJyZWFrfXE9
+bmV3IFAuUm4oIiIpCnA9YSsiKCIKcS5hPXAKbz1ILnFDKGIsMCx0LEgudDYoYikuYykKbj1vLiR0aQpu
+PXArbmV3IEgubEoobyxuLkMoInFVKihhTC5FKSIpLmEobmV3IE0uTm8oKSksbi5DKCJsSjxhTC5FLHFV
+Kj4iKSkuSCgwLCIsICIpCnEuYT1uCnEuYT1uKygiKTogcGFydCAiKyhzLTEpKyIgd2FzIG51bGwsIGJ1
+dCBwYXJ0ICIrcysiIHdhcyBub3QuIikKdGhyb3cgSC5iKFAueFkocS53KDApKSl9fSwKbEk6ZnVuY3Rp
+b24gbEkoYSl7dGhpcy5hPWF9LApNaTpmdW5jdGlvbiBNaSgpe30sCnE3OmZ1bmN0aW9uIHE3KCl7fSwK
+Tm86ZnVuY3Rpb24gTm8oKXt9fSxVPXsKbno6ZnVuY3Rpb24oYSl7dmFyIHQ9SC51UChhLnEoMCwibm9k
+ZUlkIikpCnJldHVybiBuZXcgVS5MTChDLk5tLkh0KEMucmssbmV3IFUuTUQoYSkpLHQpfSwKTEw6ZnVu
+Y3Rpb24gTEwoYSxiKXt0aGlzLmE9YQp0aGlzLmI9Yn0sCk1EOmZ1bmN0aW9uIE1EKGEpe3RoaXMuYT1h
+fSwKamY6ZnVuY3Rpb24oYSl7dmFyIHQscyxyLHEKaWYoYT09bnVsbCl0PW51bGwKZWxzZXt0PUguVk0o
+W10sdS5kNykKZm9yKHM9Si5JVCh1LlUuYShhKSk7cy5GKCk7KXtyPXMuZ2woKQpxPUouVTYocikKQy5O
+bS5pKHQsbmV3IFUuU2UoSC5oKHEucShyLCJkZXNjcmlwdGlvbiIpKSxILmgocS5xKHIsImhyZWYiKSkp
+KX19cmV0dXJuIHR9LApOZDpmdW5jdGlvbihhKXt2YXIgdCxzCmlmKGE9PW51bGwpdD1udWxsCmVsc2V7
+dD1ILlZNKFtdLHUuYUEpCmZvcihzPUouSVQodS5VLmEoYSkpO3MuRigpOylDLk5tLmkodCxVLk5mKHMu
+Z2woKSkpfXJldHVybiB0fSwKTmY6ZnVuY3Rpb24oYSl7dmFyIHQ9Si5VNihhKSxzPUguaCh0LnEoYSwi
+ZGVzY3JpcHRpb24iKSkscj1ILlZNKFtdLHUuYUopCmZvcih0PUouSVQodS5VLmEodC5xKGEsImVudHJp
+ZXMiKSkpO3QuRigpOylDLk5tLmkocixVLlJqKHQuZ2woKSkpCnJldHVybiBuZXcgVS55RChzLHIpfSwK
+Umo6ZnVuY3Rpb24oYSl7dmFyIHQscz1KLlU2KGEpLHI9SC5oKHMucShhLCJkZXNjcmlwdGlvbiIpKSxx
+PUguaChzLnEoYSwiZnVuY3Rpb24iKSkscD1zLnEoYSwibGluayIpCmlmKHA9PW51bGwpcD1udWxsCmVs
+c2V7dD1KLlU2KHApCnA9bmV3IFUuTWwoSC5oKHQucShwLCJocmVmIikpLEgudVAodC5xKHAsImxpbmUi
+KSksSC5oKHQucShwLCJwYXRoIikpKX1zPXUuZksuYShzLnEoYSwiaGludEFjdGlvbnMiKSkKcz1zPT1u
+dWxsP251bGw6Si5NMShzLG5ldyBVLmFOKCksdS5hWCkKcz1zPT1udWxsP251bGw6UC5DSChzLCEwLHMu
+JHRpLkMoImFMLkUiKSkKcmV0dXJuIG5ldyBVLndiKHIscSxwLHM9PW51bGw/Qy5kbjpzKX0sCmQyOmZ1
+bmN0aW9uIGQyKGEsYixjLGQsZSxmKXt2YXIgXz10aGlzCl8uYT1hCl8uYj1iCl8uYz1jCl8uZD1kCl8u
+ZT1lCl8uZj1mfSwKU2U6ZnVuY3Rpb24gU2UoYSxiKXt0aGlzLmE9YQp0aGlzLmI9Yn0sCk1sOmZ1bmN0
+aW9uIE1sKGEsYixjKXt0aGlzLmE9YQp0aGlzLmI9Ygp0aGlzLmM9Y30sCnlEOmZ1bmN0aW9uIHlEKGEs
+Yil7dGhpcy5hPWEKdGhpcy5iPWJ9LAp3YjpmdW5jdGlvbiB3YihhLGIsYyxkKXt2YXIgXz10aGlzCl8u
+YT1hCl8uYj1iCl8uYz1jCl8uZD1kfSwKYU46ZnVuY3Rpb24gYU4oKXt9LApiMDpmdW5jdGlvbiBiMCgp
+e319LEI9ewpZZjpmdW5jdGlvbihhKXt2YXIgdCxzLHIscSxwLG8sbixtLGw9SC5oKGEucSgwLCJyZWdp
+b25zIikpLGs9SC5oKGEucSgwLCJuYXZpZ2F0aW9uQ29udGVudCIpKSxqPUguaChhLnEoMCwic291cmNl
+Q29kZSIpKSxpPVAuRmwodS5YLHUuZF8pCmZvcih0PXUudC5hKGEucSgwLCJlZGl0cyIpKSx0PXQuZ1B1
+KHQpLHQ9dC5na3oodCkscz11LlUscj11Lmg0O3QuRigpOyl7cT10LmdsKCkKcD1xLmEKbz1ILlZNKFtd
+LHIpCmZvcihxPUouSVQocy5hKHEuYikpO3EuRigpOyl7bj1xLmdsKCkKbT1KLlU2KG4pCkMuTm0uaShv
+LG5ldyBCLmo4KEgudVAobS5xKG4sImxpbmUiKSksSC5oKG0ucShuLCJleHBsYW5hdGlvbiIpKSxILnVQ
+KG0ucShuLCJvZmZzZXQiKSkpKX1pLlkoMCxwLG8pfXJldHVybiBuZXcgQi5xcChsLGssaixpKX0sCmo4
+OmZ1bmN0aW9uIGo4KGEsYixjKXt0aGlzLmE9YQp0aGlzLmI9Ygp0aGlzLmM9Y30sCnFwOmZ1bmN0aW9u
+IHFwKGEsYixjLGQpe3ZhciBfPXRoaXMKXy5hPWEKXy5iPWIKXy5jPWMKXy5kPWR9LApmdjpmdW5jdGlv
+biBmdigpe30sCk9TOmZ1bmN0aW9uKGEpe3ZhciB0CmlmKCEoYT49NjUmJmE8PTkwKSl0PWE+PTk3JiZh
+PD0xMjIKZWxzZSB0PSEwCnJldHVybiB0fSwKWXU6ZnVuY3Rpb24oYSxiKXt2YXIgdD1hLmxlbmd0aCxz
+PWIrMgppZih0PHMpcmV0dXJuITEKaWYoIUIuT1MoQy54Qi5tKGEsYikpKXJldHVybiExCmlmKEMueEIu
+bShhLGIrMSkhPT01OClyZXR1cm4hMQppZih0PT09cylyZXR1cm4hMApyZXR1cm4gQy54Qi5tKGEscyk9
+PT00N319LFQ9e21ROmZ1bmN0aW9uIG1RKCl7fX0sTD17CklxOmZ1bmN0aW9uKCl7Qy5CWi5CKGRvY3Vt
+ZW50LCJET01Db250ZW50TG9hZGVkIixuZXcgTC5lKCkpCkMub2wuQih3aW5kb3csInBvcHN0YXRlIixu
+ZXcgTC5MKCkpfSwKa3o6ZnVuY3Rpb24oYSl7dmFyIHQscz11LmcuYShhLnBhcmVudE5vZGUpLnF1ZXJ5
+U2VsZWN0b3IoIjpzY29wZSA+IHVsIikscj1zLnN0eWxlLHE9IiIrQy5DRC56UShzLm9mZnNldEhlaWdo
+dCkqMisicHgiCnIubWF4SGVpZ2h0PXEKcj1KLnFGKGEpCnE9ci4kdGkKdD1xLkMoIn4oMSk/IikuYShu
+ZXcgTC5XeChzLGEpKQp1LlouYShudWxsKQpXLkpFKHIuYSxyLmIsdCwhMSxxLmMpfSwKeVg6ZnVuY3Rp
+b24oYSxiKXt2YXIgdCxzLHIscSxwLG8sbj0icXVlcnlTZWxlY3RvckFsbCIsbT1kb2N1bWVudC5xdWVy
+eVNlbGVjdG9yKGEpLGw9dS5nCm0udG9TdHJpbmcKdD11LmgKSC5EaChsLHQsIlQiLG4pCnM9dS5SCnI9
+bmV3IFcud3oobS5xdWVyeVNlbGVjdG9yQWxsKCIubmF2LWxpbmsiKSxzKQpyLksocixuZXcgTC5BTyhi
+KSkKSC5EaChsLHQsIlQiLG4pCnE9bmV3IFcud3oobS5xdWVyeVNlbGVjdG9yQWxsKCIucmVnaW9uIiks
+cykKaWYocS5nQShxKSE9PTApe3A9bS5xdWVyeVNlbGVjdG9yKCJ0YWJsZVtkYXRhLXBhdGhdIikKcC50
+b1N0cmluZwpxLksocSxuZXcgTC5IbyhwLmdldEF0dHJpYnV0ZSgiZGF0YS0iK25ldyBXLlN5KG5ldyBX
+Lmk3KHApKS5PKCJwYXRoIikpKSl9SC5EaChsLHQsIlQiLG4pCm89bmV3IFcud3oobS5xdWVyeVNlbGVj
+dG9yQWxsKCIuYWRkLWhpbnQtbGluayIpLHMpCm8uSyhvLG5ldyBMLklDKCkpfSwKUTY6ZnVuY3Rpb24o
+YSxiLGMpe3ZhciB0PW5ldyBYTUxIdHRwUmVxdWVzdCgpCkMuRHQuZW8odCwiR0VUIixMLlE0KGEsYiks
+ITApCnQuc2V0UmVxdWVzdEhlYWRlcigiQ29udGVudC1UeXBlIiwiYXBwbGljYXRpb24vanNvbjsgY2hh
+cnNldD1VVEYtOCIpCnJldHVybiBMLkxVKHQsbnVsbCxjLkMoIjAqIikpfSwKdHk6ZnVuY3Rpb24oYSxi
+KXt2YXIgdD1uZXcgWE1MSHR0cFJlcXVlc3QoKSxzPXUuWApDLkR0LmVvKHQsIlBPU1QiLEwuUTQoYSxQ
+LkZsKHMscykpLCEwKQp0LnNldFJlcXVlc3RIZWFkZXIoIkNvbnRlbnQtVHlwZSIsImFwcGxpY2F0aW9u
+L2pzb247IGNoYXJzZXQ9VVRGLTgiKQpyZXR1cm4gTC5MVSh0LGIsdS50KX0sCkxVOmZ1bmN0aW9uKGEs
+YixjKXtyZXR1cm4gTC5UZyhhLGIsYyxjLkMoIjAqIikpfSwKVGc6ZnVuY3Rpb24oYSxiLGMsZCl7dmFy
+IHQ9MCxzPVAuRlgoZCkscixxPTIscCxvPVtdLG4sbSxsLGssaixpLGgsZwp2YXIgJGFzeW5jJExVPVAu
+bHooZnVuY3Rpb24oZSxmKXtpZihlPT09MSl7cD1mCnQ9cX13aGlsZSh0cnVlKXN3aXRjaCh0KXtjYXNl
+IDA6aj1uZXcgUC5aZihuZXcgUC52cygkLlgzLHUuZ1YpLHUuYkMpCmk9dS5lYgpoPWkuYShuZXcgTC5m
+QyhqLGEpKQp1LlouYShudWxsKQptPXUuZVEKVy5KRShhLCJsb2FkIixoLCExLG0pClcuSkUoYSwiZXJy
+b3IiLGkuYShqLmdZSigpKSwhMSxtKQphLnNlbmQoYj09bnVsbD9udWxsOkMuQ3QuT0IoYixudWxsKSkK
+cT00CnQ9NwpyZXR1cm4gUC5qUShqLmEsJGFzeW5jJExVKQpjYXNlIDc6cT0yCnQ9NgpicmVhawpjYXNl
+IDQ6cT0zCmc9cApILlJ1KGcpCm49SC50cyhnKQppPVAuVGwoIkVycm9yIHJlYWNoaW5nIG1pZ3JhdGlv
+biBwcmV2aWV3IHNlcnZlci4iLG4pCnRocm93IEguYihpKQp0PTYKYnJlYWsKY2FzZSAzOnQ9MgpicmVh
+awpjYXNlIDY6az1DLkN0LnBXKDAsYS5yZXNwb25zZVRleHQsbnVsbCkKaWYoYS5zdGF0dXM9PT0yMDAp
+e3I9Yy5DKCIwKiIpLmEoaykKdD0xCmJyZWFrfWVsc2UgdGhyb3cgSC5iKGspCmNhc2UgMTpyZXR1cm4g
+UC55QyhyLHMpCmNhc2UgMjpyZXR1cm4gUC5mMyhwLHMpfX0pCnJldHVybiBQLkRJKCRhc3luYyRMVSxz
+KX0sCmFLOmZ1bmN0aW9uKGEpe3ZhciB0PVAuaEsoYSkuZ2hZKCkucSgwLCJsaW5lIikKcmV0dXJuIHQ9
+PW51bGw/bnVsbDpILkhwKHQsbnVsbCl9LApHNjpmdW5jdGlvbihhKXt2YXIgdD1QLmhLKGEpLmdoWSgp
+LnEoMCwib2Zmc2V0IikKcmV0dXJuIHQ9PW51bGw/bnVsbDpILkhwKHQsbnVsbCl9LAppNjpmdW5jdGlv
+bihhKXtyZXR1cm4gTC5uVyh1Lk8uYShhKSl9LApuVzpmdW5jdGlvbihhKXt2YXIgdD0wLHM9UC5GWCh1
+LnopLHI9MSxxLHA9W10sbyxuLG0sbCxrCnZhciAkYXN5bmMkaTY9UC5seihmdW5jdGlvbihiLGMpe2lm
+KGI9PT0xKXtxPWMKdD1yfXdoaWxlKHRydWUpc3dpdGNoKHQpe2Nhc2UgMDpsPXUuZy5hKFcucWMoYS5j
+dXJyZW50VGFyZ2V0KSkuZ2V0QXR0cmlidXRlKCJocmVmIikKYS5wcmV2ZW50RGVmYXVsdCgpCnI9Mwp0
+PTYKcmV0dXJuIFAualEoTC50eShsLG51bGwpLCRhc3luYyRpNikKY2FzZSA2OnUuYlouYShKLkdyKFcu
+UHYoZG9jdW1lbnQuZGVmYXVsdFZpZXcpKSkucmVsb2FkKCkKcj0xCnQ9NQpicmVhawpjYXNlIDM6cj0y
+Cms9cQpvPUguUnUoaykKbj1ILnRzKGspCkwuQzIoIkNvdWxkIG5vdCBhZGQvcmVtb3ZlIGhpbnQiLG8s
+bikKdD01CmJyZWFrCmNhc2UgMjp0PTEKYnJlYWsKY2FzZSA1OnJldHVybiBQLnlDKG51bGwscykKY2Fz
+ZSAxOnJldHVybiBQLmYzKHEscyl9fSkKcmV0dXJuIFAuREkoJGFzeW5jJGk2LHMpfSwKQzI6ZnVuY3Rp
+b24oYSxiLGMpe3ZhciB0LHMscj0iZXhjZXB0aW9uIixxPSJzdGFja1RyYWNlIixwPXUudC5iKGIpJiZK
+LlJNKGIucSgwLCJzdWNjZXNzIiksITEpJiZiLng0KHIpJiZiLng0KHEpLG89Si5pYShiKQppZihwKXt0
+PUguaChvLnEoYixyKSkKYz1vLnEoYixxKX1lbHNlIHQ9by53KGIpCnA9ZG9jdW1lbnQKcz1wLnF1ZXJ5
+U2VsZWN0b3IoIi5wb3B1cC1wYW5lIikKcy5xdWVyeVNlbGVjdG9yKCJoMiIpLmlubmVyVGV4dD1hCnMu
+cXVlcnlTZWxlY3RvcigicCIpLmlubmVyVGV4dD10CnMucXVlcnlTZWxlY3RvcigicHJlIikuaW5uZXJU
+ZXh0PUouaihjKQpvPXUuWAp1LmRkLmEocy5xdWVyeVNlbGVjdG9yKCJhLmJvdHRvbSIpKS5ocmVmPVAu
+WGQoImh0dHBzIiwiZ2l0aHViLmNvbSIsImRhcnQtbGFuZy9zZGsvaXNzdWVzL25ldyIsUC5FRihbInRp
+dGxlIiwiQ3VzdG9tZXItcmVwb3J0ZWQgaXNzdWUgd2l0aCBOTkJEIG1pZ3JhdGlvbiB0b29sOiAiK2Es
+ImxhYmVscyIsImFyZWEtYW5hbHl6ZXIsYW5hbHl6ZXItbm5iZC1taWdyYXRpb24sdHlwZS1idWciLCJi
+b2R5IixhKyJcblxuRXJyb3I6ICIrSC5Faih0KSsiXG5cblBsZWFzZSBmaWxsIGluIHRoZSBmb2xsb3dp
+bmc6XG5cbioqTmFtZSBvZiBwYWNrYWdlIGJlaW5nIG1pZ3JhdGVkIChpZiBwdWJsaWMpKio6XG4qKldo
+YXQgSSB3YXMgZG9pbmcgd2hlbiB0aGlzIGlzc3VlIG9jY3VycmVkKio6XG4qKklzIGl0IHBvc3NpYmxl
+IHRvIHdvcmsgYXJvdW5kIHRoaXMgaXNzdWUqKjpcbioqSGFzIHRoaXMgaXNzdWUgaGFwcGVuZWQgYmVm
+b3JlLCBhbmQgaWYgc28sIGhvdyBvZnRlbioqOlxuKipEYXJ0IFNESyB2ZXJzaW9uKio6ICIrSC5Faihw
+LmdldEVsZW1lbnRCeUlkKCJzZGstdmVyc2lvbiIpLnRleHRDb250ZW50KSsiXG4qKkFkZGl0aW9uYWwg
+ZGV0YWlscyoqOlxuXG5UaGFua3MgZm9yIGZpbGluZyFcblxuU3RhY2t0cmFjZTogX2F1dG8gcG9wdWxh
+dGVkIGJ5IG1pZ3JhdGlvbiBwcmV2aWV3IHRvb2wuX1xuXG5gYGBcbiIrSC5FaihjKSsiXG5gYGBcbiJd
+LG8sbykpLmduRCgpCm89cy5zdHlsZQpvLmRpc3BsYXk9ImluaXRpYWwiCnA9YSsiOiAiK0guRWooYikK
+d2luZG93CmlmKHR5cGVvZiBjb25zb2xlIT0idW5kZWZpbmVkIil3aW5kb3cuY29uc29sZS5lcnJvcihw
+KQp3aW5kb3cKcD1ILkVqKGMpCmlmKHR5cGVvZiBjb25zb2xlIT0idW5kZWZpbmVkIil3aW5kb3cuY29u
+c29sZS5lcnJvcihwKX0sCnQyOmZ1bmN0aW9uKGEsYixjKXt2YXIgdCxzLHIscSxwPXt9LG89dS5nLmEo
+Vy5xYyhhLmN1cnJlbnRUYXJnZXQpKQphLnByZXZlbnREZWZhdWx0KCkKdD1wLmE9by5nZXRBdHRyaWJ1
+dGUoImhyZWYiKQpzPUouemwodCwiPyIpP3AuYT1DLnhCLk5qKHQsMCxDLnhCLk9ZKHQsIj8iKSk6dApy
+PUwuRzYodCkKcT1MLmFLKHQpCmlmKHIhPW51bGwpTC5hZihzLHIscSxiLG5ldyBMLm5UKHAscixxKSkK
+ZWxzZSBMLmFmKHMsbnVsbCxudWxsLGIsbmV3IEwuTlkocCkpfSwKdlU6ZnVuY3Rpb24oKXt2YXIgdD1k
+b2N1bWVudApILkRoKHUuZyx1LmgsIlQiLCJxdWVyeVNlbGVjdG9yQWxsIikKdD1uZXcgVy53eih0LnF1
+ZXJ5U2VsZWN0b3JBbGwoIi5jb2RlIiksdS5SKQp0LksodCxuZXcgTC5lWCgpKX0sCmhYOmZ1bmN0aW9u
+KGEsYixjKXtyZXR1cm4gTC5ZdyhhLGIsYyl9LApZdzpmdW5jdGlvbihhLGIsYyl7dmFyIHQ9MCxzPVAu
+RlgodS56KSxyPTEscSxwPVtdLG8sbixtLGwsayxqLGksaAp2YXIgJGFzeW5jJGhYPVAubHooZnVuY3Rp
+b24oZCxlKXtpZihkPT09MSl7cT1lCnQ9cn13aGlsZSh0cnVlKXN3aXRjaCh0KXtjYXNlIDA6cj0zCms9
+dS5YCnQ9NgpyZXR1cm4gUC5qUShMLlE2KGEsUC5FRihbInJlZ2lvbiIsInJlZ2lvbiIsIm9mZnNldCIs
+SC5FaihiKV0sayxrKSx1LnQpLCRhc3luYyRoWCkKY2FzZSA2Om89ZQprPW8Kaj1KLlU2KGspCm49bmV3
+IFUuZDIoVS5qZihqLnEoaywiZWRpdHMiKSksSC5oKGoucShrLCJleHBsYW5hdGlvbiIpKSxILnVQKGou
+cShrLCJsaW5lIikpLEguaChqLnEoaywiZGlzcGxheVBhdGgiKSksSC5oKGoucShrLCJ1cmlQYXRoIikp
+LFUuTmQoai5xKGssInRyYWNlcyIpKSkKTC5UMShuKQpMLkZyKGEsYixjKQpMLnlYKCIuZWRpdC1wYW5l
+bCAucGFuZWwtY29udGVudCIsITEpCnI9MQp0PTUKYnJlYWsKY2FzZSAzOnI9MgpoPXEKbT1ILlJ1KGgp
+Cmw9SC50cyhoKQpMLkMyKCJDb3VsZCBub3QgbG9hZCBlZGl0IGRldGFpbHMiLG0sbCkKdD01CmJyZWFr
+CmNhc2UgMjp0PTEKYnJlYWsKY2FzZSA1OnJldHVybiBQLnlDKG51bGwscykKY2FzZSAxOnJldHVybiBQ
+LmYzKHEscyl9fSkKcmV0dXJuIFAuREkoJGFzeW5jJGhYLHMpfSwKRzc6ZnVuY3Rpb24oYSxiLGMsZCxl
+KXtyZXR1cm4gTC5MNShhLGIsYyxkLGUpfSwKTDU6ZnVuY3Rpb24oYSxiLGMsZCxlKXt2YXIgdD0wLHM9
+UC5GWCh1LnopLHIscT0yLHAsbz1bXSxuLG0sbCxrLGosaSxoCnZhciAkYXN5bmMkRzc9UC5seihmdW5j
+dGlvbihmLGcpe2lmKGY9PT0xKXtwPWcKdD1xfXdoaWxlKHRydWUpc3dpdGNoKHQpe2Nhc2UgMDppZigh
+Si5yWShhKS5UYyhhLCIuZGFydCIpKXtMLkJFKGEsbmV3IEIucXAoIiIsIiIsIiIsQy5DTSksZCkKTC5C
+WChhLG51bGwpCmlmKGUhPW51bGwpZS4kMCgpCnQ9MQpicmVha31xPTQKaj11LlgKdD03CnJldHVybiBQ
+LmpRKEwuUTYoYSxQLkVGKFsiaW5saW5lIiwidHJ1ZSJdLGosaiksdS50KSwkYXN5bmMkRzcpCmNhc2Ug
+NzpuPWcKTC5CRShhLEIuWWYobiksZCkKTC5mRyhiLGMpCm09Qy54Qi50ZyhhLCI/Iik/Qy54Qi5Oaihh
+LDAsQy54Qi5PWShhLCI/IikpOmEKTC5CWChtLGIpCmlmKGUhPW51bGwpZS4kMCgpCnE9Mgp0PTYKYnJl
+YWsKY2FzZSA0OnE9MwpoPXAKbD1ILlJ1KGgpCms9SC50cyhoKQpMLkMyKCJDb3VsZCBub3QgbG9hZCBk
+YXJ0IGZpbGUgIithLGwsaykKdD02CmJyZWFrCmNhc2UgMzp0PTIKYnJlYWsKY2FzZSA2OmNhc2UgMTpy
+ZXR1cm4gUC55QyhyLHMpCmNhc2UgMjpyZXR1cm4gUC5mMyhwLHMpfX0pCnJldHVybiBQLkRJKCRhc3lu
+YyRHNyxzKX0sCkdlOmZ1bmN0aW9uKCl7dmFyIHQ9MCxzPVAuRlgodS56KSxyPTEscSxwPVtdLG8sbixt
+LGwsayxqLGkKdmFyICRhc3luYyRHZT1QLmx6KGZ1bmN0aW9uKGEsYil7aWYoYT09PTEpe3E9Ygp0PXJ9
+d2hpbGUodHJ1ZSlzd2l0Y2godCl7Y2FzZSAwOmo9Ii9fcHJldmlldy9uYXZpZ2F0aW9uVHJlZS5qc29u
+IgpyPTMKdD02CnJldHVybiBQLmpRKEwuUTYoaixDLldPLHUuZUUpLCRhc3luYyRHZSkKY2FzZSA2Om89
+YgpuPWRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoIi5uYXYtdHJlZSIpCkoubDUobiwiIikKTC50WChuLEwu
+bUsobykpCnI9MQp0PTUKYnJlYWsKY2FzZSAzOnI9MgppPXEKbT1ILlJ1KGkpCmw9SC50cyhpKQpMLkMy
+KCJDb3VsZCBub3QgbG9hZCBuYXZpZ2F0aW9uIHRyZWUiLG0sbCkKdD01CmJyZWFrCmNhc2UgMjp0PTEK
+YnJlYWsKY2FzZSA1OnJldHVybiBQLnlDKG51bGwscykKY2FzZSAxOnJldHVybiBQLmYzKHEscyl9fSkK
+cmV0dXJuIFAuREkoJGFzeW5jJEdlLHMpfSwKcU86ZnVuY3Rpb24oYSl7dmFyIHQscz1hLmdldEJvdW5k
+aW5nQ2xpZW50UmVjdCgpLHI9Qy5DRC56USgkLmZpKCkub2Zmc2V0SGVpZ2h0KSxxPXdpbmRvdy5pbm5l
+ckhlaWdodCxwPUMuQ0QuelEoJC5EVygpLm9mZnNldEhlaWdodCkKaWYodHlwZW9mIHEhPT0ibnVtYmVy
+IilyZXR1cm4gcS5ITigpCnQ9cy5ib3R0b20KaWYodHlwZW9mIHQhPT0ibnVtYmVyIilyZXR1cm4gdC5v
+cygpCmlmKHQ+cS0ocCsxNCkpSi5kaChhKQplbHNle3E9cy50b3AKaWYodHlwZW9mIHEhPT0ibnVtYmVy
+IilyZXR1cm4gcS5KKCkKaWYocTxyKzE0KUouZGgoYSl9fSwKZkc6ZnVuY3Rpb24oYSxiKXt2YXIgdCxz
+LHIscSxwCmlmKGEhPW51bGwpe3Q9ZG9jdW1lbnQKcz10LmdldEVsZW1lbnRCeUlkKCJvIitILkVqKGEp
+KQpyPXQucXVlcnlTZWxlY3RvcigiLmxpbmUtIitILkVqKGIpKQppZihzIT1udWxsKXtMLnFPKHMpCkou
+ZFIocykuaSgwLCJ0YXJnZXQiKX1lbHNlIGlmKHIhPW51bGwpTC5xTyhyLnBhcmVudEVsZW1lbnQpCmlm
+KHIhPW51bGwpSi5kUih1LmcuYShyLnBhcmVudE5vZGUpKS5pKDAsImhpZ2hsaWdodCIpfWVsc2V7dD1k
+b2N1bWVudApxPXUuZwpILkRoKHEsdS5oLCJUIiwicXVlcnlTZWxlY3RvckFsbCIpCnQ9dC5xdWVyeVNl
+bGVjdG9yQWxsKCIubGluZS1ubyIpCnA9bmV3IFcud3oodCx1LlIpCmlmKHAuZ0EocCk9PT0wKXJldHVy
+bgpMLnFPKHEuYShDLnQ1Lmd0SCh0KSkpfX0sCmFmOmZ1bmN0aW9uKGEsYixjLGQsZSl7dmFyIHQscyxy
+PUwuRzYod2luZG93LmxvY2F0aW9uLmhyZWYpLHE9TC5hSyh3aW5kb3cubG9jYXRpb24uaHJlZikKaWYo
+ciE9bnVsbCl7dD1kb2N1bWVudC5nZXRFbGVtZW50QnlJZCgibyIrSC5FaihyKSkKaWYodCE9bnVsbClK
+LmRSKHQpLlIoMCwidGFyZ2V0Iil9aWYocSE9bnVsbCl7cz1kb2N1bWVudC5xdWVyeVNlbGVjdG9yKCIu
+bGluZS0iK0guRWoocSkpCmlmKHMhPW51bGwpSi5kUihzLnBhcmVudEVsZW1lbnQpLlIoMCwiaGlnaGxp
+Z2h0Iil9aWYoYT09d2luZG93LmxvY2F0aW9uLnBhdGhuYW1lKXtMLmZHKGIsYykKZS4kMCgpfWVsc2Ug
+TC5HNyhhLGIsYyxkLGUpfSwKUTQ6ZnVuY3Rpb24oYSxiKXt2YXIgdCxzLHI9UC5oSyhhKSxxPXUuWApx
+PVAuRmwocSxxKQpmb3IodD1yLmdoWSgpLHQ9dC5nUHUodCksdD10Lmdreih0KTt0LkYoKTspe3M9dC5n
+bCgpCnEuWSgwLHMuYSxzLmIpfWZvcih0PWIuZ1B1KGIpLHQ9dC5na3oodCk7dC5GKCk7KXtzPXQuZ2wo
+KQpxLlkoMCxzLmEscy5iKX1xLlkoMCwiYXV0aFRva2VuIiwkLlVFKCkpCnJldHVybiByLm5tKDAscSku
+Z25EKCl9LApUMTpmdW5jdGlvbihhKXt2YXIgdCxzLHIscSxwLG8sbixtLGwsaz0kLmhMKCkKSi5sNShr
+LCIiKQppZihhPT1udWxsKXt0PWRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoInAiKQpDLkx0LnNhNCh0LCJT
+ZWUgZGV0YWlscyBhYm91dCBhIHByb3Bvc2VkIGVkaXQuIikKQy5MdC5zUCh0LEguVk0oWyJwbGFjZWhv
+bGRlciJdLHUuaSkpCmsuYXBwZW5kQ2hpbGQodCkKQy5MdC5GRih0KQpyZXR1cm59cz1hLmQKcj0kLm5V
+KCkKcT1yLnpmKHMpCnA9YS5iCm89ZG9jdW1lbnQKbj1yLkhQKHMsSi5UMChvLnF1ZXJ5U2VsZWN0b3Io
+Ii5yb290IikudGV4dENvbnRlbnQpKQptPWEuYwpsPW8uY3JlYXRlRWxlbWVudCgicCIpCmsuYXBwZW5k
+Q2hpbGQobCkKbC5hcHBlbmRDaGlsZChvLmNyZWF0ZVRleHROb2RlKEguRWoocCkrIiBhdCAiKSkKcj11
+LlgKcj1XLko2KEwuUTQoYS5lLFAuRUYoWyJsaW5lIixKLmoobSldLHIscikpKQpyLmFwcGVuZENoaWxk
+KG8uY3JlYXRlVGV4dE5vZGUoSC5FaihuKSsiOiIrSC5FaihtKSsiLiIpKQpsLmFwcGVuZENoaWxkKHIp
+CkouZGgobCkKTC5DQyhhLGsscSkKTC5GeihhLGspfSwKTEg6ZnVuY3Rpb24oYSxiLGMpe3ZhciB0LHMs
+cixxLHAsbyxuLG0sbCxrLGosaSxoLGc9JC55UCgpCkoubDUoZywiIikKaWYoYi5nQShiKT09PTApe3Q9
+ZG9jdW1lbnQKcz10LmNyZWF0ZUVsZW1lbnQoInAiKQpnLmFwcGVuZENoaWxkKHMpCnMuYXBwZW5kQ2hp
+bGQodC5jcmVhdGVUZXh0Tm9kZSgiTm8gcHJvcG9zZWQgZWRpdHMiKSl9ZWxzZSBmb3IoZz1iLmdQdShi
+KSxnPWcuZ2t6KGcpLHQ9dS5qLHI9dC5DKCJ+KDEpPyIpLHE9dS5aLHQ9dC5jO2cuRigpOyl7cD1nLmds
+KCkKbz1kb2N1bWVudApzPW8uY3JlYXRlRWxlbWVudCgicCIpCm49JC55UCgpCm4uYXBwZW5kQ2hpbGQo
+cykKcy5hcHBlbmRDaGlsZChvLmNyZWF0ZVRleHROb2RlKEguRWoocC5hKSsiOiIpKQptPW8uY3JlYXRl
+RWxlbWVudCgidWwiKQpuLmFwcGVuZENoaWxkKG0pCmZvcihwPUouSVQocC5iKTtwLkYoKTspe249cC5n
+bCgpCmw9by5jcmVhdGVFbGVtZW50KCJsaSIpCm0uYXBwZW5kQ2hpbGQobCkKSi5kUihsKS5pKDAsImVk
+aXQiKQprPW8uY3JlYXRlRWxlbWVudCgiYSIpCmwuYXBwZW5kQ2hpbGQoaykKay5jbGFzc0xpc3QuYWRk
+KCJlZGl0LWxpbmsiKQpqPW4uYwppPUguRWooaikKay5zZXRBdHRyaWJ1dGUoImRhdGEtIituZXcgVy5T
+eShuZXcgVy5pNyhrKSkuTygib2Zmc2V0IiksaSkKaD1uLmEKaT1ILkVqKGgpCmsuc2V0QXR0cmlidXRl
+KCJkYXRhLSIrbmV3IFcuU3kobmV3IFcuaTcoaykpLk8oImxpbmUiKSxpKQprLmFwcGVuZENoaWxkKG8u
+Y3JlYXRlVGV4dE5vZGUoImxpbmUgIitILkVqKGgpKSkKaT1yLmEobmV3IEwuRUUoaixoLGEpKQpxLmEo
+bnVsbCkKVy5KRShrLCJjbGljayIsaSwhMSx0KQpsLmFwcGVuZENoaWxkKG8uY3JlYXRlVGV4dE5vZGUo
+IjogIitILkVqKG4uYikpKX19aWYoYylMLlQxKG51bGwpfSwKRnI6ZnVuY3Rpb24oYSxiLGMpe3ZhciB0
+LHMscj13aW5kb3cubG9jYXRpb24scT1QLmhLKChyJiZDLkV4KS5nRHIocikrSC5FaihhKSkKcj11LlgK
+cj1QLkZsKHIscikKaWYoYiE9bnVsbClyLlkoMCwib2Zmc2V0IixILkVqKGIpKQppZihjIT1udWxsKXIu
+WSgwLCJsaW5lIixILkVqKGMpKQpyLlkoMCwiYXV0aFRva2VuIiwkLlVFKCkpCnE9cS5ubSgwLHIpCnI9
+d2luZG93Lmhpc3RvcnkKdD11LnoKcz1xLmduRCgpCnIudG9TdHJpbmcKci5wdXNoU3RhdGUobmV3IFAu
+QmYoW10sW10pLlB2KFAuRmwodCx0KSksIiIscyl9LApFbjpmdW5jdGlvbihhKXt2YXIgdD1KLmJiKGRv
+Y3VtZW50LnF1ZXJ5U2VsZWN0b3IoIi5yb290IikudGV4dENvbnRlbnQsIi8iKQppZihDLnhCLm4oYSx0
+KSlyZXR1cm4gQy54Qi5HKGEsdC5sZW5ndGgpCmVsc2UgcmV0dXJuIGF9LApCWDpmdW5jdGlvbihhLGIp
+e3ZhciB0LHM9e30Kcy5hPWEKYT1MLkVuKGEpCnMuYT1hCkouZHIoJC5EOSgpLGEpCnQ9ZG9jdW1lbnQK
+SC5EaCh1LmcsdS5oLCJUIiwicXVlcnlTZWxlY3RvckFsbCIpCnQ9bmV3IFcud3oodC5xdWVyeVNlbGVj
+dG9yQWxsKCIubmF2LXBhbmVsIC5uYXYtbGluayIpLHUuUikKdC5LKHQsbmV3IEwuVlMocykpfSwKQkU6
+ZnVuY3Rpb24oYSxiLGMpe3ZhciB0PSIucmVnaW9ucyIscz1kb2N1bWVudCxyPXMucXVlcnlTZWxlY3Rv
+cih0KSxxPXMucXVlcnlTZWxlY3RvcigiLmNvZGUiKQpKLnRIKHIsYi5hLCQuS0coKSkKSi50SChxLGIu
+YiwkLktHKCkpCkwuTEgoYSxiLmQsYykKTC52VSgpCkwueVgoIi5jb2RlIiwhMCkKTC55WCh0LCEwKX0s
+CnRYOmZ1bmN0aW9uKGEsYil7dmFyIHQscyxyLHEscCxvLG4sbSxsLGssaixpLGgsZz1kb2N1bWVudCxm
+PWcuY3JlYXRlRWxlbWVudCgidWwiKQphLmFwcGVuZENoaWxkKGYpCmZvcih0PWIubGVuZ3RoLHM9dS5a
+LHI9MDtyPGIubGVuZ3RoO2IubGVuZ3RoPT09dHx8KDAsSC5saykoYiksKytyKXtxPWJbcl0KcD1nLmNy
+ZWF0ZUVsZW1lbnQoImxpIikKZi5hcHBlbmRDaGlsZChwKQpvPUouWUUocCkKaWYocS5hPT09Qy5ZMil7
+by5nUChwKS5pKDAsImRpciIpCm49Zy5jcmVhdGVFbGVtZW50KCJzcGFuIikKcC5hcHBlbmRDaGlsZChu
+KQpvPUouWUUobikKby5nUChuKS5pKDAsImFycm93IikKby5zaGYobiwiJiN4MjVCQzsiKQptPWcuY3Jl
+YXRlRWxlbWVudCgic3BhbiIpCnAuYXBwZW5kQ2hpbGQobSkKSi5sNShtLCc8c3BhbiBjbGFzcz0ibWF0
+ZXJpYWwtaWNvbnMiPmZvbGRlcl9vcGVuPC9zcGFuPicpCnAuYXBwZW5kQ2hpbGQoZy5jcmVhdGVUZXh0
+Tm9kZShxLmIpKQpMLnRYKHAscS5jKQpMLmt6KG4pfWVsc2V7by5zaGYocCwnPHNwYW4gY2xhc3M9Im1h
+dGVyaWFsLWljb25zIj5pbnNlcnRfZHJpdmVfZmlsZTwvc3Bhbj4nKQpsPWcuY3JlYXRlRWxlbWVudCgi
+YSIpCnAuYXBwZW5kQ2hpbGQobCkKbz1KLllFKGwpCm8uZ1AobCkuaSgwLCJuYXYtbGluayIpCmwuc2V0
+QXR0cmlidXRlKCJkYXRhLSIrbmV3IFcuU3kobmV3IFcuaTcobCkpLk8oIm5hbWUiKSxxLmQpCmwuc2V0
+QXR0cmlidXRlKCJocmVmIixxLmUpCmwuYXBwZW5kQ2hpbGQoZy5jcmVhdGVUZXh0Tm9kZShxLmIpKQpv
+PW8uZ1ZsKGwpCms9by4kdGkKaj1rLkMoIn4oMSk/IikuYShuZXcgTC5URCgpKQpzLmEobnVsbCkKVy5K
+RShvLmEsby5iLGosITEsay5jKQppPXEuZgppZih0eXBlb2YgaSE9PSJudW1iZXIiKXJldHVybiBpLm9z
+KCkKaWYoaT4wKXtoPWcuY3JlYXRlRWxlbWVudCgic3BhbiIpCnAuYXBwZW5kQ2hpbGQoaCkKSi5kUiho
+KS5pKDAsImVkaXQtY291bnQiKQpvPSIiK2krIiAiCmlmKGk9PT0xKWs9ImVkaXQiCmVsc2Ugaz0iZWRp
+dHMiCmguc2V0QXR0cmlidXRlKCJ0aXRsZSIsbytrKQpoLmFwcGVuZENoaWxkKGcuY3JlYXRlVGV4dE5v
+ZGUoQy5qbi53KGkpKSl9fX19LApGejpmdW5jdGlvbihhLGIpe3ZhciB0LHMscixxLHAsbyxuLG0sbCxr
+LGosaT1hLmEKaWYoaT09bnVsbClyZXR1cm4KdD1kb2N1bWVudApzPXQuY3JlYXRlRWxlbWVudCgicCIp
+CnI9Yi5hcHBlbmRDaGlsZChzKQpzPXQuY3JlYXRlRWxlbWVudCgic3BhbiIpCnE9dS5pCkouTXUocyxI
+LlZNKFsidHlwZS1kZXNjcmlwdGlvbiJdLHEpKQpzLmFwcGVuZENoaWxkKHQuY3JlYXRlVGV4dE5vZGUo
+IkFjdGlvbnMiKSkKci5hcHBlbmRDaGlsZChzKQpyLmFwcGVuZENoaWxkKHQuY3JlYXRlVGV4dE5vZGUo
+IjoiKSkKcD10LmNyZWF0ZUVsZW1lbnQoInAiKQpiLmFwcGVuZENoaWxkKHApCmZvcihzPWkubGVuZ3Ro
+LG89dS5RLG49MDtuPGkubGVuZ3RoO2kubGVuZ3RoPT09c3x8KDAsSC5saykoaSksKytuKXttPWlbbl0K
+bD10LmNyZWF0ZUVsZW1lbnQoImEiKQpwLmFwcGVuZENoaWxkKGwpCmwuYXBwZW5kQ2hpbGQodC5jcmVh
+dGVUZXh0Tm9kZShtLmEpKQpsLnNldEF0dHJpYnV0ZSgiaHJlZiIsbS5iKQprPW8uYShILlZNKFsiYWRk
+LWhpbnQtbGluayIsImJlZm9yZS1hcHBseSIsImJ1dHRvbiJdLHEpKQpqPUouZFIobCkKai5WMSgwKQpq
+LkZWKDAsayl9fSwKQ0M6ZnVuY3Rpb24oYTgsYTksYjApe3ZhciB0LHMscixxLHAsbyxuLG0sbCxrLGos
+aSxoLGcsZixlLGQsYyxiLGEsYTAsYTEsYTIsYTMsYTQsYTUsYTYsYTcKZm9yKHQ9YTguZixzPXQubGVu
+Z3RoLHI9dS5pLHE9dS5RLHA9dS5qLG89cC5DKCJ+KDEpPyIpLG49dS5aLHA9cC5jLG09MDttPHQubGVu
+Z3RoO3QubGVuZ3RoPT09c3x8KDAsSC5saykodCksKyttKXtsPXRbbV0Kaz1kb2N1bWVudApqPWsuY3Jl
+YXRlRWxlbWVudCgicCIpCmk9cS5hKEguVk0oWyJ0cmFjZSJdLHIpKQpoPUouZFIoaikKaC5WMSgwKQpo
+LkZWKDAsaSkKZz1hOS5hcHBlbmRDaGlsZChqKQpqPWsuY3JlYXRlRWxlbWVudCgic3BhbiIpCmk9cS5h
+KEguVk0oWyJ0eXBlLWRlc2NyaXB0aW9uIl0scikpCmg9Si5kUihqKQpoLlYxKDApCmguRlYoMCxpKQpq
+LmFwcGVuZENoaWxkKGsuY3JlYXRlVGV4dE5vZGUobC5hKSkKZy5hcHBlbmRDaGlsZChqKQpnLmFwcGVu
+ZENoaWxkKGsuY3JlYXRlVGV4dE5vZGUoIjoiKSkKaj1rLmNyZWF0ZUVsZW1lbnQoInVsIikKaT1xLmEo
+SC5WTShbInRyYWNlIl0scikpCmg9Si5kUihqKQpoLlYxKDApCmguRlYoMCxpKQpmPWcuYXBwZW5kQ2hp
+bGQoaikKZm9yKGo9bC5iLGk9ai5sZW5ndGgsZT0wO2U8ai5sZW5ndGg7ai5sZW5ndGg9PT1pfHwoMCxI
+LmxrKShqKSwrK2Upe2Q9altlXQpjPWsuY3JlYXRlRWxlbWVudCgibGkiKQpmLmFwcGVuZENoaWxkKGMp
+CmI9ay5jcmVhdGVFbGVtZW50KCJzcGFuIikKYT1xLmEoSC5WTShbImZ1bmN0aW9uIl0scikpCmg9Si5k
+UihiKQpoLlYxKDApCmguRlYoMCxhKQphPWQuYgpMLmtEKGIsYT09bnVsbD8idW5rbm93biI6YSkKYy5h
+cHBlbmRDaGlsZChiKQphMD1kLmMKaWYoYTAhPW51bGwpe2MuYXBwZW5kQ2hpbGQoay5jcmVhdGVUZXh0
+Tm9kZSgiICgiKSkKYTE9YTAuYgphMj1rLmNyZWF0ZUVsZW1lbnQoImEiKQphMi5hcHBlbmRDaGlsZChr
+LmNyZWF0ZVRleHROb2RlKEguRWooYTAuYykrIjoiK0guRWooYTEpKSkKYTIuc2V0QXR0cmlidXRlKCJo
+cmVmIixhMC5hKQphMi5jbGFzc0xpc3QuYWRkKCJuYXYtbGluayIpCmMuYXBwZW5kQ2hpbGQoYTIpCmMu
+YXBwZW5kQ2hpbGQoay5jcmVhdGVUZXh0Tm9kZSgiKSIpKX1jLmFwcGVuZENoaWxkKGsuY3JlYXRlVGV4
+dE5vZGUoIjogIikpCmI9ZC5hCkwua0QoYyxiPT1udWxsPyJ1bmtub3duIjpiKQpiPWQuZAppZihiLmxl
+bmd0aCE9PTApe2E9ay5jcmVhdGVFbGVtZW50KCJwIikKYTM9cS5hKEguVk0oWyJkcmF3ZXIiLCJiZWZv
+cmUtYXBwbHkiXSxyKSkKaD1KLmRSKGEpCmguVjEoMCkKaC5GVigwLGEzKQphND1jLmFwcGVuZENoaWxk
+KGEpCmZvcihhPWIubGVuZ3RoLGE1PTA7YTU8Yi5sZW5ndGg7Yi5sZW5ndGg9PT1hfHwoMCxILmxrKShi
+KSwrK2E1KXthNj1iW2E1XQphMz1rLmNyZWF0ZUVsZW1lbnQoImJ1dHRvbiIpCmE3PW8uYShuZXcgTC5B
+UyhhNixhMCkpCm4uYShudWxsKQpXLkpFKGEzLCJjbGljayIsYTcsITEscCkKYTMuYXBwZW5kQ2hpbGQo
+ay5jcmVhdGVUZXh0Tm9kZShNLk9YKGE2LmEpKSkKYTQuYXBwZW5kQ2hpbGQoYTMpfX19fX0sCmtEOmZ1
+bmN0aW9uKGEsYil7dmFyIHQscyxyPUguVk0oYi5zcGxpdCgiLiIpLHUucykscT1DLk5tLmd0SChyKSxw
+PWRvY3VtZW50CmEuYXBwZW5kQ2hpbGQocC5jcmVhdGVUZXh0Tm9kZShxKSkKZm9yKHE9SC5xQyhyLDEs
+bnVsbCx1Lk4pLHE9bmV3IEguYTcocSxxLmdBKHEpLHEuJHRpLkMoImE3PGFMLkU+IikpLHQ9Si5ZRShh
+KTtxLkYoKTspe3M9cS5kCnQubnooYSwiYmVmb3JlZW5kIiwiJiM4MjAzOy4iLG51bGwsbnVsbCkKYS5h
+cHBlbmRDaGlsZChwLmNyZWF0ZVRleHROb2RlKHMpKX19LAplOmZ1bmN0aW9uIGUoKXt9LApWVzpmdW5j
+dGlvbiBWVyhhLGIsYyl7dGhpcy5hPWEKdGhpcy5iPWIKdGhpcy5jPWN9LApvWjpmdW5jdGlvbiBvWigp
+e30sCmpyOmZ1bmN0aW9uIGpyKCl7fSwKcWw6ZnVuY3Rpb24gcWwoKXt9LApIaTpmdW5jdGlvbiBIaSgp
+e30sCkJUOmZ1bmN0aW9uIEJUKCl7fSwKUFk6ZnVuY3Rpb24gUFkoKXt9LApMOmZ1bmN0aW9uIEwoKXt9
+LApXeDpmdW5jdGlvbiBXeChhLGIpe3RoaXMuYT1hCnRoaXMuYj1ifSwKQU86ZnVuY3Rpb24gQU8oYSl7
+dGhpcy5hPWF9LApkTjpmdW5jdGlvbiBkTihhKXt0aGlzLmE9YX0sCkhvOmZ1bmN0aW9uIEhvKGEpe3Ro
+aXMuYT1hfSwKeHo6ZnVuY3Rpb24geHooYSxiKXt0aGlzLmE9YQp0aGlzLmI9Yn0sCklDOmZ1bmN0aW9u
+IElDKCl7fSwKZkM6ZnVuY3Rpb24gZkMoYSxiKXt0aGlzLmE9YQp0aGlzLmI9Yn0sCm5UOmZ1bmN0aW9u
+IG5UKGEsYixjKXt0aGlzLmE9YQp0aGlzLmI9Ygp0aGlzLmM9Y30sCk5ZOmZ1bmN0aW9uIE5ZKGEpe3Ro
+aXMuYT1hfSwKZVg6ZnVuY3Rpb24gZVgoKXt9LApFRTpmdW5jdGlvbiBFRShhLGIsYyl7dGhpcy5hPWEK
+dGhpcy5iPWIKdGhpcy5jPWN9LApRTDpmdW5jdGlvbiBRTChhLGIpe3RoaXMuYT1hCnRoaXMuYj1ifSwK
+VlM6ZnVuY3Rpb24gVlMoYSl7dGhpcy5hPWF9LApURDpmdW5jdGlvbiBURCgpe30sCkFTOmZ1bmN0aW9u
+IEFTKGEsYil7dGhpcy5hPWEKdGhpcy5iPWJ9LApYQTpmdW5jdGlvbiBYQSgpe30sCm1LOmZ1bmN0aW9u
+KGEpe3ZhciB0LHMscixxLHAsbyxuPUguVk0oW10sdS5jUSkKZm9yKHQ9Si5JVCh1LlUuYShhKSk7dC5G
+KCk7KXtzPXQuZ2woKQpyPUouVTYocykKcT1MLnAyKEguaChyLnEocywidHlwZSIpKSkKcD1ILmgoci5x
+KHMsIm5hbWUiKSkKbz1yLnEocywic3VidHJlZSIpCm89bz09bnVsbD9udWxsOkwubUsobykKQy5ObS5p
+KG4sbmV3IEwuWloocSxwLG8sSC5oKHIucShzLCJwYXRoIikpLEguaChyLnEocywiaHJlZiIpKSxILnVQ
+KHIucShzLCJlZGl0Q291bnQiKSkpKX1yZXR1cm4gbn0sClZEOmZ1bmN0aW9uKGEpe3ZhciB0LHMscj1I
+LlZNKFtdLHUuRykKZm9yKHQ9YS5sZW5ndGgscz0wO3M8YS5sZW5ndGg7YS5sZW5ndGg9PT10fHwoMCxI
+LmxrKShhKSwrK3MpQy5ObS5pKHIsYVtzXS5MdCgpKQpyZXR1cm4gcn0sCnAyOmZ1bmN0aW9uKGEpe3N3
+aXRjaChhKXtjYXNlImRpcmVjdG9yeSI6cmV0dXJuIEMuWTIKY2FzZSJmaWxlIjpyZXR1cm4gQy5yZgpk
+ZWZhdWx0OnRocm93IEguYihQLlBWKCJVbnJlY29nbml6ZWQgbmF2aWdhdGlvbiB0cmVlIG5vZGUgdHlw
+ZTogIitILkVqKGEpKSl9fSwKdnk6ZnVuY3Rpb24oYSl7c3dpdGNoKGEpe2Nhc2UgQy5ZMjpyZXR1cm4i
+ZGlyZWN0b3J5IgpjYXNlIEMucmY6cmV0dXJuImZpbGUifXRocm93IEguYihQLlBWKCJVbnJlY29nbml6
+ZWQgbmF2aWdhdGlvbiB0cmVlIG5vZGUgdHlwZTogIithLncoMCkpKX0sClpaOmZ1bmN0aW9uIFpaKGEs
+YixjLGQsZSxmKXt2YXIgXz10aGlzCl8uYT1hCl8uYj1iCl8uYz1jCl8uZD1kCl8uZT1lCl8uZj1mfSwK
+Tzk6ZnVuY3Rpb24gTzkoYSl7dGhpcy5iPWF9LApJVjpmdW5jdGlvbiBJVihhLGIsYyxkKXt2YXIgXz10
+aGlzCl8uZD1hCl8uZT1iCl8uZj1jCl8ucj1kfX0sWD17CkNMOmZ1bmN0aW9uKGEsYil7dmFyIHQscyxy
+LHEscCxvPWIueFooYSkKYi5oSyhhKQppZihvIT1udWxsKWE9Si5LVihhLG8ubGVuZ3RoKQp0PXUuaQpz
+PUguVk0oW10sdCkKcj1ILlZNKFtdLHQpCnQ9YS5sZW5ndGgKaWYodCE9PTAmJmIucjQoQy54Qi5XKGEs
+MCkpKXtpZigwPj10KXJldHVybiBILk9IKGEsMCkKQy5ObS5pKHIsYVswXSkKcT0xfWVsc2V7Qy5ObS5p
+KHIsIiIpCnE9MH1mb3IocD1xO3A8dDsrK3ApaWYoYi5yNChDLnhCLlcoYSxwKSkpe0MuTm0uaShzLEMu
+eEIuTmooYSxxLHApKQpDLk5tLmkocixhW3BdKQpxPXArMX1pZihxPHQpe0MuTm0uaShzLEMueEIuRyhh
+LHEpKQpDLk5tLmkociwiIil9cmV0dXJuIG5ldyBYLldEKGIsbyxzLHIpfSwKV0Q6ZnVuY3Rpb24gV0Qo
+YSxiLGMsZCl7dmFyIF89dGhpcwpfLmE9YQpfLmI9YgpfLmQ9YwpfLmU9ZH0sCkk3OmZ1bmN0aW9uKGEp
+e3JldHVybiBuZXcgWC5kdihhKX0sCmR2OmZ1bmN0aW9uIGR2KGEpe3RoaXMuYT1hfX0sTz17ClJoOmZ1
+bmN0aW9uKCl7dmFyIHQscz1udWxsCmlmKFAudW8oKS5nRmkoKSE9PSJmaWxlIilyZXR1cm4gJC5FYigp
+CnQ9UC51bygpCmlmKCFDLnhCLlRjKHQuZ0lpKHQpLCIvIikpcmV0dXJuICQuRWIoKQppZihQLktMKHMs
+ImEvYiIscyxzLHMscyxzKS50NCgpPT09ImFcXGIiKXJldHVybiAkLktrKCkKcmV0dXJuICQuYkQoKX0s
+CnpMOmZ1bmN0aW9uIHpMKCl7fX0sRT17T0Y6ZnVuY3Rpb24gT0YoYSxiLGMpe3RoaXMuZD1hCnRoaXMu
+ZT1iCnRoaXMuZj1jfX0sRj17cnU6ZnVuY3Rpb24gcnUoYSxiLGMsZCl7dmFyIF89dGhpcwpfLmQ9YQpf
+LmU9YgpfLmY9YwpfLnI9ZH19LEQ9ewpSWDpmdW5jdGlvbigpe3ZhciB0LHMscixxLHA9bnVsbAp0cnl7
+cD1QLnVvKCl9Y2F0Y2godCl7aWYodS5lai5iKEguUnUodCkpKXtzPSQuRmYKaWYocyE9bnVsbClyZXR1
+cm4gcwp0aHJvdyB0fWVsc2UgdGhyb3cgdH1pZihKLlJNKHAsJC5JNikpcmV0dXJuICQuRmYKJC5JNj1w
+CmlmKCQuSGsoKT09JC5FYigpKXM9JC5GZj1wLlpJKCIuIikudygwKQplbHNle3I9cC50NCgpCnE9ci5s
+ZW5ndGgtMQpzPSQuRmY9cT09PTA/cjpDLnhCLk5qKHIsMCxxKX1yZXR1cm4gc319CnZhciB3PVtDLEgs
+SixQLFcsTSxVLEIsVCxMLFgsTyxFLEYsRF0KaHVua0hlbHBlcnMuc2V0RnVuY3Rpb25OYW1lc0lmTmVj
+ZXNzYXJ5KHcpCnZhciAkPXt9CkguRksucHJvdG90eXBlPXt9CkoudkIucHJvdG90eXBlPXsKRE46ZnVu
+Y3Rpb24oYSxiKXtyZXR1cm4gYT09PWJ9LApnaU86ZnVuY3Rpb24oYSl7cmV0dXJuIEguZVEoYSl9LAp3
+OmZ1bmN0aW9uKGEpe3JldHVybiJJbnN0YW5jZSBvZiAnIitILkVqKEguTShhKSkrIicifSwKZTc6ZnVu
+Y3Rpb24oYSxiKXt1Lm8uYShiKQp0aHJvdyBILmIoUC5scihhLGIuZ1dhKCksYi5nbmQoKSxiLmdWbSgp
+KSl9fQpKLnlFLnByb3RvdHlwZT17Cnc6ZnVuY3Rpb24oYSl7cmV0dXJuIFN0cmluZyhhKX0sCmdpTzpm
+dW5jdGlvbihhKXtyZXR1cm4gYT81MTkwMTg6MjE4MTU5fSwKJGlhMjoxfQpKLndlLnByb3RvdHlwZT17
+CkROOmZ1bmN0aW9uKGEsYil7cmV0dXJuIG51bGw9PWJ9LAp3OmZ1bmN0aW9uKGEpe3JldHVybiJudWxs
+In0sCmdpTzpmdW5jdGlvbihhKXtyZXR1cm4gMH0sCmU3OmZ1bmN0aW9uKGEsYil7cmV0dXJuIHRoaXMu
+U2ooYSx1Lm8uYShiKSl9LAokaWM4OjF9CkouTUYucHJvdG90eXBlPXsKZ2lPOmZ1bmN0aW9uKGEpe3Jl
+dHVybiAwfSwKdzpmdW5jdGlvbihhKXtyZXR1cm4gU3RyaW5nKGEpfSwKJGl2bToxfQpKLmlDLnByb3Rv
+dHlwZT17fQpKLmtkLnByb3RvdHlwZT17fQpKLmM1LnByb3RvdHlwZT17Cnc6ZnVuY3Rpb24oYSl7dmFy
+IHQ9YVskLncoKV0KaWYodD09bnVsbClyZXR1cm4gdGhpcy50KGEpCnJldHVybiJKYXZhU2NyaXB0IGZ1
+bmN0aW9uIGZvciAiK0guRWooSi5qKHQpKX0sCiRTOmZ1bmN0aW9uKCl7cmV0dXJue2Z1bmM6MSxvcHQ6
+WywsLCwsLCwsLCwsLCwsLCxdfX0sCiRpRUg6MX0KSi5qZC5wcm90b3R5cGU9ewppOmZ1bmN0aW9uKGEs
+Yil7SC50NihhKS5jLmEoYikKaWYoISFhLmZpeGVkJGxlbmd0aClILnZoKFAuTDQoImFkZCIpKQphLnB1
+c2goYil9LApXNDpmdW5jdGlvbihhLGIpe3ZhciB0CmlmKCEhYS5maXhlZCRsZW5ndGgpSC52aChQLkw0
+KCJyZW1vdmVBdCIpKQp0PWEubGVuZ3RoCmlmKGI+PXQpdGhyb3cgSC5iKFAuTzcoYixudWxsKSkKcmV0
+dXJuIGEuc3BsaWNlKGIsMSlbMF19LApVRzpmdW5jdGlvbihhLGIsYyl7dmFyIHQscyxyCkgudDYoYSku
+QygiY1g8MT4iKS5hKGMpCmlmKCEhYS5maXhlZCRsZW5ndGgpSC52aChQLkw0KCJpbnNlcnRBbGwiKSkK
+dD1hLmxlbmd0aApQLndBKGIsMCx0LCJpbmRleCIpCnM9Yy5sZW5ndGgKdGhpcy5zQShhLHQrcykKcj1i
+K3MKdGhpcy5ZVyhhLHIsYS5sZW5ndGgsYSxiKQp0aGlzLnZnKGEsYixyLGMpfSwKbXY6ZnVuY3Rpb24o
+YSl7aWYoISFhLmZpeGVkJGxlbmd0aClILnZoKFAuTDQoInJlbW92ZUxhc3QiKSkKaWYoYS5sZW5ndGg9
+PT0wKXRocm93IEguYihILkhZKGEsLTEpKQpyZXR1cm4gYS5wb3AoKX0sCkZWOmZ1bmN0aW9uKGEsYil7
+dmFyIHQKSC50NihhKS5DKCJjWDwxPiIpLmEoYikKaWYoISFhLmZpeGVkJGxlbmd0aClILnZoKFAuTDQo
+ImFkZEFsbCIpKQpmb3IodD1KLklUKGIpO3QuRigpOylhLnB1c2godC5nbCgpKX0sCkUyOmZ1bmN0aW9u
+KGEsYixjKXt2YXIgdD1ILnQ2KGEpCnJldHVybiBuZXcgSC5sSihhLHQuS3EoYykuQygiMSgyKSIpLmEo
+YiksdC5DKCJAPDE+IikuS3EoYykuQygibEo8MSwyPiIpKX0sCkg6ZnVuY3Rpb24oYSxiKXt2YXIgdCxz
+PVAuTzgoYS5sZW5ndGgsIiIsITEsdS5OKQpmb3IodD0wO3Q8YS5sZW5ndGg7Kyt0KXRoaXMuWShzLHQs
+SC5FaihhW3RdKSkKcmV0dXJuIHMuam9pbihiKX0sCk4wOmZ1bmN0aW9uKGEsYixjLGQpe3ZhciB0LHMs
+cgpkLmEoYikKSC50NihhKS5LcShkKS5DKCIxKDEsMikiKS5hKGMpCnQ9YS5sZW5ndGgKZm9yKHM9Yixy
+PTA7cjx0Oysrcil7cz1jLiQyKHMsYVtyXSkKaWYoYS5sZW5ndGghPT10KXRocm93IEguYihQLmE0KGEp
+KX1yZXR1cm4gc30sCkh0OmZ1bmN0aW9uKGEsYil7dmFyIHQscyxyLHEscApILnQ2KGEpLkMoImEyKDEp
+IikuYShiKQp0PWEubGVuZ3RoCmZvcihzPW51bGwscj0hMSxxPTA7cTx0OysrcSl7cD1hW3FdCmlmKEgu
+b1QoYi4kMShwKSkpe2lmKHIpdGhyb3cgSC5iKEguQW0oKSkKcz1wCnI9ITB9aWYodCE9PWEubGVuZ3Ro
+KXRocm93IEguYihQLmE0KGEpKX1pZihyKXJldHVybiBzCnRocm93IEguYihILldwKCkpfSwKRTpmdW5j
+dGlvbihhLGIpe2lmKGI8MHx8Yj49YS5sZW5ndGgpcmV0dXJuIEguT0goYSxiKQpyZXR1cm4gYVtiXX0s
+Cmd0SDpmdW5jdGlvbihhKXtpZihhLmxlbmd0aD4wKXJldHVybiBhWzBdCnRocm93IEguYihILldwKCkp
+fSwKZ3JaOmZ1bmN0aW9uKGEpe3ZhciB0PWEubGVuZ3RoCmlmKHQ+MClyZXR1cm4gYVt0LTFdCnRocm93
+IEguYihILldwKCkpfSwKWVc6ZnVuY3Rpb24oYSxiLGMsZCxlKXt2YXIgdCxzLHIscQpILnQ2KGEpLkMo
+ImNYPDE+IikuYShkKQppZighIWEuaW1tdXRhYmxlJGxpc3QpSC52aChQLkw0KCJzZXRSYW5nZSIpKQpQ
+LmpCKGIsYyxhLmxlbmd0aCkKdD1jLWIKaWYodD09PTApcmV0dXJuClAuazEoZSwic2tpcENvdW50IikK
+cz1kCnI9Si5VNihzKQppZihlK3Q+ci5nQShzKSl0aHJvdyBILmIoSC5hcigpKQppZihlPGIpZm9yKHE9
+dC0xO3E+PTA7LS1xKWFbYitxXT1yLnEocyxlK3EpCmVsc2UgZm9yKHE9MDtxPHQ7KytxKWFbYitxXT1y
+LnEocyxlK3EpfSwKdmc6ZnVuY3Rpb24oYSxiLGMsZCl7cmV0dXJuIHRoaXMuWVcoYSxiLGMsZCwwKX0s
+ClZyOmZ1bmN0aW9uKGEsYil7dmFyIHQscwpILnQ2KGEpLkMoImEyKDEpIikuYShiKQp0PWEubGVuZ3Ro
+CmZvcihzPTA7czx0Oysrcyl7aWYoSC5vVChiLiQxKGFbc10pKSlyZXR1cm4hMAppZihhLmxlbmd0aCE9
+PXQpdGhyb3cgSC5iKFAuYTQoYSkpfXJldHVybiExfSwKdGc6ZnVuY3Rpb24oYSxiKXt2YXIgdApmb3Io
+dD0wO3Q8YS5sZW5ndGg7Kyt0KWlmKEouUk0oYVt0XSxiKSlyZXR1cm4hMApyZXR1cm4hMX0sCmdsMDpm
+dW5jdGlvbihhKXtyZXR1cm4gYS5sZW5ndGg9PT0wfSwKZ29yOmZ1bmN0aW9uKGEpe3JldHVybiBhLmxl
+bmd0aCE9PTB9LAp3OmZ1bmN0aW9uKGEpe3JldHVybiBQLldFKGEsIlsiLCJdIil9LApna3o6ZnVuY3Rp
+b24oYSl7cmV0dXJuIG5ldyBKLm0xKGEsYS5sZW5ndGgsSC50NihhKS5DKCJtMTwxPiIpKX0sCmdpTzpm
+dW5jdGlvbihhKXtyZXR1cm4gSC5lUShhKX0sCmdBOmZ1bmN0aW9uKGEpe3JldHVybiBhLmxlbmd0aH0s
+CnNBOmZ1bmN0aW9uKGEsYil7aWYoISFhLmZpeGVkJGxlbmd0aClILnZoKFAuTDQoInNldCBsZW5ndGgi
+KSkKYS5sZW5ndGg9Yn0sCnE6ZnVuY3Rpb24oYSxiKXtILnVQKGIpCmlmKGI+PWEubGVuZ3RofHxiPDAp
+dGhyb3cgSC5iKEguSFkoYSxiKSkKcmV0dXJuIGFbYl19LApZOmZ1bmN0aW9uKGEsYixjKXtILnQ2KGEp
+LmMuYShjKQppZighIWEuaW1tdXRhYmxlJGxpc3QpSC52aChQLkw0KCJpbmRleGVkIHNldCIpKQppZihi
+Pj1hLmxlbmd0aHx8YjwwKXRocm93IEguYihILkhZKGEsYikpCmFbYl09Y30sCiRpYlE6MSwKJGljWDox
+LAokaXpNOjF9CkouUG8ucHJvdG90eXBlPXt9CkoubTEucHJvdG90eXBlPXsKZ2w6ZnVuY3Rpb24oKXty
+ZXR1cm4gdGhpcy5kfSwKRjpmdW5jdGlvbigpe3ZhciB0LHM9dGhpcyxyPXMuYSxxPXIubGVuZ3RoCmlm
+KHMuYiE9PXEpdGhyb3cgSC5iKEgubGsocikpCnQ9cy5jCmlmKHQ+PXEpe3Muc00obnVsbCkKcmV0dXJu
+ITF9cy5zTShyW3RdKTsrK3MuYwpyZXR1cm4hMH0sCnNNOmZ1bmN0aW9uKGEpe3RoaXMuZD10aGlzLiR0
+aS5DKCIxPyIpLmEoYSl9LAokaUFuOjF9CkoucUkucHJvdG90eXBlPXsKelE6ZnVuY3Rpb24oYSl7aWYo
+YT4wKXtpZihhIT09MS8wKXJldHVybiBNYXRoLnJvdW5kKGEpfWVsc2UgaWYoYT4tMS8wKXJldHVybiAw
+LU1hdGgucm91bmQoMC1hKQp0aHJvdyBILmIoUC5MNCgiIithKyIucm91bmQoKSIpKX0sCnc6ZnVuY3Rp
+b24oYSl7aWYoYT09PTAmJjEvYTwwKXJldHVybiItMC4wIgplbHNlIHJldHVybiIiK2F9LApnaU86ZnVu
+Y3Rpb24oYSl7dmFyIHQscyxyLHEscD1hfDAKaWYoYT09PXApcmV0dXJuIDUzNjg3MDkxMSZwCnQ9TWF0
+aC5hYnMoYSkKcz1NYXRoLmxvZyh0KS8wLjY5MzE0NzE4MDU1OTk0NTN8MApyPU1hdGgucG93KDIscykK
+cT10PDE/dC9yOnIvdApyZXR1cm4gNTM2ODcwOTExJigocSo5MDA3MTk5MjU0NzQwOTkyfDApKyhxKjM1
+NDIyNDMxODExNzY1MjF8MCkpKjU5OTE5NytzKjEyNTl9LAp6WTpmdW5jdGlvbihhLGIpe3ZhciB0PWEl
+YgppZih0PT09MClyZXR1cm4gMAppZih0PjApcmV0dXJuIHQKaWYoYjwwKXJldHVybiB0LWIKZWxzZSBy
+ZXR1cm4gdCtifSwKQlU6ZnVuY3Rpb24oYSxiKXtyZXR1cm4oYXwwKT09PWE/YS9ifDA6dGhpcy5ESihh
+LGIpfSwKREo6ZnVuY3Rpb24oYSxiKXt2YXIgdD1hL2IKaWYodD49LTIxNDc0ODM2NDgmJnQ8PTIxNDc0
+ODM2NDcpcmV0dXJuIHR8MAppZih0PjApe2lmKHQhPT0xLzApcmV0dXJuIE1hdGguZmxvb3IodCl9ZWxz
+ZSBpZih0Pi0xLzApcmV0dXJuIE1hdGguY2VpbCh0KQp0aHJvdyBILmIoUC5MNCgiUmVzdWx0IG9mIHRy
+dW5jYXRpbmcgZGl2aXNpb24gaXMgIitILkVqKHQpKyI6ICIrSC5FaihhKSsiIH4vICIrYikpfSwKd0c6
+ZnVuY3Rpb24oYSxiKXt2YXIgdAppZihhPjApdD10aGlzLnAzKGEsYikKZWxzZXt0PWI+MzE/MzE6Ygp0
+PWE+PnQ+Pj4wfXJldHVybiB0fSwKYmY6ZnVuY3Rpb24oYSxiKXtpZihiPDApdGhyb3cgSC5iKEgudEwo
+YikpCnJldHVybiB0aGlzLnAzKGEsYil9LApwMzpmdW5jdGlvbihhLGIpe3JldHVybiBiPjMxPzA6YT4+
+PmJ9LAokaUNQOjEsCiRpbGY6MX0KSi5iVS5wcm90b3R5cGU9eyRpSWY6MX0KSi5WQS5wcm90b3R5cGU9
+e30KSi5Eci5wcm90b3R5cGU9ewptOmZ1bmN0aW9uKGEsYil7aWYoYjwwKXRocm93IEguYihILkhZKGEs
+YikpCmlmKGI+PWEubGVuZ3RoKUgudmgoSC5IWShhLGIpKQpyZXR1cm4gYS5jaGFyQ29kZUF0KGIpfSwK
+VzpmdW5jdGlvbihhLGIpe2lmKGI+PWEubGVuZ3RoKXRocm93IEguYihILkhZKGEsYikpCnJldHVybiBh
+LmNoYXJDb2RlQXQoYil9LApkZDpmdW5jdGlvbihhLGIpe3JldHVybiBuZXcgSC51bihiLGEsMCl9LApo
+OmZ1bmN0aW9uKGEsYil7aWYodHlwZW9mIGIhPSJzdHJpbmciKXRocm93IEguYihQLkwzKGIsbnVsbCxu
+dWxsKSkKcmV0dXJuIGErYn0sClRjOmZ1bmN0aW9uKGEsYil7dmFyIHQ9Yi5sZW5ndGgscz1hLmxlbmd0
+aAppZih0PnMpcmV0dXJuITEKcmV0dXJuIGI9PT10aGlzLkcoYSxzLXQpfSwKaTc6ZnVuY3Rpb24oYSxi
+LGMsZCl7dmFyIHQ9UC5qQihiLGMsYS5sZW5ndGgpLHM9YS5zdWJzdHJpbmcoMCxiKSxyPWEuc3Vic3Ry
+aW5nKHQpCnJldHVybiBzK2Qrcn0sClFpOmZ1bmN0aW9uKGEsYixjKXt2YXIgdAppZihjPDB8fGM+YS5s
+ZW5ndGgpdGhyb3cgSC5iKFAuVEUoYywwLGEubGVuZ3RoLG51bGwsbnVsbCkpCnQ9YytiLmxlbmd0aApp
+Zih0PmEubGVuZ3RoKXJldHVybiExCnJldHVybiBiPT09YS5zdWJzdHJpbmcoYyx0KX0sCm46ZnVuY3Rp
+b24oYSxiKXtyZXR1cm4gdGhpcy5RaShhLGIsMCl9LApOajpmdW5jdGlvbihhLGIsYyl7aWYoYz09bnVs
+bCljPWEubGVuZ3RoCmlmKGI8MCl0aHJvdyBILmIoUC5PNyhiLG51bGwpKQppZihiPmMpdGhyb3cgSC5i
+KFAuTzcoYixudWxsKSkKaWYoYz5hLmxlbmd0aCl0aHJvdyBILmIoUC5PNyhjLG51bGwpKQpyZXR1cm4g
+YS5zdWJzdHJpbmcoYixjKX0sCkc6ZnVuY3Rpb24oYSxiKXtyZXR1cm4gdGhpcy5OaihhLGIsbnVsbCl9
+LApoYzpmdW5jdGlvbihhKXtyZXR1cm4gYS50b0xvd2VyQ2FzZSgpfSwKYlM6ZnVuY3Rpb24oYSl7dmFy
+IHQscyxyLHE9YS50cmltKCkscD1xLmxlbmd0aAppZihwPT09MClyZXR1cm4gcQppZih0aGlzLlcocSww
+KT09PTEzMyl7dD1KLm1tKHEsMSkKaWYodD09PXApcmV0dXJuIiJ9ZWxzZSB0PTAKcz1wLTEKcj10aGlz
+Lm0ocSxzKT09PTEzMz9KLmMxKHEscyk6cAppZih0PT09MCYmcj09PXApcmV0dXJuIHEKcmV0dXJuIHEu
+c3Vic3RyaW5nKHQscil9LApJeDpmdW5jdGlvbihhLGIpe3ZhciB0LHMKaWYoMD49YilyZXR1cm4iIgpp
+ZihiPT09MXx8YS5sZW5ndGg9PT0wKXJldHVybiBhCmlmKGIhPT1iPj4+MCl0aHJvdyBILmIoQy5FcSkK
+Zm9yKHQ9YSxzPSIiOyEwOyl7aWYoKGImMSk9PT0xKXM9dCtzCmI9Yj4+PjEKaWYoYj09PTApYnJlYWsK
+dCs9dH1yZXR1cm4gc30sClhVOmZ1bmN0aW9uKGEsYixjKXt2YXIgdAppZihjPDB8fGM+YS5sZW5ndGgp
+dGhyb3cgSC5iKFAuVEUoYywwLGEubGVuZ3RoLG51bGwsbnVsbCkpCnQ9YS5pbmRleE9mKGIsYykKcmV0
+dXJuIHR9LApPWTpmdW5jdGlvbihhLGIpe3JldHVybiB0aGlzLlhVKGEsYiwwKX0sClBrOmZ1bmN0aW9u
+KGEsYixjKXt2YXIgdCxzCmlmKGM9PW51bGwpYz1hLmxlbmd0aAplbHNlIGlmKGM8MHx8Yz5hLmxlbmd0
+aCl0aHJvdyBILmIoUC5URShjLDAsYS5sZW5ndGgsbnVsbCxudWxsKSkKdD1iLmxlbmd0aApzPWEubGVu
+Z3RoCmlmKGMrdD5zKWM9cy10CnJldHVybiBhLmxhc3RJbmRleE9mKGIsYyl9LApjbjpmdW5jdGlvbihh
+LGIpe3JldHVybiB0aGlzLlBrKGEsYixudWxsKX0sCklzOmZ1bmN0aW9uKGEsYixjKXt2YXIgdD1hLmxl
+bmd0aAppZihjPnQpdGhyb3cgSC5iKFAuVEUoYywwLHQsbnVsbCxudWxsKSkKcmV0dXJuIEgubTIoYSxi
+LGMpfSwKdGc6ZnVuY3Rpb24oYSxiKXtyZXR1cm4gdGhpcy5JcyhhLGIsMCl9LAp3OmZ1bmN0aW9uKGEp
+e3JldHVybiBhfSwKZ2lPOmZ1bmN0aW9uKGEpe3ZhciB0LHMscgpmb3IodD1hLmxlbmd0aCxzPTAscj0w
+O3I8dDsrK3Ipe3M9NTM2ODcwOTExJnMrYS5jaGFyQ29kZUF0KHIpCnM9NTM2ODcwOTExJnMrKCg1MjQy
+ODcmcyk8PDEwKQpzXj1zPj42fXM9NTM2ODcwOTExJnMrKCg2NzEwODg2MyZzKTw8MykKc149cz4+MTEK
+cmV0dXJuIDUzNjg3MDkxMSZzKygoMTYzODMmcyk8PDE1KX0sCmdBOmZ1bmN0aW9uKGEpe3JldHVybiBh
+Lmxlbmd0aH0sCnE6ZnVuY3Rpb24oYSxiKXtILnVQKGIpCmlmKGI+PWEubGVuZ3RofHwhMSl0aHJvdyBI
+LmIoSC5IWShhLGIpKQpyZXR1cm4gYVtiXX0sCiRpdlg6MSwKJGlxVToxfQpILm5kLnByb3RvdHlwZT17
+Cnc6ZnVuY3Rpb24oYSl7dmFyIHQ9IkxhdGVJbml0aWFsaXphdGlvbkVycm9yOiAiK3RoaXMuYQpyZXR1
+cm4gdH19CkgucWoucHJvdG90eXBlPXsKZ0E6ZnVuY3Rpb24oYSl7cmV0dXJuIHRoaXMuYS5sZW5ndGh9
+LApxOmZ1bmN0aW9uKGEsYil7cmV0dXJuIEMueEIubSh0aGlzLmEsSC51UChiKSl9fQpILmJRLnByb3Rv
+dHlwZT17fQpILmFMLnByb3RvdHlwZT17CmdrejpmdW5jdGlvbihhKXt2YXIgdD10aGlzCnJldHVybiBu
+ZXcgSC5hNyh0LHQuZ0EodCksSC5MaCh0KS5DKCJhNzxhTC5FPiIpKX0sCmdsMDpmdW5jdGlvbihhKXty
+ZXR1cm4gdGhpcy5nQSh0aGlzKT09PTB9LApIOmZ1bmN0aW9uKGEsYil7dmFyIHQscyxyLHE9dGhpcyxw
+PXEuZ0EocSkKaWYoYi5sZW5ndGghPT0wKXtpZihwPT09MClyZXR1cm4iIgp0PUguRWoocS5FKDAsMCkp
+CmlmKHAhPT1xLmdBKHEpKXRocm93IEguYihQLmE0KHEpKQpmb3Iocz10LHI9MTtyPHA7KytyKXtzPXMr
+YitILkVqKHEuRSgwLHIpKQppZihwIT09cS5nQShxKSl0aHJvdyBILmIoUC5hNChxKSl9cmV0dXJuIHMu
+Y2hhckNvZGVBdCgwKT09MD9zOnN9ZWxzZXtmb3Iocj0wLHM9IiI7cjxwOysrcil7cys9SC5FaihxLkUo
+MCxyKSkKaWYocCE9PXEuZ0EocSkpdGhyb3cgSC5iKFAuYTQocSkpfXJldHVybiBzLmNoYXJDb2RlQXQo
+MCk9PTA/czpzfX0sCmV2OmZ1bmN0aW9uKGEsYil7cmV0dXJuIHRoaXMuR0coMCxILkxoKHRoaXMpLkMo
+ImEyKGFMLkUpIikuYShiKSl9LApFMjpmdW5jdGlvbihhLGIsYyl7dmFyIHQ9SC5MaCh0aGlzKQpyZXR1
+cm4gbmV3IEgubEoodGhpcyx0LktxKGMpLkMoIjEoYUwuRSkiKS5hKGIpLHQuQygiQDxhTC5FPiIpLktx
+KGMpLkMoImxKPDEsMj4iKSl9fQpILm5ILnByb3RvdHlwZT17CmdVRDpmdW5jdGlvbigpe3ZhciB0PUou
+SG0odGhpcy5hKSxzPXRoaXMuYwppZihzPT1udWxsfHxzPnQpcmV0dXJuIHQKcmV0dXJuIHN9LApnQXM6
+ZnVuY3Rpb24oKXt2YXIgdD1KLkhtKHRoaXMuYSkscz10aGlzLmIKaWYocz50KXJldHVybiB0CnJldHVy
+biBzfSwKZ0E6ZnVuY3Rpb24oYSl7dmFyIHQscz1KLkhtKHRoaXMuYSkscj10aGlzLmIKaWYocj49cyly
+ZXR1cm4gMAp0PXRoaXMuYwppZih0PT1udWxsfHx0Pj1zKXJldHVybiBzLXIKaWYodHlwZW9mIHQhPT0i
+bnVtYmVyIilyZXR1cm4gdC5ITigpCnJldHVybiB0LXJ9LApFOmZ1bmN0aW9uKGEsYil7dmFyIHQ9dGhp
+cyxzPXQuZ0FzKCkrYgppZihiPDB8fHM+PXQuZ1VEKCkpdGhyb3cgSC5iKFAuQ2YoYix0LCJpbmRleCIs
+bnVsbCxudWxsKSkKcmV0dXJuIEouR0EodC5hLHMpfX0KSC5hNy5wcm90b3R5cGU9ewpnbDpmdW5jdGlv
+bigpe3ZhciB0PXRoaXMuZApyZXR1cm4gdH0sCkY6ZnVuY3Rpb24oKXt2YXIgdCxzPXRoaXMscj1zLmEs
+cT1KLlU2KHIpLHA9cS5nQShyKQppZihzLmIhPT1wKXRocm93IEguYihQLmE0KHIpKQp0PXMuYwppZih0
+Pj1wKXtzLnNJKG51bGwpCnJldHVybiExfXMuc0kocS5FKHIsdCkpOysrcy5jCnJldHVybiEwfSwKc0k6
+ZnVuY3Rpb24oYSl7dGhpcy5kPXRoaXMuJHRpLkMoIjE/IikuYShhKX0sCiRpQW46MX0KSC5pMS5wcm90
+b3R5cGU9ewpna3o6ZnVuY3Rpb24oYSl7dmFyIHQ9SC5MaCh0aGlzKQpyZXR1cm4gbmV3IEguTUgoSi5J
+VCh0aGlzLmEpLHRoaXMuYix0LkMoIkA8MT4iKS5LcSh0LlFbMV0pLkMoIk1IPDEsMj4iKSl9LApnQTpm
+dW5jdGlvbihhKXtyZXR1cm4gSi5IbSh0aGlzLmEpfX0KSC54eS5wcm90b3R5cGU9eyRpYlE6MX0KSC5N
+SC5wcm90b3R5cGU9ewpGOmZ1bmN0aW9uKCl7dmFyIHQ9dGhpcyxzPXQuYgppZihzLkYoKSl7dC5zSSh0
+LmMuJDEocy5nbCgpKSkKcmV0dXJuITB9dC5zSShudWxsKQpyZXR1cm4hMX0sCmdsOmZ1bmN0aW9uKCl7
+dmFyIHQ9dGhpcy5hCnJldHVybiB0fSwKc0k6ZnVuY3Rpb24oYSl7dGhpcy5hPXRoaXMuJHRpLkMoIjI/
+IikuYShhKX19CkgubEoucHJvdG90eXBlPXsKZ0E6ZnVuY3Rpb24oYSl7cmV0dXJuIEouSG0odGhpcy5h
+KX0sCkU6ZnVuY3Rpb24oYSxiKXtyZXR1cm4gdGhpcy5iLiQxKEouR0EodGhpcy5hLGIpKX19CkguVTUu
+cHJvdG90eXBlPXsKZ2t6OmZ1bmN0aW9uKGEpe3JldHVybiBuZXcgSC5TTyhKLklUKHRoaXMuYSksdGhp
+cy5iLHRoaXMuJHRpLkMoIlNPPDE+IikpfX0KSC5TTy5wcm90b3R5cGU9ewpGOmZ1bmN0aW9uKCl7dmFy
+IHQscwpmb3IodD10aGlzLmEscz10aGlzLmI7dC5GKCk7KWlmKEgub1Qocy4kMSh0LmdsKCkpKSlyZXR1
+cm4hMApyZXR1cm4hMX0sCmdsOmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuYS5nbCgpfX0KSC5TVS5wcm90
+b3R5cGU9e30KSC5SZS5wcm90b3R5cGU9ewpZOmZ1bmN0aW9uKGEsYixjKXtILkxoKHRoaXMpLkMoIlJl
+LkUiKS5hKGMpCnRocm93IEguYihQLkw0KCJDYW5ub3QgbW9kaWZ5IGFuIHVubW9kaWZpYWJsZSBsaXN0
+IikpfX0KSC53Mi5wcm90b3R5cGU9e30KSC53di5wcm90b3R5cGU9ewpnaU86ZnVuY3Rpb24oYSl7dmFy
+IHQ9dGhpcy5faGFzaENvZGUKaWYodCE9bnVsbClyZXR1cm4gdAp0PTUzNjg3MDkxMSY2NjQ1OTcqSi5o
+Zih0aGlzLmEpCnRoaXMuX2hhc2hDb2RlPXQKcmV0dXJuIHR9LAp3OmZ1bmN0aW9uKGEpe3JldHVybidT
+eW1ib2woIicrSC5Faih0aGlzLmEpKyciKSd9LApETjpmdW5jdGlvbihhLGIpe2lmKGI9PW51bGwpcmV0
+dXJuITEKcmV0dXJuIGIgaW5zdGFuY2VvZiBILnd2JiZ0aGlzLmE9PWIuYX0sCiRpR0Q6MX0KSC5QRC5w
+cm90b3R5cGU9e30KSC5XVS5wcm90b3R5cGU9ewpnbDA6ZnVuY3Rpb24oYSl7cmV0dXJuIHRoaXMuZ0Eo
+dGhpcyk9PT0wfSwKdzpmdW5jdGlvbihhKXtyZXR1cm4gUC5uTyh0aGlzKX0sClk6ZnVuY3Rpb24oYSxi
+LGMpe3ZhciB0PUguTGgodGhpcykKdC5jLmEoYikKdC5RWzFdLmEoYykKSC5kYygpfSwKZ1B1OmZ1bmN0
+aW9uKGEpe3JldHVybiB0aGlzLnE0KGEsSC5MaCh0aGlzKS5DKCJOMzwxLDI+IikpfSwKcTQ6ZnVuY3Rp
+b24oYSxiKXt2YXIgdD10aGlzCnJldHVybiBQLmwwKGZ1bmN0aW9uKCl7dmFyIHM9YQp2YXIgcj0wLHE9
+MSxwLG8sbixtLGwKcmV0dXJuIGZ1bmN0aW9uICRhc3luYyRnUHUoYyxkKXtpZihjPT09MSl7cD1kCnI9
+cX13aGlsZSh0cnVlKXN3aXRjaChyKXtjYXNlIDA6bz10LmdWKCksbz1vLmdreihvKSxuPUguTGgodCks
+bj1uLkMoIkA8MT4iKS5LcShuLlFbMV0pLkMoIk4zPDEsMj4iKQpjYXNlIDI6aWYoIW8uRigpKXtyPTMK
+YnJlYWt9bT1vLmdsKCkKbD10LnEoMCxtKQpsLnRvU3RyaW5nCnI9NApyZXR1cm4gbmV3IFAuTjMobSxs
+LG4pCmNhc2UgNDpyPTIKYnJlYWsKY2FzZSAzOnJldHVybiBQLlRoKCkKY2FzZSAxOnJldHVybiBQLllt
+KHApfX19LGIpfSwKJGlaMDoxfQpILkxQLnByb3RvdHlwZT17CmdBOmZ1bmN0aW9uKGEpe3JldHVybiB0
+aGlzLmF9LAp4NDpmdW5jdGlvbihhKXtpZih0eXBlb2YgYSE9InN0cmluZyIpcmV0dXJuITEKaWYoIl9f
+cHJvdG9fXyI9PT1hKXJldHVybiExCnJldHVybiB0aGlzLmIuaGFzT3duUHJvcGVydHkoYSl9LApxOmZ1
+bmN0aW9uKGEsYil7aWYoIXRoaXMueDQoYikpcmV0dXJuIG51bGwKcmV0dXJuIHRoaXMuRChiKX0sCkQ6
+ZnVuY3Rpb24oYSl7cmV0dXJuIHRoaXMuYltILmgoYSldfSwKSzpmdW5jdGlvbihhLGIpe3ZhciB0LHMs
+cixxLHA9SC5MaCh0aGlzKQpwLkMoIn4oMSwyKSIpLmEoYikKdD10aGlzLmMKZm9yKHM9dC5sZW5ndGgs
+cD1wLlFbMV0scj0wO3I8czsrK3Ipe3E9dFtyXQpiLiQyKHEscC5hKHRoaXMuRChxKSkpfX0sCmdWOmZ1
+bmN0aW9uKCl7cmV0dXJuIG5ldyBILlhSKHRoaXMsSC5MaCh0aGlzKS5DKCJYUjwxPiIpKX19CkguWFIu
+cHJvdG90eXBlPXsKZ2t6OmZ1bmN0aW9uKGEpe3ZhciB0PXRoaXMuYS5jCnJldHVybiBuZXcgSi5tMSh0
+LHQubGVuZ3RoLEgudDYodCkuQygibTE8MT4iKSl9LApnQTpmdW5jdGlvbihhKXtyZXR1cm4gdGhpcy5h
+LmMubGVuZ3RofX0KSC5MSS5wcm90b3R5cGU9ewpnV2E6ZnVuY3Rpb24oKXt2YXIgdD10aGlzLmEKcmV0
+dXJuIHR9LApnbmQ6ZnVuY3Rpb24oKXt2YXIgdCxzLHIscSxwPXRoaXMKaWYocC5jPT09MSlyZXR1cm4g
+Qy5oVQp0PXAuZApzPXQubGVuZ3RoLXAuZS5sZW5ndGgtcC5mCmlmKHM9PT0wKXJldHVybiBDLmhVCnI9
+W10KZm9yKHE9MDtxPHM7KytxKXtpZihxPj10Lmxlbmd0aClyZXR1cm4gSC5PSCh0LHEpCnIucHVzaCh0
+W3FdKX1yZXR1cm4gSi56QyhyKX0sCmdWbTpmdW5jdGlvbigpe3ZhciB0LHMscixxLHAsbyxuLG0sbD10
+aGlzCmlmKGwuYyE9PTApcmV0dXJuIEMuRHgKdD1sLmUKcz10Lmxlbmd0aApyPWwuZApxPXIubGVuZ3Ro
+LXMtbC5mCmlmKHM9PT0wKXJldHVybiBDLkR4CnA9bmV3IEguTjUodS5lbykKZm9yKG89MDtvPHM7Kytv
+KXtpZihvPj10Lmxlbmd0aClyZXR1cm4gSC5PSCh0LG8pCm49dFtvXQptPXErbwppZihtPDB8fG0+PXIu
+bGVuZ3RoKXJldHVybiBILk9IKHIsbSkKcC5ZKDAsbmV3IEgud3YobiksclttXSl9cmV0dXJuIG5ldyBI
+LlBEKHAsdS5nRil9LAokaXZROjF9CkguQ2oucHJvdG90eXBlPXsKJDI6ZnVuY3Rpb24oYSxiKXt2YXIg
+dApILmgoYSkKdD10aGlzLmEKdC5iPXQuYisiJCIrSC5FaihhKQpDLk5tLmkodGhpcy5iLGEpCkMuTm0u
+aSh0aGlzLmMsYik7Kyt0LmF9LAokUzoxM30KSC5mOS5wcm90b3R5cGU9ewpxUzpmdW5jdGlvbihhKXt2
+YXIgdCxzLHI9dGhpcyxxPW5ldyBSZWdFeHAoci5hKS5leGVjKGEpCmlmKHE9PW51bGwpcmV0dXJuIG51
+bGwKdD1PYmplY3QuY3JlYXRlKG51bGwpCnM9ci5iCmlmKHMhPT0tMSl0LmFyZ3VtZW50cz1xW3MrMV0K
+cz1yLmMKaWYocyE9PS0xKXQuYXJndW1lbnRzRXhwcj1xW3MrMV0Kcz1yLmQKaWYocyE9PS0xKXQuZXhw
+cj1xW3MrMV0Kcz1yLmUKaWYocyE9PS0xKXQubWV0aG9kPXFbcysxXQpzPXIuZgppZihzIT09LTEpdC5y
+ZWNlaXZlcj1xW3MrMV0KcmV0dXJuIHR9fQpILlcwLnByb3RvdHlwZT17Cnc6ZnVuY3Rpb24oYSl7dmFy
+IHQ9dGhpcy5iCmlmKHQ9PW51bGwpcmV0dXJuIk5vU3VjaE1ldGhvZEVycm9yOiAiK0guRWoodGhpcy5h
+KQpyZXR1cm4iTm9TdWNoTWV0aG9kRXJyb3I6IG1ldGhvZCBub3QgZm91bmQ6ICciK3QrIicgb24gbnVs
+bCJ9fQpILmF6LnByb3RvdHlwZT17Cnc6ZnVuY3Rpb24oYSl7dmFyIHQscz10aGlzLHI9Ik5vU3VjaE1l
+dGhvZEVycm9yOiBtZXRob2Qgbm90IGZvdW5kOiAnIixxPXMuYgppZihxPT1udWxsKXJldHVybiJOb1N1
+Y2hNZXRob2RFcnJvcjogIitILkVqKHMuYSkKdD1zLmMKaWYodD09bnVsbClyZXR1cm4gcitxKyInICgi
+K0guRWoocy5hKSsiKSIKcmV0dXJuIHIrcSsiJyBvbiAnIit0KyInICgiK0guRWoocy5hKSsiKSJ9fQpI
+LnZWLnByb3RvdHlwZT17Cnc6ZnVuY3Rpb24oYSl7dmFyIHQ9dGhpcy5hCnJldHVybiB0Lmxlbmd0aD09
+PTA/IkVycm9yIjoiRXJyb3I6ICIrdH19CkgudGUucHJvdG90eXBlPXsKdzpmdW5jdGlvbihhKXtyZXR1
+cm4iVGhyb3cgb2YgbnVsbCAoJyIrKHRoaXMuYT09PW51bGw/Im51bGwiOiJ1bmRlZmluZWQiKSsiJyBm
+cm9tIEphdmFTY3JpcHQpIn0sCiRpUno6MX0KSC5icS5wcm90b3R5cGU9e30KSC5YTy5wcm90b3R5cGU9
+ewp3OmZ1bmN0aW9uKGEpe3ZhciB0LHM9dGhpcy5iCmlmKHMhPW51bGwpcmV0dXJuIHMKcz10aGlzLmEK
+dD1zIT09bnVsbCYmdHlwZW9mIHM9PT0ib2JqZWN0Ij9zLnN0YWNrOm51bGwKcmV0dXJuIHRoaXMuYj10
+PT1udWxsPyIiOnR9LAokaUd6OjF9Ckgudi5wcm90b3R5cGU9ewp3OmZ1bmN0aW9uKGEpe3ZhciB0PXRo
+aXMuY29uc3RydWN0b3Iscz10PT1udWxsP251bGw6dC5uYW1lCnJldHVybiJDbG9zdXJlICciK0guTlEo
+cz09bnVsbD8idW5rbm93biI6cykrIicifSwKJGlFSDoxLApnS3U6ZnVuY3Rpb24oKXtyZXR1cm4gdGhp
+c30sCiRDOiIkMSIsCiRSOjEsCiREOm51bGx9CkgubGMucHJvdG90eXBlPXt9CkguengucHJvdG90eXBl
+PXsKdzpmdW5jdGlvbihhKXt2YXIgdD10aGlzLiRzdGF0aWNfbmFtZQppZih0PT1udWxsKXJldHVybiJD
+bG9zdXJlIG9mIHVua25vd24gc3RhdGljIG1ldGhvZCIKcmV0dXJuIkNsb3N1cmUgJyIrSC5OUSh0KSsi
+JyJ9fQpILnJULnByb3RvdHlwZT17CkROOmZ1bmN0aW9uKGEsYil7dmFyIHQ9dGhpcwppZihiPT1udWxs
+KXJldHVybiExCmlmKHQ9PT1iKXJldHVybiEwCmlmKCEoYiBpbnN0YW5jZW9mIEguclQpKXJldHVybiEx
+CnJldHVybiB0LmE9PT1iLmEmJnQuYj09PWIuYiYmdC5jPT09Yi5jfSwKZ2lPOmZ1bmN0aW9uKGEpe3Zh
+ciB0LHM9dGhpcy5jCmlmKHM9PW51bGwpdD1ILmVRKHRoaXMuYSkKZWxzZSB0PXR5cGVvZiBzIT09Im9i
+amVjdCI/Si5oZihzKTpILmVRKHMpCnJldHVybih0XkguZVEodGhpcy5iKSk+Pj4wfSwKdzpmdW5jdGlv
+bihhKXt2YXIgdD10aGlzLmMKaWYodD09bnVsbCl0PXRoaXMuYQpyZXR1cm4iQ2xvc3VyZSAnIitILkVq
+KHRoaXMuZCkrIicgb2YgIisoIkluc3RhbmNlIG9mICciK0guRWooSC5NKHQpKSsiJyIpfX0KSC5FcS5w
+cm90b3R5cGU9ewp3OmZ1bmN0aW9uKGEpe3JldHVybiJSdW50aW1lRXJyb3I6ICIrSC5Faih0aGlzLmEp
+fX0KSC5rWS5wcm90b3R5cGU9ewp3OmZ1bmN0aW9uKGEpe3JldHVybiJBc3NlcnRpb24gZmFpbGVkOiAi
+K1AucCh0aGlzLmEpfX0KSC5rci5wcm90b3R5cGU9e30KSC5ONS5wcm90b3R5cGU9ewpnQTpmdW5jdGlv
+bihhKXtyZXR1cm4gdGhpcy5hfSwKZ2wwOmZ1bmN0aW9uKGEpe3JldHVybiB0aGlzLmE9PT0wfSwKZ1Y6
+ZnVuY3Rpb24oKXtyZXR1cm4gbmV3IEguaTUodGhpcyxILkxoKHRoaXMpLkMoImk1PDE+IikpfSwKeDQ6
+ZnVuY3Rpb24oYSl7dmFyIHQscwppZih0eXBlb2YgYT09InN0cmluZyIpe3Q9dGhpcy5iCmlmKHQ9PW51
+bGwpcmV0dXJuITEKcmV0dXJuIHRoaXMuWHUodCxhKX1lbHNle3M9dGhpcy5DWChhKQpyZXR1cm4gc319
+LApDWDpmdW5jdGlvbihhKXt2YXIgdD10aGlzLmQKaWYodD09bnVsbClyZXR1cm4hMQpyZXR1cm4gdGhp
+cy5GaCh0aGlzLkJ0KHQsSi5oZihhKSYweDNmZmZmZmYpLGEpPj0wfSwKcTpmdW5jdGlvbihhLGIpe3Zh
+ciB0LHMscixxLHA9dGhpcyxvPW51bGwKaWYodHlwZW9mIGI9PSJzdHJpbmciKXt0PXAuYgppZih0PT1u
+dWxsKXJldHVybiBvCnM9cC5qMih0LGIpCnI9cz09bnVsbD9vOnMuYgpyZXR1cm4gcn1lbHNlIGlmKHR5
+cGVvZiBiPT0ibnVtYmVyIiYmKGImMHgzZmZmZmZmKT09PWIpe3E9cC5jCmlmKHE9PW51bGwpcmV0dXJu
+IG8Kcz1wLmoyKHEsYikKcj1zPT1udWxsP286cy5iCnJldHVybiByfWVsc2UgcmV0dXJuIHAuYWEoYil9
+LAphYTpmdW5jdGlvbihhKXt2YXIgdCxzLHI9dGhpcy5kCmlmKHI9PW51bGwpcmV0dXJuIG51bGwKdD10
+aGlzLkJ0KHIsSi5oZihhKSYweDNmZmZmZmYpCnM9dGhpcy5GaCh0LGEpCmlmKHM8MClyZXR1cm4gbnVs
+bApyZXR1cm4gdFtzXS5ifSwKWTpmdW5jdGlvbihhLGIsYyl7dmFyIHQscyxyLHEscCxvLG49dGhpcyxt
+PUguTGgobikKbS5jLmEoYikKbS5RWzFdLmEoYykKaWYodHlwZW9mIGI9PSJzdHJpbmciKXt0PW4uYgpu
+LkVIKHQ9PW51bGw/bi5iPW4ueksoKTp0LGIsYyl9ZWxzZSBpZih0eXBlb2YgYj09Im51bWJlciImJihi
+JjB4M2ZmZmZmZik9PT1iKXtzPW4uYwpuLkVIKHM9PW51bGw/bi5jPW4ueksoKTpzLGIsYyl9ZWxzZXty
+PW4uZAppZihyPT1udWxsKXI9bi5kPW4ueksoKQpxPUouaGYoYikmMHgzZmZmZmZmCnA9bi5CdChyLHEp
+CmlmKHA9PW51bGwpbi5FSShyLHEsW24uSG4oYixjKV0pCmVsc2V7bz1uLkZoKHAsYikKaWYobz49MClw
+W29dLmI9YwplbHNlIHAucHVzaChuLkhuKGIsYykpfX19LApLOmZ1bmN0aW9uKGEsYil7dmFyIHQscyxy
+PXRoaXMKSC5MaChyKS5DKCJ+KDEsMikiKS5hKGIpCnQ9ci5lCnM9ci5yCmZvcig7dCE9bnVsbDspe2Iu
+JDIodC5hLHQuYikKaWYocyE9PXIucil0aHJvdyBILmIoUC5hNChyKSkKdD10LmN9fSwKRUg6ZnVuY3Rp
+b24oYSxiLGMpe3ZhciB0LHM9dGhpcyxyPUguTGgocykKci5jLmEoYikKci5RWzFdLmEoYykKdD1zLmoy
+KGEsYikKaWYodD09bnVsbClzLkVJKGEsYixzLkhuKGIsYykpCmVsc2UgdC5iPWN9LAprczpmdW5jdGlv
+bigpe3RoaXMucj10aGlzLnIrMSY2NzEwODg2M30sCkhuOmZ1bmN0aW9uKGEsYil7dmFyIHQ9dGhpcyxz
+PUguTGgodCkscj1uZXcgSC5kYihzLmMuYShhKSxzLlFbMV0uYShiKSkKaWYodC5lPT1udWxsKXQuZT10
+LmY9cgplbHNle3M9dC5mCnMudG9TdHJpbmcKci5kPXMKdC5mPXMuYz1yfSsrdC5hCnQua3MoKQpyZXR1
+cm4gcn0sCkZoOmZ1bmN0aW9uKGEsYil7dmFyIHQscwppZihhPT1udWxsKXJldHVybi0xCnQ9YS5sZW5n
+dGgKZm9yKHM9MDtzPHQ7KytzKWlmKEouUk0oYVtzXS5hLGIpKXJldHVybiBzCnJldHVybi0xfSwKdzpm
+dW5jdGlvbihhKXtyZXR1cm4gUC5uTyh0aGlzKX0sCmoyOmZ1bmN0aW9uKGEsYil7cmV0dXJuIGFbYl19
+LApCdDpmdW5jdGlvbihhLGIpe3JldHVybiBhW2JdfSwKRUk6ZnVuY3Rpb24oYSxiLGMpe2FbYl09Y30s
+CnJuOmZ1bmN0aW9uKGEsYil7ZGVsZXRlIGFbYl19LApYdTpmdW5jdGlvbihhLGIpe3JldHVybiB0aGlz
+LmoyKGEsYikhPW51bGx9LAp6SzpmdW5jdGlvbigpe3ZhciB0PSI8bm9uLWlkZW50aWZpZXIta2V5PiIs
+cz1PYmplY3QuY3JlYXRlKG51bGwpCnRoaXMuRUkocyx0LHMpCnRoaXMucm4ocyx0KQpyZXR1cm4gc30s
+CiRpRm86MX0KSC5kYi5wcm90b3R5cGU9e30KSC5pNS5wcm90b3R5cGU9ewpnQTpmdW5jdGlvbihhKXty
+ZXR1cm4gdGhpcy5hLmF9LApnbDA6ZnVuY3Rpb24oYSl7cmV0dXJuIHRoaXMuYS5hPT09MH0sCmdrejpm
+dW5jdGlvbihhKXt2YXIgdD10aGlzLmEscz1uZXcgSC5ONih0LHQucix0aGlzLiR0aS5DKCJONjwxPiIp
+KQpzLmM9dC5lCnJldHVybiBzfSwKdGc6ZnVuY3Rpb24oYSxiKXtyZXR1cm4gdGhpcy5hLng0KGIpfX0K
+SC5ONi5wcm90b3R5cGU9ewpnbDpmdW5jdGlvbigpe3JldHVybiB0aGlzLmR9LApGOmZ1bmN0aW9uKCl7
+dmFyIHQscz10aGlzLHI9cy5hCmlmKHMuYiE9PXIucil0aHJvdyBILmIoUC5hNChyKSkKdD1zLmMKaWYo
+dD09bnVsbCl7cy5zcVkobnVsbCkKcmV0dXJuITF9ZWxzZXtzLnNxWSh0LmEpCnMuYz10LmMKcmV0dXJu
+ITB9fSwKc3FZOmZ1bmN0aW9uKGEpe3RoaXMuZD10aGlzLiR0aS5DKCIxPyIpLmEoYSl9LAokaUFuOjF9
+CkguZEMucHJvdG90eXBlPXsKJDE6ZnVuY3Rpb24oYSl7cmV0dXJuIHRoaXMuYShhKX0sCiRTOjR9Ckgu
+d04ucHJvdG90eXBlPXsKJDI6ZnVuY3Rpb24oYSxiKXtyZXR1cm4gdGhpcy5hKGEsYil9LAokUzo0MX0K
+SC5WWC5wcm90b3R5cGU9ewokMTpmdW5jdGlvbihhKXtyZXR1cm4gdGhpcy5hKEguaChhKSl9LAokUzo0
+MH0KSC5WUi5wcm90b3R5cGU9ewp3OmZ1bmN0aW9uKGEpe3JldHVybiJSZWdFeHAvIit0aGlzLmErIi8i
+K3RoaXMuYi5mbGFnc30sCmdIYzpmdW5jdGlvbigpe3ZhciB0PXRoaXMscz10LmMKaWYocyE9bnVsbCly
+ZXR1cm4gcwpzPXQuYgpyZXR1cm4gdC5jPUgudjQodC5hLHMubXVsdGlsaW5lLCFzLmlnbm9yZUNhc2Us
+cy51bmljb2RlLHMuZG90QWxsLCEwKX0sCmRkOmZ1bmN0aW9uKGEsYil7cmV0dXJuIG5ldyBILktXKHRo
+aXMsYiwwKX0sClVaOmZ1bmN0aW9uKGEsYil7dmFyIHQscz10aGlzLmdIYygpCnMubGFzdEluZGV4PWIK
+dD1zLmV4ZWMoYSkKaWYodD09bnVsbClyZXR1cm4gbnVsbApyZXR1cm4gbmV3IEguRUsodCl9LAokaXZY
+OjEsCiRpd0w6MX0KSC5FSy5wcm90b3R5cGU9ewpxOmZ1bmN0aW9uKGEsYil7dmFyIHQKSC51UChiKQp0
+PXRoaXMuYgppZihiPj10Lmxlbmd0aClyZXR1cm4gSC5PSCh0LGIpCnJldHVybiB0W2JdfSwKJGlPZDox
+LAokaWliOjF9CkguS1cucHJvdG90eXBlPXsKZ2t6OmZ1bmN0aW9uKGEpe3JldHVybiBuZXcgSC5QYih0
+aGlzLmEsdGhpcy5iLHRoaXMuYyl9fQpILlBiLnByb3RvdHlwZT17CmdsOmZ1bmN0aW9uKCl7dmFyIHQ9
+dGhpcy5kCnQudG9TdHJpbmcKcmV0dXJuIHR9LApGOmZ1bmN0aW9uKCl7dmFyIHQscyxyLHEscCxvLG49
+dGhpcyxtPW4uYgppZihtPT1udWxsKXJldHVybiExCnQ9bi5jCnM9bS5sZW5ndGgKaWYodDw9cyl7cj1u
+LmEKcT1yLlVaKG0sdCkKaWYocSE9bnVsbCl7bi5kPXEKdD1xLmIKcD10LmluZGV4Cm89cCt0WzBdLmxl
+bmd0aAppZihwPT09byl7aWYoci5iLnVuaWNvZGUpe3Q9bi5jCnI9dCsxCmlmKHI8cyl7dD1DLnhCLm0o
+bSx0KQppZih0Pj01NTI5NiYmdDw9NTYzMTkpe3Q9Qy54Qi5tKG0scikKdD10Pj01NjMyMCYmdDw9NTcz
+NDN9ZWxzZSB0PSExfWVsc2UgdD0hMX1lbHNlIHQ9ITEKbz0odD9vKzE6bykrMX1uLmM9bwpyZXR1cm4h
+MH19bi5iPW4uZD1udWxsCnJldHVybiExfSwKJGlBbjoxfQpILnRRLnByb3RvdHlwZT17CnE6ZnVuY3Rp
+b24oYSxiKXtILnVQKGIpCmlmKGIhPT0wKUgudmgoUC5PNyhiLG51bGwpKQpyZXR1cm4gdGhpcy5jfSwK
+JGlPZDoxfQpILnVuLnByb3RvdHlwZT17CmdrejpmdW5jdGlvbihhKXtyZXR1cm4gbmV3IEguU2QodGhp
+cy5hLHRoaXMuYix0aGlzLmMpfX0KSC5TZC5wcm90b3R5cGU9ewpGOmZ1bmN0aW9uKCl7dmFyIHQscyxy
+PXRoaXMscT1yLmMscD1yLmIsbz1wLmxlbmd0aCxuPXIuYSxtPW4ubGVuZ3RoCmlmKHErbz5tKXtyLmQ9
+bnVsbApyZXR1cm4hMX10PW4uaW5kZXhPZihwLHEpCmlmKHQ8MCl7ci5jPW0rMQpyLmQ9bnVsbApyZXR1
+cm4hMX1zPXQrbwpyLmQ9bmV3IEgudFEodCxwKQpyLmM9cz09PXIuYz9zKzE6cwpyZXR1cm4hMH0sCmds
+OmZ1bmN0aW9uKCl7dmFyIHQ9dGhpcy5kCnQudG9TdHJpbmcKcmV0dXJuIHR9LAokaUFuOjF9CkguRVQu
+cHJvdG90eXBlPXskaUVUOjEsJGllcToxfQpILkxaLnByb3RvdHlwZT17CmdBOmZ1bmN0aW9uKGEpe3Jl
+dHVybiBhLmxlbmd0aH0sCiRpWGo6MX0KSC5EZy5wcm90b3R5cGU9ewpxOmZ1bmN0aW9uKGEsYil7SC51
+UChiKQpILm9kKGIsYSxhLmxlbmd0aCkKcmV0dXJuIGFbYl19LApZOmZ1bmN0aW9uKGEsYixjKXtILkdI
+KGMpCkgub2QoYixhLGEubGVuZ3RoKQphW2JdPWN9LAokaWJROjEsCiRpY1g6MSwKJGl6TToxfQpILlBn
+LnByb3RvdHlwZT17Clk6ZnVuY3Rpb24oYSxiLGMpe0gudVAoYykKSC5vZChiLGEsYS5sZW5ndGgpCmFb
+Yl09Y30sCiRpYlE6MSwKJGljWDoxLAokaXpNOjF9CkgueGoucHJvdG90eXBlPXsKcTpmdW5jdGlvbihh
+LGIpe0gudVAoYikKSC5vZChiLGEsYS5sZW5ndGgpCnJldHVybiBhW2JdfX0KSC5kRS5wcm90b3R5cGU9
+ewpxOmZ1bmN0aW9uKGEsYil7SC51UChiKQpILm9kKGIsYSxhLmxlbmd0aCkKcmV0dXJuIGFbYl19fQpI
+LlpBLnByb3RvdHlwZT17CnE6ZnVuY3Rpb24oYSxiKXtILnVQKGIpCkgub2QoYixhLGEubGVuZ3RoKQpy
+ZXR1cm4gYVtiXX19CkguZFQucHJvdG90eXBlPXsKcTpmdW5jdGlvbihhLGIpe0gudVAoYikKSC5vZChi
+LGEsYS5sZW5ndGgpCnJldHVybiBhW2JdfX0KSC5QcS5wcm90b3R5cGU9ewpxOmZ1bmN0aW9uKGEsYil7
+SC51UChiKQpILm9kKGIsYSxhLmxlbmd0aCkKcmV0dXJuIGFbYl19fQpILmVFLnByb3RvdHlwZT17CmdB
+OmZ1bmN0aW9uKGEpe3JldHVybiBhLmxlbmd0aH0sCnE6ZnVuY3Rpb24oYSxiKXtILnVQKGIpCkgub2Qo
+YixhLGEubGVuZ3RoKQpyZXR1cm4gYVtiXX19CkguVjYucHJvdG90eXBlPXsKZ0E6ZnVuY3Rpb24oYSl7
+cmV0dXJuIGEubGVuZ3RofSwKcTpmdW5jdGlvbihhLGIpe0gudVAoYikKSC5vZChiLGEsYS5sZW5ndGgp
+CnJldHVybiBhW2JdfSwKJGlWNjoxLAokaW42OjF9CkguUkcucHJvdG90eXBlPXt9CkguVlAucHJvdG90
+eXBlPXt9CkguV0IucHJvdG90eXBlPXt9CkguWkcucHJvdG90eXBlPXt9CkguSmMucHJvdG90eXBlPXsK
+QzpmdW5jdGlvbihhKXtyZXR1cm4gSC5jRSh2LnR5cGVVbml2ZXJzZSx0aGlzLGEpfSwKS3E6ZnVuY3Rp
+b24oYSl7cmV0dXJuIEgudjUodi50eXBlVW5pdmVyc2UsdGhpcyxhKX19CkguRy5wcm90b3R5cGU9e30K
+SC51OS5wcm90b3R5cGU9ewp3OmZ1bmN0aW9uKGEpe3JldHVybiB0aGlzLmF9fQpILmlNLnByb3RvdHlw
+ZT17fQpQLnRoLnByb3RvdHlwZT17CiQxOmZ1bmN0aW9uKGEpe3ZhciB0PXRoaXMuYSxzPXQuYQp0LmE9
+bnVsbApzLiQwKCl9LAokUzo5fQpQLmhhLnByb3RvdHlwZT17CiQxOmZ1bmN0aW9uKGEpe3ZhciB0LHMK
+dGhpcy5hLmE9dS5NLmEoYSkKdD10aGlzLmIKcz10aGlzLmMKdC5maXJzdENoaWxkP3QucmVtb3ZlQ2hp
+bGQocyk6dC5hcHBlbmRDaGlsZChzKX0sCiRTOjUxfQpQLlZzLnByb3RvdHlwZT17CiQwOmZ1bmN0aW9u
+KCl7dGhpcy5hLiQwKCl9LAokQzoiJDAiLAokUjowLAokUzowfQpQLkZ0LnByb3RvdHlwZT17CiQwOmZ1
+bmN0aW9uKCl7dGhpcy5hLiQwKCl9LAokQzoiJDAiLAokUjowLAokUzowfQpQLlczLnByb3RvdHlwZT17
+CkNZOmZ1bmN0aW9uKGEsYil7aWYoc2VsZi5zZXRUaW1lb3V0IT1udWxsKXNlbGYuc2V0VGltZW91dChI
+LnRSKG5ldyBQLnlIKHRoaXMsYiksMCksYSkKZWxzZSB0aHJvdyBILmIoUC5MNCgiYHNldFRpbWVvdXQo
+KWAgbm90IGZvdW5kLiIpKX19ClAueUgucHJvdG90eXBlPXsKJDA6ZnVuY3Rpb24oKXt0aGlzLmIuJDAo
+KX0sCiRDOiIkMCIsCiRSOjAsCiRTOjF9ClAuaWgucHJvdG90eXBlPXsKYU06ZnVuY3Rpb24oYSxiKXt2
+YXIgdCxzPXRoaXMscj1zLiR0aQpyLkMoIjEvPyIpLmEoYikKaWYoIXMuYilzLmEuWGYoYikKZWxzZXt0
+PXMuYQppZihyLkMoImI4PDE+IikuYihiKSl0LmNVKGIpCmVsc2UgdC5YMihyLmMuYShiKSl9fSwKdzA6
+ZnVuY3Rpb24oYSxiKXt2YXIgdAppZihiPT1udWxsKWI9UC52MChhKQp0PXRoaXMuYQppZih0aGlzLmIp
+dC5aTChhLGIpCmVsc2UgdC5OayhhLGIpfX0KUC5XTS5wcm90b3R5cGU9ewokMTpmdW5jdGlvbihhKXty
+ZXR1cm4gdGhpcy5hLiQyKDAsYSl9LAokUzo0NX0KUC5TWC5wcm90b3R5cGU9ewokMjpmdW5jdGlvbihh
+LGIpe3RoaXMuYS4kMigxLG5ldyBILmJxKGEsdS5sLmEoYikpKX0sCiRDOiIkMiIsCiRSOjIsCiRTOjI2
+fQpQLkdzLnByb3RvdHlwZT17CiQyOmZ1bmN0aW9uKGEsYil7dGhpcy5hKEgudVAoYSksYil9LAokUzoy
+OH0KUC5GeS5wcm90b3R5cGU9ewp3OmZ1bmN0aW9uKGEpe3JldHVybiJJdGVyYXRpb25NYXJrZXIoIit0
+aGlzLmIrIiwgIitILkVqKHRoaXMuYSkrIikifX0KUC5HVi5wcm90b3R5cGU9ewpnbDpmdW5jdGlvbigp
+e3ZhciB0PXRoaXMuYwppZih0PT1udWxsKXJldHVybiB0aGlzLiR0aS5jLmEodGhpcy5iKQpyZXR1cm4g
+dC5nbCgpfSwKRjpmdW5jdGlvbigpe3ZhciB0LHMscixxLHAsbyxuPXRoaXMKZm9yKHQ9bi4kdGkuQygi
+QW48MT4iKTshMDspe3M9bi5jCmlmKHMhPW51bGwpaWYocy5GKCkpcmV0dXJuITAKZWxzZSBuLnNYOShu
+dWxsKQpyPWZ1bmN0aW9uKGEsYixjKXt2YXIgbSxsPWIKd2hpbGUodHJ1ZSl0cnl7cmV0dXJuIGEobCxt
+KX1jYXRjaChrKXttPWsKbD1jfX0obi5hLDAsMSkKaWYociBpbnN0YW5jZW9mIFAuRnkpe3E9ci5iCmlm
+KHE9PT0yKXtwPW4uZAppZihwPT1udWxsfHxwLmxlbmd0aD09PTApe24uc0VDKG51bGwpCnJldHVybiEx
+fWlmKDA+PXAubGVuZ3RoKXJldHVybiBILk9IKHAsLTEpCm4uYT1wLnBvcCgpCmNvbnRpbnVlfWVsc2V7
+cz1yLmEKaWYocT09PTMpdGhyb3cgcwplbHNle289dC5hKEouSVQocykpCmlmKG8gaW5zdGFuY2VvZiBQ
+LkdWKXtzPW4uZAppZihzPT1udWxsKXM9bi5kPVtdCkMuTm0uaShzLG4uYSkKbi5hPW8uYQpjb250aW51
+ZX1lbHNle24uc1g5KG8pCmNvbnRpbnVlfX19fWVsc2V7bi5zRUMocikKcmV0dXJuITB9fXJldHVybiEx
+fSwKc0VDOmZ1bmN0aW9uKGEpe3RoaXMuYj10aGlzLiR0aS5DKCIxPyIpLmEoYSl9LApzWDk6ZnVuY3Rp
+b24oYSl7dGhpcy5jPXRoaXMuJHRpLkMoIkFuPDE+PyIpLmEoYSl9LAokaUFuOjF9ClAucTQucHJvdG90
+eXBlPXsKZ2t6OmZ1bmN0aW9uKGEpe3JldHVybiBuZXcgUC5HVih0aGlzLmEoKSx0aGlzLiR0aS5DKCJH
+VjwxPiIpKX19ClAuYjgucHJvdG90eXBlPXt9ClAuUGYucHJvdG90eXBlPXsKdzA6ZnVuY3Rpb24oYSxi
+KXt2YXIgdApQLlVJKGEsImVycm9yIix1LkspCnQ9dGhpcy5hCmlmKHQuYSE9PTApdGhyb3cgSC5iKFAu
+UFYoIkZ1dHVyZSBhbHJlYWR5IGNvbXBsZXRlZCIpKQppZihiPT1udWxsKWI9UC52MChhKQp0Lk5rKGEs
+Yil9LApwbTpmdW5jdGlvbihhKXtyZXR1cm4gdGhpcy53MChhLG51bGwpfX0KUC5aZi5wcm90b3R5cGU9
+ewphTTpmdW5jdGlvbihhLGIpe3ZhciB0LHM9dGhpcy4kdGkKcy5DKCIxLz8iKS5hKGIpCnQ9dGhpcy5h
+CmlmKHQuYSE9PTApdGhyb3cgSC5iKFAuUFYoIkZ1dHVyZSBhbHJlYWR5IGNvbXBsZXRlZCIpKQp0Llhm
+KHMuQygiMS8iKS5hKGIpKX19ClAuRmUucHJvdG90eXBlPXsKSFI6ZnVuY3Rpb24oYSl7aWYoKHRoaXMu
+YyYxNSkhPT02KXJldHVybiEwCnJldHVybiB0aGlzLmIuYi5idih1LmFsLmEodGhpcy5kKSxhLmEsdS55
+LHUuSyl9LApLdzpmdW5jdGlvbihhKXt2YXIgdD10aGlzLmUscz11Lnoscj11LksscT10aGlzLiR0aS5D
+KCIyLyIpLHA9dGhpcy5iLmIKaWYodS5hZy5iKHQpKXJldHVybiBxLmEocC5ycCh0LGEuYSxhLmIscyxy
+LHUubCkpCmVsc2UgcmV0dXJuIHEuYShwLmJ2KHUuYkkuYSh0KSxhLmEscyxyKSl9fQpQLnZzLnByb3Rv
+dHlwZT17ClNxOmZ1bmN0aW9uKGEsYixjKXt2YXIgdCxzLHIscT10aGlzLiR0aQpxLktxKGMpLkMoIjEv
+KDIpIikuYShhKQp0PSQuWDMKaWYodCE9PUMuTlUpe2MuQygiQDwwLz4iKS5LcShxLmMpLkMoIjEoMiki
+KS5hKGEpCmlmKGIhPW51bGwpYj1QLlZIKGIsdCl9cz1uZXcgUC52cygkLlgzLGMuQygidnM8MD4iKSkK
+cj1iPT1udWxsPzE6Mwp0aGlzLnhmKG5ldyBQLkZlKHMscixhLGIscS5DKCJAPDE+IikuS3EoYykuQygi
+RmU8MSwyPiIpKSkKcmV0dXJuIHN9LApXNzpmdW5jdGlvbihhLGIpe3JldHVybiB0aGlzLlNxKGEsbnVs
+bCxiKX0sClFkOmZ1bmN0aW9uKGEsYixjKXt2YXIgdCxzPXRoaXMuJHRpCnMuS3EoYykuQygiMS8oMiki
+KS5hKGEpCnQ9bmV3IFAudnMoJC5YMyxjLkMoInZzPDA+IikpCnRoaXMueGYobmV3IFAuRmUodCwxOSxh
+LGIscy5DKCJAPDE+IikuS3EoYykuQygiRmU8MSwyPiIpKSkKcmV0dXJuIHR9LAp4ZjpmdW5jdGlvbihh
+KXt2YXIgdCxzPXRoaXMscj1zLmEKaWYocjw9MSl7YS5hPXUuRi5hKHMuYykKcy5jPWF9ZWxzZXtpZihy
+PT09Mil7dD11LmMuYShzLmMpCnI9dC5hCmlmKHI8NCl7dC54ZihhKQpyZXR1cm59cy5hPXIKcy5jPXQu
+Y31QLlRrKG51bGwsbnVsbCxzLmIsdS5NLmEobmV3IFAuZGEocyxhKSkpfX0sCmpROmZ1bmN0aW9uKGEp
+e3ZhciB0LHMscixxLHAsbyxuPXRoaXMsbT17fQptLmE9YQppZihhPT1udWxsKXJldHVybgp0PW4uYQpp
+Zih0PD0xKXtzPXUuRi5hKG4uYykKbi5jPWEKaWYocyE9bnVsbCl7cj1hLmEKZm9yKHE9YTtyIT1udWxs
+O3E9cixyPXApcD1yLmEKcS5hPXN9fWVsc2V7aWYodD09PTIpe289dS5jLmEobi5jKQp0PW8uYQppZih0
+PDQpe28ualEoYSkKcmV0dXJufW4uYT10Cm4uYz1vLmN9bS5hPW4uTjgoYSkKUC5UayhudWxsLG51bGws
+bi5iLHUuTS5hKG5ldyBQLm9RKG0sbikpKX19LAphaDpmdW5jdGlvbigpe3ZhciB0PXUuRi5hKHRoaXMu
+YykKdGhpcy5jPW51bGwKcmV0dXJuIHRoaXMuTjgodCl9LApOODpmdW5jdGlvbihhKXt2YXIgdCxzLHIK
+Zm9yKHQ9YSxzPW51bGw7dCE9bnVsbDtzPXQsdD1yKXtyPXQuYQp0LmE9c31yZXR1cm4gc30sCkhIOmZ1
+bmN0aW9uKGEpe3ZhciB0LHM9dGhpcyxyPXMuJHRpCnIuQygiMS8iKS5hKGEpCmlmKHIuQygiYjg8MT4i
+KS5iKGEpKWlmKHIuYihhKSlQLkE5KGEscykKZWxzZSBQLmszKGEscykKZWxzZXt0PXMuYWgoKQpyLmMu
+YShhKQpzLmE9NApzLmM9YQpQLkhaKHMsdCl9fSwKWDI6ZnVuY3Rpb24oYSl7dmFyIHQscz10aGlzCnMu
+JHRpLmMuYShhKQp0PXMuYWgoKQpzLmE9NApzLmM9YQpQLkhaKHMsdCl9LApaTDpmdW5jdGlvbihhLGIp
+e3ZhciB0LHMscj10aGlzCnUubC5hKGIpCnQ9ci5haCgpCnM9UC5UbChhLGIpCnIuYT04CnIuYz1zClAu
+SFoocix0KX0sClhmOmZ1bmN0aW9uKGEpe3ZhciB0PXRoaXMuJHRpCnQuQygiMS8iKS5hKGEpCmlmKHQu
+QygiYjg8MT4iKS5iKGEpKXt0aGlzLmNVKGEpCnJldHVybn10aGlzLndVKHQuYy5hKGEpKX0sCndVOmZ1
+bmN0aW9uKGEpe3ZhciB0PXRoaXMKdC4kdGkuYy5hKGEpCnQuYT0xClAuVGsobnVsbCxudWxsLHQuYix1
+Lk0uYShuZXcgUC5ydCh0LGEpKSl9LApjVTpmdW5jdGlvbihhKXt2YXIgdD10aGlzLHM9dC4kdGkKcy5D
+KCJiODwxPiIpLmEoYSkKaWYocy5iKGEpKXtpZihhLmE9PT04KXt0LmE9MQpQLlRrKG51bGwsbnVsbCx0
+LmIsdS5NLmEobmV3IFAuS0YodCxhKSkpfWVsc2UgUC5BOShhLHQpCnJldHVybn1QLmszKGEsdCl9LApO
+azpmdW5jdGlvbihhLGIpe3RoaXMuYT0xClAuVGsobnVsbCxudWxsLHRoaXMuYix1Lk0uYShuZXcgUC5a
+TCh0aGlzLGEsYikpKX0sCiRpYjg6MX0KUC5kYS5wcm90b3R5cGU9ewokMDpmdW5jdGlvbigpe1AuSFoo
+dGhpcy5hLHRoaXMuYil9LAokUzowfQpQLm9RLnByb3RvdHlwZT17CiQwOmZ1bmN0aW9uKCl7UC5IWih0
+aGlzLmIsdGhpcy5hLmEpfSwKJFM6MH0KUC5wVi5wcm90b3R5cGU9ewokMTpmdW5jdGlvbihhKXt2YXIg
+dD10aGlzLmEKdC5hPTAKdC5ISChhKX0sCiRTOjl9ClAuVTcucHJvdG90eXBlPXsKJDI6ZnVuY3Rpb24o
+YSxiKXt0aGlzLmEuWkwoYSx1LmwuYShiKSl9LAokQzoiJDIiLAokUjoyLAokUzozMX0KUC52ci5wcm90
+b3R5cGU9ewokMDpmdW5jdGlvbigpe3RoaXMuYS5aTCh0aGlzLmIsdGhpcy5jKX0sCiRTOjB9ClAucnQu
+cHJvdG90eXBlPXsKJDA6ZnVuY3Rpb24oKXt0aGlzLmEuWDIodGhpcy5iKX0sCiRTOjB9ClAuS0YucHJv
+dG90eXBlPXsKJDA6ZnVuY3Rpb24oKXtQLkE5KHRoaXMuYix0aGlzLmEpfSwKJFM6MH0KUC5aTC5wcm90
+b3R5cGU9ewokMDpmdW5jdGlvbigpe3RoaXMuYS5aTCh0aGlzLmIsdGhpcy5jKX0sCiRTOjB9ClAuUlQu
+cHJvdG90eXBlPXsKJDA6ZnVuY3Rpb24oKXt2YXIgdCxzLHIscSxwLG8sbj10aGlzLG09bnVsbAp0cnl7
+cj1uLmEuYQptPXIuYi5iLnp6KHUuZk8uYShyLmQpLHUueil9Y2F0Y2gocSl7dD1ILlJ1KHEpCnM9SC50
+cyhxKQppZihuLmMpe3I9dS5uLmEobi5iLmEuYykuYQpwPXQKcD1yPT1udWxsP3A9PW51bGw6cj09PXAK
+cj1wfWVsc2Ugcj0hMQpwPW4uYQppZihyKXAuYz11Lm4uYShuLmIuYS5jKQplbHNlIHAuYz1QLlRsKHQs
+cykKcC5iPSEwCnJldHVybn1pZihtIGluc3RhbmNlb2YgUC52cyYmbS5hPj00KXtpZihtLmE9PT04KXty
+PW4uYQpyLmM9dS5uLmEobS5jKQpyLmI9ITB9cmV0dXJufWlmKHUuZC5iKG0pKXtvPW4uYi5hCnI9bi5h
+CnIuYz1tLlc3KG5ldyBQLmpaKG8pLHUueikKci5iPSExfX0sCiRTOjF9ClAualoucHJvdG90eXBlPXsK
+JDE6ZnVuY3Rpb24oYSl7cmV0dXJuIHRoaXMuYX0sCiRTOjMzfQpQLnJxLnByb3RvdHlwZT17CiQwOmZ1
+bmN0aW9uKCl7dmFyIHQscyxyLHEscCxvLG4sbQp0cnl7cj10aGlzLmEKcT1yLmEKcD1xLiR0aQpvPXAu
+YwpuPW8uYSh0aGlzLmIpCnIuYz1xLmIuYi5idihwLkMoIjIvKDEpIikuYShxLmQpLG4scC5DKCIyLyIp
+LG8pfWNhdGNoKG0pe3Q9SC5SdShtKQpzPUgudHMobSkKcj10aGlzLmEKci5jPVAuVGwodCxzKQpyLmI9
+ITB9fSwKJFM6MX0KUC5SVy5wcm90b3R5cGU9ewokMDpmdW5jdGlvbigpe3ZhciB0LHMscixxLHAsbyxu
+LG0sbD10aGlzCnRyeXt0PXUubi5hKGwuYS5hLmMpCnE9bC5iCmlmKEgub1QocS5hLkhSKHQpKSYmcS5h
+LmUhPW51bGwpe3EuYz1xLmEuS3codCkKcS5iPSExfX1jYXRjaChwKXtzPUguUnUocCkKcj1ILnRzKHAp
+CnE9dS5uLmEobC5hLmEuYykKbz1xLmEKbj1zCm09bC5iCmlmKG89PW51bGw/bj09bnVsbDpvPT09bilt
+LmM9cQplbHNlIG0uYz1QLlRsKHMscikKbS5iPSEwfX0sCiRTOjF9ClAuT00ucHJvdG90eXBlPXt9ClAu
+cWgucHJvdG90eXBlPXsKZ0E6ZnVuY3Rpb24oYSl7dmFyIHQscyxyPXRoaXMscT17fSxwPW5ldyBQLnZz
+KCQuWDMsdS5mSikKcS5hPTAKdD1ILkxoKHIpCnM9dC5DKCJ+KDEpPyIpLmEobmV3IFAuQjUocSxyKSkK
+dS5aLmEobmV3IFAudU8ocSxwKSkKVy5KRShyLmEsci5iLHMsITEsdC5jKQpyZXR1cm4gcH19ClAuQjUu
+cHJvdG90eXBlPXsKJDE6ZnVuY3Rpb24oYSl7SC5MaCh0aGlzLmIpLmMuYShhKTsrK3RoaXMuYS5hfSwK
+JFM6ZnVuY3Rpb24oKXtyZXR1cm4gSC5MaCh0aGlzLmIpLkMoImM4KDEpIil9fQpQLnVPLnByb3RvdHlw
+ZT17CiQwOmZ1bmN0aW9uKCl7dGhpcy5iLkhIKHRoaXMuYS5hKX0sCiRTOjB9ClAuTU8ucHJvdG90eXBl
+PXt9ClAua1QucHJvdG90eXBlPXt9ClAueEkucHJvdG90eXBlPXt9ClAuQ3cucHJvdG90eXBlPXsKdzpm
+dW5jdGlvbihhKXtyZXR1cm4gSC5Faih0aGlzLmEpfSwKJGlYUzoxLApnSUk6ZnVuY3Rpb24oKXtyZXR1
+cm4gdGhpcy5ifX0KUC5tMC5wcm90b3R5cGU9eyRpSkI6MX0KUC5wSy5wcm90b3R5cGU9ewokMDpmdW5j
+dGlvbigpe3ZhciB0PUguYih0aGlzLmEpCnQuc3RhY2s9Si5qKHRoaXMuYikKdGhyb3cgdH0sCiRTOjB9
+ClAuSmkucHJvdG90eXBlPXsKYkg6ZnVuY3Rpb24oYSl7dmFyIHQscyxyLHE9bnVsbAp1Lk0uYShhKQp0
+cnl7aWYoQy5OVT09PSQuWDMpe2EuJDAoKQpyZXR1cm59UC5UOChxLHEsdGhpcyxhLHUuSCl9Y2F0Y2go
+cil7dD1ILlJ1KHIpCnM9SC50cyhyKQpQLkwyKHEscSx0aGlzLHQsdS5sLmEocykpfX0sCkRsOmZ1bmN0
+aW9uKGEsYixjKXt2YXIgdCxzLHIscT1udWxsCmMuQygifigwKSIpLmEoYSkKYy5hKGIpCnRyeXtpZihD
+Lk5VPT09JC5YMyl7YS4kMShiKQpyZXR1cm59UC55dihxLHEsdGhpcyxhLGIsdS5ILGMpfWNhdGNoKHIp
+e3Q9SC5SdShyKQpzPUgudHMocikKUC5MMihxLHEsdGhpcyx0LHUubC5hKHMpKX19LApSVDpmdW5jdGlv
+bihhLGIpe3JldHVybiBuZXcgUC5oaih0aGlzLGIuQygiMCgpIikuYShhKSxiKX0sCkdZOmZ1bmN0aW9u
+KGEpe3JldHVybiBuZXcgUC5WcCh0aGlzLHUuTS5hKGEpKX0sClB5OmZ1bmN0aW9uKGEsYil7cmV0dXJu
+IG5ldyBQLk9SKHRoaXMsYi5DKCJ+KDApIikuYShhKSxiKX0sCnE6ZnVuY3Rpb24oYSxiKXtyZXR1cm4g
+bnVsbH0sCnp6OmZ1bmN0aW9uKGEsYil7Yi5DKCIwKCkiKS5hKGEpCmlmKCQuWDM9PT1DLk5VKXJldHVy
+biBhLiQwKCkKcmV0dXJuIFAuVDgobnVsbCxudWxsLHRoaXMsYSxiKX0sCmJ2OmZ1bmN0aW9uKGEsYixj
+LGQpe2MuQygiQDwwPiIpLktxKGQpLkMoIjEoMikiKS5hKGEpCmQuYShiKQppZigkLlgzPT09Qy5OVSly
+ZXR1cm4gYS4kMShiKQpyZXR1cm4gUC55dihudWxsLG51bGwsdGhpcyxhLGIsYyxkKX0sCnJwOmZ1bmN0
+aW9uKGEsYixjLGQsZSxmKXtkLkMoIkA8MD4iKS5LcShlKS5LcShmKS5DKCIxKDIsMykiKS5hKGEpCmUu
+YShiKQpmLmEoYykKaWYoJC5YMz09PUMuTlUpcmV0dXJuIGEuJDIoYixjKQpyZXR1cm4gUC5ReChudWxs
+LG51bGwsdGhpcyxhLGIsYyxkLGUsZil9LApMajpmdW5jdGlvbihhLGIsYyxkKXtyZXR1cm4gYi5DKCJA
+PDA+IikuS3EoYykuS3EoZCkuQygiMSgyLDMpIikuYShhKX19ClAuaGoucHJvdG90eXBlPXsKJDA6ZnVu
+Y3Rpb24oKXtyZXR1cm4gdGhpcy5hLnp6KHRoaXMuYix0aGlzLmMpfSwKJFM6ZnVuY3Rpb24oKXtyZXR1
+cm4gdGhpcy5jLkMoIjAoKSIpfX0KUC5WcC5wcm90b3R5cGU9ewokMDpmdW5jdGlvbigpe3JldHVybiB0
+aGlzLmEuYkgodGhpcy5iKX0sCiRTOjF9ClAuT1IucHJvdG90eXBlPXsKJDE6ZnVuY3Rpb24oYSl7dmFy
+IHQ9dGhpcy5jCnJldHVybiB0aGlzLmEuRGwodGhpcy5iLHQuYShhKSx0KX0sCiRTOmZ1bmN0aW9uKCl7
+cmV0dXJuIHRoaXMuYy5DKCJ+KDApIil9fQpQLmI2LnByb3RvdHlwZT17CmdrejpmdW5jdGlvbihhKXt2
+YXIgdD10aGlzLHM9bmV3IFAubG0odCx0LnIsSC5MaCh0KS5DKCJsbTwxPiIpKQpzLmM9dC5lCnJldHVy
+biBzfSwKZ0E6ZnVuY3Rpb24oYSl7cmV0dXJuIHRoaXMuYX0sCnRnOmZ1bmN0aW9uKGEsYil7dmFyIHQs
+cwppZih0eXBlb2YgYj09InN0cmluZyImJmIhPT0iX19wcm90b19fIil7dD10aGlzLmIKaWYodD09bnVs
+bClyZXR1cm4hMQpyZXR1cm4gdS5lLmEodFtiXSkhPW51bGx9ZWxzZXtzPXRoaXMuUFIoYikKcmV0dXJu
+IHN9fSwKUFI6ZnVuY3Rpb24oYSl7dmFyIHQ9dGhpcy5kCmlmKHQ9PW51bGwpcmV0dXJuITEKcmV0dXJu
+IHRoaXMuREYodFt0aGlzLk4oYSldLGEpPj0wfSwKaTpmdW5jdGlvbihhLGIpe3ZhciB0LHMscj10aGlz
+CkguTGgocikuYy5hKGIpCmlmKHR5cGVvZiBiPT0ic3RyaW5nIiYmYiE9PSJfX3Byb3RvX18iKXt0PXIu
+YgpyZXR1cm4gci5iUSh0PT1udWxsP3IuYj1QLlQyKCk6dCxiKX1lbHNlIGlmKHR5cGVvZiBiPT0ibnVt
+YmVyIiYmKGImMTA3Mzc0MTgyMyk9PT1iKXtzPXIuYwpyZXR1cm4gci5iUShzPT1udWxsP3IuYz1QLlQy
+KCk6cyxiKX1lbHNlIHJldHVybiByLkI3KGIpfSwKQjc6ZnVuY3Rpb24oYSl7dmFyIHQscyxyLHE9dGhp
+cwpILkxoKHEpLmMuYShhKQp0PXEuZAppZih0PT1udWxsKXQ9cS5kPVAuVDIoKQpzPXEuTihhKQpyPXRb
+c10KaWYocj09bnVsbCl0W3NdPVtxLnlvKGEpXQplbHNle2lmKHEuREYocixhKT49MClyZXR1cm4hMQpy
+LnB1c2gocS55byhhKSl9cmV0dXJuITB9LApSOmZ1bmN0aW9uKGEsYil7dmFyIHQ9dGhpcwppZih0eXBl
+b2YgYj09InN0cmluZyImJmIhPT0iX19wcm90b19fIilyZXR1cm4gdC5MKHQuYixiKQplbHNlIGlmKHR5
+cGVvZiBiPT0ibnVtYmVyIiYmKGImMTA3Mzc0MTgyMyk9PT1iKXJldHVybiB0LkwodC5jLGIpCmVsc2Ug
+cmV0dXJuIHQucWcoYil9LApxZzpmdW5jdGlvbihhKXt2YXIgdCxzLHIscSxwPXRoaXMsbz1wLmQKaWYo
+bz09bnVsbClyZXR1cm4hMQp0PXAuTihhKQpzPW9bdF0Kcj1wLkRGKHMsYSkKaWYocjwwKXJldHVybiEx
+CnE9cy5zcGxpY2UociwxKVswXQppZigwPT09cy5sZW5ndGgpZGVsZXRlIG9bdF0KcC5HUyhxKQpyZXR1
+cm4hMH0sCmJROmZ1bmN0aW9uKGEsYil7SC5MaCh0aGlzKS5jLmEoYikKaWYodS5lLmEoYVtiXSkhPW51
+bGwpcmV0dXJuITEKYVtiXT10aGlzLnlvKGIpCnJldHVybiEwfSwKTDpmdW5jdGlvbihhLGIpe3ZhciB0
+CmlmKGE9PW51bGwpcmV0dXJuITEKdD11LmUuYShhW2JdKQppZih0PT1udWxsKXJldHVybiExCnRoaXMu
+R1ModCkKZGVsZXRlIGFbYl0KcmV0dXJuITB9LApTOmZ1bmN0aW9uKCl7dGhpcy5yPTEwNzM3NDE4MjMm
+dGhpcy5yKzF9LAp5bzpmdW5jdGlvbihhKXt2YXIgdCxzPXRoaXMscj1uZXcgUC5ibihILkxoKHMpLmMu
+YShhKSkKaWYocy5lPT1udWxsKXMuZT1zLmY9cgplbHNle3Q9cy5mCnQudG9TdHJpbmcKci5jPXQKcy5m
+PXQuYj1yfSsrcy5hCnMuUygpCnJldHVybiByfSwKR1M6ZnVuY3Rpb24oYSl7dmFyIHQ9dGhpcyxzPWEu
+YyxyPWEuYgppZihzPT1udWxsKXQuZT1yCmVsc2Ugcy5iPXIKaWYocj09bnVsbCl0LmY9cwplbHNlIHIu
+Yz1zOy0tdC5hCnQuUygpfSwKTjpmdW5jdGlvbihhKXtyZXR1cm4gSi5oZihhKSYxMDczNzQxODIzfSwK
+REY6ZnVuY3Rpb24oYSxiKXt2YXIgdCxzCmlmKGE9PW51bGwpcmV0dXJuLTEKdD1hLmxlbmd0aApmb3Io
+cz0wO3M8dDsrK3MpaWYoSi5STShhW3NdLmEsYikpcmV0dXJuIHMKcmV0dXJuLTF9fQpQLmJuLnByb3Rv
+dHlwZT17fQpQLmxtLnByb3RvdHlwZT17CmdsOmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuZH0sCkY6ZnVu
+Y3Rpb24oKXt2YXIgdD10aGlzLHM9dC5jLHI9dC5hCmlmKHQuYiE9PXIucil0aHJvdyBILmIoUC5hNChy
+KSkKZWxzZSBpZihzPT1udWxsKXt0LnNqKG51bGwpCnJldHVybiExfWVsc2V7dC5zaih0LiR0aS5DKCIx
+PyIpLmEocy5hKSkKdC5jPXMuYgpyZXR1cm4hMH19LApzajpmdW5jdGlvbihhKXt0aGlzLmQ9dGhpcy4k
+dGkuQygiMT8iKS5hKGEpfSwKJGlBbjoxfQpQLm1XLnByb3RvdHlwZT17fQpQLnV5LnByb3RvdHlwZT17
+JGliUToxLCRpY1g6MSwkaXpNOjF9ClAubEQucHJvdG90eXBlPXsKZ2t6OmZ1bmN0aW9uKGEpe3JldHVy
+biBuZXcgSC5hNyhhLHRoaXMuZ0EoYSksSC56KGEpLkMoImE3PGxELkU+IikpfSwKRTpmdW5jdGlvbihh
+LGIpe3JldHVybiB0aGlzLnEoYSxiKX0sCks6ZnVuY3Rpb24oYSxiKXt2YXIgdCxzCkgueihhKS5DKCJ+
+KGxELkUpIikuYShiKQp0PXRoaXMuZ0EoYSkKZm9yKHM9MDtzPHQ7KytzKXtiLiQxKHRoaXMucShhLHMp
+KQppZih0IT09dGhpcy5nQShhKSl0aHJvdyBILmIoUC5hNChhKSl9fSwKZ29yOmZ1bmN0aW9uKGEpe3Jl
+dHVybiB0aGlzLmdBKGEpIT09MH0sCkUyOmZ1bmN0aW9uKGEsYixjKXt2YXIgdD1ILnooYSkKcmV0dXJu
+IG5ldyBILmxKKGEsdC5LcShjKS5DKCIxKGxELkUpIikuYShiKSx0LkMoIkA8bEQuRT4iKS5LcShjKS5D
+KCJsSjwxLDI+IikpfSwKZHU6ZnVuY3Rpb24oYSxiLGMsZCl7dmFyIHQKSC56KGEpLkMoImxELkU/Iiku
+YShkKQpQLmpCKGIsYyx0aGlzLmdBKGEpKQpmb3IodD1iO3Q8YzsrK3QpdGhpcy5ZKGEsdCxkKX0sCnc6
+ZnVuY3Rpb24oYSl7cmV0dXJuIFAuV0UoYSwiWyIsIl0iKX19ClAuaWwucHJvdG90eXBlPXt9ClAucmEu
+cHJvdG90eXBlPXsKJDI6ZnVuY3Rpb24oYSxiKXt2YXIgdCxzPXRoaXMuYQppZighcy5hKXRoaXMuYi5h
+Kz0iLCAiCnMuYT0hMQpzPXRoaXMuYgp0PXMuYSs9SC5FaihhKQpzLmE9dCsiOiAiCnMuYSs9SC5Faihi
+KX0sCiRTOjExfQpQLllrLnByb3RvdHlwZT17Cks6ZnVuY3Rpb24oYSxiKXt2YXIgdCxzCkguTGgodGhp
+cykuQygifihZay5LLFlrLlYpIikuYShiKQpmb3IodD1KLklUKHRoaXMuZ1YoKSk7dC5GKCk7KXtzPXQu
+Z2woKQpiLiQyKHMsdGhpcy5xKDAscykpfX0sCmdQdTpmdW5jdGlvbihhKXtyZXR1cm4gSi5NMSh0aGlz
+LmdWKCksbmV3IFAueVEodGhpcyksSC5MaCh0aGlzKS5DKCJOMzxZay5LLFlrLlY+IikpfSwKeDQ6ZnVu
+Y3Rpb24oYSl7cmV0dXJuIEouemwodGhpcy5nVigpLGEpfSwKZ0E6ZnVuY3Rpb24oYSl7cmV0dXJuIEou
+SG0odGhpcy5nVigpKX0sCmdsMDpmdW5jdGlvbihhKXtyZXR1cm4gSi51VSh0aGlzLmdWKCkpfSwKdzpm
+dW5jdGlvbihhKXtyZXR1cm4gUC5uTyh0aGlzKX0sCiRpWjA6MX0KUC55US5wcm90b3R5cGU9ewokMTpm
+dW5jdGlvbihhKXt2YXIgdD10aGlzLmEscz1ILkxoKHQpCnMuQygiWWsuSyIpLmEoYSkKcmV0dXJuIG5l
+dyBQLk4zKGEsdC5xKDAsYSkscy5DKCJAPFlrLks+IikuS3Eocy5DKCJZay5WIikpLkMoIk4zPDEsMj4i
+KSl9LAokUzpmdW5jdGlvbigpe3JldHVybiBILkxoKHRoaXMuYSkuQygiTjM8WWsuSyxZay5WPihZay5L
+KSIpfX0KUC5LUC5wcm90b3R5cGU9ewpZOmZ1bmN0aW9uKGEsYixjKXt2YXIgdD1ILkxoKHRoaXMpCnQu
+Yy5hKGIpCnQuUVsxXS5hKGMpCnRocm93IEguYihQLkw0KCJDYW5ub3QgbW9kaWZ5IHVubW9kaWZpYWJs
+ZSBtYXAiKSl9fQpQLlBuLnByb3RvdHlwZT17CnE6ZnVuY3Rpb24oYSxiKXtyZXR1cm4gdGhpcy5hLnEo
+MCxiKX0sClk6ZnVuY3Rpb24oYSxiLGMpe3ZhciB0PUguTGgodGhpcykKdGhpcy5hLlkoMCx0LmMuYShi
+KSx0LlFbMV0uYShjKSl9LAp4NDpmdW5jdGlvbihhKXtyZXR1cm4gdGhpcy5hLng0KGEpfSwKSzpmdW5j
+dGlvbihhLGIpe3RoaXMuYS5LKDAsSC5MaCh0aGlzKS5DKCJ+KDEsMikiKS5hKGIpKX0sCmdsMDpmdW5j
+dGlvbihhKXt2YXIgdD10aGlzLmEKcmV0dXJuIHQuZ2wwKHQpfSwKZ0E6ZnVuY3Rpb24oYSl7dmFyIHQ9
+dGhpcy5hCnJldHVybiB0LmdBKHQpfSwKdzpmdW5jdGlvbihhKXtyZXR1cm4gSi5qKHRoaXMuYSl9LApn
+UHU6ZnVuY3Rpb24oYSl7dmFyIHQ9dGhpcy5hCnJldHVybiB0LmdQdSh0KX0sCiRpWjA6MX0KUC5Hai5w
+cm90b3R5cGU9e30KUC5NYS5wcm90b3R5cGU9ewp3OmZ1bmN0aW9uKGEpe3JldHVybiBQLldFKHRoaXMs
+InsiLCJ9Iil9fQpQLlZqLnByb3RvdHlwZT17JGliUToxLCRpY1g6MSwkaXh1OjF9ClAuWHYucHJvdG90
+eXBlPXsKRlY6ZnVuY3Rpb24oYSxiKXt2YXIgdApmb3IodD1KLklUKEguTGgodGhpcykuQygiY1g8MT4i
+KS5hKGIpKTt0LkYoKTspdGhpcy5pKDAsdC5nbCgpKX0sCnc6ZnVuY3Rpb24oYSl7cmV0dXJuIFAuV0Uo
+dGhpcywieyIsIn0iKX0sCkg6ZnVuY3Rpb24oYSxiKXt2YXIgdCxzPVAucmoodGhpcyx0aGlzLnIsSC5M
+aCh0aGlzKS5jKQppZighcy5GKCkpcmV0dXJuIiIKaWYoYj09PSIiKXt0PSIiCmRvIHQrPUguRWoocy5k
+KQp3aGlsZShzLkYoKSl9ZWxzZXt0PUguRWoocy5kKQpmb3IoO3MuRigpOyl0PXQrYitILkVqKHMuZCl9
+cmV0dXJuIHQuY2hhckNvZGVBdCgwKT09MD90OnR9LAokaWJROjEsCiRpY1g6MSwKJGl4dToxfQpQLm5Z
+LnByb3RvdHlwZT17fQpQLldZLnByb3RvdHlwZT17fQpQLlJVLnByb3RvdHlwZT17fQpQLnV3LnByb3Rv
+dHlwZT17CnE6ZnVuY3Rpb24oYSxiKXt2YXIgdCxzPXRoaXMuYgppZihzPT1udWxsKXJldHVybiB0aGlz
+LmMucSgwLGIpCmVsc2UgaWYodHlwZW9mIGIhPSJzdHJpbmciKXJldHVybiBudWxsCmVsc2V7dD1zW2Jd
+CnJldHVybiB0eXBlb2YgdD09InVuZGVmaW5lZCI/dGhpcy5mYihiKTp0fX0sCmdBOmZ1bmN0aW9uKGEp
+e3JldHVybiB0aGlzLmI9PW51bGw/dGhpcy5jLmE6dGhpcy5DZigpLmxlbmd0aH0sCmdsMDpmdW5jdGlv
+bihhKXtyZXR1cm4gdGhpcy5nQSh0aGlzKT09PTB9LApnVjpmdW5jdGlvbigpe2lmKHRoaXMuYj09bnVs
+bCl7dmFyIHQ9dGhpcy5jCnJldHVybiBuZXcgSC5pNSh0LEguTGgodCkuQygiaTU8MT4iKSl9cmV0dXJu
+IG5ldyBQLmk4KHRoaXMpfSwKWTpmdW5jdGlvbihhLGIsYyl7dmFyIHQscyxyPXRoaXMKaWYoci5iPT1u
+dWxsKXIuYy5ZKDAsYixjKQplbHNlIGlmKHIueDQoYikpe3Q9ci5iCnRbYl09YwpzPXIuYQppZihzPT1u
+dWxsP3QhPW51bGw6cyE9PXQpc1tiXT1udWxsfWVsc2Ugci5YSygpLlkoMCxiLGMpfSwKeDQ6ZnVuY3Rp
+b24oYSl7aWYodGhpcy5iPT1udWxsKXJldHVybiB0aGlzLmMueDQoYSkKcmV0dXJuIE9iamVjdC5wcm90
+b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbCh0aGlzLmEsYSl9LApLOmZ1bmN0aW9uKGEsYil7dmFyIHQs
+cyxyLHEscD10aGlzCnUuY0EuYShiKQppZihwLmI9PW51bGwpcmV0dXJuIHAuYy5LKDAsYikKdD1wLkNm
+KCkKZm9yKHM9MDtzPHQubGVuZ3RoOysrcyl7cj10W3NdCnE9cC5iW3JdCmlmKHR5cGVvZiBxPT0idW5k
+ZWZpbmVkIil7cT1QLlFlKHAuYVtyXSkKcC5iW3JdPXF9Yi4kMihyLHEpCmlmKHQhPT1wLmMpdGhyb3cg
+SC5iKFAuYTQocCkpfX0sCkNmOmZ1bmN0aW9uKCl7dmFyIHQ9dS5iTS5hKHRoaXMuYykKaWYodD09bnVs
+bCl0PXRoaXMuYz1ILlZNKE9iamVjdC5rZXlzKHRoaXMuYSksdS5zKQpyZXR1cm4gdH0sClhLOmZ1bmN0
+aW9uKCl7dmFyIHQscyxyLHEscCxvPXRoaXMKaWYoby5iPT1udWxsKXJldHVybiBvLmMKdD1QLkZsKHUu
+Tix1LnopCnM9by5DZigpCmZvcihyPTA7cT1zLmxlbmd0aCxyPHE7KytyKXtwPXNbcl0KdC5ZKDAscCxv
+LnEoMCxwKSl9aWYocT09PTApQy5ObS5pKHMsIiIpCmVsc2UgQy5ObS5zQShzLDApCm8uYT1vLmI9bnVs
+bApyZXR1cm4gby5jPXR9LApmYjpmdW5jdGlvbihhKXt2YXIgdAppZighT2JqZWN0LnByb3RvdHlwZS5o
+YXNPd25Qcm9wZXJ0eS5jYWxsKHRoaXMuYSxhKSlyZXR1cm4gbnVsbAp0PVAuUWUodGhpcy5hW2FdKQpy
+ZXR1cm4gdGhpcy5iW2FdPXR9fQpQLmk4LnByb3RvdHlwZT17CmdBOmZ1bmN0aW9uKGEpe3ZhciB0PXRo
+aXMuYQpyZXR1cm4gdC5nQSh0KX0sCkU6ZnVuY3Rpb24oYSxiKXt2YXIgdD10aGlzLmEKaWYodC5iPT1u
+dWxsKXQ9dC5nVigpLkUoMCxiKQplbHNle3Q9dC5DZigpCmlmKGI8MHx8Yj49dC5sZW5ndGgpcmV0dXJu
+IEguT0godCxiKQp0PXRbYl19cmV0dXJuIHR9LApna3o6ZnVuY3Rpb24oYSl7dmFyIHQ9dGhpcy5hCmlm
+KHQuYj09bnVsbCl7dD10LmdWKCkKdD10Lmdreih0KX1lbHNle3Q9dC5DZigpCnQ9bmV3IEoubTEodCx0
+Lmxlbmd0aCxILnQ2KHQpLkMoIm0xPDE+IikpfXJldHVybiB0fSwKdGc6ZnVuY3Rpb24oYSxiKXtyZXR1
+cm4gdGhpcy5hLng0KGIpfX0KUC5wZy5wcm90b3R5cGU9ewokMDpmdW5jdGlvbigpe3ZhciB0LHMKdHJ5
+e3Q9bmV3IFRleHREZWNvZGVyKCJ1dGYtOCIse2ZhdGFsOnRydWV9KQpyZXR1cm4gdH1jYXRjaChzKXtI
+LlJ1KHMpfXJldHVybiBudWxsfSwKJFM6MTJ9ClAuYzIucHJvdG90eXBlPXsKJDA6ZnVuY3Rpb24oKXt2
+YXIgdCxzCnRyeXt0PW5ldyBUZXh0RGVjb2RlcigidXRmLTgiLHtmYXRhbDpmYWxzZX0pCnJldHVybiB0
+fWNhdGNoKHMpe0guUnUocyl9cmV0dXJuIG51bGx9LAokUzoxMn0KUC5DVi5wcm90b3R5cGU9ewp5cjpm
dW5jdGlvbihhLGEwLGExKXt2YXIgdCxzLHIscSxwLG8sbixtLGwsayxqLGksaCxnLGYsZSxkLGMsYj0i
SW52YWxpZCBiYXNlNjQgZW5jb2RpbmcgbGVuZ3RoICIKYTE9UC5qQihhMCxhMSxhLmxlbmd0aCkKdD0k
LlY3KCkKZm9yKHM9YTAscj1zLHE9bnVsbCxwPS0xLG89LTEsbj0wO3M8YTE7cz1tKXttPXMrMQpsPUMu
eEIuVyhhLHMpCmlmKGw9PT0zNyl7az1tKzIKaWYoazw9YTEpe2o9SC5vbyhDLnhCLlcoYSxtKSkKaT1I
Lm9vKEMueEIuVyhhLG0rMSkpCmg9aioxNitpLShpJjI1NikKaWYoaD09PTM3KWg9LTEKbT1rfWVsc2Ug
aD0tMX1lbHNlIGg9bAppZigwPD1oJiZoPD0xMjcpe2lmKGg8MHx8aD49dC5sZW5ndGgpcmV0dXJuIEgu
-ayh0LGgpCmc9dFtoXQppZihnPj0wKXtoPUMueEIubSgiQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVph
-YmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ejAxMjM0NTY3ODkrLyIsZykKaWYoaD09PWwpY29udGludWUK
-bD1ofWVsc2V7aWYoZz09PS0xKXtpZihwPDApe2Y9cT09bnVsbD9udWxsOnEuYS5sZW5ndGgKaWYoZj09
-bnVsbClmPTAKcD1mKyhzLXIpCm89c30rK24KaWYobD09PTYxKWNvbnRpbnVlfWw9aH1pZihnIT09LTIp
-e2lmKHE9PW51bGwpcT1uZXcgUC5SbigiIikKcS5hKz1DLnhCLk5qKGEscixzKQpxLmErPUguTHcobCkK
-cj1tCmNvbnRpbnVlfX10aHJvdyBILmIoUC5ycigiSW52YWxpZCBiYXNlNjQgZGF0YSIsYSxzKSl9aWYo
-cSE9bnVsbCl7Zj1xLmErPUMueEIuTmooYSxyLGExKQplPWYubGVuZ3RoCmlmKHA+PTApUC54TShhLG8s
-YTEscCxuLGUpCmVsc2V7ZD1DLmpuLnpZKGUtMSw0KSsxCmlmKGQ9PT0xKXRocm93IEguYihQLnJyKGIs
-YSxhMSkpCmZvcig7ZDw0Oyl7Zis9Ij0iCnEuYT1mOysrZH19Zj1xLmEKcmV0dXJuIEMueEIuaTcoYSxh
-MCxhMSxmLmNoYXJDb2RlQXQoMCk9PTA/ZjpmKX1jPWExLWEwCmlmKHA+PTApUC54TShhLG8sYTEscCxu
-LGMpCmVsc2V7ZD1DLmpuLnpZKGMsNCkKaWYoZD09PTEpdGhyb3cgSC5iKFAucnIoYixhLGExKSkKaWYo
-ZD4xKWE9Qy54Qi5pNyhhLGExLGExLGQ9PT0yPyI9PSI6Ij0iKX1yZXR1cm4gYX19ClAuVTgucHJvdG90
-eXBlPXt9ClAuVWsucHJvdG90eXBlPXt9ClAud0kucHJvdG90eXBlPXt9ClAuWmkucHJvdG90eXBlPXt9
-ClAuVWQucHJvdG90eXBlPXsKWjpmdW5jdGlvbihhKXt2YXIgdD1QLmgodGhpcy5hKQpyZXR1cm4odGhp
-cy5iIT1udWxsPyJDb252ZXJ0aW5nIG9iamVjdCB0byBhbiBlbmNvZGFibGUgb2JqZWN0IGZhaWxlZDoi
-OiJDb252ZXJ0aW5nIG9iamVjdCBkaWQgbm90IHJldHVybiBhbiBlbmNvZGFibGUgb2JqZWN0OiIpKyIg
-Iit0fX0KUC5LOC5wcm90b3R5cGU9ewpaOmZ1bmN0aW9uKGEpe3JldHVybiJDeWNsaWMgZXJyb3IgaW4g
-SlNPTiBzdHJpbmdpZnkifX0KUC5ieS5wcm90b3R5cGU9ewpwVzpmdW5jdGlvbihhLGIsYyl7dmFyIHQK
-dS5lcC5hKGMpCnQ9UC5CUyhiLHRoaXMuZ0hlKCkuYSkKcmV0dXJuIHR9LApPQjpmdW5jdGlvbihhLGIp
-e3ZhciB0CnUuYmMuYShiKQp0PVAudVgoYSx0aGlzLmdaRSgpLmIsbnVsbCkKcmV0dXJuIHR9LApnWkU6
-ZnVuY3Rpb24oKXtyZXR1cm4gQy5uWH0sCmdIZTpmdW5jdGlvbigpe3JldHVybiBDLkEzfX0KUC5vai5w
-cm90b3R5cGU9e30KUC5NeC5wcm90b3R5cGU9e30KUC5TaC5wcm90b3R5cGU9ewp2cDpmdW5jdGlvbihh
-KXt2YXIgdCxzLHIscSxwLG8sbj1hLmxlbmd0aApmb3IodD1KLnJZKGEpLHM9dGhpcy5jLHI9MCxxPTA7
-cTxuOysrcSl7cD10LlcoYSxxKQppZihwPjkyKWNvbnRpbnVlCmlmKHA8MzIpe2lmKHE+cilzLmErPUMu
-eEIuTmooYSxyLHEpCnI9cSsxCnMuYSs9SC5Mdyg5MikKc3dpdGNoKHApe2Nhc2UgODpzLmErPUguTHco
-OTgpCmJyZWFrCmNhc2UgOTpzLmErPUguTHcoMTE2KQpicmVhawpjYXNlIDEwOnMuYSs9SC5MdygxMTAp
-CmJyZWFrCmNhc2UgMTI6cy5hKz1ILkx3KDEwMikKYnJlYWsKY2FzZSAxMzpzLmErPUguTHcoMTE0KQpi
-cmVhawpkZWZhdWx0OnMuYSs9SC5MdygxMTcpCnMuYSs9SC5Mdyg0OCkKcy5hKz1ILkx3KDQ4KQpvPXA+
-Pj40JjE1CnMuYSs9SC5MdyhvPDEwPzQ4K286ODcrbykKbz1wJjE1CnMuYSs9SC5MdyhvPDEwPzQ4K286
-ODcrbykKYnJlYWt9fWVsc2UgaWYocD09PTM0fHxwPT09OTIpe2lmKHE+cilzLmErPUMueEIuTmooYSxy
-LHEpCnI9cSsxCnMuYSs9SC5Mdyg5MikKcy5hKz1ILkx3KHApfX1pZihyPT09MClzLmErPUguZChhKQpl
-bHNlIGlmKHI8bilzLmErPXQuTmooYSxyLG4pfSwKSm46ZnVuY3Rpb24oYSl7dmFyIHQscyxyLHEKZm9y
-KHQ9dGhpcy5hLHM9dC5sZW5ndGgscj0wO3I8czsrK3Ipe3E9dFtyXQppZihhPT1udWxsP3E9PW51bGw6
-YT09PXEpdGhyb3cgSC5iKG5ldyBQLks4KGEsbnVsbCkpfUMuTm0uaSh0LGEpfSwKaVU6ZnVuY3Rpb24o
-YSl7dmFyIHQscyxyLHEscD10aGlzCmlmKHAudE0oYSkpcmV0dXJuCnAuSm4oYSkKdHJ5e3Q9cC5iLiQx
-KGEpCmlmKCFwLnRNKHQpKXtyPVAuR3koYSxudWxsLHAuZ1ZLKCkpCnRocm93IEguYihyKX1yPXAuYQpp
-ZigwPj1yLmxlbmd0aClyZXR1cm4gSC5rKHIsLTEpCnIucG9wKCl9Y2F0Y2gocSl7cz1ILlJ1KHEpCnI9
-UC5HeShhLHMscC5nVksoKSkKdGhyb3cgSC5iKHIpfX0sCnRNOmZ1bmN0aW9uKGEpe3ZhciB0LHMscj10
-aGlzCmlmKHR5cGVvZiBhPT0ibnVtYmVyIil7aWYoIWlzRmluaXRlKGEpKXJldHVybiExCnIuYy5hKz1D
-LkNELlooYSkKcmV0dXJuITB9ZWxzZSBpZihhPT09ITApe3IuYy5hKz0idHJ1ZSIKcmV0dXJuITB9ZWxz
-ZSBpZihhPT09ITEpe3IuYy5hKz0iZmFsc2UiCnJldHVybiEwfWVsc2UgaWYoYT09bnVsbCl7ci5jLmEr
-PSJudWxsIgpyZXR1cm4hMH1lbHNlIGlmKHR5cGVvZiBhPT0ic3RyaW5nIil7dD1yLmMKdC5hKz0nIicK
-ci52cChhKQp0LmErPSciJwpyZXR1cm4hMH1lbHNlIGlmKHUuai5iKGEpKXtyLkpuKGEpCnIubEsoYSkK
-dD1yLmEKaWYoMD49dC5sZW5ndGgpcmV0dXJuIEguayh0LC0xKQp0LnBvcCgpCnJldHVybiEwfWVsc2Ug
-aWYodS5HLmIoYSkpe3IuSm4oYSkKcz1yLmp3KGEpCnQ9ci5hCmlmKDA+PXQubGVuZ3RoKXJldHVybiBI
-LmsodCwtMSkKdC5wb3AoKQpyZXR1cm4gc31lbHNlIHJldHVybiExfSwKbEs6ZnVuY3Rpb24oYSl7dmFy
-IHQscyxyPXRoaXMuYwpyLmErPSJbIgp0PUouVTYoYSkKaWYodC5nb3IoYSkpe3RoaXMuaVUodC5xKGEs
-MCkpCmZvcihzPTE7czx0LmdBKGEpOysrcyl7ci5hKz0iLCIKdGhpcy5pVSh0LnEoYSxzKSl9fXIuYSs9
-Il0ifSwKanc6ZnVuY3Rpb24oYSl7dmFyIHQscyxyLHEscCxvLG49dGhpcyxtPXt9CmlmKGEuZ2wwKGEp
-KXtuLmMuYSs9Int9IgpyZXR1cm4hMH10PWEuZ0EoYSkqMgpzPW5ldyBBcnJheSh0KQpzLmZpeGVkJGxl
-bmd0aD1BcnJheQpyPW0uYT0wCm0uYj0hMAphLksoMCxuZXcgUC50aShtLHMpKQppZighbS5iKXJldHVy
-biExCnE9bi5jCnEuYSs9InsiCmZvcihwPSciJztyPHQ7cis9MixwPScsIicpe3EuYSs9cApuLnZwKEgu
-YyhzW3JdKSkKcS5hKz0nIjonCm89cisxCmlmKG8+PXQpcmV0dXJuIEguayhzLG8pCm4uaVUoc1tvXSl9
-cS5hKz0ifSIKcmV0dXJuITB9fQpQLnRpLnByb3RvdHlwZT17CiQyOmZ1bmN0aW9uKGEsYil7dmFyIHQs
-cwppZih0eXBlb2YgYSE9InN0cmluZyIpdGhpcy5hLmI9ITEKdD10aGlzLmIKcz10aGlzLmEKQy5ObS5Z
-KHQscy5hKyssYSkKQy5ObS5ZKHQscy5hKyssYil9LAokUzo0fQpQLnR1LnByb3RvdHlwZT17CmdWSzpm
-dW5jdGlvbigpe3ZhciB0PXRoaXMuYy5hCnJldHVybiB0LmNoYXJDb2RlQXQoMCk9PTA/dDp0fX0KUC51
-NS5wcm90b3R5cGU9ewpnWkU6ZnVuY3Rpb24oKXtyZXR1cm4gQy5Ra319ClAuRTMucHJvdG90eXBlPXsK
-V0o6ZnVuY3Rpb24oYSl7dmFyIHQscyxyPVAuakIoMCxudWxsLGEubGVuZ3RoKSxxPXItMAppZihxPT09
-MClyZXR1cm4gbmV3IFVpbnQ4QXJyYXkoMCkKdD1uZXcgVWludDhBcnJheShxKjMpCnM9bmV3IFAuUnco
-dCkKaWYocy5HeChhLDAscikhPT1yKXMuTzYoSi5hNihhLHItMSksMCkKcmV0dXJuIG5ldyBVaW50OEFy
-cmF5KHQuc3ViYXJyYXkoMCxILnJNKDAscy5iLHQubGVuZ3RoKSkpfX0KUC5Sdy5wcm90b3R5cGU9ewpP
-NjpmdW5jdGlvbihhLGIpe3ZhciB0LHM9dGhpcyxyPXMuYyxxPXMuYixwPXErMSxvPXIubGVuZ3RoCmlm
-KChiJjY0NTEyKT09PTU2MzIwKXt0PTY1NTM2KygoYSYxMDIzKTw8MTApfGImMTAyMwpzLmI9cAppZihx
-Pj1vKXJldHVybiBILmsocixxKQpyW3FdPTI0MHx0Pj4+MTgKcT1zLmI9cCsxCmlmKHA+PW8pcmV0dXJu
-IEguayhyLHApCnJbcF09MTI4fHQ+Pj4xMiY2MwpwPXMuYj1xKzEKaWYocT49bylyZXR1cm4gSC5rKHIs
-cSkKcltxXT0xMjh8dD4+PjYmNjMKcy5iPXArMQppZihwPj1vKXJldHVybiBILmsocixwKQpyW3BdPTEy
-OHx0JjYzCnJldHVybiEwfWVsc2V7cy5iPXAKaWYocT49bylyZXR1cm4gSC5rKHIscSkKcltxXT0yMjR8
-YT4+PjEyCnE9cy5iPXArMQppZihwPj1vKXJldHVybiBILmsocixwKQpyW3BdPTEyOHxhPj4+NiY2Mwpz
-LmI9cSsxCmlmKHE+PW8pcmV0dXJuIEguayhyLHEpCnJbcV09MTI4fGEmNjMKcmV0dXJuITF9fSwKR3g6
-ZnVuY3Rpb24oYSxiLGMpe3ZhciB0LHMscixxLHAsbyxuLG09dGhpcwppZihiIT09YyYmKEMueEIubShh
-LGMtMSkmNjQ1MTIpPT09NTUyOTYpLS1jCmZvcih0PW0uYyxzPXQubGVuZ3RoLHI9YjtyPGM7KytyKXtx
-PUMueEIuVyhhLHIpCmlmKHE8PTEyNyl7cD1tLmIKaWYocD49cylicmVhawptLmI9cCsxCnRbcF09cX1l
-bHNlIGlmKChxJjY0NTEyKT09PTU1Mjk2KXtpZihtLmIrMz49cylicmVhawpvPXIrMQppZihtLk82KHEs
-Qy54Qi5XKGEsbykpKXI9b31lbHNlIGlmKHE8PTIwNDcpe3A9bS5iCm49cCsxCmlmKG4+PXMpYnJlYWsK
-bS5iPW4KaWYocD49cylyZXR1cm4gSC5rKHQscCkKdFtwXT0xOTJ8cT4+PjYKbS5iPW4rMQp0W25dPTEy
-OHxxJjYzfWVsc2V7cD1tLmIKaWYocCsyPj1zKWJyZWFrCm49bS5iPXArMQppZihwPj1zKXJldHVybiBI
-LmsodCxwKQp0W3BdPTIyNHxxPj4+MTIKcD1tLmI9bisxCmlmKG4+PXMpcmV0dXJuIEguayh0LG4pCnRb
-bl09MTI4fHE+Pj42JjYzCm0uYj1wKzEKaWYocD49cylyZXR1cm4gSC5rKHQscCkKdFtwXT0xMjh8cSY2
-M319cmV0dXJuIHJ9fQpQLkdZLnByb3RvdHlwZT17CldKOmZ1bmN0aW9uKGEpe3ZhciB0LHMscixxLHAs
-byxuLG0sbAp1LkwuYShhKQp0PVAua3koITEsYSwwLG51bGwpCmlmKHQhPW51bGwpcmV0dXJuIHQKcz1Q
-LmpCKDAsbnVsbCxKLkgoYSkpCnI9UC5jUChhLDAscykKaWYocj4wKXtxPVAuSE0oYSwwLHIpCmlmKHI9
-PT1zKXJldHVybiBxCnA9bmV3IFAuUm4ocSkKbz1yCm49ITF9ZWxzZXtvPTAKcD1udWxsCm49ITB9aWYo
-cD09bnVsbClwPW5ldyBQLlJuKCIiKQptPW5ldyBQLmJ6KCExLHApCm0uYz1uCm0uTUUoYSxvLHMpCmlm
-KG0uZT4wKXtILnZoKFAucnIoIlVuZmluaXNoZWQgVVRGLTggb2N0ZXQgc2VxdWVuY2UiLGEscykpCnAu
-YSs9SC5Mdyg2NTUzMykKbS5mPW0uZT1tLmQ9MH1sPXAuYQpyZXR1cm4gbC5jaGFyQ29kZUF0KDApPT0w
-P2w6bH19ClAuYnoucHJvdG90eXBlPXsKTUU6ZnVuY3Rpb24oYSxiLGMpe3ZhciB0LHMscixxLHAsbyxu
-LG0sbCxrLGosaSxoPXRoaXMsZz0iQmFkIFVURi04IGVuY29kaW5nIDB4Igp1LkwuYShhKQp0PWguZApz
-PWguZQpyPWguZgpoLmY9aC5lPWguZD0wCiRsYWJlbDAkMDpmb3IocT1KLlU2KGEpLHA9aC5iLG89Yjsh
-MDtvPWopeyRsYWJlbDEkMTppZihzPjApe2Rve2lmKG89PT1jKWJyZWFrICRsYWJlbDAkMApuPXEucShh
-LG8pCmlmKHR5cGVvZiBuIT09Im51bWJlciIpcmV0dXJuIG4uek0oKQppZigobiYxOTIpIT09MTI4KXtt
-PVAucnIoZytDLmpuLldaKG4sMTYpLGEsbykKdGhyb3cgSC5iKG0pfWVsc2V7dD0odDw8NnxuJjYzKT4+
-PjA7LS1zOysrb319d2hpbGUocz4wKQptPXItMQppZihtPDB8fG0+PTQpcmV0dXJuIEguayhDLkdiLG0p
-CmlmKHQ8PUMuR2JbbV0pe209UC5ycigiT3ZlcmxvbmcgZW5jb2Rpbmcgb2YgMHgiK0Muam4uV1oodCwx
-NiksYSxvLXItMSkKdGhyb3cgSC5iKG0pfWlmKHQ+MTExNDExMSl7bT1QLnJyKCJDaGFyYWN0ZXIgb3V0
-c2lkZSB2YWxpZCBVbmljb2RlIHJhbmdlOiAweCIrQy5qbi5XWih0LDE2KSxhLG8tci0xKQp0aHJvdyBI
-LmIobSl9aWYoIWguY3x8dCE9PTY1Mjc5KXAuYSs9SC5Mdyh0KQpoLmM9ITF9Zm9yKG09bzxjO207KXts
-PVAuY1AoYSxvLGMpCmlmKGw+MCl7aC5jPSExCms9bytsCnAuYSs9UC5ITShhLG8saykKaWYoaz09PWMp
-YnJlYWt9ZWxzZSBrPW8Kaj1rKzEKbj1xLnEoYSxrKQppZih0eXBlb2YgbiE9PSJudW1iZXIiKXJldHVy
-biBuLkooKQppZihuPDApe2k9UC5ycigiTmVnYXRpdmUgVVRGLTggY29kZSB1bml0OiAtMHgiK0Muam4u
-V1ooLW4sMTYpLGEsai0xKQp0aHJvdyBILmIoaSl9ZWxzZXtpZigobiYyMjQpPT09MTkyKXt0PW4mMzEK
-cz0xCnI9MQpjb250aW51ZSAkbGFiZWwwJDB9aWYoKG4mMjQwKT09PTIyNCl7dD1uJjE1CnM9MgpyPTIK
-Y29udGludWUgJGxhYmVsMCQwfWlmKChuJjI0OCk9PT0yNDAmJm48MjQ1KXt0PW4mNwpzPTMKcj0zCmNv
-bnRpbnVlICRsYWJlbDAkMH1pPVAucnIoZytDLmpuLldaKG4sMTYpLGEsai0xKQp0aHJvdyBILmIoaSl9
-fWJyZWFrICRsYWJlbDAkMH1pZihzPjApe2guZD10CmguZT1zCmguZj1yfX19ClAuV0YucHJvdG90eXBl
-PXsKJDI6ZnVuY3Rpb24oYSxiKXt2YXIgdCxzLHIKdS5mby5hKGEpCnQ9dGhpcy5iCnM9dGhpcy5hCnQu
-YSs9cy5hCnI9dC5hKz1ILmQoYS5hKQp0LmE9cisiOiAiCnQuYSs9UC5oKGIpCnMuYT0iLCAifSwKJFM6
-NDJ9ClAuYTIucHJvdG90eXBlPXt9ClAuaVAucHJvdG90eXBlPXsKRE46ZnVuY3Rpb24oYSxiKXtpZihi
-PT1udWxsKXJldHVybiExCnJldHVybiBiIGluc3RhbmNlb2YgUC5pUCYmdGhpcy5hPT09Yi5hJiYhMH0s
-CmdpTzpmdW5jdGlvbihhKXt2YXIgdD10aGlzLmEKcmV0dXJuKHReQy5qbi53Ryh0LDMwKSkmMTA3Mzc0
-MTgyM30sClo6ZnVuY3Rpb24oYSl7dmFyIHQ9dGhpcyxzPVAuR3EoSC50Sih0KSkscj1QLmgwKEguTlMo
-dCkpLHE9UC5oMChILmpBKHQpKSxwPVAuaDAoSC5JWCh0KSksbz1QLmgwKEguY2godCkpLG49UC5oMChI
-LkpkKHQpKSxtPVAuVngoSC5WYSh0KSksbD1zKyItIityKyItIitxKyIgIitwKyI6IitvKyI6IituKyIu
-IittCnJldHVybiBsfX0KUC5DUC5wcm90b3R5cGU9e30KUC5YUy5wcm90b3R5cGU9ewpnSUk6ZnVuY3Rp
-b24oKXtyZXR1cm4gSC50cyh0aGlzLiR0aHJvd25Kc0Vycm9yKX19ClAuQzYucHJvdG90eXBlPXsKWjpm
-dW5jdGlvbihhKXt2YXIgdD10aGlzLmEKaWYodCE9bnVsbClyZXR1cm4iQXNzZXJ0aW9uIGZhaWxlZDog
-IitQLmgodCkKcmV0dXJuIkFzc2VydGlvbiBmYWlsZWQifX0KUC5MSy5wcm90b3R5cGU9ewpaOmZ1bmN0
-aW9uKGEpe3JldHVybiJUaHJvdyBvZiBudWxsLiJ9fQpQLkFULnByb3RvdHlwZT17CmdMOmZ1bmN0aW9u
-KCl7cmV0dXJuIkludmFsaWQgYXJndW1lbnQiKyghdGhpcy5hPyIocykiOiIiKX0sCmd1OmZ1bmN0aW9u
-KCl7cmV0dXJuIiJ9LApaOmZ1bmN0aW9uKGEpe3ZhciB0LHMscixxLHA9dGhpcyxvPXAuYyxuPW8hPW51
-bGw/IiAoIitvKyIpIjoiIgpvPXAuZAp0PW89PW51bGw/IiI6IjogIitILmQobykKcz1wLmdMKCkrbit0
-CmlmKCFwLmEpcmV0dXJuIHMKcj1wLmd1KCkKcT1QLmgocC5iKQpyZXR1cm4gcytyKyI6ICIrcX19ClAu
-YkoucHJvdG90eXBlPXsKZ0w6ZnVuY3Rpb24oKXtyZXR1cm4iUmFuZ2VFcnJvciJ9LApndTpmdW5jdGlv
-bigpe3ZhciB0LHMscj10aGlzLmUKaWYocj09bnVsbCl7cj10aGlzLmYKdD1yIT1udWxsPyI6IE5vdCBs
-ZXNzIHRoYW4gb3IgZXF1YWwgdG8gIitILmQocik6IiJ9ZWxzZXtzPXRoaXMuZgppZihzPT1udWxsKXQ9
-IjogTm90IGdyZWF0ZXIgdGhhbiBvciBlcXVhbCB0byAiK0guZChyKQplbHNlIGlmKHM+cil0PSI6IE5v
-dCBpbiByYW5nZSAiK0guZChyKSsiLi4iK0guZChzKSsiLCBpbmNsdXNpdmUiCmVsc2UgdD1zPHI/Ijog
-VmFsaWQgdmFsdWUgcmFuZ2UgaXMgZW1wdHkiOiI6IE9ubHkgdmFsaWQgdmFsdWUgaXMgIitILmQocil9
-cmV0dXJuIHR9fQpQLmVZLnByb3RvdHlwZT17CmdMOmZ1bmN0aW9uKCl7cmV0dXJuIlJhbmdlRXJyb3Ii
-fSwKZ3U6ZnVuY3Rpb24oKXt2YXIgdCxzPUguV1kodGhpcy5iKQppZih0eXBlb2YgcyE9PSJudW1iZXIi
-KXJldHVybiBzLkooKQppZihzPDApcmV0dXJuIjogaW5kZXggbXVzdCBub3QgYmUgbmVnYXRpdmUiCnQ9
-dGhpcy5mCmlmKHQ9PT0wKXJldHVybiI6IG5vIGluZGljZXMgYXJlIHZhbGlkIgpyZXR1cm4iOiBpbmRl
-eCBzaG91bGQgYmUgbGVzcyB0aGFuICIrSC5kKHQpfSwKZ0E6ZnVuY3Rpb24oYSl7cmV0dXJuIHRoaXMu
-Zn19ClAubXAucHJvdG90eXBlPXsKWjpmdW5jdGlvbihhKXt2YXIgdCxzLHIscSxwLG8sbixtLGw9dGhp
-cyxrPXt9LGo9bmV3IFAuUm4oIiIpCmsuYT0iIgpmb3IodD1sLmMscz10Lmxlbmd0aCxyPTAscT0iIixw
-PSIiO3I8czsrK3IscD0iLCAiKXtvPXRbcl0Kai5hPXErcApxPWouYSs9UC5oKG8pCmsuYT0iLCAifWwu
-ZC5LKDAsbmV3IFAuV0YoayxqKSkKbj1QLmgobC5hKQptPWouWigwKQp0PSJOb1N1Y2hNZXRob2RFcnJv
-cjogbWV0aG9kIG5vdCBmb3VuZDogJyIrSC5kKGwuYi5hKSsiJ1xuUmVjZWl2ZXI6ICIrbisiXG5Bcmd1
-bWVudHM6IFsiK20rIl0iCnJldHVybiB0fX0KUC51Yi5wcm90b3R5cGU9ewpaOmZ1bmN0aW9uKGEpe3Jl
-dHVybiJVbnN1cHBvcnRlZCBvcGVyYXRpb246ICIrdGhpcy5hfX0KUC5kcy5wcm90b3R5cGU9ewpaOmZ1
-bmN0aW9uKGEpe3ZhciB0PXRoaXMuYQpyZXR1cm4gdCE9bnVsbD8iVW5pbXBsZW1lbnRlZEVycm9yOiAi
-K3Q6IlVuaW1wbGVtZW50ZWRFcnJvciJ9fQpQLmxqLnByb3RvdHlwZT17Clo6ZnVuY3Rpb24oYSl7cmV0
-dXJuIkJhZCBzdGF0ZTogIit0aGlzLmF9fQpQLlVWLnByb3RvdHlwZT17Clo6ZnVuY3Rpb24oYSl7dmFy
-IHQ9dGhpcy5hCmlmKHQ9PW51bGwpcmV0dXJuIkNvbmN1cnJlbnQgbW9kaWZpY2F0aW9uIGR1cmluZyBp
-dGVyYXRpb24uIgpyZXR1cm4iQ29uY3VycmVudCBtb2RpZmljYXRpb24gZHVyaW5nIGl0ZXJhdGlvbjog
-IitQLmgodCkrIi4ifX0KUC5rNS5wcm90b3R5cGU9ewpaOmZ1bmN0aW9uKGEpe3JldHVybiJPdXQgb2Yg
-TWVtb3J5In0sCmdJSTpmdW5jdGlvbigpe3JldHVybiBudWxsfSwKJGlYUzoxfQpQLktZLnByb3RvdHlw
-ZT17Clo6ZnVuY3Rpb24oYSl7cmV0dXJuIlN0YWNrIE92ZXJmbG93In0sCmdJSTpmdW5jdGlvbigpe3Jl
-dHVybiBudWxsfSwKJGlYUzoxfQpQLnQ3LnByb3RvdHlwZT17Clo6ZnVuY3Rpb24oYSl7dmFyIHQ9dGhp
-cy5hCnJldHVybiB0PT1udWxsPyJSZWFkaW5nIHN0YXRpYyB2YXJpYWJsZSBkdXJpbmcgaXRzIGluaXRp
-YWxpemF0aW9uIjoiUmVhZGluZyBzdGF0aWMgdmFyaWFibGUgJyIrdCsiJyBkdXJpbmcgaXRzIGluaXRp
-YWxpemF0aW9uIn19ClAuQ0QucHJvdG90eXBlPXsKWjpmdW5jdGlvbihhKXtyZXR1cm4iRXhjZXB0aW9u
-OiAiK3RoaXMuYX0sCiRpUno6MX0KUC5hRS5wcm90b3R5cGU9ewpaOmZ1bmN0aW9uKGEpe3ZhciB0LHMs
-cixxLHAsbyxuLG0sbCxrLGosaSxoPXRoaXMuYSxnPWghPW51bGwmJiIiIT09aD8iRm9ybWF0RXhjZXB0
-aW9uOiAiK0guZChoKToiRm9ybWF0RXhjZXB0aW9uIixmPXRoaXMuYyxlPXRoaXMuYgppZih0eXBlb2Yg
-ZT09InN0cmluZyIpe2lmKGYhPW51bGwpaD1mPDB8fGY+ZS5sZW5ndGgKZWxzZSBoPSExCmlmKGgpZj1u
-dWxsCmlmKGY9PW51bGwpe3Q9ZS5sZW5ndGg+Nzg/Qy54Qi5OaihlLDAsNzUpKyIuLi4iOmUKcmV0dXJu
-IGcrIlxuIit0fWZvcihzPTEscj0wLHE9ITEscD0wO3A8ZjsrK3Ape289Qy54Qi5XKGUscCkKaWYobz09
-PTEwKXtpZihyIT09cHx8IXEpKytzCnI9cCsxCnE9ITF9ZWxzZSBpZihvPT09MTMpeysrcwpyPXArMQpx
-PSEwfX1nPXM+MT9nKygiIChhdCBsaW5lICIrcysiLCBjaGFyYWN0ZXIgIisoZi1yKzEpKyIpXG4iKTpn
-KygiIChhdCBjaGFyYWN0ZXIgIisoZisxKSsiKVxuIikKbj1lLmxlbmd0aApmb3IocD1mO3A8bjsrK3Ap
-e289Qy54Qi5tKGUscCkKaWYobz09PTEwfHxvPT09MTMpe249cApicmVha319aWYobi1yPjc4KWlmKGYt
-cjw3NSl7bT1yKzc1Cmw9cgprPSIiCmo9Ii4uLiJ9ZWxzZXtpZihuLWY8NzUpe2w9bi03NQptPW4Kaj0i
-In1lbHNle2w9Zi0zNgptPWYrMzYKaj0iLi4uIn1rPSIuLi4ifWVsc2V7bT1uCmw9cgprPSIiCmo9IiJ9
-aT1DLnhCLk5qKGUsbCxtKQpyZXR1cm4gZytrK2kraisiXG4iK0MueEIuSXgoIiAiLGYtbCtrLmxlbmd0
-aCkrIl5cbiJ9ZWxzZSByZXR1cm4gZiE9bnVsbD9nKygiIChhdCBvZmZzZXQgIitILmQoZikrIikiKTpn
-fSwKJGlSejoxfQpQLkVILnByb3RvdHlwZT17fQpQLklmLnByb3RvdHlwZT17fQpQLmNYLnByb3RvdHlw
-ZT17CkUyOmZ1bmN0aW9uKGEsYixjKXt2YXIgdD1ILkxoKHRoaXMpCnJldHVybiBILksxKHRoaXMsdC5L
-cShjKS5DKCIxKGNYLkUpIikuYShiKSx0LkMoImNYLkUiKSxjKX0sCmV2OmZ1bmN0aW9uKGEsYil7dmFy
-IHQ9SC5MaCh0aGlzKQpyZXR1cm4gbmV3IEguVTUodGhpcyx0LkMoImEyKGNYLkUpIikuYShiKSx0LkMo
-IlU1PGNYLkU+IikpfSwKZ0E6ZnVuY3Rpb24oYSl7dmFyIHQscz10aGlzLmdreih0aGlzKQpmb3IodD0w
-O3MuRigpOykrK3QKcmV0dXJuIHR9LApnbDA6ZnVuY3Rpb24oYSl7cmV0dXJuIXRoaXMuZ2t6KHRoaXMp
-LkYoKX0sCmdyODpmdW5jdGlvbihhKXt2YXIgdCxzPXRoaXMuZ2t6KHRoaXMpCmlmKCFzLkYoKSl0aHJv
-dyBILmIoSC5XcCgpKQp0PXMuZ2woKQppZihzLkYoKSl0aHJvdyBILmIoSC5kVSgpKQpyZXR1cm4gdH0s
-CkU6ZnVuY3Rpb24oYSxiKXt2YXIgdCxzLHIscT0iaW5kZXgiClAuVUkoYixxLHUucCkKUC5rMShiLHEp
-CmZvcih0PXRoaXMuZ2t6KHRoaXMpLHM9MDt0LkYoKTspe3I9dC5nbCgpCmlmKGI9PT1zKXJldHVybiBy
-Oysrc310aHJvdyBILmIoUC50KGIsdGhpcyxxLG51bGwscykpfSwKWjpmdW5jdGlvbihhKXtyZXR1cm4g
-UC5FUCh0aGlzLCIoIiwiKSIpfX0KUC5Bbi5wcm90b3R5cGU9e30KUC56TS5wcm90b3R5cGU9eyRpYlE6
-MSwkaWNYOjF9ClAuWjAucHJvdG90eXBlPXt9ClAuTjMucHJvdG90eXBlPXsKWjpmdW5jdGlvbihhKXty
-ZXR1cm4iTWFwRW50cnkoIitILmQodGhpcy5hKSsiOiAiK0guZCh0aGlzLmIpKyIpIn19ClAuYzgucHJv
-dG90eXBlPXsKZ2lPOmZ1bmN0aW9uKGEpe3JldHVybiBQLk1oLnByb3RvdHlwZS5naU8uY2FsbCh0aGlz
-LHRoaXMpfSwKWjpmdW5jdGlvbihhKXtyZXR1cm4ibnVsbCJ9fQpQLmxmLnByb3RvdHlwZT17fQpQLk1o
-LnByb3RvdHlwZT17Y29uc3RydWN0b3I6UC5NaCwkaU1oOjEsCkROOmZ1bmN0aW9uKGEsYil7cmV0dXJu
-IHRoaXM9PT1ifSwKZ2lPOmZ1bmN0aW9uKGEpe3JldHVybiBILmVRKHRoaXMpfSwKWjpmdW5jdGlvbihh
-KXtyZXR1cm4iSW5zdGFuY2Ugb2YgJyIrSC5kKEgubGgodGhpcykpKyInIn0sCmU3OmZ1bmN0aW9uKGEs
-Yil7dS5vLmEoYikKdGhyb3cgSC5iKFAubHIodGhpcyxiLmdXYSgpLGIuZ25kKCksYi5nVm0oKSkpfSwK
-dG9TdHJpbmc6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5aKHRoaXMpfX0KUC5PZC5wcm90b3R5cGU9e30K
-UC5pYi5wcm90b3R5cGU9eyRpT2Q6MX0KUC54dS5wcm90b3R5cGU9e30KUC5Hei5wcm90b3R5cGU9e30K
-UC5aZC5wcm90b3R5cGU9ewpaOmZ1bmN0aW9uKGEpe3JldHVybiIifSwKJGlHejoxfQpQLnFVLnByb3Rv
-dHlwZT17JGl2WDoxfQpQLlJuLnByb3RvdHlwZT17CmdBOmZ1bmN0aW9uKGEpe3JldHVybiB0aGlzLmEu
-bGVuZ3RofSwKWjpmdW5jdGlvbihhKXt2YXIgdD10aGlzLmEKcmV0dXJuIHQuY2hhckNvZGVBdCgwKT09
-MD90OnR9LAokaUJMOjF9ClAuR0QucHJvdG90eXBlPXt9ClAubjEucHJvdG90eXBlPXsKJDI6ZnVuY3Rp
-b24oYSxiKXt2YXIgdCxzLHIscQp1LmYuYShhKQpILmMoYikKdD1KLnJZKGIpLk9ZKGIsIj0iKQppZih0
-PT09LTEpe2lmKGIhPT0iIilhLlkoMCxQLmt1KGIsMCxiLmxlbmd0aCx0aGlzLmEsITApLCIiKX1lbHNl
-IGlmKHQhPT0wKXtzPUMueEIuTmooYiwwLHQpCnI9Qy54Qi5HKGIsdCsxKQpxPXRoaXMuYQphLlkoMCxQ
-Lmt1KHMsMCxzLmxlbmd0aCxxLCEwKSxQLmt1KHIsMCxyLmxlbmd0aCxxLCEwKSl9cmV0dXJuIGF9LAok
-UzoxOX0KUC5jUy5wcm90b3R5cGU9ewokMjpmdW5jdGlvbihhLGIpe3Rocm93IEguYihQLnJyKCJJbGxl
-Z2FsIElQdjQgYWRkcmVzcywgIithLHRoaXMuYSxiKSl9LAokUzo0Nn0KUC5WQy5wcm90b3R5cGU9ewok
-MjpmdW5jdGlvbihhLGIpe3Rocm93IEguYihQLnJyKCJJbGxlZ2FsIElQdjYgYWRkcmVzcywgIithLHRo
-aXMuYSxiKSl9LAokMTpmdW5jdGlvbihhKXtyZXR1cm4gdGhpcy4kMihhLG51bGwpfSwKJFM6NDd9ClAu
-SlQucHJvdG90eXBlPXsKJDI6ZnVuY3Rpb24oYSxiKXt2YXIgdAppZihiLWE+NCl0aGlzLmEuJDIoImFu
-IElQdjYgcGFydCBjYW4gb25seSBjb250YWluIGEgbWF4aW11bSBvZiA0IGhleCBkaWdpdHMiLGEpCnQ9
-UC5RQShDLnhCLk5qKHRoaXMuYixhLGIpLG51bGwsMTYpCmlmKHR5cGVvZiB0IT09Im51bWJlciIpcmV0
-dXJuIHQuSigpCmlmKHQ8MHx8dD42NTUzNSl0aGlzLmEuJDIoImVhY2ggcGFydCBtdXN0IGJlIGluIHRo
-ZSByYW5nZSBvZiBgMHgwLi4weEZGRkZgIixhKQpyZXR1cm4gdH0sCiRTOjQ4fQpQLkRuLnByb3RvdHlw
-ZT17CmdrdTpmdW5jdGlvbigpe3JldHVybiB0aGlzLmJ9LApnSmY6ZnVuY3Rpb24oYSl7dmFyIHQ9dGhp
-cy5jCmlmKHQ9PW51bGwpcmV0dXJuIiIKaWYoQy54Qi5uKHQsIlsiKSlyZXR1cm4gQy54Qi5Oaih0LDEs
-dC5sZW5ndGgtMSkKcmV0dXJuIHR9LApndHA6ZnVuY3Rpb24oYSl7dmFyIHQ9dGhpcy5kCmlmKHQ9PW51
-bGwpcmV0dXJuIFAud0sodGhpcy5hKQpyZXR1cm4gdH0sCmd0UDpmdW5jdGlvbigpe3ZhciB0PXRoaXMu
-ZgpyZXR1cm4gdD09bnVsbD8iIjp0fSwKZ0thOmZ1bmN0aW9uKCl7dmFyIHQ9dGhpcy5yCnJldHVybiB0
-PT1udWxsPyIiOnR9LApubTpmdW5jdGlvbihhLGIpe3ZhciB0LHMscixxLHAsbyxuLG0sbD10aGlzCnUu
-WC5hKG51bGwpCnUuay5hKGIpCnQ9bC5hCnM9dD09PSJmaWxlIgpyPWwuYgpxPWwuZApwPWwuYwppZigh
-KHAhPW51bGwpKXA9ci5sZW5ndGghPT0wfHxxIT1udWxsfHxzPyIiOm51bGwKbz1sLmUKaWYoIXMpbj1w
-IT1udWxsJiZvLmxlbmd0aCE9PTAKZWxzZSBuPSEwCmlmKG4mJiFDLnhCLm4obywiLyIpKW89Ii8iK28K
-bT1QLmxlKG51bGwsMCwwLGIpCnJldHVybiBuZXcgUC5Ebih0LHIscCxxLG8sbSxsLnIpfSwKZ0ZqOmZ1
-bmN0aW9uKCl7dmFyIHQscz10aGlzLngKaWYocyE9bnVsbClyZXR1cm4gcwp0PXRoaXMuZQppZih0Lmxl
-bmd0aCE9PTAmJkMueEIuVyh0LDApPT09NDcpdD1DLnhCLkcodCwxKQpzPXQ9PT0iIj9DLnhEOlAuQUYo
-bmV3IEgubEooSC5WTSh0LnNwbGl0KCIvIiksdS5zKSx1LmRPLmEoUC5QSCgpKSx1LmRvKSx1Lk4pCnRo
-aXMuc282KHMpCnJldHVybiBzfSwKZ2hZOmZ1bmN0aW9uKCl7dmFyIHQscz10aGlzCmlmKHMuUT09bnVs
-bCl7dD1zLmYKcy5zUkgobmV3IFAuR2ooUC5XWCh0PT1udWxsPyIiOnQpLHUudikpfXJldHVybiBzLlF9
-LApKaDpmdW5jdGlvbihhLGIpe3ZhciB0LHMscixxLHAsbwpmb3IodD0wLHM9MDtDLnhCLlFpKGIsIi4u
-LyIscyk7KXtzKz0zOysrdH1yPUMueEIuY24oYSwiLyIpCndoaWxlKCEwKXtpZighKHI+MCYmdD4wKSli
-cmVhawpxPUMueEIuUGsoYSwiLyIsci0xKQppZihxPDApYnJlYWsKcD1yLXEKbz1wIT09MgppZighb3x8
-cD09PTMpaWYoQy54Qi5tKGEscSsxKT09PTQ2KW89IW98fEMueEIubShhLHErMik9PT00NgplbHNlIG89
-ITEKZWxzZSBvPSExCmlmKG8pYnJlYWs7LS10CnI9cX1yZXR1cm4gQy54Qi5pNyhhLHIrMSxudWxsLEMu
-eEIuRyhiLHMtMyp0KSl9LApaSTpmdW5jdGlvbihhKXtyZXR1cm4gdGhpcy5tUyhQLmhLKGEpKX0sCm1T
-OmZ1bmN0aW9uKGEpe3ZhciB0LHMscixxLHAsbyxuLG0sbCxrPXRoaXMsaj1udWxsCmlmKGEuZ0ZpKCku
-bGVuZ3RoIT09MCl7dD1hLmdGaSgpCmlmKGEuZ2NqKCkpe3M9YS5na3UoKQpyPWEuZ0pmKGEpCnE9YS5n
-eEEoKT9hLmd0cChhKTpqfWVsc2V7cT1qCnI9cQpzPSIifXA9UC54ZShhLmdJaShhKSkKbz1hLmdRRCgp
-P2EuZ3RQKCk6an1lbHNle3Q9ay5hCmlmKGEuZ2NqKCkpe3M9YS5na3UoKQpyPWEuZ0pmKGEpCnE9UC53
-QihhLmd4QSgpP2EuZ3RwKGEpOmosdCkKcD1QLnhlKGEuZ0lpKGEpKQpvPWEuZ1FEKCk/YS5ndFAoKTpq
-fWVsc2V7cz1rLmIKcj1rLmMKcT1rLmQKaWYoYS5nSWkoYSk9PT0iIil7cD1rLmUKbz1hLmdRRCgpP2Eu
-Z3RQKCk6ay5mfWVsc2V7aWYoYS5ndFQoKSlwPVAueGUoYS5nSWkoYSkpCmVsc2V7bj1rLmUKaWYobi5s
-ZW5ndGg9PT0wKWlmKHI9PW51bGwpcD10Lmxlbmd0aD09PTA/YS5nSWkoYSk6UC54ZShhLmdJaShhKSkK
-ZWxzZSBwPVAueGUoIi8iK2EuZ0lpKGEpKQplbHNle209ay5KaChuLGEuZ0lpKGEpKQpsPXQubGVuZ3Ro
-PT09MAppZighbHx8ciE9bnVsbHx8Qy54Qi5uKG4sIi8iKSlwPVAueGUobSkKZWxzZSBwPVAud0YobSwh
-bHx8ciE9bnVsbCl9fW89YS5nUUQoKT9hLmd0UCgpOmp9fX1yZXR1cm4gbmV3IFAuRG4odCxzLHIscSxw
-LG8sYS5nWjgoKT9hLmdLYSgpOmopfSwKZ2NqOmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuYyE9bnVsbH0s
-Cmd4QTpmdW5jdGlvbigpe3JldHVybiB0aGlzLmQhPW51bGx9LApnUUQ6ZnVuY3Rpb24oKXtyZXR1cm4g
-dGhpcy5mIT1udWxsfSwKZ1o4OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuciE9bnVsbH0sCmd0VDpmdW5j
-dGlvbigpe3JldHVybiBDLnhCLm4odGhpcy5lLCIvIil9LAp0NDpmdW5jdGlvbigpe3ZhciB0LHMscj10
-aGlzLHE9ci5hCmlmKHEhPT0iIiYmcSE9PSJmaWxlIil0aHJvdyBILmIoUC5MNCgiQ2Fubm90IGV4dHJh
-Y3QgYSBmaWxlIHBhdGggZnJvbSBhICIrSC5kKHEpKyIgVVJJIikpCnE9ci5mCmlmKChxPT1udWxsPyIi
-OnEpIT09IiIpdGhyb3cgSC5iKFAuTDQoIkNhbm5vdCBleHRyYWN0IGEgZmlsZSBwYXRoIGZyb20gYSBV
-Ukkgd2l0aCBhIHF1ZXJ5IGNvbXBvbmVudCIpKQpxPXIucgppZigocT09bnVsbD8iIjpxKSE9PSIiKXRo
-cm93IEguYihQLkw0KCJDYW5ub3QgZXh0cmFjdCBhIGZpbGUgcGF0aCBmcm9tIGEgVVJJIHdpdGggYSBm
-cmFnbWVudCBjb21wb25lbnQiKSkKdD0kLk94KCkKaWYoSC5vVCh0KSlxPVAubW4ocikKZWxzZXtpZihy
-LmMhPW51bGwmJnIuZ0pmKHIpIT09IiIpSC52aChQLkw0KCJDYW5ub3QgZXh0cmFjdCBhIG5vbi1XaW5k
-b3dzIGZpbGUgcGF0aCBmcm9tIGEgZmlsZSBVUkkgd2l0aCBhbiBhdXRob3JpdHkiKSkKcz1yLmdGaigp
-ClAua0UocywhMSkKcT1QLnZnKEMueEIubihyLmUsIi8iKT8iLyI6IiIscywiLyIpCnE9cS5jaGFyQ29k
-ZUF0KDApPT0wP3E6cX1yZXR1cm4gcX0sClo6ZnVuY3Rpb24oYSl7dmFyIHQscyxyLHE9dGhpcyxwPXEu
-eQppZihwPT1udWxsKXtwPXEuYQp0PXAubGVuZ3RoIT09MD9wKyI6IjoiIgpzPXEuYwpyPXM9PW51bGwK
-aWYoIXJ8fHA9PT0iZmlsZSIpe3A9dCsiLy8iCnQ9cS5iCmlmKHQubGVuZ3RoIT09MClwPXArdCsiQCIK
-aWYoIXIpcCs9cwp0PXEuZAppZih0IT1udWxsKXA9cCsiOiIrSC5kKHQpfWVsc2UgcD10CnArPXEuZQp0
-PXEuZgppZih0IT1udWxsKXA9cCsiPyIrdAp0PXEucgppZih0IT1udWxsKXA9cCsiIyIrdApwPXEueT1w
-LmNoYXJDb2RlQXQoMCk9PTA/cDpwfXJldHVybiBwfSwKRE46ZnVuY3Rpb24oYSxiKXt2YXIgdCxzLHI9
-dGhpcwppZihiPT1udWxsKXJldHVybiExCmlmKHI9PT1iKXJldHVybiEwCmlmKHUudy5iKGIpKWlmKHIu
-YT09Yi5nRmkoKSlpZihyLmMhPW51bGw9PT1iLmdjaigpKWlmKHIuYj09Yi5na3UoKSlpZihyLmdKZihy
-KT09Yi5nSmYoYikpaWYoci5ndHAocik9PWIuZ3RwKGIpKWlmKHIuZT09PWIuZ0lpKGIpKXt0PXIuZgpz
-PXQ9PW51bGwKaWYoIXM9PT1iLmdRRCgpKXtpZihzKXQ9IiIKaWYodD09PWIuZ3RQKCkpe3Q9ci5yCnM9
-dD09bnVsbAppZighcz09PWIuZ1o4KCkpe2lmKHMpdD0iIgp0PXQ9PT1iLmdLYSgpfWVsc2UgdD0hMX1l
-bHNlIHQ9ITF9ZWxzZSB0PSExfWVsc2UgdD0hMQplbHNlIHQ9ITEKZWxzZSB0PSExCmVsc2UgdD0hMQpl
-bHNlIHQ9ITEKZWxzZSB0PSExCmVsc2UgdD0hMQpyZXR1cm4gdH0sCmdpTzpmdW5jdGlvbihhKXt2YXIg
-dD10aGlzLnoKcmV0dXJuIHQ9PW51bGw/dGhpcy56PUMueEIuZ2lPKHRoaXMuWigwKSk6dH0sCnNvNjpm
-dW5jdGlvbihhKXt0aGlzLng9dS5hLmEoYSl9LApzUkg6ZnVuY3Rpb24oYSl7dGhpcy5RPXUuZi5hKGEp
-fSwKJGlpRDoxLApnRmk6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5hfSwKZ0lpOmZ1bmN0aW9uKGEpe3Jl
-dHVybiB0aGlzLmV9fQpQLmUxLnByb3RvdHlwZT17CiQxOmZ1bmN0aW9uKGEpe3Rocm93IEguYihQLnJy
-KCJJbnZhbGlkIHBvcnQiLHRoaXMuYSx0aGlzLmIrMSkpfSwKJFM6MTN9ClAuTlkucHJvdG90eXBlPXsK
-JDE6ZnVuY3Rpb24oYSl7dmFyIHQ9IklsbGVnYWwgcGF0aCBjaGFyYWN0ZXIgIgpILmMoYSkKaWYoSi56
-bChhLCIvIikpaWYodGhpcy5hKXRocm93IEguYihQLnhZKHQrYSkpCmVsc2UgdGhyb3cgSC5iKFAuTDQo
-dCthKSl9LAokUzoxM30KUC5SWi5wcm90b3R5cGU9ewokMTpmdW5jdGlvbihhKXtyZXR1cm4gUC5lUChD
-LlpKLEguYyhhKSxDLnhNLCExKX0sCiRTOjV9ClAuTUUucHJvdG90eXBlPXsKJDI6ZnVuY3Rpb24oYSxi
-KXt2YXIgdD10aGlzLmIscz10aGlzLmEKdC5hKz1zLmEKcy5hPSImIgpzPXQuYSs9SC5kKFAuZVAoQy5G
-MyxhLEMueE0sITApKQppZihiIT1udWxsJiZiLmxlbmd0aCE9PTApe3QuYT1zKyI9Igp0LmErPUguZChQ
-LmVQKEMuRjMsYixDLnhNLCEwKSl9fSwKJFM6MjJ9ClAueTUucHJvdG90eXBlPXsKJDI6ZnVuY3Rpb24o
-YSxiKXt2YXIgdCxzCkguYyhhKQppZihiPT1udWxsfHx0eXBlb2YgYj09InN0cmluZyIpdGhpcy5hLiQy
-KGEsSC5jKGIpKQplbHNlIGZvcih0PUouSVQodS5SLmEoYikpLHM9dGhpcy5hO3QuRigpOylzLiQyKGEs
-SC5jKHQuZ2woKSkpfSwKJFM6MTJ9ClAuUEUucHJvdG90eXBlPXsKZ2xSOmZ1bmN0aW9uKCl7dmFyIHQs
-cyxyLHEscD10aGlzLG89bnVsbCxuPXAuYwppZihuIT1udWxsKXJldHVybiBuCm49cC5iCmlmKDA+PW4u
-bGVuZ3RoKXJldHVybiBILmsobiwwKQp0PXAuYQpuPW5bMF0rMQpzPUMueEIuWFUodCwiPyIsbikKcj10
-Lmxlbmd0aAppZihzPj0wKXtxPVAuUEkodCxzKzEscixDLlZDLCExKQpyPXN9ZWxzZSBxPW8KcmV0dXJu
-IHAuYz1uZXcgUC5xZSgiZGF0YSIsbyxvLG8sUC5QSSh0LG4scixDLldkLCExKSxxLG8pfSwKWjpmdW5j
-dGlvbihhKXt2YXIgdCxzPXRoaXMuYgppZigwPj1zLmxlbmd0aClyZXR1cm4gSC5rKHMsMCkKdD10aGlz
-LmEKcmV0dXJuIHNbMF09PT0tMT8iZGF0YToiK3Q6dH19ClAucTMucHJvdG90eXBlPXsKJDE6ZnVuY3Rp
-b24oYSl7cmV0dXJuIG5ldyBVaW50OEFycmF5KDk2KX0sCiRTOjIzfQpQLnlJLnByb3RvdHlwZT17CiQy
-OmZ1bmN0aW9uKGEsYil7dmFyIHQ9dGhpcy5hCmlmKGE+PXQubGVuZ3RoKXJldHVybiBILmsodCxhKQp0
-PXRbYV0KSi5DTSh0LDAsOTYsYikKcmV0dXJuIHR9LAokUzoyNH0KUC5jNi5wcm90b3R5cGU9ewokMzpm
-dW5jdGlvbihhLGIsYyl7dmFyIHQscyxyLHEKZm9yKHQ9Yi5sZW5ndGgscz1hLmxlbmd0aCxyPTA7cjx0
-Oysrcil7cT1DLnhCLlcoYixyKV45NgppZihxPj1zKXJldHVybiBILmsoYSxxKQphW3FdPWN9fX0KUC5x
-ZC5wcm90b3R5cGU9ewokMzpmdW5jdGlvbihhLGIsYyl7dmFyIHQscyxyLHEKZm9yKHQ9Qy54Qi5XKGIs
-MCkscz1DLnhCLlcoYiwxKSxyPWEubGVuZ3RoO3Q8PXM7Kyt0KXtxPSh0Xjk2KT4+PjAKaWYocT49cily
-ZXR1cm4gSC5rKGEscSkKYVtxXT1jfX19ClAuVWYucHJvdG90eXBlPXsKZ2NqOmZ1bmN0aW9uKCl7cmV0
-dXJuIHRoaXMuYz4wfSwKZ3hBOmZ1bmN0aW9uKCl7dmFyIHQscwppZih0aGlzLmM+MCl7dD10aGlzLmQK
-aWYodHlwZW9mIHQhPT0ibnVtYmVyIilyZXR1cm4gdC5oKCkKcz10aGlzLmUKaWYodHlwZW9mIHMhPT0i
-bnVtYmVyIilyZXR1cm4gSC5wWShzKQpzPXQrMTxzCnQ9c31lbHNlIHQ9ITEKcmV0dXJuIHR9LApnUUQ6
-ZnVuY3Rpb24oKXt2YXIgdD10aGlzLmYKaWYodHlwZW9mIHQhPT0ibnVtYmVyIilyZXR1cm4gdC5KKCkK
-cmV0dXJuIHQ8dGhpcy5yfSwKZ1o4OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMucjx0aGlzLmEubGVuZ3Ro
-fSwKZ053OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuYj09PTQmJkMueEIubih0aGlzLmEsImZpbGUiKX0s
-Cmd2aDpmdW5jdGlvbigpe3JldHVybiB0aGlzLmI9PT00JiZDLnhCLm4odGhpcy5hLCJodHRwIil9LApn
-UmU6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5iPT09NSYmQy54Qi5uKHRoaXMuYSwiaHR0cHMiKX0sCmd0
-VDpmdW5jdGlvbigpe3JldHVybiBDLnhCLlFpKHRoaXMuYSwiLyIsdGhpcy5lKX0sCmdGaTpmdW5jdGlv
-bigpe3ZhciB0LHM9dGhpcyxyPSJwYWNrYWdlIixxPXMuYgppZihxPD0wKXJldHVybiIiCnQ9cy54Cmlm
-KHQhPW51bGwpcmV0dXJuIHQKaWYocy5ndmgoKSlxPXMueD0iaHR0cCIKZWxzZSBpZihzLmdSZSgpKXtz
-Lng9Imh0dHBzIgpxPSJodHRwcyJ9ZWxzZSBpZihzLmdOdygpKXtzLng9ImZpbGUiCnE9ImZpbGUifWVs
-c2UgaWYocT09PTcmJkMueEIubihzLmEscikpe3MueD1yCnE9cn1lbHNle3E9Qy54Qi5OaihzLmEsMCxx
-KQpzLng9cX1yZXR1cm4gcX0sCmdrdTpmdW5jdGlvbigpe3ZhciB0PXRoaXMuYyxzPXRoaXMuYiszCnJl
-dHVybiB0PnM/Qy54Qi5Oaih0aGlzLmEscyx0LTEpOiIifSwKZ0pmOmZ1bmN0aW9uKGEpe3ZhciB0PXRo
-aXMuYwpyZXR1cm4gdD4wP0MueEIuTmoodGhpcy5hLHQsdGhpcy5kKToiIn0sCmd0cDpmdW5jdGlvbihh
-KXt2YXIgdCxzPXRoaXMKaWYocy5neEEoKSl7dD1zLmQKaWYodHlwZW9mIHQhPT0ibnVtYmVyIilyZXR1
-cm4gdC5oKCkKcmV0dXJuIFAuUUEoQy54Qi5OaihzLmEsdCsxLHMuZSksbnVsbCxudWxsKX1pZihzLmd2
-aCgpKXJldHVybiA4MAppZihzLmdSZSgpKXJldHVybiA0NDMKcmV0dXJuIDB9LApnSWk6ZnVuY3Rpb24o
-YSl7cmV0dXJuIEMueEIuTmoodGhpcy5hLHRoaXMuZSx0aGlzLmYpfSwKZ3RQOmZ1bmN0aW9uKCl7dmFy
-IHQ9dGhpcy5mLHM9dGhpcy5yCmlmKHR5cGVvZiB0IT09Im51bWJlciIpcmV0dXJuIHQuSigpCnJldHVy
-biB0PHM/Qy54Qi5Oaih0aGlzLmEsdCsxLHMpOiIifSwKZ0thOmZ1bmN0aW9uKCl7dmFyIHQ9dGhpcy5y
-LHM9dGhpcy5hCnJldHVybiB0PHMubGVuZ3RoP0MueEIuRyhzLHQrMSk6IiJ9LApnRmo6ZnVuY3Rpb24o
-KXt2YXIgdCxzLHI9dGhpcy5lLHE9dGhpcy5mLHA9dGhpcy5hCmlmKEMueEIuUWkocCwiLyIscikpe2lm
-KHR5cGVvZiByIT09Im51bWJlciIpcmV0dXJuIHIuaCgpOysrcn1pZihyPT1xKXJldHVybiBDLnhECnQ9
-SC5WTShbXSx1LnMpCnM9cgp3aGlsZSghMCl7aWYodHlwZW9mIHMhPT0ibnVtYmVyIilyZXR1cm4gcy5K
-KCkKaWYodHlwZW9mIHEhPT0ibnVtYmVyIilyZXR1cm4gSC5wWShxKQppZighKHM8cSkpYnJlYWsKaWYo
-Qy54Qi5tKHAscyk9PT00Nyl7Qy5ObS5pKHQsQy54Qi5OaihwLHIscykpCnI9cysxfSsrc31DLk5tLmko
-dCxDLnhCLk5qKHAscixxKSkKcmV0dXJuIFAuQUYodCx1Lk4pfSwKZ2hZOmZ1bmN0aW9uKCl7dmFyIHQ9
-dGhpcy5mCmlmKHR5cGVvZiB0IT09Im51bWJlciIpcmV0dXJuIHQuSigpCmlmKHQ+PXRoaXMucilyZXR1
-cm4gQy5XTwpyZXR1cm4gbmV3IFAuR2ooUC5XWCh0aGlzLmd0UCgpKSx1LnYpfSwKa1g6ZnVuY3Rpb24o
-YSl7dmFyIHQscz10aGlzLmQKaWYodHlwZW9mIHMhPT0ibnVtYmVyIilyZXR1cm4gcy5oKCkKdD1zKzEK
-cmV0dXJuIHQrYS5sZW5ndGg9PT10aGlzLmUmJkMueEIuUWkodGhpcy5hLGEsdCl9LApOOTpmdW5jdGlv
-bigpe3ZhciB0PXRoaXMscz10LnIscj10LmEKaWYocz49ci5sZW5ndGgpcmV0dXJuIHQKcmV0dXJuIG5l
-dyBQLlVmKEMueEIuTmoociwwLHMpLHQuYix0LmMsdC5kLHQuZSx0LmYscyx0LngpfSwKbm06ZnVuY3Rp
-b24oYSxiKXt2YXIgdCxzLHIscSxwLG8sbixtLGwsayxqPXRoaXMsaT1udWxsCnUuWC5hKG51bGwpCnUu
-ay5hKGIpCnQ9ai5nRmkoKQpzPXQ9PT0iZmlsZSIKcj1qLmMKcT1yPjA/Qy54Qi5OaihqLmEsai5iKzMs
-cik6IiIKcD1qLmd4QSgpP2ouZ3RwKGopOmkKcj1qLmMKaWYocj4wKW89Qy54Qi5OaihqLmEscixqLmQp
-CmVsc2Ugbz1xLmxlbmd0aCE9PTB8fHAhPW51bGx8fHM/IiI6aQpyPWouYQpuPUMueEIuTmoocixqLmUs
-ai5mKQppZighcyltPW8hPW51bGwmJm4ubGVuZ3RoIT09MAplbHNlIG09ITAKaWYobSYmIUMueEIubihu
-LCIvIikpbj0iLyIrbgpsPVAubGUoaSwwLDAsYikKbT1qLnIKaz1tPHIubGVuZ3RoP0MueEIuRyhyLG0r
-MSk6aQpyZXR1cm4gbmV3IFAuRG4odCxxLG8scCxuLGwsayl9LApaSTpmdW5jdGlvbihhKXtyZXR1cm4g
-dGhpcy5tUyhQLmhLKGEpKX0sCm1TOmZ1bmN0aW9uKGEpe2lmKGEgaW5zdGFuY2VvZiBQLlVmKXJldHVy
-biB0aGlzLnUxKHRoaXMsYSkKcmV0dXJuIHRoaXMudnMoKS5tUyhhKX0sCnUxOmZ1bmN0aW9uKGEsYil7
-dmFyIHQscyxyLHEscCxvLG4sbSxsLGssaixpLGgsZyxmLGU9Yi5iCmlmKGU+MClyZXR1cm4gYgp0PWIu
-YwppZih0PjApe3M9YS5iCmlmKHM8PTApcmV0dXJuIGIKaWYoYS5nTncoKSlyPWIuZSE9Yi5mCmVsc2Ug
-aWYoYS5ndmgoKSlyPSFiLmtYKCI4MCIpCmVsc2Ugcj0hYS5nUmUoKXx8IWIua1goIjQ0MyIpCmlmKHIp
-e3E9cysxCnA9Qy54Qi5OaihhLmEsMCxxKStDLnhCLkcoYi5hLGUrMSkKZT1iLmQKaWYodHlwZW9mIGUh
-PT0ibnVtYmVyIilyZXR1cm4gZS5oKCkKbz1iLmUKaWYodHlwZW9mIG8hPT0ibnVtYmVyIilyZXR1cm4g
-by5oKCkKbj1iLmYKaWYodHlwZW9mIG4hPT0ibnVtYmVyIilyZXR1cm4gbi5oKCkKcmV0dXJuIG5ldyBQ
-LlVmKHAscyx0K3EsZStxLG8rcSxuK3EsYi5yK3EsYS54KX1lbHNlIHJldHVybiB0aGlzLnZzKCkubVMo
-Yil9bT1iLmUKZT1iLmYKaWYobT09ZSl7dD1iLnIKaWYodHlwZW9mIGUhPT0ibnVtYmVyIilyZXR1cm4g
-ZS5KKCkKaWYoZTx0KXtzPWEuZgppZih0eXBlb2YgcyE9PSJudW1iZXIiKXJldHVybiBzLkhOKCkKcT1z
-LWUKcmV0dXJuIG5ldyBQLlVmKEMueEIuTmooYS5hLDAscykrQy54Qi5HKGIuYSxlKSxhLmIsYS5jLGEu
-ZCxhLmUsZStxLHQrcSxhLngpfWU9Yi5hCmlmKHQ8ZS5sZW5ndGgpe3M9YS5yCnJldHVybiBuZXcgUC5V
-ZihDLnhCLk5qKGEuYSwwLHMpK0MueEIuRyhlLHQpLGEuYixhLmMsYS5kLGEuZSxhLmYsdCsocy10KSxh
-LngpfXJldHVybiBhLk45KCl9dD1iLmEKaWYoQy54Qi5RaSh0LCIvIixtKSl7cz1hLmUKaWYodHlwZW9m
-IHMhPT0ibnVtYmVyIilyZXR1cm4gcy5ITigpCmlmKHR5cGVvZiBtIT09Im51bWJlciIpcmV0dXJuIEgu
-cFkobSkKcT1zLW0KcD1DLnhCLk5qKGEuYSwwLHMpK0MueEIuRyh0LG0pCmlmKHR5cGVvZiBlIT09Im51
-bWJlciIpcmV0dXJuIGUuaCgpCnJldHVybiBuZXcgUC5VZihwLGEuYixhLmMsYS5kLHMsZStxLGIucitx
-LGEueCl9bD1hLmUKaz1hLmYKaWYobD09ayYmYS5jPjApe2Zvcig7Qy54Qi5RaSh0LCIuLi8iLG0pOyl7
-aWYodHlwZW9mIG0hPT0ibnVtYmVyIilyZXR1cm4gbS5oKCkKbSs9M31pZih0eXBlb2YgbCE9PSJudW1i
-ZXIiKXJldHVybiBsLkhOKCkKaWYodHlwZW9mIG0hPT0ibnVtYmVyIilyZXR1cm4gSC5wWShtKQpxPWwt
-bSsxCnA9Qy54Qi5OaihhLmEsMCxsKSsiLyIrQy54Qi5HKHQsbSkKaWYodHlwZW9mIGUhPT0ibnVtYmVy
-IilyZXR1cm4gZS5oKCkKcmV0dXJuIG5ldyBQLlVmKHAsYS5iLGEuYyxhLmQsbCxlK3EsYi5yK3EsYS54
-KX1qPWEuYQpmb3IoaT1sO0MueEIuUWkoaiwiLi4vIixpKTspe2lmKHR5cGVvZiBpIT09Im51bWJlciIp
-cmV0dXJuIGkuaCgpCmkrPTN9aD0wCndoaWxlKCEwKXtpZih0eXBlb2YgbSE9PSJudW1iZXIiKXJldHVy
-biBtLmgoKQpnPW0rMwppZih0eXBlb2YgZSE9PSJudW1iZXIiKXJldHVybiBILnBZKGUpCmlmKCEoZzw9
-ZSYmQy54Qi5RaSh0LCIuLi8iLG0pKSlicmVhazsrK2gKbT1nfWY9IiIKd2hpbGUoITApe2lmKHR5cGVv
-ZiBrIT09Im51bWJlciIpcmV0dXJuIGsub3MoKQppZih0eXBlb2YgaSE9PSJudW1iZXIiKXJldHVybiBI
-LnBZKGkpCmlmKCEoaz5pKSlicmVhazstLWsKaWYoQy54Qi5tKGosayk9PT00Nyl7aWYoaD09PTApe2Y9
-Ii8iCmJyZWFrfS0taApmPSIvIn19aWYoaz09PWkmJmEuYjw9MCYmIUMueEIuUWkoaiwiLyIsbCkpe20t
-PWgqMwpmPSIifXE9ay1tK2YubGVuZ3RoCnJldHVybiBuZXcgUC5VZihDLnhCLk5qKGosMCxrKStmK0Mu
-eEIuRyh0LG0pLGEuYixhLmMsYS5kLGwsZStxLGIucitxLGEueCl9LAp0NDpmdW5jdGlvbigpe3ZhciB0
-LHMscixxLHA9dGhpcwppZihwLmI+PTAmJiFwLmdOdygpKXRocm93IEguYihQLkw0KCJDYW5ub3QgZXh0
-cmFjdCBhIGZpbGUgcGF0aCBmcm9tIGEgIitILmQocC5nRmkoKSkrIiBVUkkiKSkKdD1wLmYKcz1wLmEK
-aWYodHlwZW9mIHQhPT0ibnVtYmVyIilyZXR1cm4gdC5KKCkKaWYodDxzLmxlbmd0aCl7aWYodDxwLnIp
-dGhyb3cgSC5iKFAuTDQoIkNhbm5vdCBleHRyYWN0IGEgZmlsZSBwYXRoIGZyb20gYSBVUkkgd2l0aCBh
-IHF1ZXJ5IGNvbXBvbmVudCIpKQp0aHJvdyBILmIoUC5MNCgiQ2Fubm90IGV4dHJhY3QgYSBmaWxlIHBh
-dGggZnJvbSBhIFVSSSB3aXRoIGEgZnJhZ21lbnQgY29tcG9uZW50IikpfXI9JC5PeCgpCmlmKEgub1Qo
-cikpdD1QLm1uKHApCmVsc2V7cT1wLmQKaWYodHlwZW9mIHEhPT0ibnVtYmVyIilyZXR1cm4gSC5wWShx
-KQppZihwLmM8cSlILnZoKFAuTDQoIkNhbm5vdCBleHRyYWN0IGEgbm9uLVdpbmRvd3MgZmlsZSBwYXRo
-IGZyb20gYSBmaWxlIFVSSSB3aXRoIGFuIGF1dGhvcml0eSIpKQp0PUMueEIuTmoocyxwLmUsdCl9cmV0
-dXJuIHR9LApnaU86ZnVuY3Rpb24oYSl7dmFyIHQ9dGhpcy55CnJldHVybiB0PT1udWxsP3RoaXMueT1D
-LnhCLmdpTyh0aGlzLmEpOnR9LApETjpmdW5jdGlvbihhLGIpe2lmKGI9PW51bGwpcmV0dXJuITEKaWYo
-dGhpcz09PWIpcmV0dXJuITAKcmV0dXJuIHUudy5iKGIpJiZ0aGlzLmE9PT1iLlooMCl9LAp2czpmdW5j
-dGlvbigpe3ZhciB0PXRoaXMscz1udWxsLHI9dC5nRmkoKSxxPXQuZ2t1KCkscD10LmM+MD90LmdKZih0
-KTpzLG89dC5neEEoKT90Lmd0cCh0KTpzLG49dC5hLG09dC5mLGw9Qy54Qi5OaihuLHQuZSxtKSxrPXQu
-cgppZih0eXBlb2YgbSE9PSJudW1iZXIiKXJldHVybiBtLkooKQptPW08az90Lmd0UCgpOnMKcmV0dXJu
-IG5ldyBQLkRuKHIscSxwLG8sbCxtLGs8bi5sZW5ndGg/dC5nS2EoKTpzKX0sClo6ZnVuY3Rpb24oYSl7
-cmV0dXJuIHRoaXMuYX0sCiRpaUQ6MX0KUC5xZS5wcm90b3R5cGU9e30KVy5xRS5wcm90b3R5cGU9e30K
-Vy5HaC5wcm90b3R5cGU9ewpaOmZ1bmN0aW9uKGEpe3JldHVybiBTdHJpbmcoYSl9LAokaUdoOjF9Clcu
-ZlkucHJvdG90eXBlPXsKWjpmdW5jdGlvbihhKXtyZXR1cm4gU3RyaW5nKGEpfX0KVy5uQi5wcm90b3R5
-cGU9eyRpbkI6MX0KVy5Bei5wcm90b3R5cGU9eyRpQXo6MX0KVy5RUC5wcm90b3R5cGU9eyRpUVA6MX0K
-Vy5ueC5wcm90b3R5cGU9ewpnQTpmdW5jdGlvbihhKXtyZXR1cm4gYS5sZW5ndGh9fQpXLm9KLnByb3Rv
-dHlwZT17CmdBOmZ1bmN0aW9uKGEpe3JldHVybiBhLmxlbmd0aH19ClcuaWQucHJvdG90eXBlPXt9Clcu
-UUYucHJvdG90eXBlPXt9ClcuTmgucHJvdG90eXBlPXsKWjpmdW5jdGlvbihhKXtyZXR1cm4gU3RyaW5n
-KGEpfX0KVy5JQi5wcm90b3R5cGU9ewpaOmZ1bmN0aW9uKGEpe3JldHVybiJSZWN0YW5nbGUgKCIrSC5k
-KGEubGVmdCkrIiwgIitILmQoYS50b3ApKyIpICIrSC5kKGEud2lkdGgpKyIgeCAiK0guZChhLmhlaWdo
-dCl9LApETjpmdW5jdGlvbihhLGIpe2lmKGI9PW51bGwpcmV0dXJuITEKcmV0dXJuIHUucS5iKGIpJiZh
-LmxlZnQ9PWIubGVmdCYmYS50b3A9PWIudG9wJiZhLndpZHRoPT1iLndpZHRoJiZhLmhlaWdodD09Yi5o
-ZWlnaHR9LApnaU86ZnVuY3Rpb24oYSl7cmV0dXJuIFcuckUoSi5oZihhLmxlZnQpLEouaGYoYS50b3Ap
-LEouaGYoYS53aWR0aCksSi5oZihhLmhlaWdodCkpfSwKJGl0bjoxfQpXLm43LnByb3RvdHlwZT17CmdB
-OmZ1bmN0aW9uKGEpe3JldHVybiBhLmxlbmd0aH19Clcud3oucHJvdG90eXBlPXsKZ0E6ZnVuY3Rpb24o
-YSl7cmV0dXJuIHRoaXMuYS5sZW5ndGh9LApxOmZ1bmN0aW9uKGEsYil7dmFyIHQKSC5XWShiKQp0PXRo
-aXMuYQppZihiPDB8fGI+PXQubGVuZ3RoKXJldHVybiBILmsodCxiKQpyZXR1cm4gdGhpcy4kdGkuYy5h
-KHRbYl0pfSwKWTpmdW5jdGlvbihhLGIsYyl7dGhpcy4kdGkuYy5hKGMpCnRocm93IEguYihQLkw0KCJD
-YW5ub3QgbW9kaWZ5IGxpc3QiKSl9fQpXLmN2LnByb3RvdHlwZT17CmdRZzpmdW5jdGlvbihhKXtyZXR1
-cm4gbmV3IFcuaTcoYSl9LApnUDpmdW5jdGlvbihhKXtyZXR1cm4gbmV3IFcuSTQoYSl9LApzUDpmdW5j
-dGlvbihhLGIpe3ZhciB0CnUuWC5hKGIpCnQ9dGhpcy5nUChhKQp0LlYxKDApCnQuRlYoMCxiKX0sClo6
-ZnVuY3Rpb24oYSl7cmV0dXJuIGEubG9jYWxOYW1lfSwKRkY6ZnVuY3Rpb24oYSl7dmFyIHQ9ISFhLnNj
-cm9sbEludG9WaWV3SWZOZWVkZWQKaWYodClhLnNjcm9sbEludG9WaWV3SWZOZWVkZWQoKQplbHNlIGEu
-c2Nyb2xsSW50b1ZpZXcoKX0sCm56OmZ1bmN0aW9uKGEsYixjLGQsZSl7dmFyIHQscz10aGlzLnI2KGEs
-YyxkLGUpCnN3aXRjaChiLnRvTG93ZXJDYXNlKCkpe2Nhc2UiYmVmb3JlYmVnaW4iOmEucGFyZW50Tm9k
-ZS5pbnNlcnRCZWZvcmUocyxhKQpicmVhawpjYXNlImFmdGVyYmVnaW4iOnQ9YS5jaGlsZE5vZGVzCmEu
-aW5zZXJ0QmVmb3JlKHMsdC5sZW5ndGg+MD90WzBdOm51bGwpCmJyZWFrCmNhc2UiYmVmb3JlZW5kIjph
-LmFwcGVuZENoaWxkKHMpCmJyZWFrCmNhc2UiYWZ0ZXJlbmQiOmEucGFyZW50Tm9kZS5pbnNlcnRCZWZv
-cmUocyxhLm5leHRTaWJsaW5nKQpicmVhawpkZWZhdWx0OkgudmgoUC54WSgiSW52YWxpZCBwb3NpdGlv
-biAiK2IpKX19LApyNjpmdW5jdGlvbihhLGIsYyxkKXt2YXIgdCxzLHIscQppZihjPT1udWxsKXtpZihk
-PT1udWxsKXt0PSQubHQKaWYodD09bnVsbCl7dD1ILlZNKFtdLHUuaSkKcz1uZXcgVy52RCh0KQpDLk5t
-LmkodCxXLlR3KG51bGwpKQpDLk5tLmkodCxXLkJsKCkpCiQubHQ9cwpkPXN9ZWxzZSBkPXR9dD0kLkVV
-CmlmKHQ9PW51bGwpe3Q9bmV3IFcuS28oZCkKJC5FVT10CmM9dH1lbHNle3QuYT1kCmM9dH19ZWxzZSBp
-ZihkIT1udWxsKXRocm93IEguYihQLnhZKCJ2YWxpZGF0b3IgY2FuIG9ubHkgYmUgcGFzc2VkIGlmIHRy
-ZWVTYW5pdGl6ZXIgaXMgbnVsbCIpKQppZigkLnhvPT1udWxsKXt0PWRvY3VtZW50CnM9dC5pbXBsZW1l
-bnRhdGlvbi5jcmVhdGVIVE1MRG9jdW1lbnQoIiIpCiQueG89cwokLkJPPXMuY3JlYXRlUmFuZ2UoKQpz
-PSQueG8uY3JlYXRlRWxlbWVudCgiYmFzZSIpCnUuY1IuYShzKQpzLmhyZWY9dC5iYXNlVVJJCiQueG8u
-aGVhZC5hcHBlbmRDaGlsZChzKX10PSQueG8KaWYodC5ib2R5PT1udWxsKXtzPXQuY3JlYXRlRWxlbWVu
-dCgiYm9keSIpCnQuYm9keT11LlkuYShzKX10PSQueG8KaWYodS5ZLmIoYSkpcj10LmJvZHkKZWxzZXty
-PXQuY3JlYXRlRWxlbWVudChhLnRhZ05hbWUpCiQueG8uYm9keS5hcHBlbmRDaGlsZChyKX1pZigiY3Jl
-YXRlQ29udGV4dHVhbEZyYWdtZW50IiBpbiB3aW5kb3cuUmFuZ2UucHJvdG90eXBlJiYhQy5ObS50ZyhD
-LlNxLGEudGFnTmFtZSkpeyQuQk8uc2VsZWN0Tm9kZUNvbnRlbnRzKHIpCnE9JC5CTy5jcmVhdGVDb250
-ZXh0dWFsRnJhZ21lbnQoYil9ZWxzZXtyLmlubmVySFRNTD1iCnE9JC54by5jcmVhdGVEb2N1bWVudEZy
-YWdtZW50KCkKZm9yKDt0PXIuZmlyc3RDaGlsZCx0IT1udWxsOylxLmFwcGVuZENoaWxkKHQpfXQ9JC54
-by5ib2R5CmlmKHI9PW51bGw/dCE9bnVsbDpyIT09dClKLkx0KHIpCmMuUG4ocSkKZG9jdW1lbnQuYWRv
-cHROb2RlKHEpCnJldHVybiBxfSwKQUg6ZnVuY3Rpb24oYSxiLGMpe3JldHVybiB0aGlzLnI2KGEsYixj
-LG51bGwpfSwKc2hmOmZ1bmN0aW9uKGEsYil7dGhpcy5ZQyhhLGIpfSwKcGs6ZnVuY3Rpb24oYSxiLGMp
-e2EudGV4dENvbnRlbnQ9bnVsbAphLmFwcGVuZENoaWxkKHRoaXMucjYoYSxiLG51bGwsYykpfSwKWUM6
-ZnVuY3Rpb24oYSxiKXtyZXR1cm4gdGhpcy5wayhhLGIsbnVsbCl9LApnbnM6ZnVuY3Rpb24oYSl7cmV0
-dXJuIGEudGFnTmFtZX0sCmdWbDpmdW5jdGlvbihhKXtyZXR1cm4gbmV3IFcuZXUoYSwiY2xpY2siLCEx
-LHUuUSl9LAokaWN2OjF9ClcuQ3YucHJvdG90eXBlPXsKJDE6ZnVuY3Rpb24oYSl7cmV0dXJuIHUuaC5i
-KHUuQS5hKGEpKX0sCiRTOjI1fQpXLmVhLnByb3RvdHlwZT17JGllYToxfQpXLkQwLnByb3RvdHlwZT17
-Ck9uOmZ1bmN0aW9uKGEsYixjLGQpe3UuVS5hKGMpCmlmKGMhPW51bGwpdGhpcy52KGEsYixjLGQpfSwK
-QjpmdW5jdGlvbihhLGIsYyl7cmV0dXJuIHRoaXMuT24oYSxiLGMsbnVsbCl9LAp2OmZ1bmN0aW9uKGEs
-YixjLGQpe3JldHVybiBhLmFkZEV2ZW50TGlzdGVuZXIoYixILnRSKHUuVS5hKGMpLDEpLGQpfSwKJGlE
-MDoxfQpXLmhILnByb3RvdHlwZT17JGloSDoxfQpXLmg0LnByb3RvdHlwZT17CmdBOmZ1bmN0aW9uKGEp
-e3JldHVybiBhLmxlbmd0aH19ClcuYnIucHJvdG90eXBlPXsKZ0E6ZnVuY3Rpb24oYSl7cmV0dXJuIGEu
-bGVuZ3RofX0KVy5WYi5wcm90b3R5cGU9e30KVy5mSi5wcm90b3R5cGU9ewplbzpmdW5jdGlvbihhLGIs
-YyxkKXtyZXR1cm4gYS5vcGVuKGIsYywhMCl9LAokaWZKOjF9Clcud2EucHJvdG90eXBlPXt9ClcuU2cu
-cHJvdG90eXBlPXskaVNnOjF9ClcudTgucHJvdG90eXBlPXsKZ0RyOmZ1bmN0aW9uKGEpe2lmKCJvcmln
-aW4iIGluIGEpcmV0dXJuIGEub3JpZ2luCnJldHVybiBILmQoYS5wcm90b2NvbCkrIi8vIitILmQoYS5o
-b3N0KX0sClo6ZnVuY3Rpb24oYSl7cmV0dXJuIFN0cmluZyhhKX0sCiRpdTg6MX0KVy5PSy5wcm90b3R5
-cGU9eyRpT0s6MX0KVy5lNy5wcm90b3R5cGU9ewpncjg6ZnVuY3Rpb24oYSl7dmFyIHQ9dGhpcy5hLHM9
-dC5jaGlsZE5vZGVzLmxlbmd0aAppZihzPT09MCl0aHJvdyBILmIoUC5QVigiTm8gZWxlbWVudHMiKSkK
-aWYocz4xKXRocm93IEguYihQLlBWKCJNb3JlIHRoYW4gb25lIGVsZW1lbnQiKSkKcmV0dXJuIHQuZmly
-c3RDaGlsZH0sCkZWOmZ1bmN0aW9uKGEsYil7dmFyIHQscyxyLHEKdS5laC5hKGIpCnQ9Yi5hCnM9dGhp
-cy5hCmlmKHQhPT1zKWZvcihyPXQuY2hpbGROb2Rlcy5sZW5ndGgscT0wO3E8cjsrK3Epcy5hcHBlbmRD
-aGlsZCh0LmZpcnN0Q2hpbGQpCnJldHVybn0sClk6ZnVuY3Rpb24oYSxiLGMpe3ZhciB0LHMKdS5BLmEo
-YykKdD10aGlzLmEKcz10LmNoaWxkTm9kZXMKaWYoYjwwfHxiPj1zLmxlbmd0aClyZXR1cm4gSC5rKHMs
-YikKdC5yZXBsYWNlQ2hpbGQoYyxzW2JdKX0sCmdrejpmdW5jdGlvbihhKXt2YXIgdD10aGlzLmEuY2hp
-bGROb2RlcwpyZXR1cm4gbmV3IFcuVzkodCx0Lmxlbmd0aCxILnEodCkuQygiVzk8R20uRT4iKSl9LApn
-QTpmdW5jdGlvbihhKXtyZXR1cm4gdGhpcy5hLmNoaWxkTm9kZXMubGVuZ3RofSwKcTpmdW5jdGlvbihh
-LGIpe3ZhciB0CkguV1koYikKdD10aGlzLmEuY2hpbGROb2RlcwppZihiPDB8fGI+PXQubGVuZ3RoKXJl
-dHVybiBILmsodCxiKQpyZXR1cm4gdFtiXX19ClcudUgucHJvdG90eXBlPXsKd2c6ZnVuY3Rpb24oYSl7
-dmFyIHQ9YS5wYXJlbnROb2RlCmlmKHQhPW51bGwpdC5yZW1vdmVDaGlsZChhKX0sCkQ0OmZ1bmN0aW9u
-KGEpe3ZhciB0CmZvcig7dD1hLmZpcnN0Q2hpbGQsdCE9bnVsbDspYS5yZW1vdmVDaGlsZCh0KX0sClo6
-ZnVuY3Rpb24oYSl7dmFyIHQ9YS5ub2RlVmFsdWUKcmV0dXJuIHQ9PW51bGw/dGhpcy5VKGEpOnR9LAok
-aXVIOjF9ClcuQkgucHJvdG90eXBlPXsKZ0E6ZnVuY3Rpb24oYSl7cmV0dXJuIGEubGVuZ3RofSwKcTpm
-dW5jdGlvbihhLGIpe0guV1koYikKaWYoYj4+PjAhPT1ifHxiPj1hLmxlbmd0aCl0aHJvdyBILmIoUC50
-KGIsYSxudWxsLG51bGwsbnVsbCkpCnJldHVybiBhW2JdfSwKWTpmdW5jdGlvbihhLGIsYyl7dS5BLmEo
-YykKdGhyb3cgSC5iKFAuTDQoIkNhbm5vdCBhc3NpZ24gZWxlbWVudCBvZiBpbW11dGFibGUgTGlzdC4i
-KSl9LApndEg6ZnVuY3Rpb24oYSl7aWYoYS5sZW5ndGg+MClyZXR1cm4gYVswXQp0aHJvdyBILmIoUC5Q
-VigiTm8gZWxlbWVudHMiKSl9LApFOmZ1bmN0aW9uKGEsYil7aWYoYjwwfHxiPj1hLmxlbmd0aClyZXR1
-cm4gSC5rKGEsYikKcmV0dXJuIGFbYl19LAokaWJROjEsCiRpWGo6MSwKJGljWDoxLAokaXpNOjF9Clcu
-U04ucHJvdG90eXBlPXt9ClcuZXcucHJvdG90eXBlPXskaWV3OjF9ClcubHAucHJvdG90eXBlPXsKZ0E6
-ZnVuY3Rpb24oYSl7cmV0dXJuIGEubGVuZ3RofX0KVy5UYi5wcm90b3R5cGU9ewpyNjpmdW5jdGlvbihh
-LGIsYyxkKXt2YXIgdCxzCmlmKCJjcmVhdGVDb250ZXh0dWFsRnJhZ21lbnQiIGluIHdpbmRvdy5SYW5n
-ZS5wcm90b3R5cGUpcmV0dXJuIHRoaXMuRFcoYSxiLGMsZCkKdD1XLlU5KCI8dGFibGU+IitILmQoYikr
-IjwvdGFibGU+IixjLGQpCnM9ZG9jdW1lbnQuY3JlYXRlRG9jdW1lbnRGcmFnbWVudCgpCnMudG9TdHJp
-bmcKdC50b1N0cmluZwpuZXcgVy5lNyhzKS5GVigwLG5ldyBXLmU3KHQpKQpyZXR1cm4gc319ClcuSXYu
-cHJvdG90eXBlPXsKcjY6ZnVuY3Rpb24oYSxiLGMsZCl7dmFyIHQscyxyLHEKaWYoImNyZWF0ZUNvbnRl
-eHR1YWxGcmFnbWVudCIgaW4gd2luZG93LlJhbmdlLnByb3RvdHlwZSlyZXR1cm4gdGhpcy5EVyhhLGIs
-YyxkKQp0PWRvY3VtZW50CnM9dC5jcmVhdGVEb2N1bWVudEZyYWdtZW50KCkKdD1DLkllLnI2KHQuY3Jl
-YXRlRWxlbWVudCgidGFibGUiKSxiLGMsZCkKdC50b1N0cmluZwp0PW5ldyBXLmU3KHQpCnI9dC5ncjgo
-dCkKci50b1N0cmluZwp0PW5ldyBXLmU3KHIpCnE9dC5ncjgodCkKcy50b1N0cmluZwpxLnRvU3RyaW5n
-Cm5ldyBXLmU3KHMpLkZWKDAsbmV3IFcuZTcocSkpCnJldHVybiBzfX0KVy5XUC5wcm90b3R5cGU9ewpy
-NjpmdW5jdGlvbihhLGIsYyxkKXt2YXIgdCxzLHIKaWYoImNyZWF0ZUNvbnRleHR1YWxGcmFnbWVudCIg
-aW4gd2luZG93LlJhbmdlLnByb3RvdHlwZSlyZXR1cm4gdGhpcy5EVyhhLGIsYyxkKQp0PWRvY3VtZW50
-CnM9dC5jcmVhdGVEb2N1bWVudEZyYWdtZW50KCkKdD1DLkllLnI2KHQuY3JlYXRlRWxlbWVudCgidGFi
-bGUiKSxiLGMsZCkKdC50b1N0cmluZwp0PW5ldyBXLmU3KHQpCnI9dC5ncjgodCkKcy50b1N0cmluZwpy
-LnRvU3RyaW5nCm5ldyBXLmU3KHMpLkZWKDAsbmV3IFcuZTcocikpCnJldHVybiBzfX0KVy55WS5wcm90
-b3R5cGU9ewpwazpmdW5jdGlvbihhLGIsYyl7dmFyIHQscwphLnRleHRDb250ZW50PW51bGwKdD1hLmNv
-bnRlbnQKdC50b1N0cmluZwpKLmJUKHQpCnM9dGhpcy5yNihhLGIsbnVsbCxjKQphLmNvbnRlbnQuYXBw
-ZW5kQ2hpbGQocyl9LApZQzpmdW5jdGlvbihhLGIpe3JldHVybiB0aGlzLnBrKGEsYixudWxsKX0sCiRp
-eVk6MX0KVy53Ni5wcm90b3R5cGU9e30KVy5LNS5wcm90b3R5cGU9ewpQbzpmdW5jdGlvbihhLGIsYyl7
-dmFyIHQ9Vy5QMShhLm9wZW4oYixjKSkKcmV0dXJuIHR9LApnbVc6ZnVuY3Rpb24oYSl7cmV0dXJuIGEu
-bG9jYXRpb259LAokaUs1OjEsCiRpdjY6MX0KVy5DbS5wcm90b3R5cGU9eyRpQ206MX0KVy5DUS5wcm90
-b3R5cGU9eyRpQ1E6MX0KVy53NC5wcm90b3R5cGU9ewpaOmZ1bmN0aW9uKGEpe3JldHVybiJSZWN0YW5n
-bGUgKCIrSC5kKGEubGVmdCkrIiwgIitILmQoYS50b3ApKyIpICIrSC5kKGEud2lkdGgpKyIgeCAiK0gu
-ZChhLmhlaWdodCl9LApETjpmdW5jdGlvbihhLGIpe2lmKGI9PW51bGwpcmV0dXJuITEKcmV0dXJuIHUu
-cS5iKGIpJiZhLmxlZnQ9PWIubGVmdCYmYS50b3A9PWIudG9wJiZhLndpZHRoPT1iLndpZHRoJiZhLmhl
-aWdodD09Yi5oZWlnaHR9LApnaU86ZnVuY3Rpb24oYSl7cmV0dXJuIFcuckUoSi5oZihhLmxlZnQpLEou
-aGYoYS50b3ApLEouaGYoYS53aWR0aCksSi5oZihhLmhlaWdodCkpfX0KVy5yaC5wcm90b3R5cGU9ewpn
-QTpmdW5jdGlvbihhKXtyZXR1cm4gYS5sZW5ndGh9LApxOmZ1bmN0aW9uKGEsYil7SC5XWShiKQppZihi
-Pj4+MCE9PWJ8fGI+PWEubGVuZ3RoKXRocm93IEguYihQLnQoYixhLG51bGwsbnVsbCxudWxsKSkKcmV0
-dXJuIGFbYl19LApZOmZ1bmN0aW9uKGEsYixjKXt1LkEuYShjKQp0aHJvdyBILmIoUC5MNCgiQ2Fubm90
-IGFzc2lnbiBlbGVtZW50IG9mIGltbXV0YWJsZSBMaXN0LiIpKX0sCkU6ZnVuY3Rpb24oYSxiKXtpZihi
-PDB8fGI+PWEubGVuZ3RoKXJldHVybiBILmsoYSxiKQpyZXR1cm4gYVtiXX0sCiRpYlE6MSwKJGlYajox
-LAokaWNYOjEsCiRpek06MX0KVy5jZi5wcm90b3R5cGU9ewpLOmZ1bmN0aW9uKGEsYil7dmFyIHQscyxy
-LHEscAp1LmVBLmEoYikKZm9yKHQ9dGhpcy5nVigpLHM9dC5sZW5ndGgscj10aGlzLmEscT0wO3E8dC5s
-ZW5ndGg7dC5sZW5ndGg9PT1zfHwoMCxILmxrKSh0KSwrK3Epe3A9dFtxXQpiLiQyKHAsci5nZXRBdHRy
-aWJ1dGUocCkpfX0sCmdWOmZ1bmN0aW9uKCl7dmFyIHQscyxyLHEscD10aGlzLmEuYXR0cmlidXRlcyxv
-PUguVk0oW10sdS5zKQpmb3IodD1wLmxlbmd0aCxzPXUuaDkscj0wO3I8dDsrK3Ipe2lmKHI+PXAubGVu
-Z3RoKXJldHVybiBILmsocCxyKQpxPXMuYShwW3JdKQppZihxLm5hbWVzcGFjZVVSST09bnVsbClDLk5t
-LmkobyxxLm5hbWUpfXJldHVybiBvfSwKZ2wwOmZ1bmN0aW9uKGEpe3JldHVybiB0aGlzLmdWKCkubGVu
-Z3RoPT09MH19ClcuaTcucHJvdG90eXBlPXsKeDQ6ZnVuY3Rpb24oYSl7cmV0dXJuIHRoaXMuYS5oYXNB
-dHRyaWJ1dGUoYSl9LApxOmZ1bmN0aW9uKGEsYil7cmV0dXJuIHRoaXMuYS5nZXRBdHRyaWJ1dGUoSC5j
-KGIpKX0sClk6ZnVuY3Rpb24oYSxiLGMpe3RoaXMuYS5zZXRBdHRyaWJ1dGUoYixjKX0sCmdBOmZ1bmN0
-aW9uKGEpe3JldHVybiB0aGlzLmdWKCkubGVuZ3RofX0KVy5TeS5wcm90b3R5cGU9ewp4NDpmdW5jdGlv
-bihhKXtyZXR1cm4gdGhpcy5hLmEuaGFzQXR0cmlidXRlKCJkYXRhLSIrdGhpcy5PKGEpKX0sCnE6ZnVu
-Y3Rpb24oYSxiKXtyZXR1cm4gdGhpcy5hLmEuZ2V0QXR0cmlidXRlKCJkYXRhLSIrdGhpcy5PKEguYyhi
-KSkpfSwKWTpmdW5jdGlvbihhLGIsYyl7dGhpcy5hLmEuc2V0QXR0cmlidXRlKCJkYXRhLSIrdGhpcy5P
-KGIpLGMpfSwKSzpmdW5jdGlvbihhLGIpe3RoaXMuYS5LKDAsbmV3IFcuS1ModGhpcyx1LmVBLmEoYikp
-KX0sCmdWOmZ1bmN0aW9uKCl7dmFyIHQ9SC5WTShbXSx1LnMpCnRoaXMuYS5LKDAsbmV3IFcuQTModGhp
-cyx0KSkKcmV0dXJuIHR9LApnQTpmdW5jdGlvbihhKXtyZXR1cm4gdGhpcy5nVigpLmxlbmd0aH0sCmds
-MDpmdW5jdGlvbihhKXtyZXR1cm4gdGhpcy5nVigpLmxlbmd0aD09PTB9LAprOmZ1bmN0aW9uKGEpe3Zh
-ciB0LHMscj1ILlZNKGEuc3BsaXQoIi0iKSx1LnMpCmZvcih0PTE7dDxyLmxlbmd0aDsrK3Qpe3M9clt0
-XQppZihzLmxlbmd0aD4wKUMuTm0uWShyLHQsc1swXS50b1VwcGVyQ2FzZSgpK0ouS1YocywxKSl9cmV0
-dXJuIEMuTm0uSChyLCIiKX0sCk86ZnVuY3Rpb24oYSl7dmFyIHQscyxyLHEscApmb3IodD1hLmxlbmd0
-aCxzPTAscj0iIjtzPHQ7KytzKXtxPWFbc10KcD1xLnRvTG93ZXJDYXNlKCkKcj0ocSE9PXAmJnM+MD9y
-KyItIjpyKStwfXJldHVybiByLmNoYXJDb2RlQXQoMCk9PTA/cjpyfX0KVy5LUy5wcm90b3R5cGU9ewok
-MjpmdW5jdGlvbihhLGIpe2lmKEouclkoYSkubihhLCJkYXRhLSIpKXRoaXMuYi4kMih0aGlzLmEuayhD
-LnhCLkcoYSw1KSksYil9LAokUzoxNH0KVy5BMy5wcm90b3R5cGU9ewokMjpmdW5jdGlvbihhLGIpe2lm
-KEouclkoYSkubihhLCJkYXRhLSIpKUMuTm0uaSh0aGlzLmIsdGhpcy5hLmsoQy54Qi5HKGEsNSkpKX0s
-CiRTOjE0fQpXLkk0LnByb3RvdHlwZT17Cnc6ZnVuY3Rpb24oKXt2YXIgdCxzLHIscSxwPVAuTHModS5O
-KQpmb3IodD10aGlzLmEuY2xhc3NOYW1lLnNwbGl0KCIgIikscz10Lmxlbmd0aCxyPTA7cjxzOysrcil7
-cT1KLlQwKHRbcl0pCmlmKHEubGVuZ3RoIT09MClwLmkoMCxxKX1yZXR1cm4gcH0sClg6ZnVuY3Rpb24o
-YSl7dGhpcy5hLmNsYXNzTmFtZT11LkMuYShhKS5IKDAsIiAiKX0sCmdBOmZ1bmN0aW9uKGEpe3JldHVy
-biB0aGlzLmEuY2xhc3NMaXN0Lmxlbmd0aH0sClYxOmZ1bmN0aW9uKGEpe3RoaXMuYS5jbGFzc05hbWU9
-IiJ9LAp0ZzpmdW5jdGlvbihhLGIpe3ZhciB0PXRoaXMuYS5jbGFzc0xpc3QuY29udGFpbnMoYikKcmV0
-dXJuIHR9LAppOmZ1bmN0aW9uKGEsYil7dmFyIHQ9dGhpcy5hLmNsYXNzTGlzdCxzPXQuY29udGFpbnMo
-YikKdC5hZGQoYikKcmV0dXJuIXN9LApSOmZ1bmN0aW9uKGEsYil7dmFyIHQ9dGhpcy5hLmNsYXNzTGlz
-dCxzPXQuY29udGFpbnMoYikKdC5yZW1vdmUoYikKcmV0dXJuIHN9LApGVjpmdW5jdGlvbihhLGIpe1cu
-VE4odGhpcy5hLHUuWC5hKGIpKX19ClcuRmsucHJvdG90eXBlPXt9ClcuUk8ucHJvdG90eXBlPXt9Clcu
-ZXUucHJvdG90eXBlPXt9ClcueEMucHJvdG90eXBlPXt9Clcudk4ucHJvdG90eXBlPXsKJDE6ZnVuY3Rp
-b24oYSl7cmV0dXJuIHRoaXMuYS4kMSh1LkIuYShhKSl9LAokUzoyN30KVy5KUS5wcm90b3R5cGU9ewpD
-WTpmdW5jdGlvbihhKXt2YXIgdAppZigkLm9yLmE9PT0wKXtmb3IodD0wO3Q8MjYyOysrdCkkLm9yLlko
-MCxDLmNtW3RdLFcucFMoKSkKZm9yKHQ9MDt0PDEyOysrdCkkLm9yLlkoMCxDLkJJW3RdLFcuVjQoKSl9
-fSwKaTA6ZnVuY3Rpb24oYSl7cmV0dXJuICQuQU4oKS50ZygwLFcuclMoYSkpfSwKRWI6ZnVuY3Rpb24o
-YSxiLGMpe3ZhciB0PSQub3IucSgwLEguZChXLnJTKGEpKSsiOjoiK2IpCmlmKHQ9PW51bGwpdD0kLm9y
-LnEoMCwiKjo6IitiKQppZih0PT1udWxsKXJldHVybiExCnJldHVybiBILkU5KHQuJDQoYSxiLGMsdGhp
-cykpfSwKJGlrRjoxfQpXLkdtLnByb3RvdHlwZT17CmdrejpmdW5jdGlvbihhKXtyZXR1cm4gbmV3IFcu
-VzkoYSx0aGlzLmdBKGEpLEgucShhKS5DKCJXOTxHbS5FPiIpKX19ClcudkQucHJvdG90eXBlPXsKaTA6
-ZnVuY3Rpb24oYSl7cmV0dXJuIEMuTm0uVnIodGhpcy5hLG5ldyBXLlV2KGEpKX0sCkViOmZ1bmN0aW9u
-KGEsYixjKXtyZXR1cm4gQy5ObS5Wcih0aGlzLmEsbmV3IFcuRWcoYSxiLGMpKX0sCiRpa0Y6MX0KVy5V
-di5wcm90b3R5cGU9ewokMTpmdW5jdGlvbihhKXtyZXR1cm4gdS5lLmEoYSkuaTAodGhpcy5hKX0sCiRT
-OjE1fQpXLkVnLnByb3RvdHlwZT17CiQxOmZ1bmN0aW9uKGEpe3JldHVybiB1LmUuYShhKS5FYih0aGlz
-LmEsdGhpcy5iLHRoaXMuYyl9LAokUzoxNX0KVy5tNi5wcm90b3R5cGU9ewpDWTpmdW5jdGlvbihhLGIs
-YyxkKXt2YXIgdCxzLHIKdGhpcy5hLkZWKDAsYykKdD1iLmV2KDAsbmV3IFcuRW8oKSkKcz1iLmV2KDAs
-bmV3IFcuV2soKSkKdGhpcy5iLkZWKDAsdCkKcj10aGlzLmMKci5GVigwLEMueEQpCnIuRlYoMCxzKX0s
-CmkwOmZ1bmN0aW9uKGEpe3JldHVybiB0aGlzLmEudGcoMCxXLnJTKGEpKX0sCkViOmZ1bmN0aW9uKGEs
-YixjKXt2YXIgdD10aGlzLHM9Vy5yUyhhKSxyPXQuYwppZihyLnRnKDAsSC5kKHMpKyI6OiIrYikpcmV0
-dXJuIHQuZC5EdChjKQplbHNlIGlmKHIudGcoMCwiKjo6IitiKSlyZXR1cm4gdC5kLkR0KGMpCmVsc2V7
-cj10LmIKaWYoci50ZygwLEguZChzKSsiOjoiK2IpKXJldHVybiEwCmVsc2UgaWYoci50ZygwLCIqOjoi
-K2IpKXJldHVybiEwCmVsc2UgaWYoci50ZygwLEguZChzKSsiOjoqIikpcmV0dXJuITAKZWxzZSBpZihy
-LnRnKDAsIio6OioiKSlyZXR1cm4hMH1yZXR1cm4hMX0sCiRpa0Y6MX0KVy5Fby5wcm90b3R5cGU9ewok
-MTpmdW5jdGlvbihhKXtyZXR1cm4hQy5ObS50ZyhDLkJJLEguYyhhKSl9LAokUzo3fQpXLldrLnByb3Rv
-dHlwZT17CiQxOmZ1bmN0aW9uKGEpe3JldHVybiBDLk5tLnRnKEMuQkksSC5jKGEpKX0sCiRTOjd9Clcu
-Y3QucHJvdG90eXBlPXsKRWI6ZnVuY3Rpb24oYSxiLGMpe2lmKHRoaXMuakYoYSxiLGMpKXJldHVybiEw
-CmlmKGI9PT0idGVtcGxhdGUiJiZjPT09IiIpcmV0dXJuITAKaWYoYS5nZXRBdHRyaWJ1dGUoInRlbXBs
-YXRlIik9PT0iIilyZXR1cm4gdGhpcy5lLnRnKDAsYikKcmV0dXJuITF9fQpXLklBLnByb3RvdHlwZT17
-CiQxOmZ1bmN0aW9uKGEpe3JldHVybiJURU1QTEFURTo6IitILmQoSC5jKGEpKX0sCiRTOjV9ClcuT3cu
-cHJvdG90eXBlPXsKaTA6ZnVuY3Rpb24oYSl7dmFyIHQKaWYodS5hTy5iKGEpKXJldHVybiExCnQ9dS5n
-Ny5iKGEpCmlmKHQmJlcuclMoYSk9PT0iZm9yZWlnbk9iamVjdCIpcmV0dXJuITEKaWYodClyZXR1cm4h
-MApyZXR1cm4hMX0sCkViOmZ1bmN0aW9uKGEsYixjKXtpZihiPT09ImlzInx8Qy54Qi5uKGIsIm9uIikp
-cmV0dXJuITEKcmV0dXJuIHRoaXMuaTAoYSl9LAokaWtGOjF9ClcuVzkucHJvdG90eXBlPXsKRjpmdW5j
-dGlvbigpe3ZhciB0PXRoaXMscz10LmMrMSxyPXQuYgppZihzPHIpe3Quc3AoSi54OSh0LmEscykpCnQu
-Yz1zCnJldHVybiEwfXQuc3AobnVsbCkKdC5jPXIKcmV0dXJuITF9LApnbDpmdW5jdGlvbigpe3JldHVy
-biB0aGlzLmR9LApzcDpmdW5jdGlvbihhKXt0aGlzLmQ9dGhpcy4kdGkuYy5hKGEpfSwKJGlBbjoxfQpX
-LmRXLnByb3RvdHlwZT17CmdtVzpmdW5jdGlvbihhKXtyZXR1cm4gVy5ISCh0aGlzLmEubG9jYXRpb24p
-fSwKJGlEMDoxLAokaXY2OjF9ClcuRmIucHJvdG90eXBlPXt9Clcua0YucHJvdG90eXBlPXt9ClcubWsu
-cHJvdG90eXBlPXskaXkwOjF9ClcuS28ucHJvdG90eXBlPXsKUG46ZnVuY3Rpb24oYSl7dmFyIHQ9dGhp
-cyxzPW5ldyBXLmZtKHQpCnQuYj0hMQpzLiQyKGEsbnVsbCkKZm9yKDt0LmI7KXt0LmI9ITEKcy4kMihh
-LG51bGwpfX0sCkVQOmZ1bmN0aW9uKGEsYil7dmFyIHQ9dGhpcy5iPSEwCmlmKGIhPW51bGw/YiE9PWEu
-cGFyZW50Tm9kZTp0KUouTHQoYSkKZWxzZSBiLnJlbW92ZUNoaWxkKGEpfSwKSTQ6ZnVuY3Rpb24oYSxi
-KXt2YXIgdCxzLHIscSxwLG89ITAsbj1udWxsLG09bnVsbAp0cnl7bj1KLmlnKGEpCm09bi5hLmdldEF0
-dHJpYnV0ZSgiaXMiKQp1LmguYShhKQp0PWZ1bmN0aW9uKGMpe2lmKCEoYy5hdHRyaWJ1dGVzIGluc3Rh
-bmNlb2YgTmFtZWROb2RlTWFwKSlyZXR1cm4gdHJ1ZQppZihjLmlkPT0nbGFzdENoaWxkJ3x8Yy5uYW1l
-PT0nbGFzdENoaWxkJ3x8Yy5pZD09J3ByZXZpb3VzU2libGluZyd8fGMubmFtZT09J3ByZXZpb3VzU2li
-bGluZyd8fGMuaWQ9PSdjaGlsZHJlbid8fGMubmFtZT09J2NoaWxkcmVuJylyZXR1cm4gdHJ1ZQp2YXIg
-bD1jLmNoaWxkTm9kZXMKaWYoYy5sYXN0Q2hpbGQmJmMubGFzdENoaWxkIT09bFtsLmxlbmd0aC0xXSly
-ZXR1cm4gdHJ1ZQppZihjLmNoaWxkcmVuKWlmKCEoYy5jaGlsZHJlbiBpbnN0YW5jZW9mIEhUTUxDb2xs
-ZWN0aW9ufHxjLmNoaWxkcmVuIGluc3RhbmNlb2YgTm9kZUxpc3QpKXJldHVybiB0cnVlCnZhciBrPTAK
-aWYoYy5jaGlsZHJlbilrPWMuY2hpbGRyZW4ubGVuZ3RoCmZvcih2YXIgaj0wO2o8aztqKyspe3ZhciBp
-PWMuY2hpbGRyZW5bal0KaWYoaS5pZD09J2F0dHJpYnV0ZXMnfHxpLm5hbWU9PSdhdHRyaWJ1dGVzJ3x8
-aS5pZD09J2xhc3RDaGlsZCd8fGkubmFtZT09J2xhc3RDaGlsZCd8fGkuaWQ9PSdwcmV2aW91c1NpYmxp
-bmcnfHxpLm5hbWU9PSdwcmV2aW91c1NpYmxpbmcnfHxpLmlkPT0nY2hpbGRyZW4nfHxpLm5hbWU9PSdj
-aGlsZHJlbicpcmV0dXJuIHRydWV9cmV0dXJuIGZhbHNlfShhKQpvPUgub1QodCk/ITA6IShhLmF0dHJp
-YnV0ZXMgaW5zdGFuY2VvZiBOYW1lZE5vZGVNYXApfWNhdGNoKHEpe0guUnUocSl9cz0iZWxlbWVudCB1
-bnByaW50YWJsZSIKdHJ5e3M9Si5BYyhhKX1jYXRjaChxKXtILlJ1KHEpfXRyeXtyPVcuclMoYSkKdGhp
-cy5rUih1LmguYShhKSxiLG8scyxyLHUuRy5hKG4pLEguYyhtKSl9Y2F0Y2gocSl7aWYoSC5SdShxKSBp
-bnN0YW5jZW9mIFAuQVQpdGhyb3cgcQplbHNle3RoaXMuRVAoYSxiKQp3aW5kb3cKcD0iUmVtb3Zpbmcg
-Y29ycnVwdGVkIGVsZW1lbnQgIitILmQocykKaWYodHlwZW9mIGNvbnNvbGUhPSJ1bmRlZmluZWQiKXdp
-bmRvdy5jb25zb2xlLndhcm4ocCl9fX0sCmtSOmZ1bmN0aW9uKGEsYixjLGQsZSxmLGcpe3ZhciB0LHMs
-cixxLHAsbyxuPXRoaXMKaWYoYyl7bi5FUChhLGIpCndpbmRvdwp0PSJSZW1vdmluZyBlbGVtZW50IGR1
-ZSB0byBjb3JydXB0ZWQgYXR0cmlidXRlcyBvbiA8IitkKyI+IgppZih0eXBlb2YgY29uc29sZSE9InVu
-ZGVmaW5lZCIpd2luZG93LmNvbnNvbGUud2Fybih0KQpyZXR1cm59aWYoIW4uYS5pMChhKSl7bi5FUChh
-LGIpCndpbmRvdwp0PSJSZW1vdmluZyBkaXNhbGxvd2VkIGVsZW1lbnQgPCIrSC5kKGUpKyI+IGZyb20g
-IitILmQoYikKaWYodHlwZW9mIGNvbnNvbGUhPSJ1bmRlZmluZWQiKXdpbmRvdy5jb25zb2xlLndhcm4o
-dCkKcmV0dXJufWlmKGchPW51bGwpaWYoIW4uYS5FYihhLCJpcyIsZykpe24uRVAoYSxiKQp3aW5kb3cK
-dD0iUmVtb3ZpbmcgZGlzYWxsb3dlZCB0eXBlIGV4dGVuc2lvbiA8IitILmQoZSkrJyBpcz0iJytnKyci
-PicKaWYodHlwZW9mIGNvbnNvbGUhPSJ1bmRlZmluZWQiKXdpbmRvdy5jb25zb2xlLndhcm4odCkKcmV0
-dXJufXQ9Zi5nVigpCnM9SC5WTSh0LnNsaWNlKDApLEgudDYodCkuQygiamQ8MT4iKSkKZm9yKHI9Zi5n
-VigpLmxlbmd0aC0xLHQ9Zi5hO3I+PTA7LS1yKXtpZihyPj1zLmxlbmd0aClyZXR1cm4gSC5rKHMscikK
-cT1zW3JdCnA9bi5hCm89Si5jSChxKQpILmMocSkKaWYoIXAuRWIoYSxvLHQuZ2V0QXR0cmlidXRlKHEp
-KSl7d2luZG93CnA9IlJlbW92aW5nIGRpc2FsbG93ZWQgYXR0cmlidXRlIDwiK0guZChlKSsiICIrcSsn
-PSInK0guZCh0LmdldEF0dHJpYnV0ZShxKSkrJyI+JwppZih0eXBlb2YgY29uc29sZSE9InVuZGVmaW5l
-ZCIpd2luZG93LmNvbnNvbGUud2FybihwKQp0LnJlbW92ZUF0dHJpYnV0ZShxKX19aWYodS5hVy5iKGEp
-KW4uUG4oYS5jb250ZW50KX0sCiRpb246MX0KVy5mbS5wcm90b3R5cGU9ewokMjpmdW5jdGlvbihhLGIp
-e3ZhciB0LHMscixxLHAsbyxuPXRoaXMuYQpzd2l0Y2goYS5ub2RlVHlwZSl7Y2FzZSAxOm4uSTQoYSxi
-KQpicmVhawpjYXNlIDg6Y2FzZSAxMTpjYXNlIDM6Y2FzZSA0OmJyZWFrCmRlZmF1bHQ6bi5FUChhLGIp
-fXQ9YS5sYXN0Q2hpbGQKZm9yKHI9dS5BO251bGwhPXQ7KXtzPW51bGwKdHJ5e3M9dC5wcmV2aW91c1Np
-YmxpbmcKaWYocyE9bnVsbCl7cT1zLm5leHRTaWJsaW5nCnA9dApwPXE9PW51bGw/cCE9bnVsbDpxIT09
-cApxPXB9ZWxzZSBxPSExCmlmKHEpe3E9UC5QVigiQ29ycnVwdCBIVE1MIikKdGhyb3cgSC5iKHEpfX1j
-YXRjaChvKXtILlJ1KG8pCnE9ci5hKHQpCm4uYj0hMApwPXEucGFyZW50Tm9kZQpwPWE9PW51bGw/cCE9
-bnVsbDphIT09cAppZihwKXtwPXEucGFyZW50Tm9kZQppZihwIT1udWxsKXAucmVtb3ZlQ2hpbGQocSl9
-ZWxzZSBhLnJlbW92ZUNoaWxkKHEpCnQ9bnVsbApzPWEubGFzdENoaWxkfWlmKHQhPW51bGwpdGhpcy4k
-Mih0LGEpCnQ9c319LAokUzozMH0KVy5MZS5wcm90b3R5cGU9e30KVy5LNy5wcm90b3R5cGU9e30KVy5y
-Qi5wcm90b3R5cGU9e30KVy5YVy5wcm90b3R5cGU9e30KVy5vYS5wcm90b3R5cGU9e30KUC5pSi5wcm90
-b3R5cGU9ewpWSDpmdW5jdGlvbihhKXt2YXIgdCxzPXRoaXMuYSxyPXMubGVuZ3RoCmZvcih0PTA7dDxy
-OysrdClpZihzW3RdPT09YSlyZXR1cm4gdApDLk5tLmkocyxhKQpDLk5tLmkodGhpcy5iLG51bGwpCnJl
-dHVybiByfSwKUHY6ZnVuY3Rpb24oYSl7dmFyIHQscyxyLHE9dGhpcyxwPXt9CmlmKGE9PW51bGwpcmV0
-dXJuIGEKaWYoSC5yUShhKSlyZXR1cm4gYQppZih0eXBlb2YgYT09Im51bWJlciIpcmV0dXJuIGEKaWYo
-dHlwZW9mIGE9PSJzdHJpbmciKXJldHVybiBhCmlmKGEgaW5zdGFuY2VvZiBQLmlQKXJldHVybiBuZXcg
-RGF0ZShhLmEpCmlmKHUuZnYuYihhKSl0aHJvdyBILmIoUC5uKCJzdHJ1Y3R1cmVkIGNsb25lIG9mIFJl
-Z0V4cCIpKQppZih1LmM4LmIoYSkpcmV0dXJuIGEKaWYodS5kLmIoYSkpcmV0dXJuIGEKaWYodS5JLmIo
-YSkpcmV0dXJuIGEKdD11LmRELmIoYSl8fCExCmlmKHQpcmV0dXJuIGEKaWYodS5HLmIoYSkpe3M9cS5W
-SChhKQp0PXEuYgppZihzPj10Lmxlbmd0aClyZXR1cm4gSC5rKHQscykKcj1wLmE9dFtzXQppZihyIT1u
-dWxsKXJldHVybiByCnI9e30KcC5hPXIKQy5ObS5ZKHQscyxyKQphLksoMCxuZXcgUC5qZyhwLHEpKQpy
-ZXR1cm4gcC5hfWlmKHUuai5iKGEpKXtzPXEuVkgoYSkKcD1xLmIKaWYocz49cC5sZW5ndGgpcmV0dXJu
-IEguayhwLHMpCnI9cFtzXQppZihyIT1udWxsKXJldHVybiByCnJldHVybiBxLmVrKGEscyl9aWYodS5l
-SC5iKGEpKXtzPXEuVkgoYSkKdD1xLmIKaWYocz49dC5sZW5ndGgpcmV0dXJuIEguayh0LHMpCnI9cC5i
-PXRbc10KaWYociE9bnVsbClyZXR1cm4gcgpyPXt9CnAuYj1yCkMuTm0uWSh0LHMscikKcS5pbShhLG5l
-dyBQLlRhKHAscSkpCnJldHVybiBwLmJ9dGhyb3cgSC5iKFAubigic3RydWN0dXJlZCBjbG9uZSBvZiBv
-dGhlciB0eXBlIikpfSwKZWs6ZnVuY3Rpb24oYSxiKXt2YXIgdCxzPUouVTYoYSkscj1zLmdBKGEpLHE9
-bmV3IEFycmF5KHIpCkMuTm0uWSh0aGlzLmIsYixxKQpmb3IodD0wO3Q8cjsrK3QpQy5ObS5ZKHEsdCx0
-aGlzLlB2KHMucShhLHQpKSkKcmV0dXJuIHF9fQpQLmpnLnByb3RvdHlwZT17CiQyOmZ1bmN0aW9uKGEs
-Yil7dGhpcy5hLmFbYV09dGhpcy5iLlB2KGIpfSwKJFM6NH0KUC5UYS5wcm90b3R5cGU9ewokMjpmdW5j
-dGlvbihhLGIpe3RoaXMuYS5iW2FdPXRoaXMuYi5QdihiKX0sCiRTOjR9ClAuQmYucHJvdG90eXBlPXsK
-aW06ZnVuY3Rpb24oYSxiKXt2YXIgdCxzLHIscQp1LmI4LmEoYikKZm9yKHQ9T2JqZWN0LmtleXMoYSks
-cz10Lmxlbmd0aCxyPTA7cjxzOysrcil7cT10W3JdCmIuJDIocSxhW3FdKX19fQpQLkFzLnByb3RvdHlw
-ZT17ClQ6ZnVuY3Rpb24oYSl7dmFyIHQKSC5jKGEpCnQ9JC5oRygpLmIKaWYodHlwZW9mIGEhPSJzdHJp
-bmciKUgudmgoSC5JKGEpKQppZih0LnRlc3QoYSkpcmV0dXJuIGEKdGhyb3cgSC5iKFAuTDMoYSwidmFs
-dWUiLCJOb3QgYSB2YWxpZCBjbGFzcyB0b2tlbiIpKX0sClo6ZnVuY3Rpb24oYSl7cmV0dXJuIHRoaXMu
-dygpLkgoMCwiICIpfSwKZ2t6OmZ1bmN0aW9uKGEpe3ZhciB0PXRoaXMudygpCnJldHVybiBQLnJqKHQs
-dC5yLEguTGgodCkuYyl9LApnQTpmdW5jdGlvbihhKXtyZXR1cm4gdGhpcy53KCkuYX0sCnRnOmZ1bmN0
-aW9uKGEsYil7dGhpcy5UKGIpCnJldHVybiB0aGlzLncoKS50ZygwLGIpfSwKaTpmdW5jdGlvbihhLGIp
-e3RoaXMuVChiKQpyZXR1cm4gSC5FOSh0aGlzLk9TKG5ldyBQLkdFKGIpKSl9LApSOmZ1bmN0aW9uKGEs
-Yil7dmFyIHQscwp0aGlzLlQoYikKdD10aGlzLncoKQpzPXQuUigwLGIpCnRoaXMuWCh0KQpyZXR1cm4g
-c30sCkZWOmZ1bmN0aW9uKGEsYil7dGhpcy5PUyhuZXcgUC5ONyh0aGlzLHUuWC5hKGIpKSl9LApWMTpm
-dW5jdGlvbihhKXt0aGlzLk9TKG5ldyBQLnVRKCkpfSwKT1M6ZnVuY3Rpb24oYSl7dmFyIHQscwp1LmJV
-LmEoYSkKdD10aGlzLncoKQpzPWEuJDEodCkKdGhpcy5YKHQpCnJldHVybiBzfX0KUC5HRS5wcm90b3R5
-cGU9ewokMTpmdW5jdGlvbihhKXtyZXR1cm4gdS5DLmEoYSkuaSgwLHRoaXMuYSl9LAokUzozMX0KUC5O
-Ny5wcm90b3R5cGU9ewokMTpmdW5jdGlvbihhKXt2YXIgdD10aGlzLmIscz1ILnQ2KHQpCnJldHVybiB1
-LkMuYShhKS5GVigwLG5ldyBILmxKKHQscy5DKCJxVSgxKSIpLmEodGhpcy5hLmd1TSgpKSxzLkMoImxK
-PDEscVU+IikpKX0sCiRTOjExfQpQLnVRLnByb3RvdHlwZT17CiQxOmZ1bmN0aW9uKGEpe3UuQy5hKGEp
-CmlmKGEuYT4wKXthLmI9YS5jPWEuZD1hLmU9YS5mPW51bGwKYS5hPTAKYS5TKCl9cmV0dXJuIG51bGx9
-LAokUzoxMX0KUC5oRi5wcm90b3R5cGU9eyRpaEY6MX0KUC5QQy5wcm90b3R5cGU9ewokMTpmdW5jdGlv
-bihhKXt2YXIgdAp1LlouYShhKQp0PWZ1bmN0aW9uKGIsYyxkKXtyZXR1cm4gZnVuY3Rpb24oKXtyZXR1
-cm4gYihjLGQsdGhpcyxBcnJheS5wcm90b3R5cGUuc2xpY2UuYXBwbHkoYXJndW1lbnRzKSl9fShQLlI0
-LGEsITEpClAuRG0odCwkLndRKCksYSkKcmV0dXJuIHR9LAokUzoxfQpQLm10LnByb3RvdHlwZT17CiQx
-OmZ1bmN0aW9uKGEpe3JldHVybiBuZXcgdGhpcy5hKGEpfSwKJFM6MX0KUC5Oei5wcm90b3R5cGU9ewok
-MTpmdW5jdGlvbihhKXtyZXR1cm4gbmV3IFAucjcoYSl9LAokUzozM30KUC5RUy5wcm90b3R5cGU9ewok
-MTpmdW5jdGlvbihhKXtyZXR1cm4gbmV3IFAuVHooYSx1LmFtKX0sCiRTOjM0fQpQLm5wLnByb3RvdHlw
-ZT17CiQxOmZ1bmN0aW9uKGEpe3JldHVybiBuZXcgUC5FNChhKX0sCiRTOjM1fQpQLkU0LnByb3RvdHlw
-ZT17CnE6ZnVuY3Rpb24oYSxiKXtpZih0eXBlb2YgYiE9InN0cmluZyImJnR5cGVvZiBiIT0ibnVtYmVy
-Iil0aHJvdyBILmIoUC54WSgicHJvcGVydHkgaXMgbm90IGEgU3RyaW5nIG9yIG51bSIpKQpyZXR1cm4g
-UC5MNyh0aGlzLmFbYl0pfSwKWTpmdW5jdGlvbihhLGIsYyl7aWYodHlwZW9mIGIhPSJzdHJpbmciJiZ0
-eXBlb2YgYiE9Im51bWJlciIpdGhyb3cgSC5iKFAueFkoInByb3BlcnR5IGlzIG5vdCBhIFN0cmluZyBv
-ciBudW0iKSkKdGhpcy5hW2JdPVAud1koYyl9LApETjpmdW5jdGlvbihhLGIpe2lmKGI9PW51bGwpcmV0
-dXJuITEKcmV0dXJuIGIgaW5zdGFuY2VvZiBQLkU0JiZ0aGlzLmE9PT1iLmF9LApaOmZ1bmN0aW9uKGEp
-e3ZhciB0LHMKdHJ5e3Q9U3RyaW5nKHRoaXMuYSkKcmV0dXJuIHR9Y2F0Y2gocyl7SC5SdShzKQp0PXRo
-aXMueGIoMCkKcmV0dXJuIHR9fSwKVjc6ZnVuY3Rpb24oYSxiKXt2YXIgdCxzPXRoaXMuYQppZihiPT1u
-dWxsKXQ9bnVsbAplbHNle3Q9SC50NihiKQp0PVAuQ0gobmV3IEgubEooYix0LkMoIkAoMSkiKS5hKFAu
-aUcoKSksdC5DKCJsSjwxLEA+IikpLCEwLHUueil9cmV0dXJuIFAuTDcoc1thXS5hcHBseShzLHQpKX0s
-CmdpTzpmdW5jdGlvbihhKXtyZXR1cm4gMH19ClAucjcucHJvdG90eXBlPXt9ClAuVHoucHJvdG90eXBl
-PXsKY1A6ZnVuY3Rpb24oYSl7dmFyIHQ9dGhpcyxzPWE8MHx8YT49dC5nQSh0KQppZihzKXRocm93IEgu
-YihQLlRFKGEsMCx0LmdBKHQpLG51bGwsbnVsbCkpfSwKcTpmdW5jdGlvbihhLGIpe2lmKHR5cGVvZiBi
-PT0ibnVtYmVyIiYmYj09PUMuam4ueXUoYikpdGhpcy5jUChILldZKGIpKQpyZXR1cm4gdGhpcy4kdGku
-Yy5hKHRoaXMuVXIoMCxiKSl9LApZOmZ1bmN0aW9uKGEsYixjKXt2YXIgdAp0aGlzLiR0aS5jLmEoYykK
-dD1DLmpuLnl1KGIpCmlmKGI9PT10KXRoaXMuY1AoYikKdGhpcy5lNCgwLGIsYyl9LApnQTpmdW5jdGlv
-bihhKXt2YXIgdD10aGlzLmEubGVuZ3RoCmlmKHR5cGVvZiB0PT09Im51bWJlciImJnQ+Pj4wPT09dCly
-ZXR1cm4gdAp0aHJvdyBILmIoUC5QVigiQmFkIEpzQXJyYXkgbGVuZ3RoIikpfSwKJGliUToxLAokaWNY
-OjEsCiRpek06MX0KUC5jby5wcm90b3R5cGU9e30KUC5uZC5wcm90b3R5cGU9eyRpbmQ6MX0KUC5LZS5w
-cm90b3R5cGU9ewp3OmZ1bmN0aW9uKCl7dmFyIHQscyxyLHEscD10aGlzLmEuZ2V0QXR0cmlidXRlKCJj
-bGFzcyIpLG89UC5Mcyh1Lk4pCmlmKHA9PW51bGwpcmV0dXJuIG8KZm9yKHQ9cC5zcGxpdCgiICIpLHM9
-dC5sZW5ndGgscj0wO3I8czsrK3Ipe3E9Si5UMCh0W3JdKQppZihxLmxlbmd0aCE9PTApby5pKDAscSl9
-cmV0dXJuIG99LApYOmZ1bmN0aW9uKGEpe3RoaXMuYS5zZXRBdHRyaWJ1dGUoImNsYXNzIixhLkgoMCwi
-ICIpKX19ClAuZDUucHJvdG90eXBlPXsKZ1A6ZnVuY3Rpb24oYSl7cmV0dXJuIG5ldyBQLktlKGEpfSwK
-c2hmOmZ1bmN0aW9uKGEsYil7dGhpcy5ZQyhhLGIpfSwKcjY6ZnVuY3Rpb24oYSxiLGMsZCl7dmFyIHQs
-cyxyLHEscCxvCmlmKGQ9PW51bGwpe3Q9SC5WTShbXSx1LmkpCmQ9bmV3IFcudkQodCkKQy5ObS5pKHQs
-Vy5UdyhudWxsKSkKQy5ObS5pKHQsVy5CbCgpKQpDLk5tLmkodCxuZXcgVy5PdygpKX1jPW5ldyBXLktv
-KGQpCnM9JzxzdmcgdmVyc2lvbj0iMS4xIj4nK0guZChiKSsiPC9zdmc+Igp0PWRvY3VtZW50CnI9dC5i
-b2R5CnE9KHImJkMuUlkpLkFIKHIscyxjKQpwPXQuY3JlYXRlRG9jdW1lbnRGcmFnbWVudCgpCnEudG9T
+T0godCxoKQpnPXRbaF0KaWYoZz49MCl7aD1DLnhCLm0oIkFCQ0RFRkdISUpLTE1OT1BRUlNUVVZXWFla
+YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXowMTIzNDU2Nzg5Ky8iLGcpCmlmKGg9PT1sKWNvbnRpbnVl
+Cmw9aH1lbHNle2lmKGc9PT0tMSl7aWYocDwwKXtmPXE9PW51bGw/bnVsbDpxLmEubGVuZ3RoCmlmKGY9
+PW51bGwpZj0wCnA9Zisocy1yKQpvPXN9KytuCmlmKGw9PT02MSljb250aW51ZX1sPWh9aWYoZyE9PS0y
+KXtpZihxPT1udWxsKXtxPW5ldyBQLlJuKCIiKQpmPXF9ZWxzZSBmPXEKZi5hKz1DLnhCLk5qKGEscixz
+KQpmLmErPUguTHcobCkKcj1tCmNvbnRpbnVlfX10aHJvdyBILmIoUC5ycigiSW52YWxpZCBiYXNlNjQg
+ZGF0YSIsYSxzKSl9aWYocSE9bnVsbCl7Zj1xLmErPUMueEIuTmooYSxyLGExKQplPWYubGVuZ3RoCmlm
+KHA+PTApUC54TShhLG8sYTEscCxuLGUpCmVsc2V7ZD1DLmpuLnpZKGUtMSw0KSsxCmlmKGQ9PT0xKXRo
+cm93IEguYihQLnJyKGIsYSxhMSkpCmZvcig7ZDw0Oyl7Zis9Ij0iCnEuYT1mOysrZH19Zj1xLmEKcmV0
+dXJuIEMueEIuaTcoYSxhMCxhMSxmLmNoYXJDb2RlQXQoMCk9PTA/ZjpmKX1jPWExLWEwCmlmKHA+PTAp
+UC54TShhLG8sYTEscCxuLGMpCmVsc2V7ZD1DLmpuLnpZKGMsNCkKaWYoZD09PTEpdGhyb3cgSC5iKFAu
+cnIoYixhLGExKSkKaWYoZD4xKWE9Qy54Qi5pNyhhLGExLGExLGQ9PT0yPyI9PSI6Ij0iKX1yZXR1cm4g
+YX19ClAuVTgucHJvdG90eXBlPXt9ClAuVWsucHJvdG90eXBlPXt9ClAud0kucHJvdG90eXBlPXt9ClAu
+WmkucHJvdG90eXBlPXt9ClAuVWQucHJvdG90eXBlPXsKdzpmdW5jdGlvbihhKXt2YXIgdD1QLnAodGhp
+cy5hKQpyZXR1cm4odGhpcy5iIT1udWxsPyJDb252ZXJ0aW5nIG9iamVjdCB0byBhbiBlbmNvZGFibGUg
+b2JqZWN0IGZhaWxlZDoiOiJDb252ZXJ0aW5nIG9iamVjdCBkaWQgbm90IHJldHVybiBhbiBlbmNvZGFi
+bGUgb2JqZWN0OiIpKyIgIit0fX0KUC5LOC5wcm90b3R5cGU9ewp3OmZ1bmN0aW9uKGEpe3JldHVybiJD
+eWNsaWMgZXJyb3IgaW4gSlNPTiBzdHJpbmdpZnkifX0KUC5ieS5wcm90b3R5cGU9ewpwVzpmdW5jdGlv
+bihhLGIsYyl7dmFyIHQKdS5mVi5hKGMpCnQ9UC5CUyhiLHRoaXMuZ0hlKCkuYSkKcmV0dXJuIHR9LApP
+QjpmdW5jdGlvbihhLGIpe3ZhciB0CnUuZEEuYShiKQp0PVAudVgoYSx0aGlzLmdaRSgpLmIsbnVsbCkK
+cmV0dXJuIHR9LApnWkU6ZnVuY3Rpb24oKXtyZXR1cm4gQy5uWH0sCmdIZTpmdW5jdGlvbigpe3JldHVy
+biBDLkEzfX0KUC5vai5wcm90b3R5cGU9e30KUC5NeC5wcm90b3R5cGU9e30KUC5TaC5wcm90b3R5cGU9
+ewp2cDpmdW5jdGlvbihhKXt2YXIgdCxzLHIscSxwLG8sbixtPWEubGVuZ3RoCmZvcih0PUouclkoYSks
+cz10aGlzLmMscj0wLHE9MDtxPG07KytxKXtwPXQuVyhhLHEpCmlmKHA+OTIpe2lmKHA+PTU1Mjk2KXtv
+PXAmNjQ1MTIKaWYobz09PTU1Mjk2KXtuPXErMQpuPSEobjxtJiYoQy54Qi5XKGEsbikmNjQ1MTIpPT09
+NTYzMjApfWVsc2Ugbj0hMQppZighbilpZihvPT09NTYzMjApe289cS0xCm89IShvPj0wJiYoQy54Qi5t
+KGEsbykmNjQ1MTIpPT09NTUyOTYpfWVsc2Ugbz0hMQplbHNlIG89ITAKaWYobyl7aWYocT5yKXMuYSs9
+Qy54Qi5OaihhLHIscSkKcj1xKzEKcy5hKz1ILkx3KDkyKQpzLmErPUguTHcoMTE3KQpzLmErPUguTHco
+MTAwKQpvPXA+Pj44JjE1CnMuYSs9SC5MdyhvPDEwPzQ4K286ODcrbykKbz1wPj4+NCYxNQpzLmErPUgu
+THcobzwxMD80OCtvOjg3K28pCm89cCYxNQpzLmErPUguTHcobzwxMD80OCtvOjg3K28pfX1jb250aW51
+ZX1pZihwPDMyKXtpZihxPnIpcy5hKz1DLnhCLk5qKGEscixxKQpyPXErMQpzLmErPUguTHcoOTIpCnN3
+aXRjaChwKXtjYXNlIDg6cy5hKz1ILkx3KDk4KQpicmVhawpjYXNlIDk6cy5hKz1ILkx3KDExNikKYnJl
+YWsKY2FzZSAxMDpzLmErPUguTHcoMTEwKQpicmVhawpjYXNlIDEyOnMuYSs9SC5MdygxMDIpCmJyZWFr
+CmNhc2UgMTM6cy5hKz1ILkx3KDExNCkKYnJlYWsKZGVmYXVsdDpzLmErPUguTHcoMTE3KQpzLmErPUgu
+THcoNDgpCnMuYSs9SC5Mdyg0OCkKbz1wPj4+NCYxNQpzLmErPUguTHcobzwxMD80OCtvOjg3K28pCm89
+cCYxNQpzLmErPUguTHcobzwxMD80OCtvOjg3K28pCmJyZWFrfX1lbHNlIGlmKHA9PT0zNHx8cD09PTky
+KXtpZihxPnIpcy5hKz1DLnhCLk5qKGEscixxKQpyPXErMQpzLmErPUguTHcoOTIpCnMuYSs9SC5Mdyhw
+KX19aWYocj09PTApcy5hKz1ILkVqKGEpCmVsc2UgaWYocjxtKXMuYSs9dC5OaihhLHIsbSl9LApKbjpm
+dW5jdGlvbihhKXt2YXIgdCxzLHIscQpmb3IodD10aGlzLmEscz10Lmxlbmd0aCxyPTA7cjxzOysrcil7
+cT10W3JdCmlmKGE9PW51bGw/cT09bnVsbDphPT09cSl0aHJvdyBILmIobmV3IFAuSzgoYSxudWxsKSl9
+Qy5ObS5pKHQsYSl9LAppVTpmdW5jdGlvbihhKXt2YXIgdCxzLHIscSxwPXRoaXMKaWYocC50TShhKSly
+ZXR1cm4KcC5KbihhKQp0cnl7dD1wLmIuJDEoYSkKaWYoIXAudE0odCkpe3I9UC5HeShhLG51bGwscC5n
+VksoKSkKdGhyb3cgSC5iKHIpfXI9cC5hCmlmKDA+PXIubGVuZ3RoKXJldHVybiBILk9IKHIsLTEpCnIu
+cG9wKCl9Y2F0Y2gocSl7cz1ILlJ1KHEpCnI9UC5HeShhLHMscC5nVksoKSkKdGhyb3cgSC5iKHIpfX0s
+CnRNOmZ1bmN0aW9uKGEpe3ZhciB0LHMscj10aGlzCmlmKHR5cGVvZiBhPT0ibnVtYmVyIil7aWYoIWlz
+RmluaXRlKGEpKXJldHVybiExCnIuYy5hKz1DLkNELncoYSkKcmV0dXJuITB9ZWxzZSBpZihhPT09ITAp
+e3IuYy5hKz0idHJ1ZSIKcmV0dXJuITB9ZWxzZSBpZihhPT09ITEpe3IuYy5hKz0iZmFsc2UiCnJldHVy
+biEwfWVsc2UgaWYoYT09bnVsbCl7ci5jLmErPSJudWxsIgpyZXR1cm4hMH1lbHNlIGlmKHR5cGVvZiBh
+PT0ic3RyaW5nIil7dD1yLmMKdC5hKz0nIicKci52cChhKQp0LmErPSciJwpyZXR1cm4hMH1lbHNlIGlm
+KHUuYi5iKGEpKXtyLkpuKGEpCnIubEsoYSkKdD1yLmEKaWYoMD49dC5sZW5ndGgpcmV0dXJuIEguT0go
+dCwtMSkKdC5wb3AoKQpyZXR1cm4hMH1lbHNlIGlmKHUuZi5iKGEpKXtyLkpuKGEpCnM9ci5qdyhhKQp0
+PXIuYQppZigwPj10Lmxlbmd0aClyZXR1cm4gSC5PSCh0LC0xKQp0LnBvcCgpCnJldHVybiBzfWVsc2Ug
+cmV0dXJuITF9LApsSzpmdW5jdGlvbihhKXt2YXIgdCxzLHI9dGhpcy5jCnIuYSs9IlsiCnQ9Si5VNihh
+KQppZih0LmdvcihhKSl7dGhpcy5pVSh0LnEoYSwwKSkKZm9yKHM9MTtzPHQuZ0EoYSk7KytzKXtyLmEr
+PSIsIgp0aGlzLmlVKHQucShhLHMpKX19ci5hKz0iXSJ9LApqdzpmdW5jdGlvbihhKXt2YXIgdCxzLHIs
+cSxwLG89dGhpcyxuPXt9CmlmKGEuZ2wwKGEpKXtvLmMuYSs9Int9IgpyZXR1cm4hMH10PVAuTzgoYS5n
+QShhKSoyLG51bGwsITEsdS5XKQpzPW4uYT0wCm4uYj0hMAphLksoMCxuZXcgUC50aShuLHQpKQppZigh
+bi5iKXJldHVybiExCnI9by5jCnIuYSs9InsiCmZvcihxPSciJztzPHQubGVuZ3RoO3MrPTIscT0nLCIn
+KXtyLmErPXEKby52cChILmgodFtzXSkpCnIuYSs9JyI6JwpwPXMrMQppZihwPj10Lmxlbmd0aClyZXR1
+cm4gSC5PSCh0LHApCm8uaVUodFtwXSl9ci5hKz0ifSIKcmV0dXJuITB9fQpQLnRpLnByb3RvdHlwZT17
+CiQyOmZ1bmN0aW9uKGEsYil7dmFyIHQscwppZih0eXBlb2YgYSE9InN0cmluZyIpdGhpcy5hLmI9ITEK
+dD10aGlzLmIKcz10aGlzLmEKQy5ObS5ZKHQscy5hKyssYSkKQy5ObS5ZKHQscy5hKyssYil9LAokUzox
+MX0KUC50dS5wcm90b3R5cGU9ewpnVks6ZnVuY3Rpb24oKXt2YXIgdD10aGlzLmMuYQpyZXR1cm4gdC5j
+aGFyQ29kZUF0KDApPT0wP3Q6dH19ClAudTUucHJvdG90eXBlPXsKZ1pFOmZ1bmN0aW9uKCl7cmV0dXJu
+IEMuUWt9fQpQLkUzLnByb3RvdHlwZT17CldKOmZ1bmN0aW9uKGEpe3ZhciB0LHMscj1QLmpCKDAsbnVs
+bCxhLmxlbmd0aCkscT1yLTAKaWYocT09PTApcmV0dXJuIG5ldyBVaW50OEFycmF5KDApCnQ9bmV3IFVp
+bnQ4QXJyYXkocSozKQpzPW5ldyBQLlJ3KHQpCmlmKHMuR3goYSwwLHIpIT09cil7Si5hNihhLHItMSkK
+cy5STygpfXJldHVybiBuZXcgVWludDhBcnJheSh0LnN1YmFycmF5KDAsSC5yTSgwLHMuYix0Lmxlbmd0
+aCkpKX19ClAuUncucHJvdG90eXBlPXsKUk86ZnVuY3Rpb24oKXt2YXIgdD10aGlzLHM9dC5jLHI9dC5i
+LHE9dC5iPXIrMSxwPXMubGVuZ3RoCmlmKHI+PXApcmV0dXJuIEguT0gocyxyKQpzW3JdPTIzOQpyPXQu
+Yj1xKzEKaWYocT49cClyZXR1cm4gSC5PSChzLHEpCnNbcV09MTkxCnQuYj1yKzEKaWYocj49cClyZXR1
+cm4gSC5PSChzLHIpCnNbcl09MTg5fSwKTzY6ZnVuY3Rpb24oYSxiKXt2YXIgdCxzLHIscSxwLG89dGhp
+cwppZigoYiY2NDUxMik9PT01NjMyMCl7dD02NTUzNisoKGEmMTAyMyk8PDEwKXxiJjEwMjMKcz1vLmMK
+cj1vLmIKcT1vLmI9cisxCnA9cy5sZW5ndGgKaWYocj49cClyZXR1cm4gSC5PSChzLHIpCnNbcl09MjQw
+fHQ+Pj4xOApyPW8uYj1xKzEKaWYocT49cClyZXR1cm4gSC5PSChzLHEpCnNbcV09MTI4fHQ+Pj4xMiY2
+MwpxPW8uYj1yKzEKaWYocj49cClyZXR1cm4gSC5PSChzLHIpCnNbcl09MTI4fHQ+Pj42JjYzCm8uYj1x
+KzEKaWYocT49cClyZXR1cm4gSC5PSChzLHEpCnNbcV09MTI4fHQmNjMKcmV0dXJuITB9ZWxzZXtvLlJP
+KCkKcmV0dXJuITF9fSwKR3g6ZnVuY3Rpb24oYSxiLGMpe3ZhciB0LHMscixxLHAsbyxuLG09dGhpcwpp
+ZihiIT09YyYmKEMueEIubShhLGMtMSkmNjQ1MTIpPT09NTUyOTYpLS1jCmZvcih0PW0uYyxzPXQubGVu
+Z3RoLHI9YjtyPGM7KytyKXtxPUMueEIuVyhhLHIpCmlmKHE8PTEyNyl7cD1tLmIKaWYocD49cylicmVh
+awptLmI9cCsxCnRbcF09cX1lbHNle3A9cSY2NDUxMgppZihwPT09NTUyOTYpe2lmKG0uYis0PnMpYnJl
+YWsKbz1yKzEKaWYobS5PNihxLEMueEIuVyhhLG8pKSlyPW99ZWxzZSBpZihwPT09NTYzMjApe2lmKG0u
+YiszPnMpYnJlYWsKbS5STygpfWVsc2UgaWYocTw9MjA0Nyl7cD1tLmIKbj1wKzEKaWYobj49cylicmVh
+awptLmI9bgppZihwPj1zKXJldHVybiBILk9IKHQscCkKdFtwXT0xOTJ8cT4+PjYKbS5iPW4rMQp0W25d
+PTEyOHxxJjYzfWVsc2V7cD1tLmIKaWYocCsyPj1zKWJyZWFrCm49bS5iPXArMQppZihwPj1zKXJldHVy
+biBILk9IKHQscCkKdFtwXT0yMjR8cT4+PjEyCnA9bS5iPW4rMQppZihuPj1zKXJldHVybiBILk9IKHQs
+bikKdFtuXT0xMjh8cT4+PjYmNjMKbS5iPXArMQppZihwPj1zKXJldHVybiBILk9IKHQscCkKdFtwXT0x
+Mjh8cSY2M319fXJldHVybiByfX0KUC5HWS5wcm90b3R5cGU9ewpXSjpmdW5jdGlvbihhKXt2YXIgdCxz
+CnUuTC5hKGEpCnQ9dGhpcy5hCnM9UC5reSh0LGEsMCxudWxsKQppZihzIT1udWxsKXJldHVybiBzCnJl
+dHVybiBuZXcgUC5ieih0KS5OZShhLDAsbnVsbCwhMCl9fQpQLmJ6LnByb3RvdHlwZT17Ck5lOmZ1bmN0
+aW9uKGEsYixjLGQpe3ZhciB0LHMscixxLHAsbz10aGlzCnUuTC5hKGEpCnQ9UC5qQihiLGMsSi5IbShh
+KSkKaWYoYj09PXQpcmV0dXJuIiIKcz1QLmp5KGEsYix0KQpyPW8uaE8ocywwLHQtYiwhMCkKcT1vLmIK
+aWYoKHEmMSkhPT0wKXtwPVAuajQocSkKby5iPTAKdGhyb3cgSC5iKFAucnIocCxhLGIrby5jKSl9cmV0
+dXJuIHJ9LApoTzpmdW5jdGlvbihhLGIsYyxkKXt2YXIgdCxzLHI9dGhpcwppZihjLWI+MTAwMCl7dD1D
+LmpuLkJVKGIrYywyKQpzPXIuaE8oYSxiLHQsITEpCmlmKChyLmImMSkhPT0wKXJldHVybiBzCnJldHVy
+biBzK3IuaE8oYSx0LGMsZCl9cmV0dXJuIHIuRWgoYSxiLGMsZCl9LApFaDpmdW5jdGlvbihhLGIsYyxk
+KXt2YXIgdCxzLHIscSxwLG8sbixtLGw9dGhpcyxrPTY1NTMzLGo9bC5iLGk9bC5jLGg9bmV3IFAuUm4o
+IiIpLGc9YisxLGY9YS5sZW5ndGgKaWYoYjwwfHxiPj1mKXJldHVybiBILk9IKGEsYikKdD1hW2JdCiRs
+YWJlbDAkMDpmb3Iocz1sLmE7ITA7KXtmb3IoOyEwO2c9cCl7cj1DLnhCLlcoIkFBQUFBQUFBQUFBQUFB
+QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFB
+QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBRkZGRkZG
+RkZGRkZGRkZGRkdHR0dHR0dHR0dHR0dHR0dISEhISEhISEhISEhISEhISEhISEhISEhISEhJSEhISkVF
+QkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCS0NDQ0NDQ0NDQ0NDQ0RDTE9OTk5NRUVFRUVFRUVF
+RUUiLHQpJjMxCmk9ajw9MzI/dCY2MTY5ND4+PnI6KHQmNjN8aTw8Nik+Pj4wCmo9Qy54Qi5XKCIgXHgw
+MDA6WEVDQ0NDQ046bERiIFx4MDAwOlhFQ0NDQ0NOdmxEYiBceDAwMDpYRUNDQ0NDTjpsRGIgQUFBQUFc
+eDAwXHgwMFx4MDBceDAwXHgwMEFBQUFBMDAwMDBBQUFBQTo6Ojo6QUFBQUFHRzAwMEFBQUFBMDBLS0tB
+QUFBQUc6Ojo6QUFBQUE6SUlJSUFBQUFBMDAwXHg4MDBBQUFBQVx4MDBceDAwXHgwMFx4MDAgQUFBQUEi
+LGorcikKaWYoaj09PTApe2guYSs9SC5MdyhpKQppZihnPT09YylicmVhayAkbGFiZWwwJDAKYnJlYWt9
+ZWxzZSBpZigoaiYxKSE9PTApe2lmKHMpc3dpdGNoKGope2Nhc2UgNjk6Y2FzZSA2NzpoLmErPUguTHco
+aykKYnJlYWsKY2FzZSA2NTpoLmErPUguTHcoayk7LS1nCmJyZWFrCmRlZmF1bHQ6cT1oLmErPUguTHco
+aykKaC5hPXErSC5MdyhrKQpicmVha31lbHNle2wuYj1qCmwuYz1nLTEKcmV0dXJuIiJ9aj0wfWlmKGc9
+PT1jKWJyZWFrICRsYWJlbDAkMApwPWcrMQppZihnPDB8fGc+PWYpcmV0dXJuIEguT0goYSxnKQp0PWFb
+Z119cD1nKzEKaWYoZzwwfHxnPj1mKXJldHVybiBILk9IKGEsZykKdD1hW2ddCmlmKHQ8MTI4KXt3aGls
+ZSghMCl7aWYoIShwPGMpKXtvPWMKYnJlYWt9bj1wKzEKaWYocDwwfHxwPj1mKXJldHVybiBILk9IKGEs
+cCkKdD1hW3BdCmlmKHQ+PTEyOCl7bz1uLTEKcD1uCmJyZWFrfXA9bn1pZihvLWc8MjApZm9yKG09Zztt
+PG87KyttKXtpZihtPj1mKXJldHVybiBILk9IKGEsbSkKaC5hKz1ILkx3KGFbbV0pfWVsc2UgaC5hKz1Q
+LkhNKGEsZyxvKQppZihvPT09YylicmVhayAkbGFiZWwwJDAKZz1wfWVsc2UgZz1wfWlmKGQmJmo+MzIp
+aWYocyloLmErPUguTHcoaykKZWxzZXtsLmI9NzcKbC5jPWMKcmV0dXJuIiJ9bC5iPWoKbC5jPWkKZj1o
+LmEKcmV0dXJuIGYuY2hhckNvZGVBdCgwKT09MD9mOmZ9fQpQLldGLnByb3RvdHlwZT17CiQyOmZ1bmN0
+aW9uKGEsYil7dmFyIHQscyxyCnUuZm8uYShhKQp0PXRoaXMuYgpzPXRoaXMuYQp0LmErPXMuYQpyPXQu
+YSs9SC5FaihhLmEpCnQuYT1yKyI6ICIKdC5hKz1QLnAoYikKcy5hPSIsICJ9LAokUzo0M30KUC5hMi5w
+cm90b3R5cGU9e30KUC5pUC5wcm90b3R5cGU9ewpETjpmdW5jdGlvbihhLGIpe2lmKGI9PW51bGwpcmV0
+dXJuITEKcmV0dXJuIGIgaW5zdGFuY2VvZiBQLmlQJiZ0aGlzLmE9PT1iLmEmJiEwfSwKZ2lPOmZ1bmN0
+aW9uKGEpe3ZhciB0PXRoaXMuYQpyZXR1cm4odF5DLmpuLndHKHQsMzApKSYxMDczNzQxODIzfSwKdzpm
+dW5jdGlvbihhKXt2YXIgdD10aGlzLHM9UC5HcShILnRKKHQpKSxyPVAuaDAoSC5OUyh0KSkscT1QLmgw
+KEguakEodCkpLHA9UC5oMChILklYKHQpKSxvPVAuaDAoSC5jaCh0KSksbj1QLmgwKEguSmQodCkpLG09
+UC5WeChILm8xKHQpKSxsPXMrIi0iK3IrIi0iK3ErIiAiK3ArIjoiK28rIjoiK24rIi4iK20KcmV0dXJu
+IGx9fQpQLkNQLnByb3RvdHlwZT17fQpQLlhTLnByb3RvdHlwZT17CmdJSTpmdW5jdGlvbigpe3JldHVy
+biBILnRzKHRoaXMuJHRocm93bkpzRXJyb3IpfX0KUC5DNi5wcm90b3R5cGU9ewp3OmZ1bmN0aW9uKGEp
+e3ZhciB0PXRoaXMuYQppZih0IT1udWxsKXJldHVybiJBc3NlcnRpb24gZmFpbGVkOiAiK1AucCh0KQpy
+ZXR1cm4iQXNzZXJ0aW9uIGZhaWxlZCJ9fQpQLm4ucHJvdG90eXBlPXsKdzpmdW5jdGlvbihhKXtyZXR1
+cm4iVGhyb3cgb2YgbnVsbC4ifX0KUC51LnByb3RvdHlwZT17CmdaOmZ1bmN0aW9uKCl7cmV0dXJuIklu
+dmFsaWQgYXJndW1lbnQiKyghdGhpcy5hPyIocykiOiIiKX0sCmd1OmZ1bmN0aW9uKCl7cmV0dXJuIiJ9
+LAp3OmZ1bmN0aW9uKGEpe3ZhciB0LHMscj10aGlzLHE9ci5jLHA9cT09bnVsbD8iIjoiICgiK3ErIiki
+LG89ci5kLG49bz09bnVsbD8iIjoiOiAiK0guRWoobyksbT1yLmdaKCkrcCtuCmlmKCFyLmEpcmV0dXJu
+IG0KdD1yLmd1KCkKcz1QLnAoci5iKQpyZXR1cm4gbSt0KyI6ICIrc319ClAuYkoucHJvdG90eXBlPXsK
+Z1o6ZnVuY3Rpb24oKXtyZXR1cm4iUmFuZ2VFcnJvciJ9LApndTpmdW5jdGlvbigpe3ZhciB0LHM9dGhp
+cy5lLHI9dGhpcy5mCmlmKHM9PW51bGwpdD1yIT1udWxsPyI6IE5vdCBsZXNzIHRoYW4gb3IgZXF1YWwg
+dG8gIitILkVqKHIpOiIiCmVsc2UgaWYocj09bnVsbCl0PSI6IE5vdCBncmVhdGVyIHRoYW4gb3IgZXF1
+YWwgdG8gIitILkVqKHMpCmVsc2UgaWYocj5zKXQ9IjogTm90IGluIGluY2x1c2l2ZSByYW5nZSAiK0gu
+RWoocykrIi4uIitILkVqKHIpCmVsc2UgdD1yPHM/IjogVmFsaWQgdmFsdWUgcmFuZ2UgaXMgZW1wdHki
+OiI6IE9ubHkgdmFsaWQgdmFsdWUgaXMgIitILkVqKHMpCnJldHVybiB0fX0KUC5lWS5wcm90b3R5cGU9
+ewpnWjpmdW5jdGlvbigpe3JldHVybiJSYW5nZUVycm9yIn0sCmd1OmZ1bmN0aW9uKCl7dmFyIHQscz1I
+LnVQKHRoaXMuYikKaWYodHlwZW9mIHMhPT0ibnVtYmVyIilyZXR1cm4gcy5KKCkKaWYoczwwKXJldHVy
+biI6IGluZGV4IG11c3Qgbm90IGJlIG5lZ2F0aXZlIgp0PXRoaXMuZgppZih0PT09MClyZXR1cm4iOiBu
+byBpbmRpY2VzIGFyZSB2YWxpZCIKcmV0dXJuIjogaW5kZXggc2hvdWxkIGJlIGxlc3MgdGhhbiAiK0gu
+RWoodCl9LApnQTpmdW5jdGlvbihhKXtyZXR1cm4gdGhpcy5mfX0KUC5tcC5wcm90b3R5cGU9ewp3OmZ1
+bmN0aW9uKGEpe3ZhciB0LHMscixxLHAsbyxuLG0sbD10aGlzLGs9e30saj1uZXcgUC5SbigiIikKay5h
+PSIiCnQ9bC5jCmZvcihzPXQubGVuZ3RoLHI9MCxxPSIiLHA9IiI7cjxzOysrcixwPSIsICIpe289dFty
+XQpqLmE9cStwCnE9ai5hKz1QLnAobykKay5hPSIsICJ9bC5kLksoMCxuZXcgUC5XRihrLGopKQpuPVAu
+cChsLmEpCm09ai53KDApCnM9Ik5vU3VjaE1ldGhvZEVycm9yOiBtZXRob2Qgbm90IGZvdW5kOiAnIitI
+LkVqKGwuYi5hKSsiJ1xuUmVjZWl2ZXI6ICIrbisiXG5Bcmd1bWVudHM6IFsiK20rIl0iCnJldHVybiBz
+fX0KUC51Yi5wcm90b3R5cGU9ewp3OmZ1bmN0aW9uKGEpe3JldHVybiJVbnN1cHBvcnRlZCBvcGVyYXRp
+b246ICIrdGhpcy5hfX0KUC5kcy5wcm90b3R5cGU9ewp3OmZ1bmN0aW9uKGEpe3ZhciB0PXRoaXMuYQpy
+ZXR1cm4gdCE9bnVsbD8iVW5pbXBsZW1lbnRlZEVycm9yOiAiK3Q6IlVuaW1wbGVtZW50ZWRFcnJvciJ9
+fQpQLmxqLnByb3RvdHlwZT17Cnc6ZnVuY3Rpb24oYSl7cmV0dXJuIkJhZCBzdGF0ZTogIit0aGlzLmF9
+fQpQLlVWLnByb3RvdHlwZT17Cnc6ZnVuY3Rpb24oYSl7dmFyIHQ9dGhpcy5hCmlmKHQ9PW51bGwpcmV0
+dXJuIkNvbmN1cnJlbnQgbW9kaWZpY2F0aW9uIGR1cmluZyBpdGVyYXRpb24uIgpyZXR1cm4iQ29uY3Vy
+cmVudCBtb2RpZmljYXRpb24gZHVyaW5nIGl0ZXJhdGlvbjogIitQLnAodCkrIi4ifX0KUC5rNS5wcm90
+b3R5cGU9ewp3OmZ1bmN0aW9uKGEpe3JldHVybiJPdXQgb2YgTWVtb3J5In0sCmdJSTpmdW5jdGlvbigp
+e3JldHVybiBudWxsfSwKJGlYUzoxfQpQLktZLnByb3RvdHlwZT17Cnc6ZnVuY3Rpb24oYSl7cmV0dXJu
+IlN0YWNrIE92ZXJmbG93In0sCmdJSTpmdW5jdGlvbigpe3JldHVybiBudWxsfSwKJGlYUzoxfQpQLmMu
+cHJvdG90eXBlPXsKdzpmdW5jdGlvbihhKXt2YXIgdD10aGlzLmEKcmV0dXJuIHQ9PW51bGw/IlJlYWRp
+bmcgc3RhdGljIHZhcmlhYmxlIGR1cmluZyBpdHMgaW5pdGlhbGl6YXRpb24iOiJSZWFkaW5nIHN0YXRp
+YyB2YXJpYWJsZSAnIit0KyInIGR1cmluZyBpdHMgaW5pdGlhbGl6YXRpb24ifX0KUC5DRC5wcm90b3R5
+cGU9ewp3OmZ1bmN0aW9uKGEpe3JldHVybiJFeGNlcHRpb246ICIrdGhpcy5hfSwKJGlSejoxfQpQLmFF
+LnByb3RvdHlwZT17Cnc6ZnVuY3Rpb24oYSl7dmFyIHQscyxyLHEscCxvLG4sbSxsLGssaixpLGg9dGhp
+cy5hLGc9aCE9bnVsbCYmIiIhPT1oPyJGb3JtYXRFeGNlcHRpb246ICIrSC5FaihoKToiRm9ybWF0RXhj
+ZXB0aW9uIixmPXRoaXMuYyxlPXRoaXMuYgppZih0eXBlb2YgZT09InN0cmluZyIpe2lmKGYhPW51bGwp
+dD1mPDB8fGY+ZS5sZW5ndGgKZWxzZSB0PSExCmlmKHQpZj1udWxsCmlmKGY9PW51bGwpe2lmKGUubGVu
+Z3RoPjc4KWU9Qy54Qi5OaihlLDAsNzUpKyIuLi4iCnJldHVybiBnKyJcbiIrZX1mb3Iocz0xLHI9MCxx
+PSExLHA9MDtwPGY7KytwKXtvPUMueEIuVyhlLHApCmlmKG89PT0xMCl7aWYociE9PXB8fCFxKSsrcwpy
+PXArMQpxPSExfWVsc2UgaWYobz09PTEzKXsrK3MKcj1wKzEKcT0hMH19Zz1zPjE/ZysoIiAoYXQgbGlu
+ZSAiK3MrIiwgY2hhcmFjdGVyICIrKGYtcisxKSsiKVxuIik6ZysoIiAoYXQgY2hhcmFjdGVyICIrKGYr
+MSkrIilcbiIpCm49ZS5sZW5ndGgKZm9yKHA9ZjtwPG47KytwKXtvPUMueEIubShlLHApCmlmKG89PT0x
+MHx8bz09PTEzKXtuPXAKYnJlYWt9fWlmKG4tcj43OClpZihmLXI8NzUpe209cis3NQpsPXIKaz0iIgpq
+PSIuLi4ifWVsc2V7aWYobi1mPDc1KXtsPW4tNzUKbT1uCmo9IiJ9ZWxzZXtsPWYtMzYKbT1mKzM2Cmo9
+Ii4uLiJ9az0iLi4uIn1lbHNle209bgpsPXIKaz0iIgpqPSIifWk9Qy54Qi5OaihlLGwsbSkKcmV0dXJu
+IGcraytpK2orIlxuIitDLnhCLkl4KCIgIixmLWwray5sZW5ndGgpKyJeXG4ifWVsc2UgcmV0dXJuIGYh
+PW51bGw/ZysoIiAoYXQgb2Zmc2V0ICIrSC5FaihmKSsiKSIpOmd9LAokaVJ6OjF9ClAuRUgucHJvdG90
+eXBlPXt9ClAuSWYucHJvdG90eXBlPXt9ClAuY1gucHJvdG90eXBlPXsKRTI6ZnVuY3Rpb24oYSxiLGMp
+e3ZhciB0PUguTGgodGhpcykKcmV0dXJuIEguSzEodGhpcyx0LktxKGMpLkMoIjEoY1guRSkiKS5hKGIp
+LHQuQygiY1guRSIpLGMpfSwKZXY6ZnVuY3Rpb24oYSxiKXt2YXIgdD1ILkxoKHRoaXMpCnJldHVybiBu
+ZXcgSC5VNSh0aGlzLHQuQygiYTIoY1guRSkiKS5hKGIpLHQuQygiVTU8Y1guRT4iKSl9LApnQTpmdW5j
+dGlvbihhKXt2YXIgdCxzPXRoaXMuZ2t6KHRoaXMpCmZvcih0PTA7cy5GKCk7KSsrdApyZXR1cm4gdH0s
+CmdsMDpmdW5jdGlvbihhKXtyZXR1cm4hdGhpcy5na3oodGhpcykuRigpfSwKZ3I4OmZ1bmN0aW9uKGEp
+e3ZhciB0LHM9dGhpcy5na3oodGhpcykKaWYoIXMuRigpKXRocm93IEguYihILldwKCkpCnQ9cy5nbCgp
+CmlmKHMuRigpKXRocm93IEguYihILkFtKCkpCnJldHVybiB0fSwKRTpmdW5jdGlvbihhLGIpe3ZhciB0
+LHMscgpQLmsxKGIsImluZGV4IikKZm9yKHQ9dGhpcy5na3oodGhpcykscz0wO3QuRigpOyl7cj10Lmds
+KCkKaWYoYj09PXMpcmV0dXJuIHI7KytzfXRocm93IEguYihQLkNmKGIsdGhpcywiaW5kZXgiLG51bGws
+cykpfSwKdzpmdW5jdGlvbihhKXtyZXR1cm4gUC5FUCh0aGlzLCIoIiwiKSIpfX0KUC5Bbi5wcm90b3R5
+cGU9e30KUC56TS5wcm90b3R5cGU9eyRpYlE6MSwkaWNYOjF9ClAuWjAucHJvdG90eXBlPXt9ClAuTjMu
+cHJvdG90eXBlPXsKdzpmdW5jdGlvbihhKXtyZXR1cm4iTWFwRW50cnkoIitILkVqKEouaih0aGlzLmEp
+KSsiOiAiK0guRWooSi5qKHRoaXMuYikpKyIpIn19ClAuYzgucHJvdG90eXBlPXsKZ2lPOmZ1bmN0aW9u
+KGEpe3JldHVybiBQLk1oLnByb3RvdHlwZS5naU8uY2FsbChDLmpOLHRoaXMpfSwKdzpmdW5jdGlvbihh
+KXtyZXR1cm4ibnVsbCJ9fQpQLmxmLnByb3RvdHlwZT17fQpQLk1oLnByb3RvdHlwZT17Y29uc3RydWN0
+b3I6UC5NaCwkaU1oOjEsCkROOmZ1bmN0aW9uKGEsYil7cmV0dXJuIHRoaXM9PT1ifSwKZ2lPOmZ1bmN0
+aW9uKGEpe3JldHVybiBILmVRKHRoaXMpfSwKdzpmdW5jdGlvbihhKXtyZXR1cm4iSW5zdGFuY2Ugb2Yg
+JyIrSC5FaihILk0odGhpcykpKyInIn0sCmU3OmZ1bmN0aW9uKGEsYil7dS5vLmEoYikKdGhyb3cgSC5i
+KFAubHIodGhpcyxiLmdXYSgpLGIuZ25kKCksYi5nVm0oKSkpfSwKdG9TdHJpbmc6ZnVuY3Rpb24oKXty
+ZXR1cm4gdGhpcy53KHRoaXMpfX0KUC5PZC5wcm90b3R5cGU9e30KUC5pYi5wcm90b3R5cGU9eyRpT2Q6
+MX0KUC54dS5wcm90b3R5cGU9e30KUC5Hei5wcm90b3R5cGU9e30KUC5aZC5wcm90b3R5cGU9ewp3OmZ1
+bmN0aW9uKGEpe3JldHVybiIifSwKJGlHejoxfQpQLnFVLnByb3RvdHlwZT17JGl2WDoxfQpQLlJuLnBy
+b3RvdHlwZT17CmdBOmZ1bmN0aW9uKGEpe3JldHVybiB0aGlzLmEubGVuZ3RofSwKdzpmdW5jdGlvbihh
+KXt2YXIgdD10aGlzLmEKcmV0dXJuIHQuY2hhckNvZGVBdCgwKT09MD90OnR9LAokaUJMOjF9ClAuR0Qu
+cHJvdG90eXBlPXt9ClAubjEucHJvdG90eXBlPXsKJDI6ZnVuY3Rpb24oYSxiKXt2YXIgdCxzLHIscQp1
+LkouYShhKQpILmgoYikKdD1KLnJZKGIpLk9ZKGIsIj0iKQppZih0PT09LTEpe2lmKGIhPT0iIilhLlko
+MCxQLmt1KGIsMCxiLmxlbmd0aCx0aGlzLmEsITApLCIiKX1lbHNlIGlmKHQhPT0wKXtzPUMueEIuTmoo
+YiwwLHQpCnI9Qy54Qi5HKGIsdCsxKQpxPXRoaXMuYQphLlkoMCxQLmt1KHMsMCxzLmxlbmd0aCxxLCEw
+KSxQLmt1KHIsMCxyLmxlbmd0aCxxLCEwKSl9cmV0dXJuIGF9LAokUzoyMX0KUC5jUy5wcm90b3R5cGU9
+ewokMjpmdW5jdGlvbihhLGIpe3Rocm93IEguYihQLnJyKCJJbGxlZ2FsIElQdjQgYWRkcmVzcywgIith
+LHRoaXMuYSxiKSl9LAokUzo0N30KUC5WQy5wcm90b3R5cGU9ewokMjpmdW5jdGlvbihhLGIpe3Rocm93
+IEguYihQLnJyKCJJbGxlZ2FsIElQdjYgYWRkcmVzcywgIithLHRoaXMuYSxiKSl9LAokMTpmdW5jdGlv
+bihhKXtyZXR1cm4gdGhpcy4kMihhLG51bGwpfSwKJFM6NDh9ClAuSlQucHJvdG90eXBlPXsKJDI6ZnVu
+Y3Rpb24oYSxiKXt2YXIgdAppZihiLWE+NCl0aGlzLmEuJDIoImFuIElQdjYgcGFydCBjYW4gb25seSBj
+b250YWluIGEgbWF4aW11bSBvZiA0IGhleCBkaWdpdHMiLGEpCnQ9UC5RQShDLnhCLk5qKHRoaXMuYixh
+LGIpLDE2KQppZih0eXBlb2YgdCE9PSJudW1iZXIiKXJldHVybiB0LkooKQppZih0PDB8fHQ+NjU1MzUp
+dGhpcy5hLiQyKCJlYWNoIHBhcnQgbXVzdCBiZSBpbiB0aGUgcmFuZ2Ugb2YgYDB4MC4uMHhGRkZGYCIs
+YSkKcmV0dXJuIHR9LAokUzo1MH0KUC5Ebi5wcm90b3R5cGU9ewpnbkQ6ZnVuY3Rpb24oKXt2YXIgdCxz
+LHIscT10aGlzLHA9cS54CmlmKHA9PW51bGwpe3A9cS5hCnQ9cC5sZW5ndGghPT0wP3ArIjoiOiIiCnM9
+cS5jCnI9cz09bnVsbAppZighcnx8cD09PSJmaWxlIil7cD10KyIvLyIKdD1xLmIKaWYodC5sZW5ndGgh
+PT0wKXA9cCt0KyJAIgppZighcilwKz1zCnQ9cS5kCmlmKHQhPW51bGwpcD1wKyI6IitILkVqKHQpfWVs
+c2UgcD10CnArPXEuZQp0PXEuZgppZih0IT1udWxsKXA9cCsiPyIrdAp0PXEucgppZih0IT1udWxsKXA9
+cCsiIyIrdApwPXAuY2hhckNvZGVBdCgwKT09MD9wOnAKaWYocS54PT1udWxsKXEueD1wCmVsc2UgcD1I
+LnZoKEgueVIoIkZpZWxkICdfdGV4dCcgaGFzIGJlZW4gYXNzaWduZWQgZHVyaW5nIGluaXRpYWxpemF0
+aW9uLiIpKX1yZXR1cm4gcH0sCmdGajpmdW5jdGlvbigpe3ZhciB0LHM9dGhpcyxyPXMueQppZihyPT1u
+dWxsKXt0PXMuZQppZih0Lmxlbmd0aCE9PTAmJkMueEIuVyh0LDApPT09NDcpdD1DLnhCLkcodCwxKQpy
+PXQubGVuZ3RoPT09MD9DLnhEOlAuQUYobmV3IEgubEooSC5WTSh0LnNwbGl0KCIvIiksdS5zKSx1LmRP
+LmEoUC5QSCgpKSx1LmRvKSx1Lk4pCmlmKHMueT09bnVsbClzLnNLcChyKQplbHNlIHI9SC52aChILnlS
+KCJGaWVsZCAncGF0aFNlZ21lbnRzJyBoYXMgYmVlbiBhc3NpZ25lZCBkdXJpbmcgaW5pdGlhbGl6YXRp
+b24uIikpfXJldHVybiByfSwKZ2lPOmZ1bmN0aW9uKGEpe3ZhciB0PXRoaXMscz10LnoKaWYocz09bnVs
+bCl7cz1DLnhCLmdpTyh0LmduRCgpKQppZih0Lno9PW51bGwpdC56PXMKZWxzZSBzPUgudmgoSC55Uigi
+RmllbGQgJ2hhc2hDb2RlJyBoYXMgYmVlbiBhc3NpZ25lZCBkdXJpbmcgaW5pdGlhbGl6YXRpb24uIikp
+fXJldHVybiBzfSwKZ2hZOmZ1bmN0aW9uKCl7dmFyIHQ9dGhpcyxzPXQuUQppZihzPT1udWxsKXtzPW5l
+dyBQLkdqKFAuV1godC5ndFAoKSksdS5FKQppZih0LlE9PW51bGwpdC5zTk0ocykKZWxzZSBzPUgudmgo
+SC55UigiRmllbGQgJ3F1ZXJ5UGFyYW1ldGVycycgaGFzIGJlZW4gYXNzaWduZWQgZHVyaW5nIGluaXRp
+YWxpemF0aW9uLiIpKX1yZXR1cm4gc30sCmdrdTpmdW5jdGlvbigpe3JldHVybiB0aGlzLmJ9LApnSmY6
+ZnVuY3Rpb24oYSl7dmFyIHQ9dGhpcy5jCmlmKHQ9PW51bGwpcmV0dXJuIiIKaWYoQy54Qi5uKHQsIlsi
+KSlyZXR1cm4gQy54Qi5Oaih0LDEsdC5sZW5ndGgtMSkKcmV0dXJuIHR9LApndHA6ZnVuY3Rpb24oYSl7
+dmFyIHQ9dGhpcy5kCnJldHVybiB0PT1udWxsP1Aud0sodGhpcy5hKTp0fSwKZ3RQOmZ1bmN0aW9uKCl7
+dmFyIHQ9dGhpcy5mCnJldHVybiB0PT1udWxsPyIiOnR9LApnS2E6ZnVuY3Rpb24oKXt2YXIgdD10aGlz
+LnIKcmV0dXJuIHQ9PW51bGw/IiI6dH0sCm5tOmZ1bmN0aW9uKGEsYil7dmFyIHQscyxyLHEscCxvLG4s
+bSxsLGs9dGhpcwp1LmM5LmEoYikKdD1rLmEKcz10PT09ImZpbGUiCnI9ay5iCnE9ay5kCnA9ay5jCmlm
+KCEocCE9bnVsbCkpcD1yLmxlbmd0aCE9PTB8fHEhPW51bGx8fHM/IiI6bnVsbApvPWsuZQppZighcylu
+PXAhPW51bGwmJm8ubGVuZ3RoIT09MAplbHNlIG49ITAKaWYobiYmIUMueEIubihvLCIvIikpbz0iLyIr
+bwptPW8KbD1QLmxlKG51bGwsMCwwLGIpCnJldHVybiBuZXcgUC5Ebih0LHIscCxxLG0sbCxrLnIpfSwK
+Smg6ZnVuY3Rpb24oYSxiKXt2YXIgdCxzLHIscSxwLG8KZm9yKHQ9MCxzPTA7Qy54Qi5RaShiLCIuLi8i
+LHMpOyl7cys9MzsrK3R9cj1DLnhCLmNuKGEsIi8iKQp3aGlsZSghMCl7aWYoIShyPjAmJnQ+MCkpYnJl
+YWsKcT1DLnhCLlBrKGEsIi8iLHItMSkKaWYocTwwKWJyZWFrCnA9ci1xCm89cCE9PTIKaWYoIW98fHA9
+PT0zKWlmKEMueEIubShhLHErMSk9PT00NilvPSFvfHxDLnhCLm0oYSxxKzIpPT09NDYKZWxzZSBvPSEx
+CmVsc2Ugbz0hMQppZihvKWJyZWFrOy0tdApyPXF9cmV0dXJuIEMueEIuaTcoYSxyKzEsbnVsbCxDLnhC
+LkcoYixzLTMqdCkpfSwKWkk6ZnVuY3Rpb24oYSl7cmV0dXJuIHRoaXMubVMoUC5oSyhhKSl9LAptUzpm
+dW5jdGlvbihhKXt2YXIgdCxzLHIscSxwLG8sbixtLGwsaz10aGlzLGo9bnVsbAppZihhLmdGaSgpLmxl
+bmd0aCE9PTApe3Q9YS5nRmkoKQppZihhLmdjaigpKXtzPWEuZ2t1KCkKcj1hLmdKZihhKQpxPWEuZ3hB
+KCk/YS5ndHAoYSk6an1lbHNle3E9agpyPXEKcz0iIn1wPVAueGUoYS5nSWkoYSkpCm89YS5nUUQoKT9h
+Lmd0UCgpOmp9ZWxzZXt0PWsuYQppZihhLmdjaigpKXtzPWEuZ2t1KCkKcj1hLmdKZihhKQpxPVAud0Io
+YS5neEEoKT9hLmd0cChhKTpqLHQpCnA9UC54ZShhLmdJaShhKSkKbz1hLmdRRCgpP2EuZ3RQKCk6an1l
+bHNle3M9ay5iCnI9ay5jCnE9ay5kCmlmKGEuZ0lpKGEpPT09IiIpe3A9ay5lCm89YS5nUUQoKT9hLmd0
+UCgpOmsuZn1lbHNle2lmKGEuZ3RUKCkpcD1QLnhlKGEuZ0lpKGEpKQplbHNle249ay5lCmlmKG4ubGVu
+Z3RoPT09MClpZihyPT1udWxsKXA9dC5sZW5ndGg9PT0wP2EuZ0lpKGEpOlAueGUoYS5nSWkoYSkpCmVs
+c2UgcD1QLnhlKCIvIithLmdJaShhKSkKZWxzZXttPWsuSmgobixhLmdJaShhKSkKbD10Lmxlbmd0aD09
+PTAKaWYoIWx8fHIhPW51bGx8fEMueEIubihuLCIvIikpcD1QLnhlKG0pCmVsc2UgcD1QLndGKG0sIWx8
+fHIhPW51bGwpfX1vPWEuZ1FEKCk/YS5ndFAoKTpqfX19cmV0dXJuIG5ldyBQLkRuKHQscyxyLHEscCxv
+LGEuZ1o4KCk/YS5nS2EoKTpqKX0sCmdjajpmdW5jdGlvbigpe3JldHVybiB0aGlzLmMhPW51bGx9LApn
+eEE6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5kIT1udWxsfSwKZ1FEOmZ1bmN0aW9uKCl7cmV0dXJuIHRo
+aXMuZiE9bnVsbH0sCmdaODpmdW5jdGlvbigpe3JldHVybiB0aGlzLnIhPW51bGx9LApndFQ6ZnVuY3Rp
+b24oKXtyZXR1cm4gQy54Qi5uKHRoaXMuZSwiLyIpfSwKdDQ6ZnVuY3Rpb24oKXt2YXIgdCxzPXRoaXMs
+cj1zLmEKaWYociE9PSIiJiZyIT09ImZpbGUiKXRocm93IEguYihQLkw0KCJDYW5ub3QgZXh0cmFjdCBh
+IGZpbGUgcGF0aCBmcm9tIGEgIityKyIgVVJJIikpCmlmKHMuZ3RQKCkhPT0iIil0aHJvdyBILmIoUC5M
+NCgiQ2Fubm90IGV4dHJhY3QgYSBmaWxlIHBhdGggZnJvbSBhIFVSSSB3aXRoIGEgcXVlcnkgY29tcG9u
+ZW50IikpCmlmKHMuZ0thKCkhPT0iIil0aHJvdyBILmIoUC5MNCgiQ2Fubm90IGV4dHJhY3QgYSBmaWxl
+IHBhdGggZnJvbSBhIFVSSSB3aXRoIGEgZnJhZ21lbnQgY29tcG9uZW50IikpCnI9JC53USgpCmlmKEgu
+b1Qocikpcj1QLm1uKHMpCmVsc2V7aWYocy5jIT1udWxsJiZzLmdKZihzKSE9PSIiKUgudmgoUC5MNCgi
+Q2Fubm90IGV4dHJhY3QgYSBub24tV2luZG93cyBmaWxlIHBhdGggZnJvbSBhIGZpbGUgVVJJIHdpdGgg
+YW4gYXV0aG9yaXR5IikpCnQ9cy5nRmooKQpQLmtFKHQsITEpCnI9UC52ZyhDLnhCLm4ocy5lLCIvIik/
+Ii8iOiIiLHQsIi8iKQpyPXIuY2hhckNvZGVBdCgwKT09MD9yOnJ9cmV0dXJuIHJ9LAp3OmZ1bmN0aW9u
+KGEpe3JldHVybiB0aGlzLmduRCgpfSwKRE46ZnVuY3Rpb24oYSxiKXt2YXIgdD10aGlzCmlmKGI9PW51
+bGwpcmV0dXJuITEKaWYodD09PWIpcmV0dXJuITAKcmV0dXJuIHUuZEQuYihiKSYmdC5hPT09Yi5nRmko
+KSYmdC5jIT1udWxsPT09Yi5nY2ooKSYmdC5iPT09Yi5na3UoKSYmdC5nSmYodCk9PT1iLmdKZihiKSYm
+dC5ndHAodCk9PT1iLmd0cChiKSYmdC5lPT09Yi5nSWkoYikmJnQuZiE9bnVsbD09PWIuZ1FEKCkmJnQu
+Z3RQKCk9PT1iLmd0UCgpJiZ0LnIhPW51bGw9PT1iLmdaOCgpJiZ0LmdLYSgpPT09Yi5nS2EoKX0sCnNL
+cDpmdW5jdGlvbihhKXt0aGlzLnk9dS5iay5hKGEpfSwKc05NOmZ1bmN0aW9uKGEpe3RoaXMuUT11LmNa
+LmEoYSl9LAokaWlEOjEsCmdGaTpmdW5jdGlvbigpe3JldHVybiB0aGlzLmF9LApnSWk6ZnVuY3Rpb24o
+YSl7cmV0dXJuIHRoaXMuZX19ClAuUloucHJvdG90eXBlPXsKJDE6ZnVuY3Rpb24oYSl7cmV0dXJuIFAu
+ZVAoQy5aSixILmgoYSksQy54TSwhMSl9LAokUzo1fQpQLk1FLnByb3RvdHlwZT17CiQyOmZ1bmN0aW9u
+KGEsYil7dmFyIHQ9dGhpcy5iLHM9dGhpcy5hCnQuYSs9cy5hCnMuYT0iJiIKcz10LmErPUguRWooUC5l
+UChDLkYzLGEsQy54TSwhMCkpCmlmKGIhPW51bGwmJmIubGVuZ3RoIT09MCl7dC5hPXMrIj0iCnQuYSs9
+SC5FaihQLmVQKEMuRjMsYixDLnhNLCEwKSl9fSwKJFM6MjJ9ClAueTUucHJvdG90eXBlPXsKJDI6ZnVu
+Y3Rpb24oYSxiKXt2YXIgdCxzCkguaChhKQppZihiPT1udWxsfHx0eXBlb2YgYj09InN0cmluZyIpdGhp
+cy5hLiQyKGEsSC5rKGIpKQplbHNlIGZvcih0PUouSVQodS5wLmEoYikpLHM9dGhpcy5hO3QuRigpOylz
+LiQyKGEsSC5oKHQuZ2woKSkpfSwKJFM6MTN9ClAuUEUucHJvdG90eXBlPXsKZ2xSOmZ1bmN0aW9uKCl7
+dmFyIHQscyxyLHEscD10aGlzLG89bnVsbCxuPXAuYwppZihuPT1udWxsKXtuPXAuYgppZigwPj1uLmxl
+bmd0aClyZXR1cm4gSC5PSChuLDApCnQ9cC5hCm49blswXSsxCnM9Qy54Qi5YVSh0LCI/IixuKQpyPXQu
+bGVuZ3RoCmlmKHM+PTApe3E9UC5QSSh0LHMrMSxyLEMuVkMsITEpCnI9c31lbHNlIHE9bwpuPXAuYz1u
+ZXcgUC5xZSgiZGF0YSIsIiIsbyxvLFAuUEkodCxuLHIsQy5XZCwhMSkscSxvKX1yZXR1cm4gbn0sCnc6
+ZnVuY3Rpb24oYSl7dmFyIHQscz10aGlzLmIKaWYoMD49cy5sZW5ndGgpcmV0dXJuIEguT0gocywwKQp0
+PXRoaXMuYQpyZXR1cm4gc1swXT09PS0xPyJkYXRhOiIrdDp0fX0KUC5xMy5wcm90b3R5cGU9ewokMTpm
+dW5jdGlvbihhKXtyZXR1cm4gbmV3IFVpbnQ4QXJyYXkoOTYpfSwKJFM6MjN9ClAueUkucHJvdG90eXBl
+PXsKJDI6ZnVuY3Rpb24oYSxiKXt2YXIgdD10aGlzLmEKaWYoYT49dC5sZW5ndGgpcmV0dXJuIEguT0go
+dCxhKQp0PXRbYV0KSi5DTSh0LDAsOTYsYikKcmV0dXJuIHR9LAokUzoyNH0KUC5jNi5wcm90b3R5cGU9
+ewokMzpmdW5jdGlvbihhLGIsYyl7dmFyIHQscyxyLHEKZm9yKHQ9Yi5sZW5ndGgscz1hLmxlbmd0aCxy
+PTA7cjx0Oysrcil7cT1DLnhCLlcoYixyKV45NgppZihxPj1zKXJldHVybiBILk9IKGEscSkKYVtxXT1j
+fX19ClAucWQucHJvdG90eXBlPXsKJDM6ZnVuY3Rpb24oYSxiLGMpe3ZhciB0LHMscixxCmZvcih0PUMu
+eEIuVyhiLDApLHM9Qy54Qi5XKGIsMSkscj1hLmxlbmd0aDt0PD1zOysrdCl7cT0odF45Nik+Pj4wCmlm
+KHE+PXIpcmV0dXJuIEguT0goYSxxKQphW3FdPWN9fX0KUC5VZi5wcm90b3R5cGU9ewpnY2o6ZnVuY3Rp
+b24oKXtyZXR1cm4gdGhpcy5jPjB9LApneEE6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5jPjAmJnRoaXMu
+ZCsxPHRoaXMuZX0sCmdRRDpmdW5jdGlvbigpe3JldHVybiB0aGlzLmY8dGhpcy5yfSwKZ1o4OmZ1bmN0
+aW9uKCl7cmV0dXJuIHRoaXMucjx0aGlzLmEubGVuZ3RofSwKZ053OmZ1bmN0aW9uKCl7cmV0dXJuIHRo
+aXMuYj09PTQmJkMueEIubih0aGlzLmEsImZpbGUiKX0sCmdXWjpmdW5jdGlvbigpe3JldHVybiB0aGlz
+LmI9PT00JiZDLnhCLm4odGhpcy5hLCJodHRwIil9LApnUmU6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5i
+PT09NSYmQy54Qi5uKHRoaXMuYSwiaHR0cHMiKX0sCmd0VDpmdW5jdGlvbigpe3JldHVybiBDLnhCLlFp
+KHRoaXMuYSwiLyIsdGhpcy5lKX0sCmdGaTpmdW5jdGlvbigpe3ZhciB0PXRoaXMueApyZXR1cm4gdD09
+bnVsbD90aGlzLng9dGhpcy5VMigpOnR9LApVMjpmdW5jdGlvbigpe3ZhciB0PXRoaXMscz10LmIKaWYo
+czw9MClyZXR1cm4iIgppZih0LmdXWigpKXJldHVybiJodHRwIgppZih0LmdSZSgpKXJldHVybiJodHRw
+cyIKaWYodC5nTncoKSlyZXR1cm4iZmlsZSIKaWYocz09PTcmJkMueEIubih0LmEsInBhY2thZ2UiKSly
+ZXR1cm4icGFja2FnZSIKcmV0dXJuIEMueEIuTmoodC5hLDAscyl9LApna3U6ZnVuY3Rpb24oKXt2YXIg
+dD10aGlzLmMscz10aGlzLmIrMwpyZXR1cm4gdD5zP0MueEIuTmoodGhpcy5hLHMsdC0xKToiIn0sCmdK
+ZjpmdW5jdGlvbihhKXt2YXIgdD10aGlzLmMKcmV0dXJuIHQ+MD9DLnhCLk5qKHRoaXMuYSx0LHRoaXMu
+ZCk6IiJ9LApndHA6ZnVuY3Rpb24oYSl7dmFyIHQ9dGhpcwppZih0Lmd4QSgpKXJldHVybiBQLlFBKEMu
+eEIuTmoodC5hLHQuZCsxLHQuZSksbnVsbCkKaWYodC5nV1ooKSlyZXR1cm4gODAKaWYodC5nUmUoKSly
+ZXR1cm4gNDQzCnJldHVybiAwfSwKZ0lpOmZ1bmN0aW9uKGEpe3JldHVybiBDLnhCLk5qKHRoaXMuYSx0
+aGlzLmUsdGhpcy5mKX0sCmd0UDpmdW5jdGlvbigpe3ZhciB0PXRoaXMuZixzPXRoaXMucgpyZXR1cm4g
+dDxzP0MueEIuTmoodGhpcy5hLHQrMSxzKToiIn0sCmdLYTpmdW5jdGlvbigpe3ZhciB0PXRoaXMucixz
+PXRoaXMuYQpyZXR1cm4gdDxzLmxlbmd0aD9DLnhCLkcocyx0KzEpOiIifSwKZ0ZqOmZ1bmN0aW9uKCl7
+dmFyIHQscyxyPXRoaXMuZSxxPXRoaXMuZixwPXRoaXMuYQppZihDLnhCLlFpKHAsIi8iLHIpKSsrcgpp
+ZihyPT09cSlyZXR1cm4gQy54RAp0PUguVk0oW10sdS5zKQpmb3Iocz1yO3M8cTsrK3MpaWYoQy54Qi5t
+KHAscyk9PT00Nyl7Qy5ObS5pKHQsQy54Qi5OaihwLHIscykpCnI9cysxfUMuTm0uaSh0LEMueEIuTmoo
+cCxyLHEpKQpyZXR1cm4gUC5BRih0LHUuTil9LApnaFk6ZnVuY3Rpb24oKXtpZih0aGlzLmY+PXRoaXMu
+cilyZXR1cm4gQy5XTwpyZXR1cm4gbmV3IFAuR2ooUC5XWCh0aGlzLmd0UCgpKSx1LkUpfSwKa1g6ZnVu
+Y3Rpb24oYSl7dmFyIHQ9dGhpcy5kKzEKcmV0dXJuIHQrYS5sZW5ndGg9PT10aGlzLmUmJkMueEIuUWko
+dGhpcy5hLGEsdCl9LApOOTpmdW5jdGlvbigpe3ZhciB0PXRoaXMscz10LnIscj10LmEKaWYocz49ci5s
+ZW5ndGgpcmV0dXJuIHQKcmV0dXJuIG5ldyBQLlVmKEMueEIuTmoociwwLHMpLHQuYix0LmMsdC5kLHQu
+ZSx0LmYscyx0LngpfSwKbm06ZnVuY3Rpb24oYSxiKXt2YXIgdCxzLHIscSxwLG8sbixtLGwsayxqPXRo
+aXMsaT1udWxsCnUuYzkuYShiKQp0PWouZ0ZpKCkKcz10PT09ImZpbGUiCnI9ai5jCnE9cj4wP0MueEIu
+Tmooai5hLGouYiszLHIpOiIiCnA9ai5neEEoKT9qLmd0cChqKTppCnI9ai5jCmlmKHI+MClvPUMueEIu
+Tmooai5hLHIsai5kKQplbHNlIG89cS5sZW5ndGghPT0wfHxwIT1udWxsfHxzPyIiOmkKcj1qLmEKbj1D
+LnhCLk5qKHIsai5lLGouZikKaWYoIXMpbT1vIT1udWxsJiZuLmxlbmd0aCE9PTAKZWxzZSBtPSEwCmlm
+KG0mJiFDLnhCLm4obiwiLyIpKW49Ii8iK24KbD1QLmxlKGksMCwwLGIpCm09ai5yCms9bTxyLmxlbmd0
+aD9DLnhCLkcocixtKzEpOmkKcmV0dXJuIG5ldyBQLkRuKHQscSxvLHAsbixsLGspfSwKWkk6ZnVuY3Rp
+b24oYSl7cmV0dXJuIHRoaXMubVMoUC5oSyhhKSl9LAptUzpmdW5jdGlvbihhKXtpZihhIGluc3RhbmNl
+b2YgUC5VZilyZXR1cm4gdGhpcy51MSh0aGlzLGEpCnJldHVybiB0aGlzLnZzKCkubVMoYSl9LAp1MTpm
+dW5jdGlvbihhLGIpe3ZhciB0LHMscixxLHAsbyxuLG0sbCxrLGosaSxoPWIuYgppZihoPjApcmV0dXJu
+IGIKdD1iLmMKaWYodD4wKXtzPWEuYgppZihzPD0wKXJldHVybiBiCmlmKGEuZ053KCkpcj1iLmUhPT1i
+LmYKZWxzZSBpZihhLmdXWigpKXI9IWIua1goIjgwIikKZWxzZSByPSFhLmdSZSgpfHwhYi5rWCgiNDQz
+IikKaWYocil7cT1zKzEKcmV0dXJuIG5ldyBQLlVmKEMueEIuTmooYS5hLDAscSkrQy54Qi5HKGIuYSxo
+KzEpLHMsdCtxLGIuZCtxLGIuZStxLGIuZitxLGIucitxLGEueCl9ZWxzZSByZXR1cm4gdGhpcy52cygp
+Lm1TKGIpfXA9Yi5lCmg9Yi5mCmlmKHA9PT1oKXt0PWIucgppZihoPHQpe3M9YS5mCnE9cy1oCnJldHVy
+biBuZXcgUC5VZihDLnhCLk5qKGEuYSwwLHMpK0MueEIuRyhiLmEsaCksYS5iLGEuYyxhLmQsYS5lLGgr
+cSx0K3EsYS54KX1oPWIuYQppZih0PGgubGVuZ3RoKXtzPWEucgpyZXR1cm4gbmV3IFAuVWYoQy54Qi5O
+aihhLmEsMCxzKStDLnhCLkcoaCx0KSxhLmIsYS5jLGEuZCxhLmUsYS5mLHQrKHMtdCksYS54KX1yZXR1
+cm4gYS5OOSgpfXQ9Yi5hCmlmKEMueEIuUWkodCwiLyIscCkpe3M9YS5lCnE9cy1wCnJldHVybiBuZXcg
+UC5VZihDLnhCLk5qKGEuYSwwLHMpK0MueEIuRyh0LHApLGEuYixhLmMsYS5kLHMsaCtxLGIucitxLGEu
+eCl9bz1hLmUKbj1hLmYKaWYobz09PW4mJmEuYz4wKXtmb3IoO0MueEIuUWkodCwiLi4vIixwKTspcCs9
+MwpxPW8tcCsxCnJldHVybiBuZXcgUC5VZihDLnhCLk5qKGEuYSwwLG8pKyIvIitDLnhCLkcodCxwKSxh
+LmIsYS5jLGEuZCxvLGgrcSxiLnIrcSxhLngpfW09YS5hCmZvcihsPW87Qy54Qi5RaShtLCIuLi8iLGwp
+OylsKz0zCms9MAp3aGlsZSghMCl7aj1wKzMKaWYoIShqPD1oJiZDLnhCLlFpKHQsIi4uLyIscCkpKWJy
+ZWFrOysrawpwPWp9Zm9yKGk9IiI7bj5sOyl7LS1uCmlmKEMueEIubShtLG4pPT09NDcpe2lmKGs9PT0w
+KXtpPSIvIgpicmVha30tLWsKaT0iLyJ9fWlmKG49PT1sJiZhLmI8PTAmJiFDLnhCLlFpKG0sIi8iLG8p
+KXtwLT1rKjMKaT0iIn1xPW4tcCtpLmxlbmd0aApyZXR1cm4gbmV3IFAuVWYoQy54Qi5OaihtLDAsbikr
+aStDLnhCLkcodCxwKSxhLmIsYS5jLGEuZCxvLGgrcSxiLnIrcSxhLngpfSwKdDQ6ZnVuY3Rpb24oKXt2
+YXIgdCxzLHIscT10aGlzCmlmKHEuYj49MCYmIXEuZ053KCkpdGhyb3cgSC5iKFAuTDQoIkNhbm5vdCBl
+eHRyYWN0IGEgZmlsZSBwYXRoIGZyb20gYSAiK3EuZ0ZpKCkrIiBVUkkiKSkKdD1xLmYKcz1xLmEKaWYo
+dDxzLmxlbmd0aCl7aWYodDxxLnIpdGhyb3cgSC5iKFAuTDQoIkNhbm5vdCBleHRyYWN0IGEgZmlsZSBw
+YXRoIGZyb20gYSBVUkkgd2l0aCBhIHF1ZXJ5IGNvbXBvbmVudCIpKQp0aHJvdyBILmIoUC5MNCgiQ2Fu
+bm90IGV4dHJhY3QgYSBmaWxlIHBhdGggZnJvbSBhIFVSSSB3aXRoIGEgZnJhZ21lbnQgY29tcG9uZW50
+IikpfXI9JC53USgpCmlmKEgub1QocikpdD1QLm1uKHEpCmVsc2V7aWYocS5jPHEuZClILnZoKFAuTDQo
+IkNhbm5vdCBleHRyYWN0IGEgbm9uLVdpbmRvd3MgZmlsZSBwYXRoIGZyb20gYSBmaWxlIFVSSSB3aXRo
+IGFuIGF1dGhvcml0eSIpKQp0PUMueEIuTmoocyxxLmUsdCl9cmV0dXJuIHR9LApnaU86ZnVuY3Rpb24o
+YSl7dmFyIHQ9dGhpcy55CnJldHVybiB0PT1udWxsP3RoaXMueT1DLnhCLmdpTyh0aGlzLmEpOnR9LApE
+TjpmdW5jdGlvbihhLGIpe2lmKGI9PW51bGwpcmV0dXJuITEKaWYodGhpcz09PWIpcmV0dXJuITAKcmV0
+dXJuIHUuZEQuYihiKSYmdGhpcy5hPT09Yi53KDApfSwKdnM6ZnVuY3Rpb24oKXt2YXIgdD10aGlzLHM9
+bnVsbCxyPXQuZ0ZpKCkscT10LmdrdSgpLHA9dC5jPjA/dC5nSmYodCk6cyxvPXQuZ3hBKCk/dC5ndHAo
+dCk6cyxuPXQuYSxtPXQuZixsPUMueEIuTmoobix0LmUsbSksaz10LnIKbT1tPGs/dC5ndFAoKTpzCnJl
+dHVybiBuZXcgUC5EbihyLHEscCxvLGwsbSxrPG4ubGVuZ3RoP3QuZ0thKCk6cyl9LAp3OmZ1bmN0aW9u
+KGEpe3JldHVybiB0aGlzLmF9LAokaWlEOjF9ClAucWUucHJvdG90eXBlPXt9ClcucUUucHJvdG90eXBl
+PXt9ClcuR2gucHJvdG90eXBlPXsKdzpmdW5jdGlvbihhKXtyZXR1cm4gU3RyaW5nKGEpfSwKJGlHaDox
+fQpXLmZZLnByb3RvdHlwZT17Cnc6ZnVuY3Rpb24oYSl7cmV0dXJuIFN0cmluZyhhKX19ClcubkIucHJv
+dG90eXBlPXskaW5COjF9ClcuQXoucHJvdG90eXBlPXskaUF6OjF9ClcuUVAucHJvdG90eXBlPXskaVFQ
+OjF9ClcubngucHJvdG90eXBlPXsKZ0E6ZnVuY3Rpb24oYSl7cmV0dXJuIGEubGVuZ3RofX0KVy5vSi5w
+cm90b3R5cGU9ewpnQTpmdW5jdGlvbihhKXtyZXR1cm4gYS5sZW5ndGh9fQpXLmlkLnByb3RvdHlwZT17
+fQpXLlFGLnByb3RvdHlwZT17fQpXLk5oLnByb3RvdHlwZT17Cnc6ZnVuY3Rpb24oYSl7cmV0dXJuIFN0
+cmluZyhhKX19ClcuYWUucHJvdG90eXBlPXsKRGM6ZnVuY3Rpb24oYSxiKXtyZXR1cm4gYS5jcmVhdGVI
+VE1MRG9jdW1lbnQoYil9fQpXLklCLnByb3RvdHlwZT17Cnc6ZnVuY3Rpb24oYSl7cmV0dXJuIlJlY3Rh
+bmdsZSAoIitILkVqKGEubGVmdCkrIiwgIitILkVqKGEudG9wKSsiKSAiK0guRWooYS53aWR0aCkrIiB4
+ICIrSC5FaihhLmhlaWdodCl9LApETjpmdW5jdGlvbihhLGIpe2lmKGI9PW51bGwpcmV0dXJuITEKcmV0
+dXJuIHUucS5iKGIpJiZhLmxlZnQ9PWIubGVmdCYmYS50b3A9PWIudG9wJiZhLndpZHRoPT1iLndpZHRo
+JiZhLmhlaWdodD09Yi5oZWlnaHR9LApnaU86ZnVuY3Rpb24oYSl7cmV0dXJuIFcuckUoSi5oZihhLmxl
+ZnQpLEouaGYoYS50b3ApLEouaGYoYS53aWR0aCksSi5oZihhLmhlaWdodCkpfSwKJGl0bjoxfQpXLm43
+LnByb3RvdHlwZT17CmdBOmZ1bmN0aW9uKGEpe3JldHVybiBhLmxlbmd0aH19Clcud3oucHJvdG90eXBl
+PXsKZ0E6ZnVuY3Rpb24oYSl7cmV0dXJuIHRoaXMuYS5sZW5ndGh9LApxOmZ1bmN0aW9uKGEsYil7dmFy
+IHQKSC51UChiKQp0PXRoaXMuYQppZihiPDB8fGI+PXQubGVuZ3RoKXJldHVybiBILk9IKHQsYikKcmV0
+dXJuIHRoaXMuJHRpLmMuYSh0W2JdKX0sClk6ZnVuY3Rpb24oYSxiLGMpe3RoaXMuJHRpLmMuYShjKQp0
+aHJvdyBILmIoUC5MNCgiQ2Fubm90IG1vZGlmeSBsaXN0IikpfX0KVy5jdi5wcm90b3R5cGU9ewpnUWc6
+ZnVuY3Rpb24oYSl7cmV0dXJuIG5ldyBXLmk3KGEpfSwKZ1A6ZnVuY3Rpb24oYSl7cmV0dXJuIG5ldyBX
+Lkk0KGEpfSwKc1A6ZnVuY3Rpb24oYSxiKXt2YXIgdAp1LlEuYShiKQp0PXRoaXMuZ1AoYSkKdC5WMSgw
+KQp0LkZWKDAsYil9LAp3OmZ1bmN0aW9uKGEpe3JldHVybiBhLmxvY2FsTmFtZX0sCkZGOmZ1bmN0aW9u
+KGEpe3ZhciB0PSEhYS5zY3JvbGxJbnRvVmlld0lmTmVlZGVkCmlmKHQpYS5zY3JvbGxJbnRvVmlld0lm
+TmVlZGVkKCkKZWxzZSBhLnNjcm9sbEludG9WaWV3KCl9LApuejpmdW5jdGlvbihhLGIsYyxkLGUpe3Zh
+ciB0LHM9dGhpcy5yNihhLGMsZCxlKQpzd2l0Y2goYi50b0xvd2VyQ2FzZSgpKXtjYXNlImJlZm9yZWJl
+Z2luIjp0PWEucGFyZW50Tm9kZQp0LnRvU3RyaW5nCkouRWgodCxzLGEpCmJyZWFrCmNhc2UiYWZ0ZXJi
+ZWdpbiI6dD1hLmNoaWxkTm9kZXMKdGhpcy5tSyhhLHMsdC5sZW5ndGg+MD90WzBdOm51bGwpCmJyZWFr
+CmNhc2UiYmVmb3JlZW5kIjphLmFwcGVuZENoaWxkKHMpCmJyZWFrCmNhc2UiYWZ0ZXJlbmQiOnQ9YS5w
+YXJlbnROb2RlCnQudG9TdHJpbmcKSi5FaCh0LHMsYS5uZXh0U2libGluZykKYnJlYWsKZGVmYXVsdDpI
+LnZoKFAueFkoIkludmFsaWQgcG9zaXRpb24gIitiKSl9fSwKcjY6ZnVuY3Rpb24oYSxiLGMsZCl7dmFy
+IHQscyxyLHEKaWYoYz09bnVsbCl7aWYoZD09bnVsbCl7dD0kLmx0CmlmKHQ9PW51bGwpe3Q9SC5WTShb
+XSx1LnIpCnM9bmV3IFcudkQodCkKQy5ObS5pKHQsVy5UdyhudWxsKSkKQy5ObS5pKHQsVy5CbCgpKQok
+Lmx0PXMKZD1zfWVsc2UgZD10fXQ9JC5FVQppZih0PT1udWxsKXt0PW5ldyBXLktvKGQpCiQuRVU9dApj
+PXR9ZWxzZXt0LmE9ZApjPXR9fWVsc2UgaWYoZCE9bnVsbCl0aHJvdyBILmIoUC54WSgidmFsaWRhdG9y
+IGNhbiBvbmx5IGJlIHBhc3NlZCBpZiB0cmVlU2FuaXRpemVyIGlzIG51bGwiKSkKaWYoJC54bz09bnVs
+bCl7dD1kb2N1bWVudApzPXQuaW1wbGVtZW50YXRpb24Kcz0ocyYmQy5tSCkuRGMocywiIikKJC54bz1z
+CiQuQk89cy5jcmVhdGVSYW5nZSgpCnM9JC54by5jcmVhdGVFbGVtZW50KCJiYXNlIikKdS5jUi5hKHMp
+CnMuaHJlZj10LmJhc2VVUkkKJC54by5oZWFkLmFwcGVuZENoaWxkKHMpfXQ9JC54bwppZih0LmJvZHk9
+PW51bGwpe3M9dC5jcmVhdGVFbGVtZW50KCJib2R5IikKQy5CWi5zWEcodCx1LmsuYShzKSl9dD0kLnhv
+CmlmKHUuay5iKGEpKXt0PXQuYm9keQp0LnRvU3RyaW5nCnI9dH1lbHNle3QudG9TdHJpbmcKcj10LmNy
+ZWF0ZUVsZW1lbnQoYS50YWdOYW1lKQokLnhvLmJvZHkuYXBwZW5kQ2hpbGQocil9aWYoImNyZWF0ZUNv
+bnRleHR1YWxGcmFnbWVudCIgaW4gd2luZG93LlJhbmdlLnByb3RvdHlwZSYmIUMuTm0udGcoQy5TcSxh
+LnRhZ05hbWUpKXskLkJPLnNlbGVjdE5vZGVDb250ZW50cyhyKQp0PSQuQk8KdC50b1N0cmluZwpxPXQu
+Y3JlYXRlQ29udGV4dHVhbEZyYWdtZW50KGI9PW51bGw/Im51bGwiOmIpfWVsc2V7Si53ZihyLGIpCnE9
+JC54by5jcmVhdGVEb2N1bWVudEZyYWdtZW50KCkKZm9yKDt0PXIuZmlyc3RDaGlsZCx0IT1udWxsOylx
+LmFwcGVuZENoaWxkKHQpfWlmKHIhPT0kLnhvLmJvZHkpSi5MdChyKQpjLlBuKHEpCmRvY3VtZW50LmFk
+b3B0Tm9kZShxKQpyZXR1cm4gcX0sCkFIOmZ1bmN0aW9uKGEsYixjKXtyZXR1cm4gdGhpcy5yNihhLGIs
+YyxudWxsKX0sCnNoZjpmdW5jdGlvbihhLGIpe3RoaXMuWUMoYSxiKX0sCnBrOmZ1bmN0aW9uKGEsYixj
+KXt0aGlzLnNhNChhLG51bGwpCmEuYXBwZW5kQ2hpbGQodGhpcy5yNihhLGIsbnVsbCxjKSl9LApZQzpm
+dW5jdGlvbihhLGIpe3JldHVybiB0aGlzLnBrKGEsYixudWxsKX0sCnNSTjpmdW5jdGlvbihhLGIpe2Eu
+aW5uZXJIVE1MPWJ9LApnbnM6ZnVuY3Rpb24oYSl7cmV0dXJuIGEudGFnTmFtZX0sCmdWbDpmdW5jdGlv
+bihhKXtyZXR1cm4gbmV3IFcuZXUoYSwiY2xpY2siLCExLHUuail9LAokaWN2OjF9ClcuQ3YucHJvdG90
+eXBlPXsKJDE6ZnVuY3Rpb24oYSl7cmV0dXJuIHUuaC5iKHUuQS5hKGEpKX0sCiRTOjI1fQpXLmVhLnBy
+b3RvdHlwZT17JGllYToxfQpXLkQwLnByb3RvdHlwZT17Ck9uOmZ1bmN0aW9uKGEsYixjLGQpe3UuYncu
+YShjKQppZihjIT1udWxsKXRoaXMudihhLGIsYyxkKX0sCkI6ZnVuY3Rpb24oYSxiLGMpe3JldHVybiB0
+aGlzLk9uKGEsYixjLG51bGwpfSwKdjpmdW5jdGlvbihhLGIsYyxkKXtyZXR1cm4gYS5hZGRFdmVudExp
+c3RlbmVyKGIsSC50Uih1LmJ3LmEoYyksMSksZCl9LAokaUQwOjF9ClcuaEgucHJvdG90eXBlPXskaWhI
+OjF9ClcuaDQucHJvdG90eXBlPXsKZ0E6ZnVuY3Rpb24oYSl7cmV0dXJuIGEubGVuZ3RofX0KVy5ici5w
+cm90b3R5cGU9ewpnQTpmdW5jdGlvbihhKXtyZXR1cm4gYS5sZW5ndGh9fQpXLlZiLnByb3RvdHlwZT17
+CnNYRzpmdW5jdGlvbihhLGIpe2EuYm9keT1ifX0KVy5mSi5wcm90b3R5cGU9ewplbzpmdW5jdGlvbihh
+LGIsYyxkKXtyZXR1cm4gYS5vcGVuKGIsYywhMCl9LAokaWZKOjF9Clcud2EucHJvdG90eXBlPXt9Clcu
+U2cucHJvdG90eXBlPXskaVNnOjF9ClcudTgucHJvdG90eXBlPXsKZ0RyOmZ1bmN0aW9uKGEpe2lmKCJv
+cmlnaW4iIGluIGEpcmV0dXJuIGEub3JpZ2luCnJldHVybiBILkVqKGEucHJvdG9jb2wpKyIvLyIrSC5F
+aihhLmhvc3QpfSwKdzpmdW5jdGlvbihhKXtyZXR1cm4gU3RyaW5nKGEpfSwKJGl1ODoxfQpXLkFqLnBy
+b3RvdHlwZT17JGlBajoxfQpXLmU3LnByb3RvdHlwZT17CmdyODpmdW5jdGlvbihhKXt2YXIgdD10aGlz
+LmEscz10LmNoaWxkTm9kZXMubGVuZ3RoCmlmKHM9PT0wKXRocm93IEguYihQLlBWKCJObyBlbGVtZW50
+cyIpKQppZihzPjEpdGhyb3cgSC5iKFAuUFYoIk1vcmUgdGhhbiBvbmUgZWxlbWVudCIpKQp0PXQuZmly
+c3RDaGlsZAp0LnRvU3RyaW5nCnJldHVybiB0fSwKRlY6ZnVuY3Rpb24oYSxiKXt2YXIgdCxzLHIscSxw
+CnUuZWguYShiKQp0PWIuYQpzPXRoaXMuYQppZih0IT09cylmb3Iocj10LmNoaWxkTm9kZXMubGVuZ3Ro
+LHE9MDtxPHI7KytxKXtwPXQuZmlyc3RDaGlsZApwLnRvU3RyaW5nCnMuYXBwZW5kQ2hpbGQocCl9cmV0
+dXJufSwKWTpmdW5jdGlvbihhLGIsYyl7dmFyIHQscwp1LkEuYShjKQp0PXRoaXMuYQpzPXQuY2hpbGRO
+b2RlcwppZihiPDB8fGI+PXMubGVuZ3RoKXJldHVybiBILk9IKHMsYikKdC5yZXBsYWNlQ2hpbGQoYyxz
+W2JdKX0sCmdrejpmdW5jdGlvbihhKXt2YXIgdD10aGlzLmEuY2hpbGROb2RlcwpyZXR1cm4gbmV3IFcu
+VzkodCx0Lmxlbmd0aCxILnoodCkuQygiVzk8R20uRT4iKSl9LApnQTpmdW5jdGlvbihhKXtyZXR1cm4g
+dGhpcy5hLmNoaWxkTm9kZXMubGVuZ3RofSwKcTpmdW5jdGlvbihhLGIpe3ZhciB0CkgudVAoYikKdD10
+aGlzLmEuY2hpbGROb2RlcwppZihiPDB8fGI+PXQubGVuZ3RoKXJldHVybiBILk9IKHQsYikKcmV0dXJu
+IHRbYl19fQpXLnVILnByb3RvdHlwZT17CndnOmZ1bmN0aW9uKGEpe3ZhciB0PWEucGFyZW50Tm9kZQpp
+Zih0IT1udWxsKXQucmVtb3ZlQ2hpbGQoYSl9LApENDpmdW5jdGlvbihhKXt2YXIgdApmb3IoO3Q9YS5m
+aXJzdENoaWxkLHQhPW51bGw7KWEucmVtb3ZlQ2hpbGQodCl9LAp3OmZ1bmN0aW9uKGEpe3ZhciB0PWEu
+bm9kZVZhbHVlCnJldHVybiB0PT1udWxsP3RoaXMuVShhKTp0fSwKc2E0OmZ1bmN0aW9uKGEsYil7YS50
+ZXh0Q29udGVudD1ifSwKbUs6ZnVuY3Rpb24oYSxiLGMpe3JldHVybiBhLmluc2VydEJlZm9yZShiLGMp
+fSwKJGl1SDoxfQpXLkJILnByb3RvdHlwZT17CmdBOmZ1bmN0aW9uKGEpe3JldHVybiBhLmxlbmd0aH0s
+CnE6ZnVuY3Rpb24oYSxiKXtILnVQKGIpCmlmKGI+Pj4wIT09Ynx8Yj49YS5sZW5ndGgpdGhyb3cgSC5i
+KFAuQ2YoYixhLG51bGwsbnVsbCxudWxsKSkKcmV0dXJuIGFbYl19LApZOmZ1bmN0aW9uKGEsYixjKXt1
+LkEuYShjKQp0aHJvdyBILmIoUC5MNCgiQ2Fubm90IGFzc2lnbiBlbGVtZW50IG9mIGltbXV0YWJsZSBM
+aXN0LiIpKX0sCmd0SDpmdW5jdGlvbihhKXtpZihhLmxlbmd0aD4wKXJldHVybiBhWzBdCnRocm93IEgu
+YihQLlBWKCJObyBlbGVtZW50cyIpKX0sCkU6ZnVuY3Rpb24oYSxiKXtpZihiPDB8fGI+PWEubGVuZ3Ro
+KXJldHVybiBILk9IKGEsYikKcmV0dXJuIGFbYl19LAokaWJROjEsCiRpWGo6MSwKJGljWDoxLAokaXpN
+OjF9ClcuU04ucHJvdG90eXBlPXt9ClcuZXcucHJvdG90eXBlPXskaWV3OjF9ClcubHAucHJvdG90eXBl
+PXsKZ0E6ZnVuY3Rpb24oYSl7cmV0dXJuIGEubGVuZ3RofX0KVy5UYi5wcm90b3R5cGU9ewpyNjpmdW5j
+dGlvbihhLGIsYyxkKXt2YXIgdCxzCmlmKCJjcmVhdGVDb250ZXh0dWFsRnJhZ21lbnQiIGluIHdpbmRv
+dy5SYW5nZS5wcm90b3R5cGUpcmV0dXJuIHRoaXMuRFcoYSxiLGMsZCkKdD1XLlU5KCI8dGFibGU+IitI
+LkVqKGIpKyI8L3RhYmxlPiIsYyxkKQpzPWRvY3VtZW50LmNyZWF0ZURvY3VtZW50RnJhZ21lbnQoKQpz
+LnRvU3RyaW5nCnQudG9TdHJpbmcKbmV3IFcuZTcocykuRlYoMCxuZXcgVy5lNyh0KSkKcmV0dXJuIHN9
+fQpXLkl2LnByb3RvdHlwZT17CnI2OmZ1bmN0aW9uKGEsYixjLGQpe3ZhciB0LHMscixxCmlmKCJjcmVh
+dGVDb250ZXh0dWFsRnJhZ21lbnQiIGluIHdpbmRvdy5SYW5nZS5wcm90b3R5cGUpcmV0dXJuIHRoaXMu
+RFcoYSxiLGMsZCkKdD1kb2N1bWVudApzPXQuY3JlYXRlRG9jdW1lbnRGcmFnbWVudCgpCnQ9Qy5JZS5y
+Nih0LmNyZWF0ZUVsZW1lbnQoInRhYmxlIiksYixjLGQpCnQudG9TdHJpbmcKdD1uZXcgVy5lNyh0KQpy
+PXQuZ3I4KHQpCnIudG9TdHJpbmcKdD1uZXcgVy5lNyhyKQpxPXQuZ3I4KHQpCnMudG9TdHJpbmcKcS50
+b1N0cmluZwpuZXcgVy5lNyhzKS5GVigwLG5ldyBXLmU3KHEpKQpyZXR1cm4gc319ClcuV1AucHJvdG90
+eXBlPXsKcjY6ZnVuY3Rpb24oYSxiLGMsZCl7dmFyIHQscyxyCmlmKCJjcmVhdGVDb250ZXh0dWFsRnJh
+Z21lbnQiIGluIHdpbmRvdy5SYW5nZS5wcm90b3R5cGUpcmV0dXJuIHRoaXMuRFcoYSxiLGMsZCkKdD1k
+b2N1bWVudApzPXQuY3JlYXRlRG9jdW1lbnRGcmFnbWVudCgpCnQ9Qy5JZS5yNih0LmNyZWF0ZUVsZW1l
+bnQoInRhYmxlIiksYixjLGQpCnQudG9TdHJpbmcKdD1uZXcgVy5lNyh0KQpyPXQuZ3I4KHQpCnMudG9T
+dHJpbmcKci50b1N0cmluZwpuZXcgVy5lNyhzKS5GVigwLG5ldyBXLmU3KHIpKQpyZXR1cm4gc319Clcu
+eVkucHJvdG90eXBlPXsKcGs6ZnVuY3Rpb24oYSxiLGMpe3ZhciB0LHMKdGhpcy5zYTQoYSxudWxsKQp0
+PWEuY29udGVudAp0LnRvU3RyaW5nCkouYlQodCkKcz10aGlzLnI2KGEsYixudWxsLGMpCmEuY29udGVu
+dC5hcHBlbmRDaGlsZChzKX0sCllDOmZ1bmN0aW9uKGEsYil7cmV0dXJuIHRoaXMucGsoYSxiLG51bGwp
+fSwKJGl5WToxfQpXLnc2LnByb3RvdHlwZT17fQpXLks1LnByb3RvdHlwZT17ClBvOmZ1bmN0aW9uKGEs
+YixjKXt2YXIgdD1XLlAxKGEub3BlbihiLGMpKQpyZXR1cm4gdH0sCmdtVzpmdW5jdGlvbihhKXtyZXR1
+cm4gYS5sb2NhdGlvbn0sCnVzOmZ1bmN0aW9uKGEsYil7cmV0dXJuIGEuY29uZmlybShiKX0sCiRpSzU6
+MSwKJGl2NjoxfQpXLkNtLnByb3RvdHlwZT17JGlDbToxfQpXLkNRLnByb3RvdHlwZT17JGlDUToxfQpX
+Lnc0LnByb3RvdHlwZT17Cnc6ZnVuY3Rpb24oYSl7cmV0dXJuIlJlY3RhbmdsZSAoIitILkVqKGEubGVm
+dCkrIiwgIitILkVqKGEudG9wKSsiKSAiK0guRWooYS53aWR0aCkrIiB4ICIrSC5FaihhLmhlaWdodCl9
+LApETjpmdW5jdGlvbihhLGIpe2lmKGI9PW51bGwpcmV0dXJuITEKcmV0dXJuIHUucS5iKGIpJiZhLmxl
+ZnQ9PWIubGVmdCYmYS50b3A9PWIudG9wJiZhLndpZHRoPT1iLndpZHRoJiZhLmhlaWdodD09Yi5oZWln
+aHR9LApnaU86ZnVuY3Rpb24oYSl7cmV0dXJuIFcuckUoSi5oZihhLmxlZnQpLEouaGYoYS50b3ApLEou
+aGYoYS53aWR0aCksSi5oZihhLmhlaWdodCkpfX0KVy5yaC5wcm90b3R5cGU9ewpnQTpmdW5jdGlvbihh
+KXtyZXR1cm4gYS5sZW5ndGh9LApxOmZ1bmN0aW9uKGEsYil7SC51UChiKQppZihiPj4+MCE9PWJ8fGI+
+PWEubGVuZ3RoKXRocm93IEguYihQLkNmKGIsYSxudWxsLG51bGwsbnVsbCkpCnJldHVybiBhW2JdfSwK
+WTpmdW5jdGlvbihhLGIsYyl7dS5BLmEoYykKdGhyb3cgSC5iKFAuTDQoIkNhbm5vdCBhc3NpZ24gZWxl
+bWVudCBvZiBpbW11dGFibGUgTGlzdC4iKSl9LApFOmZ1bmN0aW9uKGEsYil7aWYoYjwwfHxiPj1hLmxl
+bmd0aClyZXR1cm4gSC5PSChhLGIpCnJldHVybiBhW2JdfSwKJGliUToxLAokaVhqOjEsCiRpY1g6MSwK
+JGl6TToxfQpXLmNmLnByb3RvdHlwZT17Cks6ZnVuY3Rpb24oYSxiKXt2YXIgdCxzLHIscSxwCnUuZUEu
+YShiKQpmb3IodD10aGlzLmdWKCkscz10Lmxlbmd0aCxyPXRoaXMuYSxxPTA7cTx0Lmxlbmd0aDt0Lmxl
+bmd0aD09PXN8fCgwLEgubGspKHQpLCsrcSl7cD10W3FdCmIuJDIocCxyLmdldEF0dHJpYnV0ZShwKSl9
+fSwKZ1Y6ZnVuY3Rpb24oKXt2YXIgdCxzLHIscSxwPXRoaXMuYS5hdHRyaWJ1dGVzLG89SC5WTShbXSx1
+LnMpCmZvcih0PXAubGVuZ3RoLHM9dS5oOSxyPTA7cjx0Oysrcil7aWYocj49cC5sZW5ndGgpcmV0dXJu
+IEguT0gocCxyKQpxPXMuYShwW3JdKQppZihxLm5hbWVzcGFjZVVSST09bnVsbClDLk5tLmkobyxxLm5h
+bWUpfXJldHVybiBvfSwKZ2wwOmZ1bmN0aW9uKGEpe3JldHVybiB0aGlzLmdWKCkubGVuZ3RoPT09MH19
+ClcuaTcucHJvdG90eXBlPXsKeDQ6ZnVuY3Rpb24oYSl7dmFyIHQ9SC5vVCh0aGlzLmEuaGFzQXR0cmli
+dXRlKGEpKQpyZXR1cm4gdH0sCnE6ZnVuY3Rpb24oYSxiKXtyZXR1cm4gdGhpcy5hLmdldEF0dHJpYnV0
+ZShILmgoYikpfSwKWTpmdW5jdGlvbihhLGIsYyl7dGhpcy5hLnNldEF0dHJpYnV0ZShiLGMpfSwKZ0E6
+ZnVuY3Rpb24oYSl7cmV0dXJuIHRoaXMuZ1YoKS5sZW5ndGh9fQpXLlN5LnByb3RvdHlwZT17Cng0OmZ1
+bmN0aW9uKGEpe3ZhciB0PUgub1QodGhpcy5hLmEuaGFzQXR0cmlidXRlKCJkYXRhLSIrdGhpcy5PKGEp
+KSkKcmV0dXJuIHR9LApxOmZ1bmN0aW9uKGEsYil7cmV0dXJuIHRoaXMuYS5hLmdldEF0dHJpYnV0ZSgi
+ZGF0YS0iK3RoaXMuTyhILmgoYikpKX0sClk6ZnVuY3Rpb24oYSxiLGMpe3RoaXMuYS5hLnNldEF0dHJp
+YnV0ZSgiZGF0YS0iK3RoaXMuTyhiKSxjKX0sCks6ZnVuY3Rpb24oYSxiKXt0aGlzLmEuSygwLG5ldyBX
+LktTKHRoaXMsdS5lQS5hKGIpKSl9LApnVjpmdW5jdGlvbigpe3ZhciB0PUguVk0oW10sdS5zKQp0aGlz
+LmEuSygwLG5ldyBXLkEzKHRoaXMsdCkpCnJldHVybiB0fSwKZ0E6ZnVuY3Rpb24oYSl7cmV0dXJuIHRo
+aXMuZ1YoKS5sZW5ndGh9LApnbDA6ZnVuY3Rpb24oYSl7cmV0dXJuIHRoaXMuZ1YoKS5sZW5ndGg9PT0w
+fSwKazpmdW5jdGlvbihhKXt2YXIgdCxzLHI9SC5WTShhLnNwbGl0KCItIiksdS5zKQpmb3IodD0xO3Q8
+ci5sZW5ndGg7Kyt0KXtzPXJbdF0KaWYocy5sZW5ndGg+MClDLk5tLlkocix0LHNbMF0udG9VcHBlckNh
+c2UoKStKLktWKHMsMSkpfXJldHVybiBDLk5tLkgociwiIil9LApPOmZ1bmN0aW9uKGEpe3ZhciB0LHMs
+cixxLHAKZm9yKHQ9YS5sZW5ndGgscz0wLHI9IiI7czx0Oysrcyl7cT1hW3NdCnA9cS50b0xvd2VyQ2Fz
+ZSgpCnI9KHEhPT1wJiZzPjA/cisiLSI6cikrcH1yZXR1cm4gci5jaGFyQ29kZUF0KDApPT0wP3I6cn19
+ClcuS1MucHJvdG90eXBlPXsKJDI6ZnVuY3Rpb24oYSxiKXtpZihKLnJZKGEpLm4oYSwiZGF0YS0iKSl0
+aGlzLmIuJDIodGhpcy5hLmsoQy54Qi5HKGEsNSkpLGIpfSwKJFM6MTB9ClcuQTMucHJvdG90eXBlPXsK
+JDI6ZnVuY3Rpb24oYSxiKXtpZihKLnJZKGEpLm4oYSwiZGF0YS0iKSlDLk5tLmkodGhpcy5iLHRoaXMu
+YS5rKEMueEIuRyhhLDUpKSl9LAokUzoxMH0KVy5JNC5wcm90b3R5cGU9ewpERzpmdW5jdGlvbigpe3Zh
+ciB0LHMscixxLHA9UC5Mcyh1Lk4pCmZvcih0PXRoaXMuYS5jbGFzc05hbWUuc3BsaXQoIiAiKSxzPXQu
+bGVuZ3RoLHI9MDtyPHM7KytyKXtxPUouVDAodFtyXSkKaWYocS5sZW5ndGghPT0wKXAuaSgwLHEpfXJl
+dHVybiBwfSwKWDpmdW5jdGlvbihhKXt0aGlzLmEuY2xhc3NOYW1lPXUuQy5hKGEpLkgoMCwiICIpfSwK
+Z0E6ZnVuY3Rpb24oYSl7cmV0dXJuIHRoaXMuYS5jbGFzc0xpc3QubGVuZ3RofSwKVjE6ZnVuY3Rpb24o
+YSl7dGhpcy5hLmNsYXNzTmFtZT0iIn0sCnRnOmZ1bmN0aW9uKGEsYil7dmFyIHQ9dGhpcy5hLmNsYXNz
+TGlzdC5jb250YWlucyhiKQpyZXR1cm4gdH0sCmk6ZnVuY3Rpb24oYSxiKXt2YXIgdD10aGlzLmEuY2xh
+c3NMaXN0LHM9dC5jb250YWlucyhiKQp0LmFkZChiKQpyZXR1cm4hc30sClI6ZnVuY3Rpb24oYSxiKXt2
+YXIgdD10aGlzLmEuY2xhc3NMaXN0LHM9dC5jb250YWlucyhiKQp0LnJlbW92ZShiKQpyZXR1cm4gc30s
+CkZWOmZ1bmN0aW9uKGEsYil7Vy5UTih0aGlzLmEsdS5RLmEoYikpfX0KVy5Gay5wcm90b3R5cGU9e30K
+Vy5STy5wcm90b3R5cGU9e30KVy5ldS5wcm90b3R5cGU9e30KVy54Qy5wcm90b3R5cGU9e30KVy52Ti5w
+cm90b3R5cGU9ewokMTpmdW5jdGlvbihhKXtyZXR1cm4gdGhpcy5hLiQxKHUuQi5hKGEpKX0sCiRTOjI3
+fQpXLkpRLnByb3RvdHlwZT17CkNZOmZ1bmN0aW9uKGEpe3ZhciB0CmlmKCQub3IuYT09PTApe2Zvcih0
+PTA7dDwyNjI7Kyt0KSQub3IuWSgwLEMuY21bdF0sVy5wUygpKQpmb3IodD0wO3Q8MTI7Kyt0KSQub3Iu
+WSgwLEMuQklbdF0sVy5WNCgpKX19LAppMDpmdW5jdGlvbihhKXtyZXR1cm4gJC5BTigpLnRnKDAsVy5y
+UyhhKSl9LApFYjpmdW5jdGlvbihhLGIsYyl7dmFyIHQ9JC5vci5xKDAsSC5FaihXLnJTKGEpKSsiOjoi
+K2IpCmlmKHQ9PW51bGwpdD0kLm9yLnEoMCwiKjo6IitiKQppZih0PT1udWxsKXJldHVybiExCnJldHVy
+biBILnk4KHQuJDQoYSxiLGMsdGhpcykpfSwKJGlrRjoxfQpXLkdtLnByb3RvdHlwZT17CmdrejpmdW5j
+dGlvbihhKXtyZXR1cm4gbmV3IFcuVzkoYSx0aGlzLmdBKGEpLEgueihhKS5DKCJXOTxHbS5FPiIpKX19
+ClcudkQucHJvdG90eXBlPXsKaTA6ZnVuY3Rpb24oYSl7cmV0dXJuIEMuTm0uVnIodGhpcy5hLG5ldyBX
+LlV2KGEpKX0sCkViOmZ1bmN0aW9uKGEsYixjKXtyZXR1cm4gQy5ObS5Wcih0aGlzLmEsbmV3IFcuRWco
+YSxiLGMpKX0sCiRpa0Y6MX0KVy5Vdi5wcm90b3R5cGU9ewokMTpmdW5jdGlvbihhKXtyZXR1cm4gdS5E
+LmEoYSkuaTAodGhpcy5hKX0sCiRTOjE0fQpXLkVnLnByb3RvdHlwZT17CiQxOmZ1bmN0aW9uKGEpe3Jl
+dHVybiB1LkQuYShhKS5FYih0aGlzLmEsdGhpcy5iLHRoaXMuYyl9LAokUzoxNH0KVy5tNi5wcm90b3R5
+cGU9ewpDWTpmdW5jdGlvbihhLGIsYyxkKXt2YXIgdCxzLHIKdGhpcy5hLkZWKDAsYykKdD1iLmV2KDAs
+bmV3IFcuRW8oKSkKcz1iLmV2KDAsbmV3IFcuV2soKSkKdGhpcy5iLkZWKDAsdCkKcj10aGlzLmMKci5G
+VigwLEMueEQpCnIuRlYoMCxzKX0sCmkwOmZ1bmN0aW9uKGEpe3JldHVybiB0aGlzLmEudGcoMCxXLnJT
+KGEpKX0sCkViOmZ1bmN0aW9uKGEsYixjKXt2YXIgdD10aGlzLHM9Vy5yUyhhKSxyPXQuYwppZihyLnRn
+KDAsSC5FaihzKSsiOjoiK2IpKXJldHVybiB0LmQuRHQoYykKZWxzZSBpZihyLnRnKDAsIio6OiIrYikp
+cmV0dXJuIHQuZC5EdChjKQplbHNle3I9dC5iCmlmKHIudGcoMCxILkVqKHMpKyI6OiIrYikpcmV0dXJu
+ITAKZWxzZSBpZihyLnRnKDAsIio6OiIrYikpcmV0dXJuITAKZWxzZSBpZihyLnRnKDAsSC5FaihzKSsi
+OjoqIikpcmV0dXJuITAKZWxzZSBpZihyLnRnKDAsIio6OioiKSlyZXR1cm4hMH1yZXR1cm4hMX0sCiRp
+a0Y6MX0KVy5Fby5wcm90b3R5cGU9ewokMTpmdW5jdGlvbihhKXtyZXR1cm4hQy5ObS50ZyhDLkJJLEgu
+aChhKSl9LAokUzoxNX0KVy5Xay5wcm90b3R5cGU9ewokMTpmdW5jdGlvbihhKXtyZXR1cm4gQy5ObS50
+ZyhDLkJJLEguaChhKSl9LAokUzoxNX0KVy5jdC5wcm90b3R5cGU9ewpFYjpmdW5jdGlvbihhLGIsYyl7
+aWYodGhpcy5qRihhLGIsYykpcmV0dXJuITAKaWYoYj09PSJ0ZW1wbGF0ZSImJmM9PT0iIilyZXR1cm4h
+MAppZihhLmdldEF0dHJpYnV0ZSgidGVtcGxhdGUiKT09PSIiKXJldHVybiB0aGlzLmUudGcoMCxiKQpy
+ZXR1cm4hMX19ClcuSUEucHJvdG90eXBlPXsKJDE6ZnVuY3Rpb24oYSl7cmV0dXJuIlRFTVBMQVRFOjoi
+K0guRWooSC5oKGEpKX0sCiRTOjV9ClcuT3cucHJvdG90eXBlPXsKaTA6ZnVuY3Rpb24oYSl7dmFyIHQK
+aWYodS5ldy5iKGEpKXJldHVybiExCnQ9dS5nNy5iKGEpCmlmKHQmJlcuclMoYSk9PT0iZm9yZWlnbk9i
+amVjdCIpcmV0dXJuITEKaWYodClyZXR1cm4hMApyZXR1cm4hMX0sCkViOmZ1bmN0aW9uKGEsYixjKXtp
+ZihiPT09ImlzInx8Qy54Qi5uKGIsIm9uIikpcmV0dXJuITEKcmV0dXJuIHRoaXMuaTAoYSl9LAokaWtG
+OjF9ClcuVzkucHJvdG90eXBlPXsKRjpmdW5jdGlvbigpe3ZhciB0PXRoaXMscz10LmMrMSxyPXQuYgpp
+ZihzPHIpe3Quc3AoSi54OSh0LmEscykpCnQuYz1zCnJldHVybiEwfXQuc3AobnVsbCkKdC5jPXIKcmV0
+dXJuITF9LApnbDpmdW5jdGlvbigpe3JldHVybiB0aGlzLmR9LApzcDpmdW5jdGlvbihhKXt0aGlzLmQ9
+dGhpcy4kdGkuQygiMT8iKS5hKGEpfSwKJGlBbjoxfQpXLmRXLnByb3RvdHlwZT17CmdtVzpmdW5jdGlv
+bihhKXtyZXR1cm4gVy5ISCh0aGlzLmEubG9jYXRpb24pfSwKJGlEMDoxLAokaXY2OjF9ClcuRmIucHJv
+dG90eXBlPXt9Clcua0YucHJvdG90eXBlPXt9ClcubWsucHJvdG90eXBlPXskaXkwOjF9ClcuS28ucHJv
+dG90eXBlPXsKUG46ZnVuY3Rpb24oYSl7dmFyIHQ9dGhpcyxzPW5ldyBXLmZtKHQpCnQuYj0hMQpzLiQy
+KGEsbnVsbCkKZm9yKDt0LmI7KXt0LmI9ITEKcy4kMihhLG51bGwpfX0sCkVQOmZ1bmN0aW9uKGEsYil7
+dmFyIHQ9dGhpcy5iPSEwCmlmKGIhPW51bGw/YiE9PWEucGFyZW50Tm9kZTp0KUouTHQoYSkKZWxzZSBi
+LnJlbW92ZUNoaWxkKGEpfSwKSTQ6ZnVuY3Rpb24oYSxiKXt2YXIgdCxzLHIscSxwLG89ITAsbj1udWxs
+LG09bnVsbAp0cnl7bj1KLmlnKGEpCm09bi5hLmdldEF0dHJpYnV0ZSgiaXMiKQp1LmguYShhKQp0PWZ1
+bmN0aW9uKGMpe2lmKCEoYy5hdHRyaWJ1dGVzIGluc3RhbmNlb2YgTmFtZWROb2RlTWFwKSlyZXR1cm4g
+dHJ1ZQppZihjLmlkPT0nbGFzdENoaWxkJ3x8Yy5uYW1lPT0nbGFzdENoaWxkJ3x8Yy5pZD09J3ByZXZp
+b3VzU2libGluZyd8fGMubmFtZT09J3ByZXZpb3VzU2libGluZyd8fGMuaWQ9PSdjaGlsZHJlbid8fGMu
+bmFtZT09J2NoaWxkcmVuJylyZXR1cm4gdHJ1ZQp2YXIgbD1jLmNoaWxkTm9kZXMKaWYoYy5sYXN0Q2hp
+bGQmJmMubGFzdENoaWxkIT09bFtsLmxlbmd0aC0xXSlyZXR1cm4gdHJ1ZQppZihjLmNoaWxkcmVuKWlm
+KCEoYy5jaGlsZHJlbiBpbnN0YW5jZW9mIEhUTUxDb2xsZWN0aW9ufHxjLmNoaWxkcmVuIGluc3RhbmNl
+b2YgTm9kZUxpc3QpKXJldHVybiB0cnVlCnZhciBrPTAKaWYoYy5jaGlsZHJlbilrPWMuY2hpbGRyZW4u
+bGVuZ3RoCmZvcih2YXIgaj0wO2o8aztqKyspe3ZhciBpPWMuY2hpbGRyZW5bal0KaWYoaS5pZD09J2F0
+dHJpYnV0ZXMnfHxpLm5hbWU9PSdhdHRyaWJ1dGVzJ3x8aS5pZD09J2xhc3RDaGlsZCd8fGkubmFtZT09
+J2xhc3RDaGlsZCd8fGkuaWQ9PSdwcmV2aW91c1NpYmxpbmcnfHxpLm5hbWU9PSdwcmV2aW91c1NpYmxp
+bmcnfHxpLmlkPT0nY2hpbGRyZW4nfHxpLm5hbWU9PSdjaGlsZHJlbicpcmV0dXJuIHRydWV9cmV0dXJu
+IGZhbHNlfShhKQpvPUgub1QodCk/ITA6IShhLmF0dHJpYnV0ZXMgaW5zdGFuY2VvZiBOYW1lZE5vZGVN
+YXApfWNhdGNoKHEpe0guUnUocSl9cz0iZWxlbWVudCB1bnByaW50YWJsZSIKdHJ5e3M9Si5qKGEpfWNh
+dGNoKHEpe0guUnUocSl9dHJ5e3I9Vy5yUyhhKQp0aGlzLmtSKHUuaC5hKGEpLGIsbyxzLHIsdS5mLmEo
+biksSC5rKG0pKX1jYXRjaChxKXtpZihILlJ1KHEpIGluc3RhbmNlb2YgUC51KXRocm93IHEKZWxzZXt0
+aGlzLkVQKGEsYikKd2luZG93CnA9IlJlbW92aW5nIGNvcnJ1cHRlZCBlbGVtZW50ICIrSC5FaihzKQpp
+Zih0eXBlb2YgY29uc29sZSE9InVuZGVmaW5lZCIpd2luZG93LmNvbnNvbGUud2FybihwKX19fSwKa1I6
+ZnVuY3Rpb24oYSxiLGMsZCxlLGYsZyl7dmFyIHQscyxyLHEscCxvLG49dGhpcwppZihjKXtuLkVQKGEs
+YikKd2luZG93CnQ9IlJlbW92aW5nIGVsZW1lbnQgZHVlIHRvIGNvcnJ1cHRlZCBhdHRyaWJ1dGVzIG9u
+IDwiK2QrIj4iCmlmKHR5cGVvZiBjb25zb2xlIT0idW5kZWZpbmVkIil3aW5kb3cuY29uc29sZS53YXJu
+KHQpCnJldHVybn1pZighbi5hLmkwKGEpKXtuLkVQKGEsYikKd2luZG93CnQ9IlJlbW92aW5nIGRpc2Fs
+bG93ZWQgZWxlbWVudCA8IitILkVqKGUpKyI+IGZyb20gIitILkVqKGIpCmlmKHR5cGVvZiBjb25zb2xl
+IT0idW5kZWZpbmVkIil3aW5kb3cuY29uc29sZS53YXJuKHQpCnJldHVybn1pZihnIT1udWxsKWlmKCFu
+LmEuRWIoYSwiaXMiLGcpKXtuLkVQKGEsYikKd2luZG93CnQ9IlJlbW92aW5nIGRpc2FsbG93ZWQgdHlw
+ZSBleHRlbnNpb24gPCIrSC5FaihlKSsnIGlzPSInK2crJyI+JwppZih0eXBlb2YgY29uc29sZSE9InVu
+ZGVmaW5lZCIpd2luZG93LmNvbnNvbGUud2Fybih0KQpyZXR1cm59dD1mLmdWKCkKcz1ILlZNKHQuc2xp
+Y2UoMCksSC50Nih0KS5DKCJqZDwxPiIpKQpmb3Iocj1mLmdWKCkubGVuZ3RoLTEsdD1mLmE7cj49MDst
+LXIpe2lmKHI+PXMubGVuZ3RoKXJldHVybiBILk9IKHMscikKcT1zW3JdCnA9bi5hCm89Si5jSChxKQpI
+LmgocSkKaWYoIXAuRWIoYSxvLHQuZ2V0QXR0cmlidXRlKHEpKSl7d2luZG93CnA9IlJlbW92aW5nIGRp
+c2FsbG93ZWQgYXR0cmlidXRlIDwiK0guRWooZSkrIiAiK3ErJz0iJytILkVqKHQuZ2V0QXR0cmlidXRl
+KHEpKSsnIj4nCmlmKHR5cGVvZiBjb25zb2xlIT0idW5kZWZpbmVkIil3aW5kb3cuY29uc29sZS53YXJu
+KHApCnQucmVtb3ZlQXR0cmlidXRlKHEpfX1pZih1LmFXLmIoYSkpbi5QbihhLmNvbnRlbnQpfSwKJGlv
+bjoxfQpXLmZtLnByb3RvdHlwZT17CiQyOmZ1bmN0aW9uKGEsYil7dmFyIHQscyxyLHEscCxvLG49dGhp
+cy5hCnN3aXRjaChhLm5vZGVUeXBlKXtjYXNlIDE6bi5JNChhLGIpCmJyZWFrCmNhc2UgODpjYXNlIDEx
+OmNhc2UgMzpjYXNlIDQ6YnJlYWsKZGVmYXVsdDpuLkVQKGEsYil9dD1hLmxhc3RDaGlsZApmb3Iocj11
+LkE7bnVsbCE9dDspe3M9bnVsbAp0cnl7cz10LnByZXZpb3VzU2libGluZwppZihzIT1udWxsKXtxPXMu
+bmV4dFNpYmxpbmcKcD10CnA9cT09bnVsbD9wIT1udWxsOnEhPT1wCnE9cH1lbHNlIHE9ITEKaWYocSl7
+cT1QLlBWKCJDb3JydXB0IEhUTUwiKQp0aHJvdyBILmIocSl9fWNhdGNoKG8pe0guUnUobykKcT1yLmEo
+dCkKbi5iPSEwCnA9cS5wYXJlbnROb2RlCnA9YT09bnVsbD9wIT1udWxsOmEhPT1wCmlmKHApe3A9cS5w
+YXJlbnROb2RlCmlmKHAhPW51bGwpcC5yZW1vdmVDaGlsZChxKX1lbHNlIGEucmVtb3ZlQ2hpbGQocSkK
+dD1udWxsCnM9YS5sYXN0Q2hpbGR9aWYodCE9bnVsbCl0aGlzLiQyKHQsYSkKdD1zfX0sCiRTOjMwfQpX
+LkxlLnByb3RvdHlwZT17fQpXLks3LnByb3RvdHlwZT17fQpXLnJCLnByb3RvdHlwZT17fQpXLlhXLnBy
+b3RvdHlwZT17fQpXLm9hLnByb3RvdHlwZT17fQpQLmlKLnByb3RvdHlwZT17ClZIOmZ1bmN0aW9uKGEp
+e3ZhciB0LHM9dGhpcy5hLHI9cy5sZW5ndGgKZm9yKHQ9MDt0PHI7Kyt0KWlmKHNbdF09PT1hKXJldHVy
+biB0CkMuTm0uaShzLGEpCkMuTm0uaSh0aGlzLmIsbnVsbCkKcmV0dXJuIHJ9LApQdjpmdW5jdGlvbihh
+KXt2YXIgdCxzLHIscT10aGlzLHA9e30KaWYoYT09bnVsbClyZXR1cm4gYQppZihILmwoYSkpcmV0dXJu
+IGEKaWYodHlwZW9mIGE9PSJudW1iZXIiKXJldHVybiBhCmlmKHR5cGVvZiBhPT0ic3RyaW5nIilyZXR1
+cm4gYQppZihhIGluc3RhbmNlb2YgUC5pUClyZXR1cm4gbmV3IERhdGUoYS5hKQppZih1LmZ2LmIoYSkp
+dGhyb3cgSC5iKFAuU1koInN0cnVjdHVyZWQgY2xvbmUgb2YgUmVnRXhwIikpCmlmKHUuYzguYihhKSly
+ZXR1cm4gYQppZih1LncuYihhKSlyZXR1cm4gYQppZih1LkkuYihhKSlyZXR1cm4gYQp0PXUuZEUuYihh
+KXx8ITEKaWYodClyZXR1cm4gYQppZih1LmYuYihhKSl7cz1xLlZIKGEpCnQ9cS5iCmlmKHM+PXQubGVu
+Z3RoKXJldHVybiBILk9IKHQscykKcj1wLmE9dFtzXQppZihyIT1udWxsKXJldHVybiByCnI9e30KcC5h
+PXIKQy5ObS5ZKHQscyxyKQphLksoMCxuZXcgUC5qZyhwLHEpKQpyZXR1cm4gcC5hfWlmKHUuYi5iKGEp
+KXtzPXEuVkgoYSkKcD1xLmIKaWYocz49cC5sZW5ndGgpcmV0dXJuIEguT0gocCxzKQpyPXBbc10KaWYo
+ciE9bnVsbClyZXR1cm4gcgpyZXR1cm4gcS5layhhLHMpfWlmKHUuZUguYihhKSl7cz1xLlZIKGEpCnQ9
+cS5iCmlmKHM+PXQubGVuZ3RoKXJldHVybiBILk9IKHQscykKcj1wLmI9dFtzXQppZihyIT1udWxsKXJl
+dHVybiByCnI9e30KcC5iPXIKQy5ObS5ZKHQscyxyKQpxLmltKGEsbmV3IFAuVGEocCxxKSkKcmV0dXJu
+IHAuYn10aHJvdyBILmIoUC5TWSgic3RydWN0dXJlZCBjbG9uZSBvZiBvdGhlciB0eXBlIikpfSwKZWs6
+ZnVuY3Rpb24oYSxiKXt2YXIgdCxzPUouVTYoYSkscj1zLmdBKGEpLHE9bmV3IEFycmF5KHIpCkMuTm0u
+WSh0aGlzLmIsYixxKQpmb3IodD0wO3Q8cjsrK3QpQy5ObS5ZKHEsdCx0aGlzLlB2KHMucShhLHQpKSkK
+cmV0dXJuIHF9fQpQLmpnLnByb3RvdHlwZT17CiQyOmZ1bmN0aW9uKGEsYil7dGhpcy5hLmFbYV09dGhp
+cy5iLlB2KGIpfSwKJFM6Nn0KUC5UYS5wcm90b3R5cGU9ewokMjpmdW5jdGlvbihhLGIpe3RoaXMuYS5i
+W2FdPXRoaXMuYi5QdihiKX0sCiRTOjZ9ClAuQmYucHJvdG90eXBlPXsKaW06ZnVuY3Rpb24oYSxiKXt2
+YXIgdCxzLHIscQp1LmI4LmEoYikKZm9yKHQ9T2JqZWN0LmtleXMoYSkscz10Lmxlbmd0aCxyPTA7cjxz
+Oysrcil7cT10W3JdCmIuJDIocSxhW3FdKX19fQpQLkFzLnByb3RvdHlwZT17ClQ6ZnVuY3Rpb24oYSl7
+dmFyIHQKSC5oKGEpCnQ9JC5oRygpLmIKaWYodHlwZW9mIGEhPSJzdHJpbmciKUgudmgoSC50TChhKSkK
+aWYodC50ZXN0KGEpKXJldHVybiBhCnRocm93IEguYihQLkwzKGEsInZhbHVlIiwiTm90IGEgdmFsaWQg
+Y2xhc3MgdG9rZW4iKSl9LAp3OmZ1bmN0aW9uKGEpe3JldHVybiB0aGlzLkRHKCkuSCgwLCIgIil9LApn
+a3o6ZnVuY3Rpb24oYSl7dmFyIHQ9dGhpcy5ERygpCnJldHVybiBQLnJqKHQsdC5yLEguTGgodCkuYyl9
+LApnQTpmdW5jdGlvbihhKXtyZXR1cm4gdGhpcy5ERygpLmF9LAp0ZzpmdW5jdGlvbihhLGIpe3RoaXMu
+VChiKQpyZXR1cm4gdGhpcy5ERygpLnRnKDAsYil9LAppOmZ1bmN0aW9uKGEsYil7dmFyIHQKdGhpcy5U
+KGIpCnQ9dGhpcy5PUyhuZXcgUC5HRShiKSkKcmV0dXJuIEgueTgodD09bnVsbD8hMTp0KX0sClI6ZnVu
+Y3Rpb24oYSxiKXt2YXIgdCxzCnRoaXMuVChiKQp0PXRoaXMuREcoKQpzPXQuUigwLGIpCnRoaXMuWCh0
+KQpyZXR1cm4gc30sCkZWOmZ1bmN0aW9uKGEsYil7dGhpcy5PUyhuZXcgUC5ONyh0aGlzLHUuUS5hKGIp
+KSl9LApWMTpmdW5jdGlvbihhKXt0aGlzLk9TKG5ldyBQLnVRKCkpfSwKT1M6ZnVuY3Rpb24oYSl7dmFy
+IHQscwp1LmJVLmEoYSkKdD10aGlzLkRHKCkKcz1hLiQxKHQpCnRoaXMuWCh0KQpyZXR1cm4gc319ClAu
+R0UucHJvdG90eXBlPXsKJDE6ZnVuY3Rpb24oYSl7cmV0dXJuIHUuQy5hKGEpLmkoMCx0aGlzLmEpfSwK
+JFM6MzJ9ClAuTjcucHJvdG90eXBlPXsKJDE6ZnVuY3Rpb24oYSl7dmFyIHQ9dGhpcy5iLHM9SC50Nih0
+KQpyZXR1cm4gdS5DLmEoYSkuRlYoMCxuZXcgSC5sSih0LHMuQygicVUoMSkiKS5hKHRoaXMuYS5ndU0o
+KSkscy5DKCJsSjwxLHFVPiIpKSl9LAokUzoxNn0KUC51US5wcm90b3R5cGU9ewokMTpmdW5jdGlvbihh
+KXt1LkMuYShhKQppZihhLmE+MCl7YS5iPWEuYz1hLmQ9YS5lPWEuZj1udWxsCmEuYT0wCmEuUygpfXJl
+dHVybiBudWxsfSwKJFM6MTZ9ClAuaEYucHJvdG90eXBlPXskaWhGOjF9ClAuUEMucHJvdG90eXBlPXsK
+JDE6ZnVuY3Rpb24oYSl7dmFyIHQKdS5ZLmEoYSkKdD1mdW5jdGlvbihiLGMsZCl7cmV0dXJuIGZ1bmN0
+aW9uKCl7cmV0dXJuIGIoYyxkLHRoaXMsQXJyYXkucHJvdG90eXBlLnNsaWNlLmFwcGx5KGFyZ3VtZW50
+cykpfX0oUC5SNCxhLCExKQpQLkRtKHQsJC53KCksYSkKcmV0dXJuIHR9LAokUzo0fQpQLm10LnByb3Rv
+dHlwZT17CiQxOmZ1bmN0aW9uKGEpe3JldHVybiBuZXcgdGhpcy5hKGEpfSwKJFM6NH0KUC5Oei5wcm90
+b3R5cGU9ewokMTpmdW5jdGlvbihhKXtyZXR1cm4gbmV3IFAucjcoYSl9LAokUzozNH0KUC5RUy5wcm90
+b3R5cGU9ewokMTpmdW5jdGlvbihhKXtyZXR1cm4gbmV3IFAuVHooYSx1LmFtKX0sCiRTOjUzfQpQLm5w
+LnByb3RvdHlwZT17CiQxOmZ1bmN0aW9uKGEpe3JldHVybiBuZXcgUC5FNChhKX0sCiRTOjM2fQpQLkU0
+LnByb3RvdHlwZT17CnE6ZnVuY3Rpb24oYSxiKXtpZih0eXBlb2YgYiE9InN0cmluZyImJnR5cGVvZiBi
+IT0ibnVtYmVyIil0aHJvdyBILmIoUC54WSgicHJvcGVydHkgaXMgbm90IGEgU3RyaW5nIG9yIG51bSIp
+KQpyZXR1cm4gUC5kVSh0aGlzLmFbYl0pfSwKWTpmdW5jdGlvbihhLGIsYyl7aWYodHlwZW9mIGIhPSJz
+dHJpbmciJiZ0eXBlb2YgYiE9Im51bWJlciIpdGhyb3cgSC5iKFAueFkoInByb3BlcnR5IGlzIG5vdCBh
+IFN0cmluZyBvciBudW0iKSkKdGhpcy5hW2JdPVAud1koYyl9LApETjpmdW5jdGlvbihhLGIpe2lmKGI9
+PW51bGwpcmV0dXJuITEKcmV0dXJuIGIgaW5zdGFuY2VvZiBQLkU0JiZ0aGlzLmE9PT1iLmF9LAp3OmZ1
+bmN0aW9uKGEpe3ZhciB0LHMKdHJ5e3Q9U3RyaW5nKHRoaXMuYSkKcmV0dXJuIHR9Y2F0Y2gocyl7SC5S
+dShzKQp0PXRoaXMueGIoMCkKcmV0dXJuIHR9fSwKVjc6ZnVuY3Rpb24oYSxiKXt2YXIgdCxzPXRoaXMu
+YQppZihiPT1udWxsKXQ9bnVsbAplbHNle3Q9SC50NihiKQp0PVAuQ0gobmV3IEgubEooYix0LkMoIkAo
+MSkiKS5hKFAuaUcoKSksdC5DKCJsSjwxLEA+IikpLCEwLHUueil9cmV0dXJuIFAuZFUoc1thXS5hcHBs
+eShzLHQpKX0sCmdpTzpmdW5jdGlvbihhKXtyZXR1cm4gMH19ClAucjcucHJvdG90eXBlPXt9ClAuVHou
+cHJvdG90eXBlPXsKY1A6ZnVuY3Rpb24oYSl7dmFyIHQ9dGhpcyxzPWE8MHx8YT49dC5nQSh0KQppZihz
+KXRocm93IEguYihQLlRFKGEsMCx0LmdBKHQpLG51bGwsbnVsbCkpfSwKcTpmdW5jdGlvbihhLGIpe2lm
+KEgub2soYikpdGhpcy5jUChiKQpyZXR1cm4gdGhpcy4kdGkuYy5hKHRoaXMuVXIoMCxiKSl9LApZOmZ1
+bmN0aW9uKGEsYixjKXt0aGlzLmNQKGIpCnRoaXMuZTQoMCxiLGMpfSwKZ0E6ZnVuY3Rpb24oYSl7dmFy
+IHQ9dGhpcy5hLmxlbmd0aAppZih0eXBlb2YgdD09PSJudW1iZXIiJiZ0Pj4+MD09PXQpcmV0dXJuIHQK
+dGhyb3cgSC5iKFAuUFYoIkJhZCBKc0FycmF5IGxlbmd0aCIpKX0sCiRpYlE6MSwKJGljWDoxLAokaXpN
+OjF9ClAuY28ucHJvdG90eXBlPXt9ClAuYkIucHJvdG90eXBlPXskaWJCOjF9ClAuS2UucHJvdG90eXBl
+PXsKREc6ZnVuY3Rpb24oKXt2YXIgdCxzLHIscSxwPXRoaXMuYS5nZXRBdHRyaWJ1dGUoImNsYXNzIiks
+bz1QLkxzKHUuTikKaWYocD09bnVsbClyZXR1cm4gbwpmb3IodD1wLnNwbGl0KCIgIikscz10Lmxlbmd0
+aCxyPTA7cjxzOysrcil7cT1KLlQwKHRbcl0pCmlmKHEubGVuZ3RoIT09MClvLmkoMCxxKX1yZXR1cm4g
+b30sClg6ZnVuY3Rpb24oYSl7dGhpcy5hLnNldEF0dHJpYnV0ZSgiY2xhc3MiLGEuSCgwLCIgIikpfX0K
+UC5kNS5wcm90b3R5cGU9ewpnUDpmdW5jdGlvbihhKXtyZXR1cm4gbmV3IFAuS2UoYSl9LApzaGY6ZnVu
+Y3Rpb24oYSxiKXt0aGlzLllDKGEsYil9LApyNjpmdW5jdGlvbihhLGIsYyxkKXt2YXIgdCxzLHIscSxw
+LG8KaWYoZD09bnVsbCl7dD1ILlZNKFtdLHUucikKZD1uZXcgVy52RCh0KQpDLk5tLmkodCxXLlR3KG51
+bGwpKQpDLk5tLmkodCxXLkJsKCkpCkMuTm0uaSh0LG5ldyBXLk93KCkpfWM9bmV3IFcuS28oZCkKcz0n
+PHN2ZyB2ZXJzaW9uPSIxLjEiPicrSC5FaihiKSsiPC9zdmc+Igp0PWRvY3VtZW50CnI9dC5ib2R5CnIu
+dG9TdHJpbmcKcT1DLlJZLkFIKHIscyxjKQpwPXQuY3JlYXRlRG9jdW1lbnRGcmFnbWVudCgpCnEudG9T
dHJpbmcKdD1uZXcgVy5lNyhxKQpvPXQuZ3I4KHQpCmZvcig7dD1vLmZpcnN0Q2hpbGQsdCE9bnVsbDsp
cC5hcHBlbmRDaGlsZCh0KQpyZXR1cm4gcH0sCm56OmZ1bmN0aW9uKGEsYixjLGQsZSl7dGhyb3cgSC5i
KFAuTDQoIkNhbm5vdCBpbnZva2UgaW5zZXJ0QWRqYWNlbnRIdG1sIG9uIFNWRy4iKSl9LApnVmw6ZnVu
-Y3Rpb24oYSl7cmV0dXJuIG5ldyBXLmV1KGEsImNsaWNrIiwhMSx1LlEpfSwKJGlkNToxfQpQLm42LnBy
-b3RvdHlwZT17JGliUToxLCRpY1g6MSwkaXpNOjEsJGllcToxfQpNLkg3LnByb3RvdHlwZT17Clo6ZnVu
+Y3Rpb24oYSl7cmV0dXJuIG5ldyBXLmV1KGEsImNsaWNrIiwhMSx1LmopfSwKJGlkNToxfQpQLm42LnBy
+b3RvdHlwZT17JGliUToxLCRpY1g6MSwkaXpNOjEsJGllcToxfQpNLkg3LnByb3RvdHlwZT17Cnc6ZnVu
Y3Rpb24oYSl7cmV0dXJuIHRoaXMuYn19ClUuTEwucHJvdG90eXBlPXsKTHQ6ZnVuY3Rpb24oKXtyZXR1
-cm4gUC5FRihbIm5vZGVJZCIsdGhpcy5iLCJraW5kIix0aGlzLmEuYV0sdS5OLHUuSyl9fQpVLk1ELnBy
-b3RvdHlwZT17CiQxOmZ1bmN0aW9uKGEpe3JldHVybiB1LmdwLmEoYSkuYT09PXRoaXMuYS5xKDAsImtp
-bmQiKX0sCiRTOjM2fQpVLmQyLnByb3RvdHlwZT17Ckx0OmZ1bmN0aW9uKCl7dmFyIHQscyxyLHEscD10
-aGlzLG89dS5OLG49dS5LLG09UC5GbChvLG4pLGw9cC5hCmlmKGwhPW51bGwpe3Q9SC5WTShbXSx1Lkop
+cm4gUC5FRihbIm5vZGVJZCIsdGhpcy5iLCJraW5kIix0aGlzLmEuYV0sdS5YLHUuXyl9fQpVLk1ELnBy
+b3RvdHlwZT17CiQxOmZ1bmN0aW9uKGEpe3JldHVybiB1LmZFLmEoYSkuYT09PXRoaXMuYS5xKDAsImtp
+bmQiKX0sCiRTOjM3fQpVLmQyLnByb3RvdHlwZT17Ckx0OmZ1bmN0aW9uKCl7dmFyIHQscyxyLHEscD10
+aGlzLG89dS5YLG49dS5fLG09UC5GbChvLG4pLGw9cC5hCmlmKGwhPW51bGwpe3Q9SC5WTShbXSx1Lkcp
CmZvcihzPWwubGVuZ3RoLHI9MDtyPGwubGVuZ3RoO2wubGVuZ3RoPT09c3x8KDAsSC5saykobCksKyty
KXtxPWxbcl0KQy5ObS5pKHQsUC5FRihbImRlc2NyaXB0aW9uIixxLmEsImhyZWYiLHEuYl0sbyxuKSl9
bS5ZKDAsImVkaXRzIix0KX1tLlkoMCwiZXhwbGFuYXRpb24iLHAuYikKbS5ZKDAsImxpbmUiLHAuYykK
bS5ZKDAsImRpc3BsYXlQYXRoIixwLmQpCm0uWSgwLCJ1cmlQYXRoIixwLmUpCm89cC5mCmlmKG8hPW51
-bGwpe249SC5WTShbXSx1LkopCmZvcihsPW8ubGVuZ3RoLHI9MDtyPG8ubGVuZ3RoO28ubGVuZ3RoPT09
+bGwpe249SC5WTShbXSx1LkcpCmZvcihsPW8ubGVuZ3RoLHI9MDtyPG8ubGVuZ3RoO28ubGVuZ3RoPT09
bHx8KDAsSC5saykobyksKytyKUMuTm0uaShuLG9bcl0uTHQoKSkKbS5ZKDAsInRyYWNlcyIsbil9cmV0
dXJuIG19fQpVLlNlLnByb3RvdHlwZT17Ckx0OmZ1bmN0aW9uKCl7cmV0dXJuIFAuRUYoWyJkZXNjcmlw
-dGlvbiIsdGhpcy5hLCJocmVmIix0aGlzLmJdLHUuTix1LkspfX0KVS5NbC5wcm90b3R5cGU9ewpMdDpm
+dGlvbiIsdGhpcy5hLCJocmVmIix0aGlzLmJdLHUuWCx1Ll8pfX0KVS5NbC5wcm90b3R5cGU9ewpMdDpm
dW5jdGlvbigpe3JldHVybiBQLkVGKFsiaHJlZiIsdGhpcy5hLCJsaW5lIix0aGlzLmIsInBhdGgiLHRo
-aXMuY10sdS5OLHUuSyl9fQpVLnlELnByb3RvdHlwZT17Ckx0OmZ1bmN0aW9uKCl7dmFyIHQscyxyLHE9
-SC5WTShbXSx1LkopCmZvcih0PXRoaXMuYixzPXQubGVuZ3RoLHI9MDtyPHQubGVuZ3RoO3QubGVuZ3Ro
+aXMuY10sdS5YLHUuXyl9fQpVLnlELnByb3RvdHlwZT17Ckx0OmZ1bmN0aW9uKCl7dmFyIHQscyxyLHE9
+SC5WTShbXSx1LkcpCmZvcih0PXRoaXMuYixzPXQubGVuZ3RoLHI9MDtyPHQubGVuZ3RoO3QubGVuZ3Ro
PT09c3x8KDAsSC5saykodCksKytyKUMuTm0uaShxLHRbcl0uTHQoKSkKcmV0dXJuIFAuRUYoWyJkZXNj
-cmlwdGlvbiIsdGhpcy5hLCJlbnRyaWVzIixxXSx1Lk4sdS5LKX19ClUud2IucHJvdG90eXBlPXsKTHQ6
-ZnVuY3Rpb24oKXt2YXIgdCxzLHI9dGhpcyxxPVAuRmwodS5OLHUuSykKcS5ZKDAsImRlc2NyaXB0aW9u
-IixyLmEpCnQ9ci5iCmlmKHQhPW51bGwpcS5ZKDAsImZ1bmN0aW9uIix0KQp0PXIuYwppZih0IT1udWxs
-KXEuWSgwLCJsaW5rIix0Lkx0KCkpCnQ9ci5kCmlmKHQubGVuZ3RoIT09MCl7cz1ILnQ2KHQpCnEuWSgw
-LCJoaW50QWN0aW9ucyIsbmV3IEgubEoodCxzLkMoIlowPHFVLE1oPigxKSIpLmEobmV3IFUuYjAoKSks
-cy5DKCJsSjwxLFowPHFVLE1oPj4iKSkuYnIoMCkpfXJldHVybiBxfX0KVS5hTi5wcm90b3R5cGU9ewok
-MTpmdW5jdGlvbihhKXtyZXR1cm4gVS5ueih1LlMuYShhKSl9LAokUzozN30KVS5iMC5wcm90b3R5cGU9
-ewokMTpmdW5jdGlvbihhKXtyZXR1cm4gdS5FLmEoYSkuTHQoKX0sCiRTOjM4fQpCLmo4LnByb3RvdHlw
-ZT17Ckx0OmZ1bmN0aW9uKCl7cmV0dXJuIFAuRUYoWyJsaW5lIix0aGlzLmEsImV4cGxhbmF0aW9uIix0
-aGlzLmIsIm9mZnNldCIsdGhpcy5jXSx1Lk4sdS5LKX19CkIucXAucHJvdG90eXBlPXsKTHQ6ZnVuY3Rp
-b24oKXt2YXIgdCxzLHIscSxwLG8sbixtPXRoaXMsbD11Lk4saz1QLkZsKGwsdS5kMykKZm9yKHQ9bS5k
-LHQ9dC5nUHUodCksdD10Lmdreih0KSxzPXUuSyxyPXUuSjt0LkYoKTspe3E9dC5nbCgpCnA9cS5hCm89
-SC5WTShbXSxyKQpmb3IocT1KLklUKHEuYik7cS5GKCk7KXtuPXEuZ2woKQpDLk5tLmkobyxQLkVGKFsi
-bGluZSIsbi5hLCJleHBsYW5hdGlvbiIsbi5iLCJvZmZzZXQiLG4uY10sbCxzKSl9ay5ZKDAscCxvKX1y
-ZXR1cm4gUC5FRihbInJlZ2lvbnMiLG0uYSwibmF2aWdhdGlvbkNvbnRlbnQiLG0uYiwic291cmNlQ29k
-ZSIsbS5jLCJlZGl0cyIsa10sbCxzKX19ClQubVEucHJvdG90eXBlPXt9CkwuZS5wcm90b3R5cGU9ewok
-MTpmdW5jdGlvbihhKXt2YXIgdCxzLHIscSxwLG8sbgp1LkIuYShhKQp0PXdpbmRvdy5sb2NhdGlvbi5w
-YXRobmFtZQpzPUwuRzYod2luZG93LmxvY2F0aW9uLmhyZWYpCnI9TC5hSyh3aW5kb3cubG9jYXRpb24u
-aHJlZikKTC5HZSgpCmlmKHQhPT0iLyImJnQhPT1KLlQwKGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoIi5y
-b290IikudGV4dENvbnRlbnQpKUwuRzcodCxzLHIsITAsbmV3IEwuVlcodCxzLHIpKQpxPWRvY3VtZW50
-CnA9Si5xRihxLnF1ZXJ5U2VsZWN0b3IoIi5hcHBseS1taWdyYXRpb24iKSkKbz1wLiR0aQpuPW8uQygi
-figxKSIpLmEobmV3IEwub1ooKSkKdS5NLmEobnVsbCkKVy5KRShwLmEscC5iLG4sITEsby5jKQpvPUou
-cUYocS5xdWVyeVNlbGVjdG9yKCIucmVydW4tbWlncmF0aW9uIikpCm49by4kdGkKVy5KRShvLmEsby5i
-LG4uQygifigxKSIpLmEobmV3IEwueTgoKSksITEsbi5jKQpuPUoucUYocS5xdWVyeVNlbGVjdG9yKCIu
-cmVwb3J0LXByb2JsZW0iKSkKbz1uLiR0aQpXLkpFKG4uYSxuLmIsby5DKCJ+KDEpIikuYShuZXcgTC5I
-aSgpKSwhMSxvLmMpCnE9Si5xRihxLnF1ZXJ5U2VsZWN0b3IoIi5wb3B1cC1wYW5lIC5jbG9zZSIpKQpv
-PXEuJHRpClcuSkUocS5hLHEuYixvLkMoIn4oMSkiKS5hKG5ldyBMLkJUKCkpLCExLG8uYyl9LAokUzox
-Nn0KTC5WVy5wcm90b3R5cGU9ewokMDpmdW5jdGlvbigpe0wuRnIodGhpcy5hLHRoaXMuYix0aGlzLmMp
-fSwKJFM6MH0KTC5vWi5wcm90b3R5cGU9ewokMTpmdW5jdGlvbihhKXt2YXIgdCxzLHIscQp1LlYuYShh
-KQppZihILm9UKHdpbmRvdy5jb25maXJtKCJUaGlzIHdpbGwgYXBwbHkgdGhlIGNoYW5nZXMgeW91J3Zl
-IHByZXZpZXdlZCB0byB5b3VyIHdvcmtpbmcgZGlyZWN0b3J5LiBJdCBpcyByZWNvbW1lbmRlZCB5b3Ug
-Y29tbWl0IGFueSBjaGFuZ2VzIHlvdSBtYWRlIGJlZm9yZSBkb2luZyB0aGlzLiIpKSl7dD1MLnR5KCIv
-YXBwbHktbWlncmF0aW9uIixudWxsKS5XNyhuZXcgTC5qcigpLHUuUCkKcz1uZXcgTC5xbCgpCnUuYmYu
-YShudWxsKQpyPXQuJHRpCnE9JC5YMwppZihxIT09Qy5OVSlzPVAuVkgocyxxKQp0LnhmKG5ldyBQLkZl
-KG5ldyBQLnZzKHEsciksMixudWxsLHMsci5DKCJAPDE+IikuS3Eoci5jKS5DKCJGZTwxLDI+IikpKX19
-LAokUzozfQpMLmpyLnByb3RvdHlwZT17CiQxOmZ1bmN0aW9uKGEpe3ZhciB0CnUuUy5hKGEpCnQ9ZG9j
-dW1lbnQuYm9keQp0LmNsYXNzTGlzdC5yZW1vdmUoInByb3Bvc2VkIikKdC5jbGFzc0xpc3QuYWRkKCJh
-cHBsaWVkIil9LAokUzo0MX0KTC5xbC5wcm90b3R5cGU9ewokMjpmdW5jdGlvbihhLGIpe0wuQzIoIkNv
-dWxkIG5vdCBhcHBseSBtaWdyYXRpb24iLGEsYil9LAokQzoiJDIiLAokUjoyLAokUzo0fQpMLnk4LnBy
-b3RvdHlwZT17CiQxOmZ1bmN0aW9uKGEpe3JldHVybiB0aGlzLnhuKHUuVi5hKGEpKX0sCnhuOmZ1bmN0
-aW9uKGEpe3ZhciB0PTAscz1QLkZYKHUuUCkscj0xLHEscD1bXSxvLG4sbSxsCnZhciAkYXN5bmMkJDE9
-UC5seihmdW5jdGlvbihiLGMpe2lmKGI9PT0xKXtxPWMKdD1yfXdoaWxlKHRydWUpc3dpdGNoKHQpe2Nh
-c2UgMDpyPTMKZG9jdW1lbnQuYm9keS5jbGFzc0xpc3QuYWRkKCJyZXJ1bm5pbmciKQp0PTYKcmV0dXJu
-IFAualEoTC50eSgiL3JlcnVuLW1pZ3JhdGlvbiIsbnVsbCksJGFzeW5jJCQxKQpjYXNlIDY6d2luZG93
-LmxvY2F0aW9uLnJlbG9hZCgpCnAucHVzaCg1KQp0PTQKYnJlYWsKY2FzZSAzOnI9MgpsPXEKbz1ILlJ1
-KGwpCm49SC50cyhsKQpMLkMyKCJGYWlsZWQgdG8gcmVydW4gbWlncmF0aW9uIixvLG4pCnAucHVzaCg1
-KQp0PTQKYnJlYWsKY2FzZSAyOnA9WzFdCmNhc2UgNDpyPTEKZG9jdW1lbnQuYm9keS5jbGFzc0xpc3Qu
-cmVtb3ZlKCJyZXJ1bm5pbmciKQp0PXAucG9wKCkKYnJlYWsKY2FzZSA1OnJldHVybiBQLnlDKG51bGws
-cykKY2FzZSAxOnJldHVybiBQLmYzKHEscyl9fSkKcmV0dXJuIFAuREkoJGFzeW5jJCQxLHMpfSwKJFM6
-MTd9CkwuSGkucHJvdG90eXBlPXsKJDE6ZnVuY3Rpb24oYSl7dmFyIHQKdS5WLmEoYSkKdD11Lk4KQy5v
-bC5Qbyh3aW5kb3csUC5YZCgiaHR0cHMiLCJnaXRodWIuY29tIiwiZGFydC1sYW5nL3Nkay9pc3N1ZXMv
-bmV3IixQLkVGKFsidGl0bGUiLCJDdXN0b21lci1yZXBvcnRlZCBpc3N1ZSB3aXRoIE5OQkQgbWlncmF0
-aW9uIHRvb2wiLCJsYWJlbHMiLCJhcmVhLWFuYWx5emVyLGFuYWx5emVyLW5uYmQtbWlncmF0aW9uLHR5
-cGUtYnVnIiwiYm9keSIsIiMjIyMgU3RlcHMgdG8gcmVwcm9kdWNlXG5cbiMjIyMgV2hhdCBkaWQgeW91
-IGV4cGVjdCB0byBoYXBwZW4/XG5cbiMjIyMgV2hhdCBhY3R1YWxseSBoYXBwZW5lZD9cblxuX1NjcmVl
-bnNob3RzIGFyZSBhcHByZWNpYXRlZF9cblxuKipEYXJ0IFNESyB2ZXJzaW9uKio6ICIrSC5kKGRvY3Vt
-ZW50LmdldEVsZW1lbnRCeUlkKCJzZGstdmVyc2lvbiIpLnRleHRDb250ZW50KSsiXG5cblRoYW5rcyBm
-b3IgZmlsaW5nIVxuIl0sdCx0KSkuWigwKSwicmVwb3J0LXByb2JsZW0iKX0sCiRTOjN9CkwuQlQucHJv
-dG90eXBlPXsKJDE6ZnVuY3Rpb24oYSl7dmFyIHQKdS5WLmEoYSkKdD1kb2N1bWVudC5xdWVyeVNlbGVj
-dG9yKCIucG9wdXAtcGFuZSIpLnN0eWxlCnQuZGlzcGxheT0ibm9uZSIKcmV0dXJuIm5vbmUifSwKJFM6
-NDN9CkwuTC5wcm90b3R5cGU9ewokMTpmdW5jdGlvbihhKXt2YXIgdCxzLHIKdS5CLmEoYSkKdD13aW5k
-b3cubG9jYXRpb24ucGF0aG5hbWUKcz1MLkc2KHdpbmRvdy5sb2NhdGlvbi5ocmVmKQpyPUwuYUsod2lu
-ZG93LmxvY2F0aW9uLmhyZWYpCmlmKHQubGVuZ3RoPjEpTC5HNyh0LHMsciwhMSxudWxsKQplbHNle0wu
-QkUodCxuZXcgQi5xcCgiIiwiIiwiIixDLkNNKSwhMCkKTC5CWCgiJm5ic3A7IixudWxsKX19LAokUzox
-Nn0KTC5XeC5wcm90b3R5cGU9ewokMTpmdW5jdGlvbihhKXt2YXIgdCxzLHIscT0iY29sbGFwc2VkIgp1
-LlYuYShhKQp0PXRoaXMuYQpzPUouUkUodCkKcj10aGlzLmIKaWYoIXMuZ1AodCkudGcoMCxxKSl7cy5n
-UCh0KS5pKDAscSkKSi5kUihyKS5pKDAscSl9ZWxzZXtzLmdQKHQpLlIoMCxxKQpKLmRSKHIpLlIoMCxx
-KX19LAokUzozfQpMLkFPLnByb3RvdHlwZT17CiQxOmZ1bmN0aW9uKGEpe3ZhciB0PUoucUYodS5oLmEo
-YSkpLHM9dC4kdGkscj1zLkMoIn4oMSkiKS5hKG5ldyBMLmROKHRoaXMuYSkpCnUuTS5hKG51bGwpClcu
-SkUodC5hLHQuYixyLCExLHMuYyl9LAokUzo2fQpMLmROLnByb3RvdHlwZT17CiQxOmZ1bmN0aW9uKGEp
-e3ZhciB0CnUuVi5hKGEpCnQ9ZG9jdW1lbnQucXVlcnlTZWxlY3RvcigidGFibGVbZGF0YS1wYXRoXSIp
-CnQudG9TdHJpbmcKTC50MihhLHRoaXMuYSx0LmdldEF0dHJpYnV0ZSgiZGF0YS0iK25ldyBXLlN5KG5l
-dyBXLmk3KHQpKS5PKCJwYXRoIikpKX0sCiRTOjN9CkwuSG8ucHJvdG90eXBlPXsKJDE6ZnVuY3Rpb24o
-YSl7dmFyIHQscyxyCnUuaC5hKGEpCnQ9Si5xRihhKQpzPXQuJHRpCnI9cy5DKCJ+KDEpIikuYShuZXcg
-TC54eihhLHRoaXMuYSkpCnUuTS5hKG51bGwpClcuSkUodC5hLHQuYixyLCExLHMuYyl9LAokUzo2fQpM
-Lnh6LnByb3RvdHlwZT17CiQxOmZ1bmN0aW9uKGEpe3ZhciB0LHM9bnVsbAp1LlYuYShhKQp0PXRoaXMu
-YQpMLmhYKHRoaXMuYixQLlFBKHQuZ2V0QXR0cmlidXRlKCJkYXRhLSIrbmV3IFcuU3kobmV3IFcuaTco
-dCkpLk8oIm9mZnNldCIpKSxzLHMpLFAuUUEodC5nZXRBdHRyaWJ1dGUoImRhdGEtIituZXcgVy5TeShu
-ZXcgVy5pNyh0KSkuTygibGluZSIpKSxzLHMpKX0sCiRTOjN9CkwuSUMucHJvdG90eXBlPXsKJDE6ZnVu
-Y3Rpb24oYSl7dmFyIHQ9Si5xRih1LmguYShhKSkscz10LiR0aQpzLkMoIn4oMSkiKS5hKEwuaVMoKSkK
-dS5NLmEobnVsbCkKVy5KRSh0LmEsdC5iLEwuaVMoKSwhMSxzLmMpfSwKJFM6Nn0KTC5mQy5wcm90b3R5
-cGU9ewokMTpmdW5jdGlvbihhKXt1LkYuYShhKQp0aGlzLmEuYU0oMCx0aGlzLmIpfSwKJFM6NDV9Ckwu
-blQucHJvdG90eXBlPXsKJDA6ZnVuY3Rpb24oKXtMLkZyKHRoaXMuYS5hLHRoaXMuYix0aGlzLmMpfSwK
-JFM6MH0KTC5CWi5wcm90b3R5cGU9ewokMDpmdW5jdGlvbigpe0wuRnIodGhpcy5hLmEsbnVsbCxudWxs
-KX0sCiRTOjB9CkwuR0gucHJvdG90eXBlPXsKJDE6ZnVuY3Rpb24oYSl7dS5oLmEoYSkKJC56QigpLnRv
-U3RyaW5nCnUubS5hKCQub3coKS5xKDAsImhsanMiKSkuVjcoImhpZ2hsaWdodEJsb2NrIixbYV0pfSwK
-JFM6Nn0KTC5FRS5wcm90b3R5cGU9ewokMTpmdW5jdGlvbihhKXt2YXIgdCxzCnUuVi5hKGEpCnQ9dGhp
-cy5hCnM9dGhpcy5iCkwuYWYod2luZG93LmxvY2F0aW9uLnBhdGhuYW1lLHQscywhMCxuZXcgTC5RTCh0
-LHMpKQpMLmhYKHRoaXMuYyx0LHMpfSwKJFM6M30KTC5RTC5wcm90b3R5cGU9ewokMDpmdW5jdGlvbigp
-e0wuRnIod2luZG93LmxvY2F0aW9uLnBhdGhuYW1lLHRoaXMuYSx0aGlzLmIpfSwKJFM6MH0KTC5WUy5w
-cm90b3R5cGU9ewokMTpmdW5jdGlvbihhKXt2YXIgdCxzPSJzZWxlY3RlZC1maWxlIgp1LmguYShhKQph
-LnRvU3RyaW5nCnQ9Si5SRShhKQppZihhLmdldEF0dHJpYnV0ZSgiZGF0YS0iK25ldyBXLlN5KG5ldyBX
-Lmk3KGEpKS5PKCJuYW1lIikpPT09dGhpcy5hLmEpdC5nUChhKS5pKDAscykKZWxzZSB0LmdQKGEpLlIo
-MCxzKX0sCiRTOjZ9CkwuVEQucHJvdG90eXBlPXsKJDE6ZnVuY3Rpb24oYSl7cmV0dXJuIEwudDIodS5W
-LmEoYSksITAsbnVsbCl9LAokUzoxOH0KTC5BUy5wcm90b3R5cGU9ewokMTpmdW5jdGlvbihhKXtyZXR1
-cm4gdGhpcy5QTCh1LlYuYShhKSl9LApQTDpmdW5jdGlvbihhKXt2YXIgdD0wLHM9UC5GWCh1LlApLHI9
-MSxxLHA9W10sbz10aGlzLG4sbSxsLGssagp2YXIgJGFzeW5jJCQxPVAubHooZnVuY3Rpb24oYixjKXtp
-ZihiPT09MSl7cT1jCnQ9cn13aGlsZSh0cnVlKXN3aXRjaCh0KXtjYXNlIDA6cj0zCmw9dS5OCnQ9Ngpy
-ZXR1cm4gUC5qUShMLnR5KEwuUTQoIi9hcHBseS1oaW50IixQLkZsKGwsbCkpLG8uYS5MdCgpKSwkYXN5
-bmMkJDEpCmNhc2UgNjpsPW8uYgpMLkc3KGwuYyxudWxsLGwuYiwhMSxudWxsKQpkb2N1bWVudC5ib2R5
-LmNsYXNzTGlzdC5hZGQoIm5lZWRzLXJlcnVuIikKcj0xCnQ9NQpicmVhawpjYXNlIDM6cj0yCmo9cQpu
-PUguUnUoaikKbT1ILnRzKGopCkwuQzIoIkNvdWxkIG5vdCBhcHBseSBoaW50IixuLG0pCnQ9NQpicmVh
-awpjYXNlIDI6dD0xCmJyZWFrCmNhc2UgNTpyZXR1cm4gUC55QyhudWxsLHMpCmNhc2UgMTpyZXR1cm4g
-UC5mMyhxLHMpfX0pCnJldHVybiBQLkRJKCRhc3luYyQkMSxzKX0sCiRTOjE3fQpMLlhBLnByb3RvdHlw
-ZT17CkViOmZ1bmN0aW9uKGEsYixjKXtyZXR1cm4hMH0sCmkwOmZ1bmN0aW9uKGEpe3JldHVybiEwfSwK
-JGlrRjoxfQpMLlpaLnByb3RvdHlwZT17Ckx0OmZ1bmN0aW9uKCl7dmFyIHQscz10aGlzLHI9UC5GbCh1
-Lk4sdS5LKQpyLlkoMCwidHlwZSIsTC52eShzLmEpKQpyLlkoMCwibmFtZSIscy5iKQp0PXMuYwppZih0
-IT1udWxsKXIuWSgwLCJzdWJ0cmVlIixMLlZEKHQpKQp0PXMuZAppZih0IT1udWxsKXIuWSgwLCJwYXRo
-Iix0KQp0PXMuZQppZih0IT1udWxsKXIuWSgwLCJocmVmIix0KQp0PXMuZgppZih0IT1udWxsKXIuWSgw
-LCJlZGl0Q291bnQiLHQpCnJldHVybiByfX0KTC5POS5wcm90b3R5cGU9ewpaOmZ1bmN0aW9uKGEpe3Jl
-dHVybiB0aGlzLmJ9fQpNLmxJLnByb3RvdHlwZT17CldPOmZ1bmN0aW9uKGEsYil7dmFyIHQscz1udWxs
-Ck0uWUYoImFic29sdXRlIixILlZNKFtiLG51bGwsbnVsbCxudWxsLG51bGwsbnVsbCxudWxsXSx1LnMp
-KQp0PXRoaXMuYQp0PXQuWXIoYik+MCYmIXQuaEsoYikKaWYodClyZXR1cm4gYgp0PUQuUlgoKQpyZXR1
-cm4gdGhpcy5xNygwLHQsYixzLHMscyxzLHMscyl9LAp6ZjpmdW5jdGlvbihhKXt2YXIgdCxzLHI9WC5D
-TChhLHRoaXMuYSkKci5JVigpCnQ9ci5kCnM9dC5sZW5ndGgKaWYocz09PTApe3Q9ci5iCnJldHVybiB0
-PT1udWxsPyIuIjp0fWlmKHM9PT0xKXt0PXIuYgpyZXR1cm4gdD09bnVsbD8iLiI6dH1pZigwPj1zKXJl
-dHVybiBILmsodCwtMSkKdC5wb3AoKQpDLk5tLm12KHIuZSkKci5JVigpCnJldHVybiByLlooMCl9LApx
-NzpmdW5jdGlvbihhLGIsYyxkLGUsZixnLGgsaSl7dmFyIHQ9SC5WTShbYixjLGQsZSxmLGcsaCxpXSx1
-LnMpCk0uWUYoImpvaW4iLHQpCnJldHVybiB0aGlzLklQKG5ldyBILlU1KHQsdS5iQi5hKG5ldyBNLk1p
-KCkpLHUuY2MpKX0sCklQOmZ1bmN0aW9uKGEpe3ZhciB0LHMscixxLHAsbyxuLG0sbCxrCnUuWC5hKGEp
-CmZvcih0PWEuJHRpLHM9dC5DKCJhMihjWC5FKSIpLmEobmV3IE0ucTcoKSkscj1hLmdreihhKSx0PW5l
-dyBILnZHKHIscyx0LkMoInZHPGNYLkU+IikpLHM9dGhpcy5hLHE9ITEscD0hMSxvPSIiO3QuRigpOyl7
-bj1yLmdsKCkKaWYocy5oSyhuKSYmcCl7bT1YLkNMKG4scykKbD1vLmNoYXJDb2RlQXQoMCk9PTA/bzpv
-Cm89Qy54Qi5OaihsLDAscy5TcChsLCEwKSkKbS5iPW8KaWYocy5kcyhvKSlDLk5tLlkobS5lLDAscy5n
-bUkoKSkKbz1tLlooMCl9ZWxzZSBpZihzLllyKG4pPjApe3A9IXMuaEsobikKbz1ILmQobil9ZWxzZXtr
-PW4ubGVuZ3RoCmlmKGshPT0wKXtpZigwPj1rKXJldHVybiBILmsobiwwKQprPXMuVWQoblswXSl9ZWxz
-ZSBrPSExCmlmKCFrKWlmKHEpbys9cy5nbUkoKQpvKz1ufXE9cy5kcyhuKX1yZXR1cm4gby5jaGFyQ29k
-ZUF0KDApPT0wP286b30sCm81OmZ1bmN0aW9uKGEpe3ZhciB0CmlmKCF0aGlzLnkzKGEpKXJldHVybiBh
-CnQ9WC5DTChhLHRoaXMuYSkKdC5yUigpCnJldHVybiB0LlooMCl9LAp5MzpmdW5jdGlvbihhKXt2YXIg
-dCxzLHIscSxwLG8sbixtLGwsawphLnRvU3RyaW5nCnQ9dGhpcy5hCnM9dC5ZcihhKQppZihzIT09MCl7
-aWYodD09PSQuS2soKSlmb3Iocj0wO3I8czsrK3IpaWYoQy54Qi5XKGEscik9PT00NylyZXR1cm4hMApx
-PXMKcD00N31lbHNle3E9MApwPW51bGx9Zm9yKG89bmV3IEgucWooYSkuYSxuPW8ubGVuZ3RoLHI9cSxt
-PW51bGw7cjxuOysrcixtPXAscD1sKXtsPUMueEIubShvLHIpCmlmKHQucjQobCkpe2lmKHQ9PT0kLktr
-KCkmJmw9PT00NylyZXR1cm4hMAppZihwIT1udWxsJiZ0LnI0KHApKXJldHVybiEwCmlmKHA9PT00Nilr
-PW09PW51bGx8fG09PT00Nnx8dC5yNChtKQplbHNlIGs9ITEKaWYoaylyZXR1cm4hMH19aWYocD09bnVs
-bClyZXR1cm4hMAppZih0LnI0KHApKXJldHVybiEwCmlmKHA9PT00Nil0PW09PW51bGx8fHQucjQobSl8
-fG09PT00NgplbHNlIHQ9ITEKaWYodClyZXR1cm4hMApyZXR1cm4hMX0sCkhQOmZ1bmN0aW9uKGEsYil7
-dmFyIHQscyxyLHEscCxvLG4sbT10aGlzLGw9J1VuYWJsZSB0byBmaW5kIGEgcGF0aCB0byAiJwpiPW0u
-V08oMCxiKQp0PW0uYQppZih0LllyKGIpPD0wJiZ0LllyKGEpPjApcmV0dXJuIG0ubzUoYSkKaWYodC5Z
-cihhKTw9MHx8dC5oSyhhKSlhPW0uV08oMCxhKQppZih0LllyKGEpPD0wJiZ0LllyKGIpPjApdGhyb3cg
-SC5iKFguSTcobCtILmQoYSkrJyIgZnJvbSAiJytILmQoYikrJyIuJykpCnM9WC5DTChiLHQpCnMuclIo
-KQpyPVguQ0woYSx0KQpyLnJSKCkKcT1zLmQKcD1xLmxlbmd0aAppZihwIT09MCl7aWYoMD49cClyZXR1
-cm4gSC5rKHEsMCkKcT1KLlJNKHFbMF0sIi4iKX1lbHNlIHE9ITEKaWYocSlyZXR1cm4gci5aKDApCnE9
-cy5iCnA9ci5iCmlmKHEhPXApcT1xPT1udWxsfHxwPT1udWxsfHwhdC5OYyhxLHApCmVsc2UgcT0hMQpp
-ZihxKXJldHVybiByLlooMCkKd2hpbGUoITApe3E9cy5kCnA9cS5sZW5ndGgKaWYocCE9PTApe289ci5k
-Cm49by5sZW5ndGgKaWYobiE9PTApe2lmKDA+PXApcmV0dXJuIEguayhxLDApCnE9cVswXQppZigwPj1u
-KXJldHVybiBILmsobywwKQpvPXQuTmMocSxvWzBdKQpxPW99ZWxzZSBxPSExfWVsc2UgcT0hMQppZigh
-cSlicmVhawpDLk5tLlc0KHMuZCwwKQpDLk5tLlc0KHMuZSwxKQpDLk5tLlc0KHIuZCwwKQpDLk5tLlc0
-KHIuZSwxKX1xPXMuZApwPXEubGVuZ3RoCmlmKHAhPT0wKXtpZigwPj1wKXJldHVybiBILmsocSwwKQpx
-PUouUk0ocVswXSwiLi4iKX1lbHNlIHE9ITEKaWYocSl0aHJvdyBILmIoWC5JNyhsK0guZChhKSsnIiBm
-cm9tICInK0guZChiKSsnIi4nKSkKcT11Lk4KQy5ObS5VRyhyLmQsMCxQLk84KHMuZC5sZW5ndGgsIi4u
-IiwhMSxxKSkKQy5ObS5ZKHIuZSwwLCIiKQpDLk5tLlVHKHIuZSwxLFAuTzgocy5kLmxlbmd0aCx0Lmdt
-SSgpLCExLHEpKQp0PXIuZApxPXQubGVuZ3RoCmlmKHE9PT0wKXJldHVybiIuIgppZihxPjEmJkouUk0o
-Qy5ObS5ncloodCksIi4iKSl7dD1yLmQKaWYoMD49dC5sZW5ndGgpcmV0dXJuIEguayh0LC0xKQp0LnBv
-cCgpCnQ9ci5lCkMuTm0ubXYodCkKQy5ObS5tdih0KQpDLk5tLmkodCwiIil9ci5iPSIiCnIuSVYoKQpy
-ZXR1cm4gci5aKDApfX0KTS5NaS5wcm90b3R5cGU9ewokMTpmdW5jdGlvbihhKXtyZXR1cm4gSC5jKGEp
-IT1udWxsfSwKJFM6N30KTS5xNy5wcm90b3R5cGU9ewokMTpmdW5jdGlvbihhKXtyZXR1cm4gSC5jKGEp
-IT09IiJ9LAokUzo3fQpNLk5vLnByb3RvdHlwZT17CiQxOmZ1bmN0aW9uKGEpe0guYyhhKQpyZXR1cm4g
-YT09bnVsbD8ibnVsbCI6JyInK2ErJyInfSwKJFM6NX0KQi5mdi5wcm90b3R5cGU9ewp4WjpmdW5jdGlv
-bihhKXt2YXIgdCxzPXRoaXMuWXIoYSkKaWYocz4wKXJldHVybiBKLmxkKGEsMCxzKQppZih0aGlzLmhL
-KGEpKXtpZigwPj1hLmxlbmd0aClyZXR1cm4gSC5rKGEsMCkKdD1hWzBdfWVsc2UgdD1udWxsCnJldHVy
-biB0fSwKTmM6ZnVuY3Rpb24oYSxiKXtyZXR1cm4gYT09Yn19ClguV0QucHJvdG90eXBlPXsKSVY6ZnVu
-Y3Rpb24oKXt2YXIgdCxzLHI9dGhpcwp3aGlsZSghMCl7dD1yLmQKaWYoISh0Lmxlbmd0aCE9PTAmJkou
-Uk0oQy5ObS5ncloodCksIiIpKSlicmVhawp0PXIuZAppZigwPj10Lmxlbmd0aClyZXR1cm4gSC5rKHQs
-LTEpCnQucG9wKCkKQy5ObS5tdihyLmUpfXQ9ci5lCnM9dC5sZW5ndGgKaWYocyE9PTApQy5ObS5ZKHQs
-cy0xLCIiKX0sCnJSOmZ1bmN0aW9uKCl7dmFyIHQscyxyLHEscCxvLG49dGhpcyxtPUguVk0oW10sdS5z
-KQpmb3IodD1uLmQscz10Lmxlbmd0aCxyPTAscT0wO3E8dC5sZW5ndGg7dC5sZW5ndGg9PT1zfHwoMCxI
-LmxrKSh0KSwrK3Epe3A9dFtxXQpvPUouaWEocCkKaWYoIShvLkROKHAsIi4iKXx8by5ETihwLCIiKSkp
-aWYoby5ETihwLCIuLiIpKXtvPW0ubGVuZ3RoCmlmKG8hPT0wKXtpZigwPj1vKXJldHVybiBILmsobSwt
-MSkKbS5wb3AoKX1lbHNlICsrcn1lbHNlIEMuTm0uaShtLHApfWlmKG4uYj09bnVsbClDLk5tLlVHKG0s
-MCxQLk84KHIsIi4uIiwhMSx1Lk4pKQppZihtLmxlbmd0aD09PTAmJm4uYj09bnVsbClDLk5tLmkobSwi
-LiIpCm4uc25KKG0pCnQ9bi5hCm4uc1BoKFAuTzgobS5sZW5ndGgrMSx0LmdtSSgpLCEwLHUuTikpCnM9
-bi5iCmlmKHM9PW51bGx8fG0ubGVuZ3RoPT09MHx8IXQuZHMocykpQy5ObS5ZKG4uZSwwLCIiKQpzPW4u
-YgppZihzIT1udWxsJiZ0PT09JC5LaygpKXtzLnRvU3RyaW5nCm4uYj1ILnlzKHMsIi8iLCJcXCIpfW4u
-SVYoKX0sClo6ZnVuY3Rpb24oYSl7dmFyIHQscyxyPXRoaXMscT1yLmIKcT1xIT1udWxsP3E6IiIKZm9y
-KHQ9MDt0PHIuZC5sZW5ndGg7Kyt0KXtzPXIuZQppZih0Pj1zLmxlbmd0aClyZXR1cm4gSC5rKHMsdCkK
-cz1xK0guZChzW3RdKQpxPXIuZAppZih0Pj1xLmxlbmd0aClyZXR1cm4gSC5rKHEsdCkKcT1zK0guZChx
-W3RdKX1xKz1ILmQoQy5ObS5nclooci5lKSkKcmV0dXJuIHEuY2hhckNvZGVBdCgwKT09MD9xOnF9LApz
-bko6ZnVuY3Rpb24oYSl7dGhpcy5kPXUuYS5hKGEpfSwKc1BoOmZ1bmN0aW9uKGEpe3RoaXMuZT11LmEu
-YShhKX19ClguZHYucHJvdG90eXBlPXsKWjpmdW5jdGlvbihhKXtyZXR1cm4iUGF0aEV4Y2VwdGlvbjog
-Iit0aGlzLmF9LAokaVJ6OjF9Ck8uekwucHJvdG90eXBlPXsKWjpmdW5jdGlvbihhKXtyZXR1cm4gdGhp
-cy5nb2ModGhpcyl9fQpFLk9GLnByb3RvdHlwZT17ClVkOmZ1bmN0aW9uKGEpe3JldHVybiBDLnhCLnRn
-KGEsIi8iKX0sCnI0OmZ1bmN0aW9uKGEpe3JldHVybiBhPT09NDd9LApkczpmdW5jdGlvbihhKXt2YXIg
-dD1hLmxlbmd0aApyZXR1cm4gdCE9PTAmJkMueEIubShhLHQtMSkhPT00N30sClNwOmZ1bmN0aW9uKGEs
-Yil7aWYoYS5sZW5ndGghPT0wJiZDLnhCLlcoYSwwKT09PTQ3KXJldHVybiAxCnJldHVybiAwfSwKWXI6
-ZnVuY3Rpb24oYSl7cmV0dXJuIHRoaXMuU3AoYSwhMSl9LApoSzpmdW5jdGlvbihhKXtyZXR1cm4hMX0s
-CmdvYzpmdW5jdGlvbigpe3JldHVybiJwb3NpeCJ9LApnbUk6ZnVuY3Rpb24oKXtyZXR1cm4iLyJ9fQpG
-LnJ1LnByb3RvdHlwZT17ClVkOmZ1bmN0aW9uKGEpe3JldHVybiBDLnhCLnRnKGEsIi8iKX0sCnI0OmZ1
-bmN0aW9uKGEpe3JldHVybiBhPT09NDd9LApkczpmdW5jdGlvbihhKXt2YXIgdD1hLmxlbmd0aAppZih0
-PT09MClyZXR1cm4hMQppZihDLnhCLm0oYSx0LTEpIT09NDcpcmV0dXJuITAKcmV0dXJuIEMueEIuVGMo
-YSwiOi8vIikmJnRoaXMuWXIoYSk9PT10fSwKU3A6ZnVuY3Rpb24oYSxiKXt2YXIgdCxzLHIscSxwPWEu
-bGVuZ3RoCmlmKHA9PT0wKXJldHVybiAwCmlmKEMueEIuVyhhLDApPT09NDcpcmV0dXJuIDEKZm9yKHQ9
-MDt0PHA7Kyt0KXtzPUMueEIuVyhhLHQpCmlmKHM9PT00NylyZXR1cm4gMAppZihzPT09NTgpe2lmKHQ9
-PT0wKXJldHVybiAwCnI9Qy54Qi5YVShhLCIvIixDLnhCLlFpKGEsIi8vIix0KzEpP3QrMzp0KQppZihy
-PD0wKXJldHVybiBwCmlmKCFifHxwPHIrMylyZXR1cm4gcgppZighQy54Qi5uKGEsImZpbGU6Ly8iKSly
-ZXR1cm4gcgppZighQi5ZdShhLHIrMSkpcmV0dXJuIHIKcT1yKzMKcmV0dXJuIHA9PT1xP3E6cis0fX1y
-ZXR1cm4gMH0sCllyOmZ1bmN0aW9uKGEpe3JldHVybiB0aGlzLlNwKGEsITEpfSwKaEs6ZnVuY3Rpb24o
-YSl7cmV0dXJuIGEubGVuZ3RoIT09MCYmQy54Qi5XKGEsMCk9PT00N30sCmdvYzpmdW5jdGlvbigpe3Jl
-dHVybiJ1cmwifSwKZ21JOmZ1bmN0aW9uKCl7cmV0dXJuIi8ifX0KTC5JVi5wcm90b3R5cGU9ewpVZDpm
-dW5jdGlvbihhKXtyZXR1cm4gQy54Qi50ZyhhLCIvIil9LApyNDpmdW5jdGlvbihhKXtyZXR1cm4gYT09
-PTQ3fHxhPT09OTJ9LApkczpmdW5jdGlvbihhKXt2YXIgdD1hLmxlbmd0aAppZih0PT09MClyZXR1cm4h
-MQp0PUMueEIubShhLHQtMSkKcmV0dXJuISh0PT09NDd8fHQ9PT05Mil9LApTcDpmdW5jdGlvbihhLGIp
-e3ZhciB0LHMscj1hLmxlbmd0aAppZihyPT09MClyZXR1cm4gMAp0PUMueEIuVyhhLDApCmlmKHQ9PT00
-NylyZXR1cm4gMQppZih0PT09OTIpe2lmKHI8Mnx8Qy54Qi5XKGEsMSkhPT05MilyZXR1cm4gMQpzPUMu
-eEIuWFUoYSwiXFwiLDIpCmlmKHM+MCl7cz1DLnhCLlhVKGEsIlxcIixzKzEpCmlmKHM+MClyZXR1cm4g
-c31yZXR1cm4gcn1pZihyPDMpcmV0dXJuIDAKaWYoIUIuT1ModCkpcmV0dXJuIDAKaWYoQy54Qi5XKGEs
-MSkhPT01OClyZXR1cm4gMApyPUMueEIuVyhhLDIpCmlmKCEocj09PTQ3fHxyPT09OTIpKXJldHVybiAw
-CnJldHVybiAzfSwKWXI6ZnVuY3Rpb24oYSl7cmV0dXJuIHRoaXMuU3AoYSwhMSl9LApoSzpmdW5jdGlv
-bihhKXtyZXR1cm4gdGhpcy5ZcihhKT09PTF9LApPdDpmdW5jdGlvbihhLGIpe3ZhciB0CmlmKGE9PT1i
-KXJldHVybiEwCmlmKGE9PT00NylyZXR1cm4gYj09PTkyCmlmKGE9PT05MilyZXR1cm4gYj09PTQ3Cmlm
-KChhXmIpIT09MzIpcmV0dXJuITEKdD1hfDMyCnJldHVybiB0Pj05NyYmdDw9MTIyfSwKTmM6ZnVuY3Rp
-b24oYSxiKXt2YXIgdCxzLHIKaWYoYT09YilyZXR1cm4hMAp0PWEubGVuZ3RoCmlmKHQhPT1iLmxlbmd0
-aClyZXR1cm4hMQpmb3Iocz1KLnJZKGIpLHI9MDtyPHQ7KytyKWlmKCF0aGlzLk90KEMueEIuVyhhLHIp
-LHMuVyhiLHIpKSlyZXR1cm4hMQpyZXR1cm4hMH0sCmdvYzpmdW5jdGlvbigpe3JldHVybiJ3aW5kb3dz
-In0sCmdtSTpmdW5jdGlvbigpe3JldHVybiJcXCJ9fTsoZnVuY3Rpb24gYWxpYXNlcygpe3ZhciB0PUou
-dkIucHJvdG90eXBlCnQuVT10LloKdC5Taj10LmU3CnQ9Si5NRi5wcm90b3R5cGUKdC50PXQuWgp0PVAu
-Y1gucHJvdG90eXBlCnQuR0c9dC5ldgp0PVAuTWgucHJvdG90eXBlCnQueGI9dC5aCnQ9Vy5jdi5wcm90
-b3R5cGUKdC5EVz10LnI2CnQ9Vy5tNi5wcm90b3R5cGUKdC5qRj10LkViCnQ9UC5FNC5wcm90b3R5cGUK
-dC5Vcj10LnEKdC5lND10Lll9KSgpOyhmdW5jdGlvbiBpbnN0YWxsVGVhck9mZnMoKXt2YXIgdD1odW5r
-SGVscGVycy5fc3RhdGljXzEscz1odW5rSGVscGVycy5fc3RhdGljXzAscj1odW5rSGVscGVycy5pbnN0
-YWxsSW5zdGFuY2VUZWFyT2ZmLHE9aHVua0hlbHBlcnMuaW5zdGFsbFN0YXRpY1RlYXJPZmYscD1odW5r
-SGVscGVycy5faW5zdGFuY2VfMXUKdChQLCJFWCIsIlpWIiw4KQp0KFAsInl0Iiwib0EiLDgpCnQoUCwi
-cVciLCJCeiIsOCkKcyhQLCJWOSIsImVOIiwyKQpyKFAuUGYucHJvdG90eXBlLCJnWUoiLDAsMSxudWxs
-LFsiJDIiLCIkMSJdLFsidzAiLCJwbSJdLDI4LDApCnQoUCwiQ3kiLCJOQyIsMSkKdChQLCJQSCIsIk10
-Iiw1KQpxKFcsInBTIiw0LG51bGwsWyIkNCJdLFsicUQiXSw5LDApCnEoVywiVjQiLDQsbnVsbCxbIiQ0
-Il0sWyJRVyJdLDksMCkKcChQLkFzLnByb3RvdHlwZSwiZ3VNIiwiVCIsNSkKdChQLCJpRyIsIndZIiwx
-KQp0KFAsIncwIiwiTDciLDMyKQp0KEwsImlTIiwiaTYiLDE4KX0pKCk7KGZ1bmN0aW9uIGluaGVyaXRh
-bmNlKCl7dmFyIHQ9aHVua0hlbHBlcnMubWl4aW4scz1odW5rSGVscGVycy5pbmhlcml0LHI9aHVua0hl
-bHBlcnMuaW5oZXJpdE1hbnkKcyhQLk1oLG51bGwpCnIoUC5NaCxbSC5GSyxKLnZCLEoubTEsUC5uWSxQ
-LmNYLEguYTcsUC5BbixILlNVLEguUmUsSC53dixQLlBuLEguV1UsSC5MSSxILlRwLEguZjksUC5YUyxI
-LmJxLEguWE8sUC5ZayxILmRiLEguTjYsSC5WUixILkVLLEguUGIsSC50USxILlNkLEguSmMsSC5FVCxQ
-LlczLFAuaWgsUC5GeSxQLkdWLFAuYjgsUC5QZixQLkZlLFAudnMsUC5PTSxQLnFoLFAuTU8sUC5rVCxQ
-LnhJLFAuT0gsUC5tMCxQLlh2LFAuYm4sUC5sbSxQLmxELFAuS1AsUC5NYSxQLlRDLFAuVWssUC5TaCxQ
-LlJ3LFAuYnosUC5hMixQLmlQLFAubGYsUC5rNSxQLktZLFAuQ0QsUC5hRSxQLkVILFAuek0sUC5aMCxQ
-Lk4zLFAuYzgsUC5PZCxQLmliLFAuR3osUC5aZCxQLnFVLFAuUm4sUC5HRCxQLkRuLFAuUEUsUC5VZixX
-LmlkLFcuRmssVy5KUSxXLkdtLFcudkQsVy5tNixXLk93LFcuVzksVy5kVyxXLkZiLFcua0YsVy5tayxX
-LktvLFAuaUosUC5FNCxQLm42LE0uSDcsVS5MTCxVLmQyLFUuU2UsVS5NbCxVLnlELFUud2IsQi5qOCxC
-LnFwLFQubVEsTC5YQSxMLlpaLEwuTzksTS5sSSxPLnpMLFguV0QsWC5kdl0pCnIoSi52QixbSi55RSxK
-LllFLEouTUYsSi5qZCxKLnFJLEouRHIsSC5lSCxXLkQwLFcuQXosVy5MZSxXLk5oLFcuSUIsVy5uNyxX
-LmVhLFcuYnIsVy5TZyxXLnU4LFcuSzcsVy5YVyxQLmhGXSkKcihKLk1GLFtKLmlDLEoua2QsSi5jNV0p
-CnMoSi5QbyxKLmpkKQpyKEoucUksW0ouYlUsSi5WQV0pCnMoUC51eSxQLm5ZKQpyKFAudXksW0gudzIs
-Vy53eixXLmU3XSkKcyhILnFqLEgudzIpCnIoUC5jWCxbSC5iUSxILmkxLEguVTUsSC5YUixQLm1XLEgu
-TkZdKQpyKEguYlEsW0guYUwsSC5pNSxQLnh1XSkKcihILmFMLFtILm5ILEgubEosUC5pOF0pCnMoSC54
-eSxILmkxKQpyKFAuQW4sW0guTUgsSC52R10pCnMoUC5SVSxQLlBuKQpzKFAuR2osUC5SVSkKcyhILlBE
-LFAuR2opCnMoSC5MUCxILldVKQpyKEguVHAsW0guQ2osSC5BbSxILmxjLEgucixILmRDLEgud04sUC50
-aCxQLmhhLFAuVnMsUC5GdCxQLnlILFAuV00sUC5TWCxQLkdzLFAuZGEsUC5vUSxQLnBWLFAuVTcsUC52
-cixQLnJILFAuS0YsUC5aTCxQLlJULFAualosUC5ycSxQLlJXLFAuQjUsUC51TyxQLnBLLFAuaGosUC5W
-cCxQLk9SLFAucmEsUC55USxQLnRpLFAuV0YsUC5uMSxQLmNTLFAuVkMsUC5KVCxQLmUxLFAuTlksUC5S
-WixQLk1FLFAueTUsUC5xMyxQLnlJLFAuYzYsUC5xZCxXLkN2LFcuS1MsVy5BMyxXLnZOLFcuVXYsVy5F
-ZyxXLkVvLFcuV2ssVy5JQSxXLmZtLFAuamcsUC5UYSxQLkdFLFAuTjcsUC51USxQLlBDLFAubXQsUC5O
-eixQLlFTLFAubnAsVS5NRCxVLmFOLFUuYjAsTC5lLEwuVlcsTC5vWixMLmpyLEwucWwsTC55OCxMLkhp
-LEwuQlQsTC5MLEwuV3gsTC5BTyxMLmROLEwuSG8sTC54eixMLklDLEwuZkMsTC5uVCxMLkJaLEwuR0gs
-TC5FRSxMLlFMLEwuVlMsTC5URCxMLkFTLE0uTWksTS5xNyxNLk5vXSkKcihQLlhTLFtILlcwLEguYXos
-SC52VixILkVxLFAuQzYsSC51OSxQLlVkLFAuTEssUC5BVCxQLm1wLFAudWIsUC5kcyxQLmxqLFAuVVYs
-UC50N10pCnIoSC5sYyxbSC56eCxILmp5XSkKcyhILmtZLFAuQzYpCnMoUC5pbCxQLllrKQpyKFAuaWws
-W0guTjUsUC51dyxXLmNmLFcuU3ldKQpyKFAubVcsW0guS1csUC5xNF0pCnMoSC5MWixILmVIKQpyKEgu
-TFosW0guUkcsSC5XQl0pCnMoSC5WUCxILlJHKQpzKEguRGcsSC5WUCkKcyhILlpHLEguV0IpCnMoSC5Q
-ZyxILlpHKQpyKEguUGcsW0gueGosSC5kRSxILlpBLEgud2YsSC5QcSxILmVFLEguVjZdKQpzKEgueCxI
-LnU5KQpzKFAuWmYsUC5QZikKcyhQLkppLFAubTApCnMoUC5iNixQLlh2KQpzKFAuVmosUC5UQykKcihQ
-LlVrLFtQLkNWLFAuWmksUC5ieV0pCnMoUC53SSxQLmtUKQpyKFAud0ksW1AuVTgsUC5vaixQLk14LFAu
-RTMsUC5HWV0pCnMoUC5LOCxQLlVkKQpzKFAudHUsUC5TaCkKcyhQLnU1LFAuWmkpCnIoUC5sZixbUC5D
-UCxQLklmXSkKcihQLkFULFtQLmJKLFAuZVldKQpzKFAucWUsUC5EbikKcihXLkQwLFtXLnVILFcud2Es
-Vy5LNSxXLkNtXSkKcihXLnVILFtXLmN2LFcubngsVy5RRixXLkNRXSkKcihXLmN2LFtXLnFFLFAuZDVd
-KQpyKFcucUUsW1cuR2gsVy5mWSxXLm5CLFcuUVAsVy5oNCxXLlNOLFcubHAsVy5UYixXLkl2LFcuV1As
-Vy55WV0pCnMoVy5vSixXLkxlKQpzKFcuaEgsVy5BeikKcyhXLlZiLFcuUUYpCnMoVy5mSixXLndhKQpy
-KFcuZWEsW1cudzYsVy5ld10pCnMoVy5PSyxXLnc2KQpzKFcuckIsVy5LNykKcyhXLkJILFcuckIpCnMo
-Vy53NCxXLklCKQpzKFcub2EsVy5YVykKcyhXLnJoLFcub2EpCnMoVy5pNyxXLmNmKQpzKFAuQXMsUC5W
-aikKcihQLkFzLFtXLkk0LFAuS2VdKQpzKFcuUk8sUC5xaCkKcyhXLmV1LFcuUk8pCnMoVy54QyxQLk1P
-KQpzKFcuY3QsVy5tNikKcyhQLkJmLFAuaUopCnIoUC5FNCxbUC5yNyxQLmNvXSkKcyhQLlR6LFAuY28p
-CnMoUC5uZCxQLmQ1KQpzKEIuZnYsTy56TCkKcihCLmZ2LFtFLk9GLEYucnUsTC5JVl0pCnQoSC53MixI
-LlJlKQp0KEguUkcsUC5sRCkKdChILlZQLEguU1UpCnQoSC5XQixQLmxEKQp0KEguWkcsSC5TVSkKdChQ
-Lm5ZLFAubEQpCnQoUC5UQyxQLk1hKQp0KFAuUlUsUC5LUCkKdChXLkxlLFcuaWQpCnQoVy5LNyxQLmxE
-KQp0KFcuckIsVy5HbSkKdChXLlhXLFAubEQpCnQoVy5vYSxXLkdtKQp0KFAuY28sUC5sRCl9KSgpCnZh
-ciB2PXt0eXBlVW5pdmVyc2U6e2VDOm5ldyBNYXAoKSx0Ujp7fSxlVDp7fSx0UFY6e30sc0VBOltdfSxt
-YW5nbGVkR2xvYmFsTmFtZXM6e0lmOiJpbnQiLENQOiJkb3VibGUiLGxmOiJudW0iLHFVOiJTdHJpbmci
-LGEyOiJib29sIixjODoiTnVsbCIsek06Ikxpc3QifSxtYW5nbGVkTmFtZXM6e30sZ2V0VHlwZUZyb21O
-YW1lOmdldEdsb2JhbEZyb21OYW1lLG1ldGFkYXRhOltdLHR5cGVzOlsiYzgoKSIsIkAoQCkiLCJ+KCki
-LCJjOChPSykiLCJjOChALEApIiwicVUocVUpIiwiYzgoY3YpIiwiYTIocVUpIiwifih+KCkpIiwiYTIo
-Y3YscVUscVUsSlEpIiwiYzgoQCkiLCJ+KHh1PHFVPikiLCJjOChxVSxAKSIsImM4KHFVKSIsImM4KHFV
-LHFVKSIsImEyKGtGKSIsImM4KGVhKSIsImI4PGM4PihPSykiLCJ+KE9LKSIsIlowPHFVLHFVPihaMDxx
-VSxxVT4scVUpIiwiYzgofigpKSIsImM4KEAsR3opIiwifihxVSxxVSkiLCJuNihJZikiLCJuNihALEAp
-IiwiYTIodUgpIiwiYzgoSWYsQCkiLCJAKGVhKSIsIn4oTWhbR3pdKSIsImM4KEBbR3pdKSIsIn4odUgs
-dUgpIiwiYTIoeHU8cVU+KSIsIk1oKEApIiwicjcoQCkiLCJUejxAPihAKSIsIkU0KEApIiwiYTIoSDcp
-IiwiTEwoQCkiLCJaMDxxVSxNaD4oTEwpIiwidnM8QD4oQCkiLCJAKHFVKSIsImM4KFowPHFVLE1oPiki
-LCJjOChHRCxAKSIsInFVKE9LKSIsIkAoQCxxVSkiLCJjOChldykiLCJ+KHFVLElmKSIsIn4ocVVbQF0p
-IiwiSWYoSWYsSWYpIiwifihAKSJdLGludGVyY2VwdG9yc0J5VGFnOm51bGwsbGVhZlRhZ3M6bnVsbCxh
-cnJheVJ0aTp0eXBlb2YgU3ltYm9sPT0iZnVuY3Rpb24iJiZ0eXBlb2YgU3ltYm9sKCk9PSJzeW1ib2wi
-P1N5bWJvbCgiJHRpIik6IiR0aSJ9CkgueGIodi50eXBlVW5pdmVyc2UsSlNPTi5wYXJzZSgneyJjNSI6
-Ik1GIiwiaUMiOiJNRiIsImtkIjoiTUYiLCJyeCI6ImVhIiwiZTUiOiJlYSIsIlkwIjoiZDUiLCJ0cCI6
-ImQ1IiwiRzgiOiJldyIsIk1yIjoicUUiLCJlTCI6InFFIiwiSTAiOiJ1SCIsImhzIjoidUgiLCJYZyI6
-IlFGIiwibnIiOiJPSyIsInk0IjoidzYiLCJhUCI6IkNtIiwieGMiOiJueCIsImtKIjoibngiLCJ6VSI6
-IkRnIiwiZGYiOiJlSCIsInlFIjp7ImEyIjpbXX0sIllFIjp7ImM4IjpbXX0sIk1GIjp7InZtIjpbXSwi
-RUgiOltdfSwiamQiOnsiek0iOlsiMSJdLCJiUSI6WyIxIl0sImNYIjpbIjEiXX0sIlBvIjp7ImpkIjpb
-IjEiXSwiek0iOlsiMSJdLCJiUSI6WyIxIl0sImNYIjpbIjEiXX0sIm0xIjp7IkFuIjpbIjEiXX0sInFJ
-Ijp7IkNQIjpbXSwibGYiOltdfSwiYlUiOnsiSWYiOltdLCJDUCI6W10sImxmIjpbXX0sIlZBIjp7IkNQ
-IjpbXSwibGYiOltdfSwiRHIiOnsicVUiOltdLCJ2WCI6W119LCJxaiI6eyJSZSI6WyJJZiJdLCJsRCI6
-WyJJZiJdLCJ6TSI6WyJJZiJdLCJiUSI6WyJJZiJdLCJjWCI6WyJJZiJdLCJsRC5FIjoiSWYiLCJSZS5F
-IjoiSWYifSwiYlEiOnsiY1giOlsiMSJdfSwiYUwiOnsiYlEiOlsiMSJdLCJjWCI6WyIxIl19LCJuSCI6
-eyJhTCI6WyIxIl0sImJRIjpbIjEiXSwiY1giOlsiMSJdLCJhTC5FIjoiMSIsImNYLkUiOiIxIn0sImE3
-Ijp7IkFuIjpbIjEiXX0sImkxIjp7ImNYIjpbIjIiXSwiY1guRSI6IjIifSwieHkiOnsiaTEiOlsiMSIs
-IjIiXSwiYlEiOlsiMiJdLCJjWCI6WyIyIl0sImNYLkUiOiIyIn0sIk1IIjp7IkFuIjpbIjIiXX0sImxK
-Ijp7ImFMIjpbIjIiXSwiYlEiOlsiMiJdLCJjWCI6WyIyIl0sImFMLkUiOiIyIiwiY1guRSI6IjIifSwi
-VTUiOnsiY1giOlsiMSJdLCJjWC5FIjoiMSJ9LCJ2RyI6eyJBbiI6WyIxIl19LCJ3MiI6eyJSZSI6WyIx
-Il0sImxEIjpbIjEiXSwiek0iOlsiMSJdLCJiUSI6WyIxIl0sImNYIjpbIjEiXX0sInd2Ijp7IkdEIjpb
-XX0sIlBEIjp7IkdqIjpbIjEiLCIyIl0sIlJVIjpbIjEiLCIyIl0sIlBuIjpbIjEiLCIyIl0sIktQIjpb
-IjEiLCIyIl0sIlowIjpbIjEiLCIyIl19LCJXVSI6eyJaMCI6WyIxIiwiMiJdfSwiTFAiOnsiV1UiOlsi
-MSIsIjIiXSwiWjAiOlsiMSIsIjIiXX0sIlhSIjp7ImNYIjpbIjEiXSwiY1guRSI6IjEifSwiTEkiOnsi
-dlEiOltdfSwiVzAiOnsiWFMiOltdfSwiYXoiOnsiWFMiOltdfSwidlYiOnsiWFMiOltdfSwiWE8iOnsi
-R3oiOltdfSwiVHAiOnsiRUgiOltdfSwibGMiOnsiRUgiOltdfSwiengiOnsiRUgiOltdfSwiankiOnsi
-RUgiOltdfSwiRXEiOnsiWFMiOltdfSwia1kiOnsiWFMiOltdfSwiTjUiOnsiRm8iOlsiMSIsIjIiXSwi
-WWsiOlsiMSIsIjIiXSwiWjAiOlsiMSIsIjIiXSwiWWsuSyI6IjEiLCJZay5WIjoiMiJ9LCJpNSI6eyJi
-USI6WyIxIl0sImNYIjpbIjEiXSwiY1guRSI6IjEifSwiTjYiOnsiQW4iOlsiMSJdfSwiVlIiOnsid0wi
-OltdLCJ2WCI6W119LCJFSyI6eyJpYiI6W10sIk9kIjpbXX0sIktXIjp7ImNYIjpbImliIl0sImNYLkUi
-OiJpYiJ9LCJQYiI6eyJBbiI6WyJpYiJdfSwidFEiOnsiT2QiOltdfSwiTkYiOnsiY1giOlsiT2QiXSwi
-Y1guRSI6Ik9kIn0sIlNkIjp7IkFuIjpbIk9kIl19LCJlSCI6eyJlcSI6W119LCJMWiI6eyJYaiI6WyJA
-Il0sImVIIjpbXSwiZXEiOltdfSwiRGciOnsibEQiOlsiQ1AiXSwiWGoiOlsiQCJdLCJ6TSI6WyJDUCJd
-LCJlSCI6W10sImJRIjpbIkNQIl0sIlNVIjpbIkNQIl0sImVxIjpbXSwiY1giOlsiQ1AiXSwibEQuRSI6
-IkNQIn0sIlBnIjp7ImxEIjpbIklmIl0sInpNIjpbIklmIl0sIlhqIjpbIkAiXSwiZUgiOltdLCJiUSI6
-WyJJZiJdLCJTVSI6WyJJZiJdLCJlcSI6W10sImNYIjpbIklmIl19LCJ4aiI6eyJsRCI6WyJJZiJdLCJ6
-TSI6WyJJZiJdLCJYaiI6WyJAIl0sImVIIjpbXSwiYlEiOlsiSWYiXSwiU1UiOlsiSWYiXSwiZXEiOltd
-LCJjWCI6WyJJZiJdLCJsRC5FIjoiSWYifSwiZEUiOnsibEQiOlsiSWYiXSwiek0iOlsiSWYiXSwiWGoi
-OlsiQCJdLCJlSCI6W10sImJRIjpbIklmIl0sIlNVIjpbIklmIl0sImVxIjpbXSwiY1giOlsiSWYiXSwi
-bEQuRSI6IklmIn0sIlpBIjp7ImxEIjpbIklmIl0sInpNIjpbIklmIl0sIlhqIjpbIkAiXSwiZUgiOltd
-LCJiUSI6WyJJZiJdLCJTVSI6WyJJZiJdLCJlcSI6W10sImNYIjpbIklmIl0sImxELkUiOiJJZiJ9LCJ3
-ZiI6eyJsRCI6WyJJZiJdLCJ6TSI6WyJJZiJdLCJYaiI6WyJAIl0sImVIIjpbXSwiYlEiOlsiSWYiXSwi
-U1UiOlsiSWYiXSwiZXEiOltdLCJjWCI6WyJJZiJdLCJsRC5FIjoiSWYifSwiUHEiOnsibEQiOlsiSWYi
-XSwiek0iOlsiSWYiXSwiWGoiOlsiQCJdLCJlSCI6W10sImJRIjpbIklmIl0sIlNVIjpbIklmIl0sImVx
-IjpbXSwiY1giOlsiSWYiXSwibEQuRSI6IklmIn0sImVFIjp7ImxEIjpbIklmIl0sInpNIjpbIklmIl0s
-IlhqIjpbIkAiXSwiZUgiOltdLCJiUSI6WyJJZiJdLCJTVSI6WyJJZiJdLCJlcSI6W10sImNYIjpbIklm
-Il0sImxELkUiOiJJZiJ9LCJWNiI6eyJuNiI6W10sImxEIjpbIklmIl0sInpNIjpbIklmIl0sIlhqIjpb
-IkAiXSwiZUgiOltdLCJiUSI6WyJJZiJdLCJTVSI6WyJJZiJdLCJlcSI6W10sImNYIjpbIklmIl0sImxE
-LkUiOiJJZiJ9LCJ1OSI6eyJYUyI6W119LCJ4Ijp7IlhTIjpbXX0sIkdWIjp7IkFuIjpbIjEiXX0sInE0
-Ijp7ImNYIjpbIjEiXSwiY1guRSI6IjEifSwiWmYiOnsiUGYiOlsiMSJdfSwidnMiOnsiYjgiOlsiMSJd
-fSwiT0giOnsiWFMiOltdfSwibTAiOnsiSkIiOltdfSwiSmkiOnsiSkIiOltdfSwiYjYiOnsiWHYiOlsi
-MSJdLCJ4dSI6WyIxIl0sImJRIjpbIjEiXSwiY1giOlsiMSJdfSwibG0iOnsiQW4iOlsiMSJdfSwibVci
-OnsiY1giOlsiMSJdfSwidXkiOnsibEQiOlsiMSJdLCJ6TSI6WyIxIl0sImJRIjpbIjEiXSwiY1giOlsi
-MSJdfSwiaWwiOnsiWWsiOlsiMSIsIjIiXSwiWjAiOlsiMSIsIjIiXX0sIllrIjp7IlowIjpbIjEiLCIy
-Il19LCJQbiI6eyJaMCI6WyIxIiwiMiJdfSwiR2oiOnsiUlUiOlsiMSIsIjIiXSwiUG4iOlsiMSIsIjIi
-XSwiS1AiOlsiMSIsIjIiXSwiWjAiOlsiMSIsIjIiXX0sIlZqIjp7Ik1hIjpbIjEiXSwieHUiOlsiMSJd
-LCJiUSI6WyIxIl0sImNYIjpbIjEiXX0sIlh2Ijp7Inh1IjpbIjEiXSwiYlEiOlsiMSJdLCJjWCI6WyIx
-Il19LCJ1dyI6eyJZayI6WyJxVSIsIkAiXSwiWjAiOlsicVUiLCJAIl0sIllrLksiOiJxVSIsIllrLlYi
-OiJAIn0sImk4Ijp7ImFMIjpbInFVIl0sImJRIjpbInFVIl0sImNYIjpbInFVIl0sImFMLkUiOiJxVSIs
-ImNYLkUiOiJxVSJ9LCJDViI6eyJVayI6WyJ6TTxJZj4iLCJxVSJdLCJVay5TIjoiek08SWY+In0sIlU4
-Ijp7IndJIjpbInpNPElmPiIsInFVIl19LCJaaSI6eyJVayI6WyJxVSIsInpNPElmPiJdfSwiVWQiOnsi
-WFMiOltdfSwiSzgiOnsiWFMiOltdfSwiYnkiOnsiVWsiOlsiTWgiLCJxVSJdLCJVay5TIjoiTWgifSwi
-b2oiOnsid0kiOlsiTWgiLCJxVSJdfSwiTXgiOnsid0kiOlsicVUiLCJNaCJdfSwidTUiOnsiVWsiOlsi
-cVUiLCJ6TTxJZj4iXSwiVWsuUyI6InFVIn0sIkUzIjp7IndJIjpbInFVIiwiek08SWY+Il19LCJHWSI6
-eyJ3SSI6WyJ6TTxJZj4iLCJxVSJdfSwiQ1AiOnsibGYiOltdfSwiQzYiOnsiWFMiOltdfSwiTEsiOnsi
-WFMiOltdfSwiQVQiOnsiWFMiOltdfSwiYkoiOnsiWFMiOltdfSwiZVkiOnsiWFMiOltdfSwibXAiOnsi
-WFMiOltdfSwidWIiOnsiWFMiOltdfSwiZHMiOnsiWFMiOltdfSwibGoiOnsiWFMiOltdfSwiVVYiOnsi
-WFMiOltdfSwiazUiOnsiWFMiOltdfSwiS1kiOnsiWFMiOltdfSwidDciOnsiWFMiOltdfSwiQ0QiOnsi
-UnoiOltdfSwiYUUiOnsiUnoiOltdfSwiSWYiOnsibGYiOltdfSwiek0iOnsiYlEiOlsiMSJdLCJjWCI6
-WyIxIl19LCJpYiI6eyJPZCI6W119LCJ4dSI6eyJiUSI6WyIxIl0sImNYIjpbIjEiXX0sIlpkIjp7Ikd6
-IjpbXX0sInFVIjp7InZYIjpbXX0sIlJuIjp7IkJMIjpbXX0sIkRuIjp7ImlEIjpbXX0sIlVmIjp7ImlE
-IjpbXX0sInFlIjp7ImlEIjpbXX0sInFFIjp7ImN2IjpbXSwidUgiOltdLCJEMCI6W119LCJHaCI6eyJj
-diI6W10sInVIIjpbXSwiRDAiOltdfSwiZlkiOnsiY3YiOltdLCJ1SCI6W10sIkQwIjpbXX0sIm5CIjp7
-ImN2IjpbXSwidUgiOltdLCJEMCI6W119LCJRUCI6eyJjdiI6W10sInVIIjpbXSwiRDAiOltdfSwibngi
-OnsidUgiOltdLCJEMCI6W119LCJRRiI6eyJ1SCI6W10sIkQwIjpbXX0sIklCIjp7InRuIjpbImxmIl19
-LCJ3eiI6eyJsRCI6WyIxIl0sInpNIjpbIjEiXSwiYlEiOlsiMSJdLCJjWCI6WyIxIl0sImxELkUiOiIx
-In0sImN2Ijp7InVIIjpbXSwiRDAiOltdfSwiaEgiOnsiQXoiOltdfSwiaDQiOnsiY3YiOltdLCJ1SCI6
-W10sIkQwIjpbXX0sIlZiIjp7InVIIjpbXSwiRDAiOltdfSwiZkoiOnsiRDAiOltdfSwid2EiOnsiRDAi
-OltdfSwiT0siOnsiZWEiOltdfSwiZTciOnsibEQiOlsidUgiXSwiek0iOlsidUgiXSwiYlEiOlsidUgi
-XSwiY1giOlsidUgiXSwibEQuRSI6InVIIn0sInVIIjp7IkQwIjpbXX0sIkJIIjp7IkdtIjpbInVIIl0s
-ImxEIjpbInVIIl0sInpNIjpbInVIIl0sIlhqIjpbInVIIl0sImJRIjpbInVIIl0sImNYIjpbInVIIl0s
-ImxELkUiOiJ1SCIsIkdtLkUiOiJ1SCJ9LCJTTiI6eyJjdiI6W10sInVIIjpbXSwiRDAiOltdfSwiZXci
-OnsiZWEiOltdfSwibHAiOnsiY3YiOltdLCJ1SCI6W10sIkQwIjpbXX0sIlRiIjp7ImN2IjpbXSwidUgi
-OltdLCJEMCI6W119LCJJdiI6eyJjdiI6W10sInVIIjpbXSwiRDAiOltdfSwiV1AiOnsiY3YiOltdLCJ1
-SCI6W10sIkQwIjpbXX0sInlZIjp7ImN2IjpbXSwidUgiOltdLCJEMCI6W119LCJ3NiI6eyJlYSI6W119
-LCJLNSI6eyJ2NiI6W10sIkQwIjpbXX0sIkNtIjp7IkQwIjpbXX0sIkNRIjp7InVIIjpbXSwiRDAiOltd
-fSwidzQiOnsidG4iOlsibGYiXX0sInJoIjp7IkdtIjpbInVIIl0sImxEIjpbInVIIl0sInpNIjpbInVI
-Il0sIlhqIjpbInVIIl0sImJRIjpbInVIIl0sImNYIjpbInVIIl0sImxELkUiOiJ1SCIsIkdtLkUiOiJ1
-SCJ9LCJjZiI6eyJZayI6WyJxVSIsInFVIl0sIlowIjpbInFVIiwicVUiXX0sImk3Ijp7IllrIjpbInFV
-IiwicVUiXSwiWjAiOlsicVUiLCJxVSJdLCJZay5LIjoicVUiLCJZay5WIjoicVUifSwiU3kiOnsiWWsi
-OlsicVUiLCJxVSJdLCJaMCI6WyJxVSIsInFVIl0sIllrLksiOiJxVSIsIllrLlYiOiJxVSJ9LCJJNCI6
-eyJNYSI6WyJxVSJdLCJ4dSI6WyJxVSJdLCJiUSI6WyJxVSJdLCJjWCI6WyJxVSJdfSwiUk8iOnsicWgi
-OlsiMSJdfSwiZXUiOnsiUk8iOlsiMSJdLCJxaCI6WyIxIl19LCJ4QyI6eyJNTyI6WyIxIl19LCJKUSI6
-eyJrRiI6W119LCJ2RCI6eyJrRiI6W119LCJtNiI6eyJrRiI6W119LCJjdCI6eyJrRiI6W119LCJPdyI6
-eyJrRiI6W119LCJXOSI6eyJBbiI6WyIxIl19LCJkVyI6eyJ2NiI6W10sIkQwIjpbXX0sIm1rIjp7Inkw
-IjpbXX0sIktvIjp7Im9uIjpbXX0sIkFzIjp7Ik1hIjpbInFVIl0sInh1IjpbInFVIl0sImJRIjpbInFV
-Il0sImNYIjpbInFVIl19LCJyNyI6eyJFNCI6W119LCJUeiI6eyJsRCI6WyIxIl0sInpNIjpbIjEiXSwi
-YlEiOlsiMSJdLCJFNCI6W10sImNYIjpbIjEiXSwibEQuRSI6IjEifSwibmQiOnsiZDUiOltdLCJjdiI6
-W10sInVIIjpbXSwiRDAiOltdfSwiS2UiOnsiTWEiOlsicVUiXSwieHUiOlsicVUiXSwiYlEiOlsicVUi
-XSwiY1giOlsicVUiXX0sImQ1Ijp7ImN2IjpbXSwidUgiOltdLCJEMCI6W119LCJuNiI6eyJ6TSI6WyJJ
-ZiJdLCJiUSI6WyJJZiJdLCJlcSI6W10sImNYIjpbIklmIl19LCJYQSI6eyJrRiI6W119LCJkdiI6eyJS
-eiI6W119LCJPRiI6eyJmdiI6W119LCJydSI6eyJmdiI6W119LCJJViI6eyJmdiI6W119fScpKQpILkZG
-KHYudHlwZVVuaXZlcnNlLEpTT04ucGFyc2UoJ3siYlEiOjEsIncyIjoxLCJNTyI6MSwia1QiOjIsIm1X
-IjoxLCJ1eSI6MSwiaWwiOjIsIlZqIjoxLCJuWSI6MSwiVEMiOjEsImNvIjoxfScpKQp2YXIgdT0oZnVu
-Y3Rpb24gcnRpaSgpe3ZhciB0PUguTjAKcmV0dXJue2JxOnQoIkdoIiksbjp0KCJPSCIpLGNSOnQoIm5C
-IiksZDp0KCJBeiIpLFk6dCgiUVAiKSxnRjp0KCJQRDxHRCxAPiIpLGd3OnQoImJRPEA+IiksaDp0KCJj
-diIpLFc6dCgiWFMiKSxCOnQoImVhIikscjp0KCJEMCIpLGc4OnQoIlJ6IiksYzg6dCgiaEgiKSxaOnQo
-IkVIIiksYVE6dCgiYjg8Yzg+IiksYzp0KCJiODxAPiIpLEU6dCgiTEwiKSxncDp0KCJINyIpLEk6dCgi
-U2ciKSxvOnQoInZRIiksZWg6dCgiY1g8dUg+IiksWDp0KCJjWDxxVT4iKSxSOnQoImNYPEA+IiksZkE6
-dCgiamQ8U2U+IiksZ2k6dCgiamQ8ajg+IiksSjp0KCJqZDxaMDxxVSxNaD4+IiksZmg6dCgiamQ8Wlo+
-IiksaTp0KCJqZDxrRj4iKSxzOnQoImpkPHFVPiIpLGhoOnQoImpkPHlEPiIpLGFKOnQoImpkPHdiPiIp
-LGI6dCgiamQ8QD4iKSx0OnQoImpkPElmPiIpLGVIOnQoInZtIiksZzp0KCJjNSIpLGFVOnQoIlhqPEA+
-IiksYW06dCgiVHo8QD4iKSxlbzp0KCJONTxHRCxAPiIpLG06dCgiRTQiKSxkejp0KCJoRiIpLGY0OnQo
-InpNPGo4PiIpLGQzOnQoInpNPFowPHFVLE1oPj4iKSxldzp0KCJ6TTxNaD4iKSxhOnQoInpNPHFVPiIp
-LGo6dCgiek08QD4iKSxMOnQoInpNPElmPiIpLGFfOnQoInU4IiksUzp0KCJaMDxxVSxNaD4iKSxmOnQo
-IlowPHFVLHFVPiIpLGs6dCgiWjA8cVUsQD4iKSxHOnQoIlowPEAsQD4iKSxkdjp0KCJsSjxxVSxxVT4i
-KSxkbzp0KCJsSjxxVSxAPiIpLFY6dCgiT0siKSxkRDp0KCJlSCIpLGJtOnQoIlY2IiksQTp0KCJ1SCIp
-LGU6dCgia0YiKSxQOnQoImM4IiksSzp0KCJNaCIpLEY6dCgiZXciKSxxOnQoInRuPGxmPiIpLGZ2OnQo
-IndMIiksYXY6dCgiSmMiKSxhTzp0KCJuZCIpLEM6dCgieHU8cVU+IiksbDp0KCJHeiIpLE46dCgicVUi
-KSxkRzp0KCJxVShxVSkiKSxnNzp0KCJkNSIpLGZvOnQoIkdEIiksYVc6dCgieVkiKSx1OnQoImVxIiks
-Z2M6dCgibjYiKSxhazp0KCJrZCIpLHY6dCgiR2o8cVUscVU+Iiksdzp0KCJpRCIpLGNjOnQoIlU1PHFV
-PiIpLGc0OnQoIks1IiksY2k6dCgidjYiKSxnMjp0KCJDbSIpLGJqOnQoIlpmPGZKPiIpLGg5OnQoIkNR
-IiksYWM6dCgiZTciKSxROnQoImV1PE9LPiIpLFQ6dCgid3o8Y3Y+IikseDp0KCJGZTxALEA+IiksYW86
-dCgidnM8Zko+IiksXzp0KCJ2czxAPiIpLGZKOnQoInZzPElmPiIpLE86dCgiSlEiKSxEOnQoImJuIiks
-eTp0KCJhMiIpLGFsOnQoImEyKE1oKSIpLGJCOnQoImEyKHFVKSIpLGJmOnQoImEyKEApIiksZ1I6dCgi
-Q1AiKSx6OnQoIkAiKSxmTzp0KCJAKCkiKSxVOnQoIkAoZWEpIiksYkk6dCgiQChNaCkiKSxlcDp0KCJA
-KE1oLE1oKSIpLGFnOnQoIkAoTWgsR3opIiksYlU6dCgiQCh4dTxxVT4pIiksZE86dCgiQChxVSkiKSxi
-Yzp0KCJAKEApIiksYjg6dCgiQChALEApIikscDp0KCJJZiIpLGRpOnQoImxmIiksSDp0KCJ+IiksTTp0
-KCJ+KCkiKSxhbjp0KCJ+KGV3KSIpLGVBOnQoIn4ocVUscVUpIiksY0E6dCgifihxVSxAKSIpfX0pKCk7
-KGZ1bmN0aW9uIGNvbnN0YW50cygpe3ZhciB0PWh1bmtIZWxwZXJzLm1ha2VDb25zdExpc3QKQy5SWT1X
-LlFQLnByb3RvdHlwZQpDLkJaPVcuVmIucHJvdG90eXBlCkMuRHQ9Vy5mSi5wcm90b3R5cGUKQy5Paz1K
-LnZCLnByb3RvdHlwZQpDLk5tPUouamQucHJvdG90eXBlCkMuam49Si5iVS5wcm90b3R5cGUKQy5DRD1K
-LnFJLnByb3RvdHlwZQpDLnhCPUouRHIucHJvdG90eXBlCkMuREc9Si5jNS5wcm90b3R5cGUKQy5FeD1X
-LnU4LnByb3RvdHlwZQpDLnQ1PVcuQkgucHJvdG90eXBlCkMuTHQ9Vy5TTi5wcm90b3R5cGUKQy5aUT1K
-LmlDLnByb3RvdHlwZQpDLkllPVcuVGIucHJvdG90eXBlCkMudkI9Si5rZC5wcm90b3R5cGUKQy5vbD1X
-Lks1LnByb3RvdHlwZQpDLnk4PW5ldyBQLlU4KCkKQy5oOT1uZXcgUC5DVigpCkMud2I9ZnVuY3Rpb24g
-Z2V0VGFnRmFsbGJhY2sobykgewogIHZhciBzID0gT2JqZWN0LnByb3RvdHlwZS50b1N0cmluZy5jYWxs
-KG8pOwogIHJldHVybiBzLnN1YnN0cmluZyg4LCBzLmxlbmd0aCAtIDEpOwp9CkMuTzQ9ZnVuY3Rpb24o
-KSB7CiAgdmFyIHRvU3RyaW5nRnVuY3Rpb24gPSBPYmplY3QucHJvdG90eXBlLnRvU3RyaW5nOwogIGZ1
-bmN0aW9uIGdldFRhZyhvKSB7CiAgICB2YXIgcyA9IHRvU3RyaW5nRnVuY3Rpb24uY2FsbChvKTsKICAg
-IHJldHVybiBzLnN1YnN0cmluZyg4LCBzLmxlbmd0aCAtIDEpOwogIH0KICBmdW5jdGlvbiBnZXRVbmtu
-b3duVGFnKG9iamVjdCwgdGFnKSB7CiAgICBpZiAoL15IVE1MW0EtWl0uKkVsZW1lbnQkLy50ZXN0KHRh
-ZykpIHsKICAgICAgdmFyIG5hbWUgPSB0b1N0cmluZ0Z1bmN0aW9uLmNhbGwob2JqZWN0KTsKICAgICAg
-aWYgKG5hbWUgPT0gIltvYmplY3QgT2JqZWN0XSIpIHJldHVybiBudWxsOwogICAgICByZXR1cm4gIkhU
-TUxFbGVtZW50IjsKICAgIH0KICB9CiAgZnVuY3Rpb24gZ2V0VW5rbm93blRhZ0dlbmVyaWNCcm93c2Vy
-KG9iamVjdCwgdGFnKSB7CiAgICBpZiAoc2VsZi5IVE1MRWxlbWVudCAmJiBvYmplY3QgaW5zdGFuY2Vv
-ZiBIVE1MRWxlbWVudCkgcmV0dXJuICJIVE1MRWxlbWVudCI7CiAgICByZXR1cm4gZ2V0VW5rbm93blRh
-ZyhvYmplY3QsIHRhZyk7CiAgfQogIGZ1bmN0aW9uIHByb3RvdHlwZUZvclRhZyh0YWcpIHsKICAgIGlm
-ICh0eXBlb2Ygd2luZG93ID09ICJ1bmRlZmluZWQiKSByZXR1cm4gbnVsbDsKICAgIGlmICh0eXBlb2Yg
-d2luZG93W3RhZ10gPT0gInVuZGVmaW5lZCIpIHJldHVybiBudWxsOwogICAgdmFyIGNvbnN0cnVjdG9y
-ID0gd2luZG93W3RhZ107CiAgICBpZiAodHlwZW9mIGNvbnN0cnVjdG9yICE9ICJmdW5jdGlvbiIpIHJl
-dHVybiBudWxsOwogICAgcmV0dXJuIGNvbnN0cnVjdG9yLnByb3RvdHlwZTsKICB9CiAgZnVuY3Rpb24g
-ZGlzY3JpbWluYXRvcih0YWcpIHsgcmV0dXJuIG51bGw7IH0KICB2YXIgaXNCcm93c2VyID0gdHlwZW9m
-IG5hdmlnYXRvciA9PSAib2JqZWN0IjsKICByZXR1cm4gewogICAgZ2V0VGFnOiBnZXRUYWcsCiAgICBn
-ZXRVbmtub3duVGFnOiBpc0Jyb3dzZXIgPyBnZXRVbmtub3duVGFnR2VuZXJpY0Jyb3dzZXIgOiBnZXRV
-bmtub3duVGFnLAogICAgcHJvdG90eXBlRm9yVGFnOiBwcm90b3R5cGVGb3JUYWcsCiAgICBkaXNjcmlt
-aW5hdG9yOiBkaXNjcmltaW5hdG9yIH07Cn0KQy5kaz1mdW5jdGlvbihnZXRUYWdGYWxsYmFjaykgewog
-IHJldHVybiBmdW5jdGlvbihob29rcykgewogICAgaWYgKHR5cGVvZiBuYXZpZ2F0b3IgIT0gIm9iamVj
-dCIpIHJldHVybiBob29rczsKICAgIHZhciB1YSA9IG5hdmlnYXRvci51c2VyQWdlbnQ7CiAgICBpZiAo
-dWEuaW5kZXhPZigiRHVtcFJlbmRlclRyZWUiKSA+PSAwKSByZXR1cm4gaG9va3M7CiAgICBpZiAodWEu
-aW5kZXhPZigiQ2hyb21lIikgPj0gMCkgewogICAgICBmdW5jdGlvbiBjb25maXJtKHApIHsKICAgICAg
-ICByZXR1cm4gdHlwZW9mIHdpbmRvdyA9PSAib2JqZWN0IiAmJiB3aW5kb3dbcF0gJiYgd2luZG93W3Bd
-Lm5hbWUgPT0gcDsKICAgICAgfQogICAgICBpZiAoY29uZmlybSgiV2luZG93IikgJiYgY29uZmlybSgi
-SFRNTEVsZW1lbnQiKSkgcmV0dXJuIGhvb2tzOwogICAgfQogICAgaG9va3MuZ2V0VGFnID0gZ2V0VGFn
-RmFsbGJhY2s7CiAgfTsKfQpDLllxPWZ1bmN0aW9uKGhvb2tzKSB7CiAgaWYgKHR5cGVvZiBkYXJ0RXhw
-ZXJpbWVudGFsRml4dXBHZXRUYWcgIT0gImZ1bmN0aW9uIikgcmV0dXJuIGhvb2tzOwogIGhvb2tzLmdl
-dFRhZyA9IGRhcnRFeHBlcmltZW50YWxGaXh1cEdldFRhZyhob29rcy5nZXRUYWcpOwp9CkMuS1U9ZnVu
-Y3Rpb24oaG9va3MpIHsKICB2YXIgZ2V0VGFnID0gaG9va3MuZ2V0VGFnOwogIHZhciBwcm90b3R5cGVG
-b3JUYWcgPSBob29rcy5wcm90b3R5cGVGb3JUYWc7CiAgZnVuY3Rpb24gZ2V0VGFnRml4ZWQobykgewog
-ICAgdmFyIHRhZyA9IGdldFRhZyhvKTsKICAgIGlmICh0YWcgPT0gIkRvY3VtZW50IikgewogICAgICBp
-ZiAoISFvLnhtbFZlcnNpb24pIHJldHVybiAiIURvY3VtZW50IjsKICAgICAgcmV0dXJuICIhSFRNTERv
-Y3VtZW50IjsKICAgIH0KICAgIHJldHVybiB0YWc7CiAgfQogIGZ1bmN0aW9uIHByb3RvdHlwZUZvclRh
-Z0ZpeGVkKHRhZykgewogICAgaWYgKHRhZyA9PSAiRG9jdW1lbnQiKSByZXR1cm4gbnVsbDsKICAgIHJl
-dHVybiBwcm90b3R5cGVGb3JUYWcodGFnKTsKICB9CiAgaG9va3MuZ2V0VGFnID0gZ2V0VGFnRml4ZWQ7
-CiAgaG9va3MucHJvdG90eXBlRm9yVGFnID0gcHJvdG90eXBlRm9yVGFnRml4ZWQ7Cn0KQy54aT1mdW5j
-dGlvbihob29rcykgewogIHZhciB1c2VyQWdlbnQgPSB0eXBlb2YgbmF2aWdhdG9yID09ICJvYmplY3Qi
-ID8gbmF2aWdhdG9yLnVzZXJBZ2VudCA6ICIiOwogIGlmICh1c2VyQWdlbnQuaW5kZXhPZigiRmlyZWZv
-eCIpID09IC0xKSByZXR1cm4gaG9va3M7CiAgdmFyIGdldFRhZyA9IGhvb2tzLmdldFRhZzsKICB2YXIg
-cXVpY2tNYXAgPSB7CiAgICAiQmVmb3JlVW5sb2FkRXZlbnQiOiAiRXZlbnQiLAogICAgIkRhdGFUcmFu
-c2ZlciI6ICJDbGlwYm9hcmQiLAogICAgIkdlb0dlb2xvY2F0aW9uIjogIkdlb2xvY2F0aW9uIiwKICAg
-ICJMb2NhdGlvbiI6ICIhTG9jYXRpb24iLAogICAgIldvcmtlck1lc3NhZ2VFdmVudCI6ICJNZXNzYWdl
-RXZlbnQiLAogICAgIlhNTERvY3VtZW50IjogIiFEb2N1bWVudCJ9OwogIGZ1bmN0aW9uIGdldFRhZ0Zp
-cmVmb3gobykgewogICAgdmFyIHRhZyA9IGdldFRhZyhvKTsKICAgIHJldHVybiBxdWlja01hcFt0YWdd
-IHx8IHRhZzsKICB9CiAgaG9va3MuZ2V0VGFnID0gZ2V0VGFnRmlyZWZveDsKfQpDLmk3PWZ1bmN0aW9u
-KGhvb2tzKSB7CiAgdmFyIHVzZXJBZ2VudCA9IHR5cGVvZiBuYXZpZ2F0b3IgPT0gIm9iamVjdCIgPyBu
-YXZpZ2F0b3IudXNlckFnZW50IDogIiI7CiAgaWYgKHVzZXJBZ2VudC5pbmRleE9mKCJUcmlkZW50LyIp
-ID09IC0xKSByZXR1cm4gaG9va3M7CiAgdmFyIGdldFRhZyA9IGhvb2tzLmdldFRhZzsKICB2YXIgcXVp
-Y2tNYXAgPSB7CiAgICAiQmVmb3JlVW5sb2FkRXZlbnQiOiAiRXZlbnQiLAogICAgIkRhdGFUcmFuc2Zl
-ciI6ICJDbGlwYm9hcmQiLAogICAgIkhUTUxEREVsZW1lbnQiOiAiSFRNTEVsZW1lbnQiLAogICAgIkhU
-TUxEVEVsZW1lbnQiOiAiSFRNTEVsZW1lbnQiLAogICAgIkhUTUxQaHJhc2VFbGVtZW50IjogIkhUTUxF
-bGVtZW50IiwKICAgICJQb3NpdGlvbiI6ICJHZW9wb3NpdGlvbiIKICB9OwogIGZ1bmN0aW9uIGdldFRh
-Z0lFKG8pIHsKICAgIHZhciB0YWcgPSBnZXRUYWcobyk7CiAgICB2YXIgbmV3VGFnID0gcXVpY2tNYXBb
-dGFnXTsKICAgIGlmIChuZXdUYWcpIHJldHVybiBuZXdUYWc7CiAgICBpZiAodGFnID09ICJPYmplY3Qi
-KSB7CiAgICAgIGlmICh3aW5kb3cuRGF0YVZpZXcgJiYgKG8gaW5zdGFuY2VvZiB3aW5kb3cuRGF0YVZp
-ZXcpKSByZXR1cm4gIkRhdGFWaWV3IjsKICAgIH0KICAgIHJldHVybiB0YWc7CiAgfQogIGZ1bmN0aW9u
-IHByb3RvdHlwZUZvclRhZ0lFKHRhZykgewogICAgdmFyIGNvbnN0cnVjdG9yID0gd2luZG93W3RhZ107
-CiAgICBpZiAoY29uc3RydWN0b3IgPT0gbnVsbCkgcmV0dXJuIG51bGw7CiAgICByZXR1cm4gY29uc3Ry
-dWN0b3IucHJvdG90eXBlOwogIH0KICBob29rcy5nZXRUYWcgPSBnZXRUYWdJRTsKICBob29rcy5wcm90
-b3R5cGVGb3JUYWcgPSBwcm90b3R5cGVGb3JUYWdJRTsKfQpDLmZRPWZ1bmN0aW9uKGhvb2tzKSB7IHJl
-dHVybiBob29rczsgfQoKQy5DdD1uZXcgUC5ieSgpCkMuRXE9bmV3IFAuazUoKQpDLnhNPW5ldyBQLnU1
-KCkKQy5Raz1uZXcgUC5FMygpCkMuTlU9bmV3IFAuSmkoKQpDLnBkPW5ldyBQLlpkKCkKQy5BZD1uZXcg
-TS5INygwLCJIaW50QWN0aW9uS2luZC5hZGROdWxsYWJsZUhpbnQiKQpDLm5lPW5ldyBNLkg3KDEsIkhp
-bnRBY3Rpb25LaW5kLmFkZE5vbk51bGxhYmxlSGludCIpCkMubXk9bmV3IE0uSDcoMiwiSGludEFjdGlv
-bktpbmQuY2hhbmdlVG9OdWxsYWJsZUhpbnQiKQpDLnJ4PW5ldyBNLkg3KDMsIkhpbnRBY3Rpb25LaW5k
-LmNoYW5nZVRvTm9uTnVsbGFibGVIaW50IikKQy53Vj1uZXcgTS5INyg0LCJIaW50QWN0aW9uS2luZC5y
-ZW1vdmVOdWxsYWJsZUhpbnQiKQpDLmZSPW5ldyBNLkg3KDUsIkhpbnRBY3Rpb25LaW5kLnJlbW92ZU5v
-bk51bGxhYmxlSGludCIpCkMuQTM9bmV3IFAuTXgobnVsbCkKQy5uWD1uZXcgUC5vaihudWxsKQpDLkdi
-PUguVk0odChbMTI3LDIwNDcsNjU1MzUsMTExNDExMV0pLHUudCkKQy5haz1ILlZNKHQoWzAsMCwzMjc3
-NiwzMzc5MiwxLDEwMjQwLDAsMF0pLHUudCkKQy5jbT1ILlZNKHQoWyIqOjpjbGFzcyIsIio6OmRpciIs
-Iio6OmRyYWdnYWJsZSIsIio6OmhpZGRlbiIsIio6OmlkIiwiKjo6aW5lcnQiLCIqOjppdGVtcHJvcCIs
-Iio6Oml0ZW1yZWYiLCIqOjppdGVtc2NvcGUiLCIqOjpsYW5nIiwiKjo6c3BlbGxjaGVjayIsIio6OnRp
-dGxlIiwiKjo6dHJhbnNsYXRlIiwiQTo6YWNjZXNza2V5IiwiQTo6Y29vcmRzIiwiQTo6aHJlZmxhbmci
-LCJBOjpuYW1lIiwiQTo6c2hhcGUiLCJBOjp0YWJpbmRleCIsIkE6OnRhcmdldCIsIkE6OnR5cGUiLCJB
-UkVBOjphY2Nlc3NrZXkiLCJBUkVBOjphbHQiLCJBUkVBOjpjb29yZHMiLCJBUkVBOjpub2hyZWYiLCJB
-UkVBOjpzaGFwZSIsIkFSRUE6OnRhYmluZGV4IiwiQVJFQTo6dGFyZ2V0IiwiQVVESU86OmNvbnRyb2xz
-IiwiQVVESU86Omxvb3AiLCJBVURJTzo6bWVkaWFncm91cCIsIkFVRElPOjptdXRlZCIsIkFVRElPOjpw
-cmVsb2FkIiwiQkRPOjpkaXIiLCJCT0RZOjphbGluayIsIkJPRFk6OmJnY29sb3IiLCJCT0RZOjpsaW5r
-IiwiQk9EWTo6dGV4dCIsIkJPRFk6OnZsaW5rIiwiQlI6OmNsZWFyIiwiQlVUVE9OOjphY2Nlc3NrZXki
-LCJCVVRUT046OmRpc2FibGVkIiwiQlVUVE9OOjpuYW1lIiwiQlVUVE9OOjp0YWJpbmRleCIsIkJVVFRP
-Tjo6dHlwZSIsIkJVVFRPTjo6dmFsdWUiLCJDQU5WQVM6OmhlaWdodCIsIkNBTlZBUzo6d2lkdGgiLCJD
-QVBUSU9OOjphbGlnbiIsIkNPTDo6YWxpZ24iLCJDT0w6OmNoYXIiLCJDT0w6OmNoYXJvZmYiLCJDT0w6
-OnNwYW4iLCJDT0w6OnZhbGlnbiIsIkNPTDo6d2lkdGgiLCJDT0xHUk9VUDo6YWxpZ24iLCJDT0xHUk9V
-UDo6Y2hhciIsIkNPTEdST1VQOjpjaGFyb2ZmIiwiQ09MR1JPVVA6OnNwYW4iLCJDT0xHUk9VUDo6dmFs
-aWduIiwiQ09MR1JPVVA6OndpZHRoIiwiQ09NTUFORDo6Y2hlY2tlZCIsIkNPTU1BTkQ6OmNvbW1hbmQi
-LCJDT01NQU5EOjpkaXNhYmxlZCIsIkNPTU1BTkQ6OmxhYmVsIiwiQ09NTUFORDo6cmFkaW9ncm91cCIs
-IkNPTU1BTkQ6OnR5cGUiLCJEQVRBOjp2YWx1ZSIsIkRFTDo6ZGF0ZXRpbWUiLCJERVRBSUxTOjpvcGVu
-IiwiRElSOjpjb21wYWN0IiwiRElWOjphbGlnbiIsIkRMOjpjb21wYWN0IiwiRklFTERTRVQ6OmRpc2Fi
-bGVkIiwiRk9OVDo6Y29sb3IiLCJGT05UOjpmYWNlIiwiRk9OVDo6c2l6ZSIsIkZPUk06OmFjY2VwdCIs
-IkZPUk06OmF1dG9jb21wbGV0ZSIsIkZPUk06OmVuY3R5cGUiLCJGT1JNOjptZXRob2QiLCJGT1JNOjpu
-YW1lIiwiRk9STTo6bm92YWxpZGF0ZSIsIkZPUk06OnRhcmdldCIsIkZSQU1FOjpuYW1lIiwiSDE6OmFs
-aWduIiwiSDI6OmFsaWduIiwiSDM6OmFsaWduIiwiSDQ6OmFsaWduIiwiSDU6OmFsaWduIiwiSDY6OmFs
-aWduIiwiSFI6OmFsaWduIiwiSFI6Om5vc2hhZGUiLCJIUjo6c2l6ZSIsIkhSOjp3aWR0aCIsIkhUTUw6
-OnZlcnNpb24iLCJJRlJBTUU6OmFsaWduIiwiSUZSQU1FOjpmcmFtZWJvcmRlciIsIklGUkFNRTo6aGVp
-Z2h0IiwiSUZSQU1FOjptYXJnaW5oZWlnaHQiLCJJRlJBTUU6Om1hcmdpbndpZHRoIiwiSUZSQU1FOjp3
-aWR0aCIsIklNRzo6YWxpZ24iLCJJTUc6OmFsdCIsIklNRzo6Ym9yZGVyIiwiSU1HOjpoZWlnaHQiLCJJ
-TUc6OmhzcGFjZSIsIklNRzo6aXNtYXAiLCJJTUc6Om5hbWUiLCJJTUc6OnVzZW1hcCIsIklNRzo6dnNw
-YWNlIiwiSU1HOjp3aWR0aCIsIklOUFVUOjphY2NlcHQiLCJJTlBVVDo6YWNjZXNza2V5IiwiSU5QVVQ6
-OmFsaWduIiwiSU5QVVQ6OmFsdCIsIklOUFVUOjphdXRvY29tcGxldGUiLCJJTlBVVDo6YXV0b2ZvY3Vz
-IiwiSU5QVVQ6OmNoZWNrZWQiLCJJTlBVVDo6ZGlzYWJsZWQiLCJJTlBVVDo6aW5wdXRtb2RlIiwiSU5Q
-VVQ6OmlzbWFwIiwiSU5QVVQ6Omxpc3QiLCJJTlBVVDo6bWF4IiwiSU5QVVQ6Om1heGxlbmd0aCIsIklO
-UFVUOjptaW4iLCJJTlBVVDo6bXVsdGlwbGUiLCJJTlBVVDo6bmFtZSIsIklOUFVUOjpwbGFjZWhvbGRl
-ciIsIklOUFVUOjpyZWFkb25seSIsIklOUFVUOjpyZXF1aXJlZCIsIklOUFVUOjpzaXplIiwiSU5QVVQ6
-OnN0ZXAiLCJJTlBVVDo6dGFiaW5kZXgiLCJJTlBVVDo6dHlwZSIsIklOUFVUOjp1c2VtYXAiLCJJTlBV
-VDo6dmFsdWUiLCJJTlM6OmRhdGV0aW1lIiwiS0VZR0VOOjpkaXNhYmxlZCIsIktFWUdFTjo6a2V5dHlw
-ZSIsIktFWUdFTjo6bmFtZSIsIkxBQkVMOjphY2Nlc3NrZXkiLCJMQUJFTDo6Zm9yIiwiTEVHRU5EOjph
-Y2Nlc3NrZXkiLCJMRUdFTkQ6OmFsaWduIiwiTEk6OnR5cGUiLCJMSTo6dmFsdWUiLCJMSU5LOjpzaXpl
-cyIsIk1BUDo6bmFtZSIsIk1FTlU6OmNvbXBhY3QiLCJNRU5VOjpsYWJlbCIsIk1FTlU6OnR5cGUiLCJN
-RVRFUjo6aGlnaCIsIk1FVEVSOjpsb3ciLCJNRVRFUjo6bWF4IiwiTUVURVI6Om1pbiIsIk1FVEVSOjp2
-YWx1ZSIsIk9CSkVDVDo6dHlwZW11c3RtYXRjaCIsIk9MOjpjb21wYWN0IiwiT0w6OnJldmVyc2VkIiwi
-T0w6OnN0YXJ0IiwiT0w6OnR5cGUiLCJPUFRHUk9VUDo6ZGlzYWJsZWQiLCJPUFRHUk9VUDo6bGFiZWwi
-LCJPUFRJT046OmRpc2FibGVkIiwiT1BUSU9OOjpsYWJlbCIsIk9QVElPTjo6c2VsZWN0ZWQiLCJPUFRJ
-T046OnZhbHVlIiwiT1VUUFVUOjpmb3IiLCJPVVRQVVQ6Om5hbWUiLCJQOjphbGlnbiIsIlBSRTo6d2lk
-dGgiLCJQUk9HUkVTUzo6bWF4IiwiUFJPR1JFU1M6Om1pbiIsIlBST0dSRVNTOjp2YWx1ZSIsIlNFTEVD
-VDo6YXV0b2NvbXBsZXRlIiwiU0VMRUNUOjpkaXNhYmxlZCIsIlNFTEVDVDo6bXVsdGlwbGUiLCJTRUxF
-Q1Q6Om5hbWUiLCJTRUxFQ1Q6OnJlcXVpcmVkIiwiU0VMRUNUOjpzaXplIiwiU0VMRUNUOjp0YWJpbmRl
-eCIsIlNPVVJDRTo6dHlwZSIsIlRBQkxFOjphbGlnbiIsIlRBQkxFOjpiZ2NvbG9yIiwiVEFCTEU6OmJv
-cmRlciIsIlRBQkxFOjpjZWxscGFkZGluZyIsIlRBQkxFOjpjZWxsc3BhY2luZyIsIlRBQkxFOjpmcmFt
-ZSIsIlRBQkxFOjpydWxlcyIsIlRBQkxFOjpzdW1tYXJ5IiwiVEFCTEU6OndpZHRoIiwiVEJPRFk6OmFs
-aWduIiwiVEJPRFk6OmNoYXIiLCJUQk9EWTo6Y2hhcm9mZiIsIlRCT0RZOjp2YWxpZ24iLCJURDo6YWJi
-ciIsIlREOjphbGlnbiIsIlREOjpheGlzIiwiVEQ6OmJnY29sb3IiLCJURDo6Y2hhciIsIlREOjpjaGFy
-b2ZmIiwiVEQ6OmNvbHNwYW4iLCJURDo6aGVhZGVycyIsIlREOjpoZWlnaHQiLCJURDo6bm93cmFwIiwi
-VEQ6OnJvd3NwYW4iLCJURDo6c2NvcGUiLCJURDo6dmFsaWduIiwiVEQ6OndpZHRoIiwiVEVYVEFSRUE6
-OmFjY2Vzc2tleSIsIlRFWFRBUkVBOjphdXRvY29tcGxldGUiLCJURVhUQVJFQTo6Y29scyIsIlRFWFRB
-UkVBOjpkaXNhYmxlZCIsIlRFWFRBUkVBOjppbnB1dG1vZGUiLCJURVhUQVJFQTo6bmFtZSIsIlRFWFRB
-UkVBOjpwbGFjZWhvbGRlciIsIlRFWFRBUkVBOjpyZWFkb25seSIsIlRFWFRBUkVBOjpyZXF1aXJlZCIs
-IlRFWFRBUkVBOjpyb3dzIiwiVEVYVEFSRUE6OnRhYmluZGV4IiwiVEVYVEFSRUE6OndyYXAiLCJURk9P
-VDo6YWxpZ24iLCJURk9PVDo6Y2hhciIsIlRGT09UOjpjaGFyb2ZmIiwiVEZPT1Q6OnZhbGlnbiIsIlRI
-OjphYmJyIiwiVEg6OmFsaWduIiwiVEg6OmF4aXMiLCJUSDo6Ymdjb2xvciIsIlRIOjpjaGFyIiwiVEg6
-OmNoYXJvZmYiLCJUSDo6Y29sc3BhbiIsIlRIOjpoZWFkZXJzIiwiVEg6OmhlaWdodCIsIlRIOjpub3dy
-YXAiLCJUSDo6cm93c3BhbiIsIlRIOjpzY29wZSIsIlRIOjp2YWxpZ24iLCJUSDo6d2lkdGgiLCJUSEVB
-RDo6YWxpZ24iLCJUSEVBRDo6Y2hhciIsIlRIRUFEOjpjaGFyb2ZmIiwiVEhFQUQ6OnZhbGlnbiIsIlRS
-OjphbGlnbiIsIlRSOjpiZ2NvbG9yIiwiVFI6OmNoYXIiLCJUUjo6Y2hhcm9mZiIsIlRSOjp2YWxpZ24i
-LCJUUkFDSzo6ZGVmYXVsdCIsIlRSQUNLOjpraW5kIiwiVFJBQ0s6OmxhYmVsIiwiVFJBQ0s6OnNyY2xh
-bmciLCJVTDo6Y29tcGFjdCIsIlVMOjp0eXBlIiwiVklERU86OmNvbnRyb2xzIiwiVklERU86OmhlaWdo
-dCIsIlZJREVPOjpsb29wIiwiVklERU86Om1lZGlhZ3JvdXAiLCJWSURFTzo6bXV0ZWQiLCJWSURFTzo6
-cHJlbG9hZCIsIlZJREVPOjp3aWR0aCJdKSx1LnMpCkMuVkM9SC5WTSh0KFswLDAsNjU0OTAsNDUwNTUs
-NjU1MzUsMzQ4MTUsNjU1MzQsMTg0MzFdKSx1LnQpCkMubUs9SC5WTSh0KFswLDAsMjY2MjQsMTAyMyw2
-NTUzNCwyMDQ3LDY1NTM0LDIwNDddKSx1LnQpCkMuU3E9SC5WTSh0KFsiSEVBRCIsIkFSRUEiLCJCQVNF
-IiwiQkFTRUZPTlQiLCJCUiIsIkNPTCIsIkNPTEdST1VQIiwiRU1CRUQiLCJGUkFNRSIsIkZSQU1FU0VU
-IiwiSFIiLCJJTUFHRSIsIklNRyIsIklOUFVUIiwiSVNJTkRFWCIsIkxJTksiLCJNRVRBIiwiUEFSQU0i
-LCJTT1VSQ0UiLCJTVFlMRSIsIlRJVExFIiwiV0JSIl0pLHUucykKQy5kbj1ILlZNKHQoW10pLEguTjAo
-ImpkPExMPiIpKQpDLnhEPUguVk0odChbXSksdS5zKQpDLmhVPUguVk0odChbXSksdS5iKQpDLnRvPUgu
-Vk0odChbMCwwLDMyNzIyLDEyMjg3LDY1NTM0LDM0ODE1LDY1NTM0LDE4NDMxXSksdS50KQpDLnJrPUgu
-Vk0odChbQy5BZCxDLm5lLEMubXksQy5yeCxDLndWLEMuZlJdKSxILk4wKCJqZDxINz4iKSkKQy5GMz1I
-LlZNKHQoWzAsMCwyNDU3NiwxMDIzLDY1NTM0LDM0ODE1LDY1NTM0LDE4NDMxXSksdS50KQpDLmVhPUgu
-Vk0odChbMCwwLDMyNzU0LDExMjYzLDY1NTM0LDM0ODE1LDY1NTM0LDE4NDMxXSksdS50KQpDLlpKPUgu
-Vk0odChbMCwwLDMyNzIyLDEyMjg3LDY1NTM1LDM0ODE1LDY1NTM0LDE4NDMxXSksdS50KQpDLldkPUgu
-Vk0odChbMCwwLDY1NDkwLDEyMjg3LDY1NTM1LDM0ODE1LDY1NTM0LDE4NDMxXSksdS50KQpDLlF4PUgu
-Vk0odChbImJpbmQiLCJpZiIsInJlZiIsInJlcGVhdCIsInN5bnRheCJdKSx1LnMpCkMuQkk9SC5WTSh0
-KFsiQTo6aHJlZiIsIkFSRUE6OmhyZWYiLCJCTE9DS1FVT1RFOjpjaXRlIiwiQk9EWTo6YmFja2dyb3Vu
-ZCIsIkNPTU1BTkQ6Omljb24iLCJERUw6OmNpdGUiLCJGT1JNOjphY3Rpb24iLCJJTUc6OnNyYyIsIklO
-UFVUOjpzcmMiLCJJTlM6OmNpdGUiLCJROjpjaXRlIiwiVklERU86OnBvc3RlciJdKSx1LnMpCkMuQ009
-bmV3IEguTFAoMCx7fSxDLnhELEguTjAoIkxQPHFVLHpNPGo4Pj4iKSkKQy5XTz1uZXcgSC5MUCgwLHt9
-LEMueEQsSC5OMCgiTFA8cVUscVU+IikpCkMuaUg9SC5WTSh0KFtdKSxILk4wKCJqZDxHRD4iKSkKQy5E
-eD1uZXcgSC5MUCgwLHt9LEMuaUgsSC5OMCgiTFA8R0QsQD4iKSkKQy5ZMj1uZXcgTC5POSgiTmF2aWdh
-dGlvblRyZWVOb2RlVHlwZS5kaXJlY3RvcnkiKQpDLnJmPW5ldyBMLk85KCJOYXZpZ2F0aW9uVHJlZU5v
-ZGVUeXBlLmZpbGUiKQpDLlRlPW5ldyBILnd2KCJjYWxsIikKQy53UT1uZXcgUC5GeShudWxsLDIpfSko
-KTsoZnVuY3Rpb24gc3RhdGljRmllbGRzKCl7JC55aj0wCiQubUo9bnVsbAokLlA0PW51bGwKJC55PW51
-bGwKJC51PW51bGwKJC54Nz1udWxsCiQuaj1udWxsCiQudj1udWxsCiQuSz1udWxsCiQuUzY9bnVsbAok
-Lms4PW51bGwKJC5tZz1udWxsCiQuVUQ9ITEKJC5YMz1DLk5VCiQueGc9W10KJC54bz1udWxsCiQuQk89
-bnVsbAokLmx0PW51bGwKJC5FVT1udWxsCiQub3I9UC5GbCh1Lk4sdS5aKQokLkk2PW51bGwKJC5GZj1u
-dWxsfSkoKTsoZnVuY3Rpb24gbGF6eUluaXRpYWxpemVycygpe3ZhciB0PWh1bmtIZWxwZXJzLmxhenkK
-dCgkLCJmYSIsIndRIixmdW5jdGlvbigpe3JldHVybiBILllnKCJfJGRhcnRfZGFydENsb3N1cmUiKX0p
-CnQoJCwiWTIiLCJBIixmdW5jdGlvbigpe3JldHVybiBILllnKCJfJGRhcnRfanMiKX0pCnQoJCwiVTIi
-LCJTbiIsZnVuY3Rpb24oKXtyZXR1cm4gSC5jTShILlM3KHsKdG9TdHJpbmc6ZnVuY3Rpb24oKXtyZXR1
-cm4iJHJlY2VpdmVyJCJ9fSkpfSkKdCgkLCJ4cSIsImxxIixmdW5jdGlvbigpe3JldHVybiBILmNNKEgu
-UzcoeyRtZXRob2QkOm51bGwsCnRvU3RyaW5nOmZ1bmN0aW9uKCl7cmV0dXJuIiRyZWNlaXZlciQifX0p
-KX0pCnQoJCwiUjEiLCJOOSIsZnVuY3Rpb24oKXtyZXR1cm4gSC5jTShILlM3KG51bGwpKX0pCnQoJCwi
-Zk4iLCJpSSIsZnVuY3Rpb24oKXtyZXR1cm4gSC5jTShmdW5jdGlvbigpe3ZhciAkYXJndW1lbnRzRXhw
-ciQ9JyRhcmd1bWVudHMkJwp0cnl7bnVsbC4kbWV0aG9kJCgkYXJndW1lbnRzRXhwciQpfWNhdGNoKHMp
-e3JldHVybiBzLm1lc3NhZ2V9fSgpKX0pCnQoJCwicWkiLCJVTiIsZnVuY3Rpb24oKXtyZXR1cm4gSC5j
-TShILlM3KHZvaWQgMCkpfSkKdCgkLCJyWiIsIlpoIixmdW5jdGlvbigpe3JldHVybiBILmNNKGZ1bmN0
-aW9uKCl7dmFyICRhcmd1bWVudHNFeHByJD0nJGFyZ3VtZW50cyQnCnRyeXsodm9pZCAwKS4kbWV0aG9k
-JCgkYXJndW1lbnRzRXhwciQpfWNhdGNoKHMpe3JldHVybiBzLm1lc3NhZ2V9fSgpKX0pCnQoJCwia3Ei
-LCJyTiIsZnVuY3Rpb24oKXtyZXR1cm4gSC5jTShILk1qKG51bGwpKX0pCnQoJCwidHQiLCJjMyIsZnVu
-Y3Rpb24oKXtyZXR1cm4gSC5jTShmdW5jdGlvbigpe3RyeXtudWxsLiRtZXRob2QkfWNhdGNoKHMpe3Jl
-dHVybiBzLm1lc3NhZ2V9fSgpKX0pCnQoJCwiZHQiLCJISyIsZnVuY3Rpb24oKXtyZXR1cm4gSC5jTShI
-Lk1qKHZvaWQgMCkpfSkKdCgkLCJBNyIsInIxIixmdW5jdGlvbigpe3JldHVybiBILmNNKGZ1bmN0aW9u
-KCl7dHJ5eyh2b2lkIDApLiRtZXRob2QkfWNhdGNoKHMpe3JldHVybiBzLm1lc3NhZ2V9fSgpKX0pCnQo
-JCwiV2MiLCJ1dCIsZnVuY3Rpb24oKXtyZXR1cm4gUC5PaigpfSkKdCgkLCJraCIsInRMIixmdW5jdGlv
-bigpe3JldHVybiBQLldJKCl9KQp0KCQsImJ0IiwiVjciLGZ1bmN0aW9uKCl7cmV0dXJuIEguRFEoSC5Y
-RihILlZNKFstMiwtMiwtMiwtMiwtMiwtMiwtMiwtMiwtMiwtMiwtMiwtMiwtMiwtMiwtMiwtMiwtMiwt
-MiwtMiwtMiwtMiwtMiwtMiwtMiwtMiwtMiwtMiwtMiwtMiwtMiwtMiwtMiwtMiwtMiwtMiwtMiwtMiwt
-MSwtMiwtMiwtMiwtMiwtMiw2MiwtMiw2MiwtMiw2Myw1Miw1Myw1NCw1NSw1Niw1Nyw1OCw1OSw2MCw2
-MSwtMiwtMiwtMiwtMSwtMiwtMiwtMiwwLDEsMiwzLDQsNSw2LDcsOCw5LDEwLDExLDEyLDEzLDE0LDE1
-LDE2LDE3LDE4LDE5LDIwLDIxLDIyLDIzLDI0LDI1LC0yLC0yLC0yLC0yLDYzLC0yLDI2LDI3LDI4LDI5
-LDMwLDMxLDMyLDMzLDM0LDM1LDM2LDM3LDM4LDM5LDQwLDQxLDQyLDQzLDQ0LDQ1LDQ2LDQ3LDQ4LDQ5
-LDUwLDUxLC0yLC0yLC0yLC0yLC0yXSx1LnQpKSl9KQp0KCQsIk01IiwiT3giLGZ1bmN0aW9uKCl7cmV0
-dXJuIHR5cGVvZiBwcm9jZXNzIT0idW5kZWZpbmVkIiYmT2JqZWN0LnByb3RvdHlwZS50b1N0cmluZy5j
-YWxsKHByb2Nlc3MpPT0iW29iamVjdCBwcm9jZXNzXSImJnByb2Nlc3MucGxhdGZvcm09PSJ3aW4zMiJ9
-KQp0KCQsIm1mIiwiejQiLGZ1bmN0aW9uKCl7cmV0dXJuIFAubnUoIl5bXFwtXFwuMC05QS1aX2Eten5d
-KiQiKX0pCnQoJCwiQXYiLCJwNiIsZnVuY3Rpb24oKXtyZXR1cm4gbmV3IEVycm9yKCkuc3RhY2shPXZv
-aWQgMH0pCnQoJCwiSkciLCJ2WiIsZnVuY3Rpb24oKXtyZXR1cm4gUC5LTigpfSkKdCgkLCJTQyIsIkFO
-IixmdW5jdGlvbigpe3JldHVybiBQLnRNKFsiQSIsIkFCQlIiLCJBQ1JPTllNIiwiQUREUkVTUyIsIkFS
-RUEiLCJBUlRJQ0xFIiwiQVNJREUiLCJBVURJTyIsIkIiLCJCREkiLCJCRE8iLCJCSUciLCJCTE9DS1FV
-T1RFIiwiQlIiLCJCVVRUT04iLCJDQU5WQVMiLCJDQVBUSU9OIiwiQ0VOVEVSIiwiQ0lURSIsIkNPREUi
-LCJDT0wiLCJDT0xHUk9VUCIsIkNPTU1BTkQiLCJEQVRBIiwiREFUQUxJU1QiLCJERCIsIkRFTCIsIkRF
-VEFJTFMiLCJERk4iLCJESVIiLCJESVYiLCJETCIsIkRUIiwiRU0iLCJGSUVMRFNFVCIsIkZJR0NBUFRJ
-T04iLCJGSUdVUkUiLCJGT05UIiwiRk9PVEVSIiwiRk9STSIsIkgxIiwiSDIiLCJIMyIsIkg0IiwiSDUi
-LCJINiIsIkhFQURFUiIsIkhHUk9VUCIsIkhSIiwiSSIsIklGUkFNRSIsIklNRyIsIklOUFVUIiwiSU5T
-IiwiS0JEIiwiTEFCRUwiLCJMRUdFTkQiLCJMSSIsIk1BUCIsIk1BUksiLCJNRU5VIiwiTUVURVIiLCJO
-QVYiLCJOT0JSIiwiT0wiLCJPUFRHUk9VUCIsIk9QVElPTiIsIk9VVFBVVCIsIlAiLCJQUkUiLCJQUk9H
-UkVTUyIsIlEiLCJTIiwiU0FNUCIsIlNFQ1RJT04iLCJTRUxFQ1QiLCJTTUFMTCIsIlNPVVJDRSIsIlNQ
-QU4iLCJTVFJJS0UiLCJTVFJPTkciLCJTVUIiLCJTVU1NQVJZIiwiU1VQIiwiVEFCTEUiLCJUQk9EWSIs
-IlREIiwiVEVYVEFSRUEiLCJURk9PVCIsIlRIIiwiVEhFQUQiLCJUSU1FIiwiVFIiLCJUUkFDSyIsIlRU
-IiwiVSIsIlVMIiwiVkFSIiwiVklERU8iLCJXQlIiXSx1Lk4pfSkKdCgkLCJYNCIsImhHIixmdW5jdGlv
-bigpe3JldHVybiBQLm51KCJeXFxTKyQiKX0pCnQoJCwid08iLCJvdyIsZnVuY3Rpb24oKXtyZXR1cm4g
-dS5tLmEoUC5ORChzZWxmKSl9KQp0KCQsImt0IiwiQ3IiLGZ1bmN0aW9uKCl7cmV0dXJuIEguWWcoIl8k
-ZGFydF9kYXJ0T2JqZWN0Iil9KQp0KCQsImZLIiwia0kiLGZ1bmN0aW9uKCl7cmV0dXJuIGZ1bmN0aW9u
-IERhcnRPYmplY3QoYSl7dGhpcy5vPWF9fSkKdCgkLCJxdCIsInpCIixmdW5jdGlvbigpe3JldHVybiBu
-ZXcgVC5tUSgpfSkKdCgkLCJPbCIsIlVFIixmdW5jdGlvbigpe3JldHVybiBQLmhLKEMub2wuZ21XKFcu
-eDMoKSkuaHJlZikuZ2hZKCkucSgwLCJhdXRoVG9rZW4iKX0pCnQoJCwiaFQiLCJ5UCIsZnVuY3Rpb24o
-KXtyZXR1cm4gVy5acigpLnF1ZXJ5U2VsZWN0b3IoIi5lZGl0LWxpc3QgLnBhbmVsLWNvbnRlbnQiKX0p
-CnQoJCwiVzYiLCJoTCIsZnVuY3Rpb24oKXtyZXR1cm4gVy5acigpLnF1ZXJ5U2VsZWN0b3IoIi5lZGl0
-LXBhbmVsIC5wYW5lbC1jb250ZW50Iil9KQp0KCQsIlRSIiwiRFciLGZ1bmN0aW9uKCl7cmV0dXJuIFcu
-WnIoKS5xdWVyeVNlbGVjdG9yKCJmb290ZXIiKX0pCnQoJCwiRVkiLCJmaSIsZnVuY3Rpb24oKXtyZXR1
-cm4gVy5acigpLnF1ZXJ5U2VsZWN0b3IoImhlYWRlciIpfSkKdCgkLCJhdiIsIkQ5IixmdW5jdGlvbigp
-e3JldHVybiBXLlpyKCkucXVlcnlTZWxlY3RvcigiI3VuaXQtbmFtZSIpfSkKdCgkLCJmZSIsIktHIixm
-dW5jdGlvbigpe3JldHVybiBuZXcgTC5YQSgpfSkKdCgkLCJlbyIsIm5VIixmdW5jdGlvbigpe3JldHVy
-biBuZXcgTS5sSSgkLkhrKCkpfSkKdCgkLCJ5ciIsImJEIixmdW5jdGlvbigpe3JldHVybiBuZXcgRS5P
-RihQLm51KCIvIiksUC5udSgiW14vXSQiKSxQLm51KCJeLyIpKX0pCnQoJCwiTWsiLCJLayIsZnVuY3Rp
-b24oKXtyZXR1cm4gbmV3IEwuSVYoUC5udSgiWy9cXFxcXSIpLFAubnUoIlteL1xcXFxdJCIpLFAubnUo
-Il4oXFxcXFxcXFxbXlxcXFxdK1xcXFxbXlxcXFwvXSt8W2EtekEtWl06Wy9cXFxcXSkiKSxQLm51KCJe
-Wy9cXFxcXSg/IVsvXFxcXF0pIikpfSkKdCgkLCJhayIsIkViIixmdW5jdGlvbigpe3JldHVybiBuZXcg
-Ri5ydShQLm51KCIvIiksUC5udSgiKF5bYS16QS1aXVstKy5hLXpBLVpcXGRdKjovL3xbXi9dKSQiKSxQ
-Lm51KCJbYS16QS1aXVstKy5hLXpBLVpcXGRdKjovL1teL10qIiksUC5udSgiXi8iKSl9KQp0KCQsImxz
-IiwiSGsiLGZ1bmN0aW9uKCl7cmV0dXJuIE8uUmgoKX0pfSkoKTsoZnVuY3Rpb24gbmF0aXZlU3VwcG9y
-dCgpeyFmdW5jdGlvbigpe3ZhciB0PWZ1bmN0aW9uKGEpe3ZhciBuPXt9Cm5bYV09MQpyZXR1cm4gT2Jq
-ZWN0LmtleXMoaHVua0hlbHBlcnMuY29udmVydFRvRmFzdE9iamVjdChuKSlbMF19CnYuZ2V0SXNvbGF0
-ZVRhZz1mdW5jdGlvbihhKXtyZXR1cm4gdCgiX19fZGFydF8iK2Erdi5pc29sYXRlVGFnKX0KdmFyIHM9
-Il9fX2RhcnRfaXNvbGF0ZV90YWdzXyIKdmFyIHI9T2JqZWN0W3NdfHwoT2JqZWN0W3NdPU9iamVjdC5j
-cmVhdGUobnVsbCkpCnZhciBxPSJfWnhZeFgiCmZvcih2YXIgcD0wOztwKyspe3ZhciBvPXQocSsiXyIr
-cCsiXyIpCmlmKCEobyBpbiByKSl7cltvXT0xCnYuaXNvbGF0ZVRhZz1vCmJyZWFrfX12LmRpc3BhdGNo
-UHJvcGVydHlOYW1lPXYuZ2V0SXNvbGF0ZVRhZygiZGlzcGF0Y2hfcmVjb3JkIil9KCkKaHVua0hlbHBl
-cnMuc2V0T3JVcGRhdGVJbnRlcmNlcHRvcnNCeVRhZyh7RE9NRXJyb3I6Si52QixET01JbXBsZW1lbnRh
-dGlvbjpKLnZCLE1lZGlhRXJyb3I6Si52QixOYXZpZ2F0b3I6Si52QixOYXZpZ2F0b3JDb25jdXJyZW50
-SGFyZHdhcmU6Si52QixOYXZpZ2F0b3JVc2VyTWVkaWFFcnJvcjpKLnZCLE92ZXJjb25zdHJhaW5lZEVy
-cm9yOkoudkIsUG9zaXRpb25FcnJvcjpKLnZCLFJhbmdlOkoudkIsU1FMRXJyb3I6Si52QixEYXRhVmll
-dzpILmVILEFycmF5QnVmZmVyVmlldzpILmVILEZsb2F0MzJBcnJheTpILkRnLEZsb2F0NjRBcnJheTpI
-LkRnLEludDE2QXJyYXk6SC54aixJbnQzMkFycmF5OkguZEUsSW50OEFycmF5OkguWkEsVWludDE2QXJy
-YXk6SC53ZixVaW50MzJBcnJheTpILlBxLFVpbnQ4Q2xhbXBlZEFycmF5OkguZUUsQ2FudmFzUGl4ZWxB
-cnJheTpILmVFLFVpbnQ4QXJyYXk6SC5WNixIVE1MQXVkaW9FbGVtZW50OlcucUUsSFRNTEJSRWxlbWVu
-dDpXLnFFLEhUTUxCdXR0b25FbGVtZW50OlcucUUsSFRNTENhbnZhc0VsZW1lbnQ6Vy5xRSxIVE1MQ29u
-dGVudEVsZW1lbnQ6Vy5xRSxIVE1MRExpc3RFbGVtZW50OlcucUUsSFRNTERhdGFFbGVtZW50OlcucUUs
-SFRNTERhdGFMaXN0RWxlbWVudDpXLnFFLEhUTUxEZXRhaWxzRWxlbWVudDpXLnFFLEhUTUxEaWFsb2dF
-bGVtZW50OlcucUUsSFRNTERpdkVsZW1lbnQ6Vy5xRSxIVE1MRW1iZWRFbGVtZW50OlcucUUsSFRNTEZp
-ZWxkU2V0RWxlbWVudDpXLnFFLEhUTUxIUkVsZW1lbnQ6Vy5xRSxIVE1MSGVhZEVsZW1lbnQ6Vy5xRSxI
-VE1MSGVhZGluZ0VsZW1lbnQ6Vy5xRSxIVE1MSHRtbEVsZW1lbnQ6Vy5xRSxIVE1MSUZyYW1lRWxlbWVu
-dDpXLnFFLEhUTUxJbWFnZUVsZW1lbnQ6Vy5xRSxIVE1MSW5wdXRFbGVtZW50OlcucUUsSFRNTExJRWxl
-bWVudDpXLnFFLEhUTUxMYWJlbEVsZW1lbnQ6Vy5xRSxIVE1MTGVnZW5kRWxlbWVudDpXLnFFLEhUTUxM
-aW5rRWxlbWVudDpXLnFFLEhUTUxNYXBFbGVtZW50OlcucUUsSFRNTE1lZGlhRWxlbWVudDpXLnFFLEhU
-TUxNZW51RWxlbWVudDpXLnFFLEhUTUxNZXRhRWxlbWVudDpXLnFFLEhUTUxNZXRlckVsZW1lbnQ6Vy5x
-RSxIVE1MTW9kRWxlbWVudDpXLnFFLEhUTUxPTGlzdEVsZW1lbnQ6Vy5xRSxIVE1MT2JqZWN0RWxlbWVu
-dDpXLnFFLEhUTUxPcHRHcm91cEVsZW1lbnQ6Vy5xRSxIVE1MT3B0aW9uRWxlbWVudDpXLnFFLEhUTUxP
-dXRwdXRFbGVtZW50OlcucUUsSFRNTFBhcmFtRWxlbWVudDpXLnFFLEhUTUxQaWN0dXJlRWxlbWVudDpX
-LnFFLEhUTUxQcmVFbGVtZW50OlcucUUsSFRNTFByb2dyZXNzRWxlbWVudDpXLnFFLEhUTUxRdW90ZUVs
-ZW1lbnQ6Vy5xRSxIVE1MU2NyaXB0RWxlbWVudDpXLnFFLEhUTUxTaGFkb3dFbGVtZW50OlcucUUsSFRN
-TFNsb3RFbGVtZW50OlcucUUsSFRNTFNvdXJjZUVsZW1lbnQ6Vy5xRSxIVE1MU3BhbkVsZW1lbnQ6Vy5x
-RSxIVE1MU3R5bGVFbGVtZW50OlcucUUsSFRNTFRhYmxlQ2FwdGlvbkVsZW1lbnQ6Vy5xRSxIVE1MVGFi
-bGVDZWxsRWxlbWVudDpXLnFFLEhUTUxUYWJsZURhdGFDZWxsRWxlbWVudDpXLnFFLEhUTUxUYWJsZUhl
-YWRlckNlbGxFbGVtZW50OlcucUUsSFRNTFRhYmxlQ29sRWxlbWVudDpXLnFFLEhUTUxUZXh0QXJlYUVs
-ZW1lbnQ6Vy5xRSxIVE1MVGltZUVsZW1lbnQ6Vy5xRSxIVE1MVGl0bGVFbGVtZW50OlcucUUsSFRNTFRy
-YWNrRWxlbWVudDpXLnFFLEhUTUxVTGlzdEVsZW1lbnQ6Vy5xRSxIVE1MVW5rbm93bkVsZW1lbnQ6Vy5x
-RSxIVE1MVmlkZW9FbGVtZW50OlcucUUsSFRNTERpcmVjdG9yeUVsZW1lbnQ6Vy5xRSxIVE1MRm9udEVs
-ZW1lbnQ6Vy5xRSxIVE1MRnJhbWVFbGVtZW50OlcucUUsSFRNTEZyYW1lU2V0RWxlbWVudDpXLnFFLEhU
-TUxNYXJxdWVlRWxlbWVudDpXLnFFLEhUTUxFbGVtZW50OlcucUUsSFRNTEFuY2hvckVsZW1lbnQ6Vy5H
-aCxIVE1MQXJlYUVsZW1lbnQ6Vy5mWSxIVE1MQmFzZUVsZW1lbnQ6Vy5uQixCbG9iOlcuQXosSFRNTEJv
-ZHlFbGVtZW50OlcuUVAsQ0RBVEFTZWN0aW9uOlcubngsQ2hhcmFjdGVyRGF0YTpXLm54LENvbW1lbnQ6
-Vy5ueCxQcm9jZXNzaW5nSW5zdHJ1Y3Rpb246Vy5ueCxUZXh0OlcubngsQ1NTU3R5bGVEZWNsYXJhdGlv
-bjpXLm9KLE1TU3R5bGVDU1NQcm9wZXJ0aWVzOlcub0osQ1NTMlByb3BlcnRpZXM6Vy5vSixYTUxEb2N1
-bWVudDpXLlFGLERvY3VtZW50OlcuUUYsRE9NRXhjZXB0aW9uOlcuTmgsRE9NUmVjdFJlYWRPbmx5Olcu
-SUIsRE9NVG9rZW5MaXN0OlcubjcsRWxlbWVudDpXLmN2LEFib3J0UGF5bWVudEV2ZW50OlcuZWEsQW5p
-bWF0aW9uRXZlbnQ6Vy5lYSxBbmltYXRpb25QbGF5YmFja0V2ZW50OlcuZWEsQXBwbGljYXRpb25DYWNo
-ZUVycm9yRXZlbnQ6Vy5lYSxCYWNrZ3JvdW5kRmV0Y2hDbGlja0V2ZW50OlcuZWEsQmFja2dyb3VuZEZl
-dGNoRXZlbnQ6Vy5lYSxCYWNrZ3JvdW5kRmV0Y2hGYWlsRXZlbnQ6Vy5lYSxCYWNrZ3JvdW5kRmV0Y2hl
-ZEV2ZW50OlcuZWEsQmVmb3JlSW5zdGFsbFByb21wdEV2ZW50OlcuZWEsQmVmb3JlVW5sb2FkRXZlbnQ6
-Vy5lYSxCbG9iRXZlbnQ6Vy5lYSxDYW5NYWtlUGF5bWVudEV2ZW50OlcuZWEsQ2xpcGJvYXJkRXZlbnQ6
-Vy5lYSxDbG9zZUV2ZW50OlcuZWEsQ3VzdG9tRXZlbnQ6Vy5lYSxEZXZpY2VNb3Rpb25FdmVudDpXLmVh
-LERldmljZU9yaWVudGF0aW9uRXZlbnQ6Vy5lYSxFcnJvckV2ZW50OlcuZWEsRXh0ZW5kYWJsZUV2ZW50
-OlcuZWEsRXh0ZW5kYWJsZU1lc3NhZ2VFdmVudDpXLmVhLEZldGNoRXZlbnQ6Vy5lYSxGb250RmFjZVNl
-dExvYWRFdmVudDpXLmVhLEZvcmVpZ25GZXRjaEV2ZW50OlcuZWEsR2FtZXBhZEV2ZW50OlcuZWEsSGFz
-aENoYW5nZUV2ZW50OlcuZWEsSW5zdGFsbEV2ZW50OlcuZWEsTWVkaWFFbmNyeXB0ZWRFdmVudDpXLmVh
-LE1lZGlhS2V5TWVzc2FnZUV2ZW50OlcuZWEsTWVkaWFRdWVyeUxpc3RFdmVudDpXLmVhLE1lZGlhU3Ry
-ZWFtRXZlbnQ6Vy5lYSxNZWRpYVN0cmVhbVRyYWNrRXZlbnQ6Vy5lYSxNZXNzYWdlRXZlbnQ6Vy5lYSxN
-SURJQ29ubmVjdGlvbkV2ZW50OlcuZWEsTUlESU1lc3NhZ2VFdmVudDpXLmVhLE11dGF0aW9uRXZlbnQ6
-Vy5lYSxOb3RpZmljYXRpb25FdmVudDpXLmVhLFBhZ2VUcmFuc2l0aW9uRXZlbnQ6Vy5lYSxQYXltZW50
-UmVxdWVzdEV2ZW50OlcuZWEsUGF5bWVudFJlcXVlc3RVcGRhdGVFdmVudDpXLmVhLFBvcFN0YXRlRXZl
-bnQ6Vy5lYSxQcmVzZW50YXRpb25Db25uZWN0aW9uQXZhaWxhYmxlRXZlbnQ6Vy5lYSxQcmVzZW50YXRp
-b25Db25uZWN0aW9uQ2xvc2VFdmVudDpXLmVhLFByb21pc2VSZWplY3Rpb25FdmVudDpXLmVhLFB1c2hF
-dmVudDpXLmVhLFJUQ0RhdGFDaGFubmVsRXZlbnQ6Vy5lYSxSVENEVE1GVG9uZUNoYW5nZUV2ZW50Olcu
-ZWEsUlRDUGVlckNvbm5lY3Rpb25JY2VFdmVudDpXLmVhLFJUQ1RyYWNrRXZlbnQ6Vy5lYSxTZWN1cml0
-eVBvbGljeVZpb2xhdGlvbkV2ZW50OlcuZWEsU2Vuc29yRXJyb3JFdmVudDpXLmVhLFNwZWVjaFJlY29n
-bml0aW9uRXJyb3I6Vy5lYSxTcGVlY2hSZWNvZ25pdGlvbkV2ZW50OlcuZWEsU3BlZWNoU3ludGhlc2lz
-RXZlbnQ6Vy5lYSxTdG9yYWdlRXZlbnQ6Vy5lYSxTeW5jRXZlbnQ6Vy5lYSxUcmFja0V2ZW50OlcuZWEs
-VHJhbnNpdGlvbkV2ZW50OlcuZWEsV2ViS2l0VHJhbnNpdGlvbkV2ZW50OlcuZWEsVlJEZXZpY2VFdmVu
-dDpXLmVhLFZSRGlzcGxheUV2ZW50OlcuZWEsVlJTZXNzaW9uRXZlbnQ6Vy5lYSxNb2pvSW50ZXJmYWNl
-UmVxdWVzdEV2ZW50OlcuZWEsVVNCQ29ubmVjdGlvbkV2ZW50OlcuZWEsSURCVmVyc2lvbkNoYW5nZUV2
-ZW50OlcuZWEsQXVkaW9Qcm9jZXNzaW5nRXZlbnQ6Vy5lYSxPZmZsaW5lQXVkaW9Db21wbGV0aW9uRXZl
-bnQ6Vy5lYSxXZWJHTENvbnRleHRFdmVudDpXLmVhLEV2ZW50OlcuZWEsSW5wdXRFdmVudDpXLmVhLFN1
-Ym1pdEV2ZW50OlcuZWEsRXZlbnRUYXJnZXQ6Vy5EMCxGaWxlOlcuaEgsSFRNTEZvcm1FbGVtZW50Olcu
-aDQsSGlzdG9yeTpXLmJyLEhUTUxEb2N1bWVudDpXLlZiLFhNTEh0dHBSZXF1ZXN0OlcuZkosWE1MSHR0
-cFJlcXVlc3RFdmVudFRhcmdldDpXLndhLEltYWdlRGF0YTpXLlNnLExvY2F0aW9uOlcudTgsTW91c2VF
-dmVudDpXLk9LLERyYWdFdmVudDpXLk9LLFBvaW50ZXJFdmVudDpXLk9LLFdoZWVsRXZlbnQ6Vy5PSyxE
-b2N1bWVudEZyYWdtZW50OlcudUgsU2hhZG93Um9vdDpXLnVILERvY3VtZW50VHlwZTpXLnVILE5vZGU6
-Vy51SCxOb2RlTGlzdDpXLkJILFJhZGlvTm9kZUxpc3Q6Vy5CSCxIVE1MUGFyYWdyYXBoRWxlbWVudDpX
-LlNOLFByb2dyZXNzRXZlbnQ6Vy5ldyxSZXNvdXJjZVByb2dyZXNzRXZlbnQ6Vy5ldyxIVE1MU2VsZWN0
-RWxlbWVudDpXLmxwLEhUTUxUYWJsZUVsZW1lbnQ6Vy5UYixIVE1MVGFibGVSb3dFbGVtZW50OlcuSXYs
-SFRNTFRhYmxlU2VjdGlvbkVsZW1lbnQ6Vy5XUCxIVE1MVGVtcGxhdGVFbGVtZW50OlcueVksQ29tcG9z
-aXRpb25FdmVudDpXLnc2LEZvY3VzRXZlbnQ6Vy53NixLZXlib2FyZEV2ZW50OlcudzYsVGV4dEV2ZW50
-OlcudzYsVG91Y2hFdmVudDpXLnc2LFVJRXZlbnQ6Vy53NixXaW5kb3c6Vy5LNSxET01XaW5kb3c6Vy5L
-NSxEZWRpY2F0ZWRXb3JrZXJHbG9iYWxTY29wZTpXLkNtLFNlcnZpY2VXb3JrZXJHbG9iYWxTY29wZTpX
-LkNtLFNoYXJlZFdvcmtlckdsb2JhbFNjb3BlOlcuQ20sV29ya2VyR2xvYmFsU2NvcGU6Vy5DbSxBdHRy
-OlcuQ1EsQ2xpZW50UmVjdDpXLnc0LERPTVJlY3Q6Vy53NCxOYW1lZE5vZGVNYXA6Vy5yaCxNb3pOYW1l
-ZEF0dHJNYXA6Vy5yaCxJREJLZXlSYW5nZTpQLmhGLFNWR1NjcmlwdEVsZW1lbnQ6UC5uZCxTVkdBRWxl
-bWVudDpQLmQ1LFNWR0FuaW1hdGVFbGVtZW50OlAuZDUsU1ZHQW5pbWF0ZU1vdGlvbkVsZW1lbnQ6UC5k
-NSxTVkdBbmltYXRlVHJhbnNmb3JtRWxlbWVudDpQLmQ1LFNWR0FuaW1hdGlvbkVsZW1lbnQ6UC5kNSxT
-VkdDaXJjbGVFbGVtZW50OlAuZDUsU1ZHQ2xpcFBhdGhFbGVtZW50OlAuZDUsU1ZHRGVmc0VsZW1lbnQ6
-UC5kNSxTVkdEZXNjRWxlbWVudDpQLmQ1LFNWR0Rpc2NhcmRFbGVtZW50OlAuZDUsU1ZHRWxsaXBzZUVs
-ZW1lbnQ6UC5kNSxTVkdGRUJsZW5kRWxlbWVudDpQLmQ1LFNWR0ZFQ29sb3JNYXRyaXhFbGVtZW50OlAu
-ZDUsU1ZHRkVDb21wb25lbnRUcmFuc2ZlckVsZW1lbnQ6UC5kNSxTVkdGRUNvbXBvc2l0ZUVsZW1lbnQ6
-UC5kNSxTVkdGRUNvbnZvbHZlTWF0cml4RWxlbWVudDpQLmQ1LFNWR0ZFRGlmZnVzZUxpZ2h0aW5nRWxl
-bWVudDpQLmQ1LFNWR0ZFRGlzcGxhY2VtZW50TWFwRWxlbWVudDpQLmQ1LFNWR0ZFRGlzdGFudExpZ2h0
-RWxlbWVudDpQLmQ1LFNWR0ZFRmxvb2RFbGVtZW50OlAuZDUsU1ZHRkVGdW5jQUVsZW1lbnQ6UC5kNSxT
-VkdGRUZ1bmNCRWxlbWVudDpQLmQ1LFNWR0ZFRnVuY0dFbGVtZW50OlAuZDUsU1ZHRkVGdW5jUkVsZW1l
-bnQ6UC5kNSxTVkdGRUdhdXNzaWFuQmx1ckVsZW1lbnQ6UC5kNSxTVkdGRUltYWdlRWxlbWVudDpQLmQ1
-LFNWR0ZFTWVyZ2VFbGVtZW50OlAuZDUsU1ZHRkVNZXJnZU5vZGVFbGVtZW50OlAuZDUsU1ZHRkVNb3Jw
-aG9sb2d5RWxlbWVudDpQLmQ1LFNWR0ZFT2Zmc2V0RWxlbWVudDpQLmQ1LFNWR0ZFUG9pbnRMaWdodEVs
-ZW1lbnQ6UC5kNSxTVkdGRVNwZWN1bGFyTGlnaHRpbmdFbGVtZW50OlAuZDUsU1ZHRkVTcG90TGlnaHRF
-bGVtZW50OlAuZDUsU1ZHRkVUaWxlRWxlbWVudDpQLmQ1LFNWR0ZFVHVyYnVsZW5jZUVsZW1lbnQ6UC5k
-NSxTVkdGaWx0ZXJFbGVtZW50OlAuZDUsU1ZHRm9yZWlnbk9iamVjdEVsZW1lbnQ6UC5kNSxTVkdHRWxl
-bWVudDpQLmQ1LFNWR0dlb21ldHJ5RWxlbWVudDpQLmQ1LFNWR0dyYXBoaWNzRWxlbWVudDpQLmQ1LFNW
-R0ltYWdlRWxlbWVudDpQLmQ1LFNWR0xpbmVFbGVtZW50OlAuZDUsU1ZHTGluZWFyR3JhZGllbnRFbGVt
-ZW50OlAuZDUsU1ZHTWFya2VyRWxlbWVudDpQLmQ1LFNWR01hc2tFbGVtZW50OlAuZDUsU1ZHTWV0YWRh
-dGFFbGVtZW50OlAuZDUsU1ZHUGF0aEVsZW1lbnQ6UC5kNSxTVkdQYXR0ZXJuRWxlbWVudDpQLmQ1LFNW
-R1BvbHlnb25FbGVtZW50OlAuZDUsU1ZHUG9seWxpbmVFbGVtZW50OlAuZDUsU1ZHUmFkaWFsR3JhZGll
-bnRFbGVtZW50OlAuZDUsU1ZHUmVjdEVsZW1lbnQ6UC5kNSxTVkdTZXRFbGVtZW50OlAuZDUsU1ZHU3Rv
-cEVsZW1lbnQ6UC5kNSxTVkdTdHlsZUVsZW1lbnQ6UC5kNSxTVkdTVkdFbGVtZW50OlAuZDUsU1ZHU3dp
-dGNoRWxlbWVudDpQLmQ1LFNWR1N5bWJvbEVsZW1lbnQ6UC5kNSxTVkdUU3BhbkVsZW1lbnQ6UC5kNSxT
-VkdUZXh0Q29udGVudEVsZW1lbnQ6UC5kNSxTVkdUZXh0RWxlbWVudDpQLmQ1LFNWR1RleHRQYXRoRWxl
-bWVudDpQLmQ1LFNWR1RleHRQb3NpdGlvbmluZ0VsZW1lbnQ6UC5kNSxTVkdUaXRsZUVsZW1lbnQ6UC5k
-NSxTVkdVc2VFbGVtZW50OlAuZDUsU1ZHVmlld0VsZW1lbnQ6UC5kNSxTVkdHcmFkaWVudEVsZW1lbnQ6
-UC5kNSxTVkdDb21wb25lbnRUcmFuc2ZlckZ1bmN0aW9uRWxlbWVudDpQLmQ1LFNWR0ZFRHJvcFNoYWRv
-d0VsZW1lbnQ6UC5kNSxTVkdNUGF0aEVsZW1lbnQ6UC5kNSxTVkdFbGVtZW50OlAuZDV9KQpodW5rSGVs
-cGVycy5zZXRPclVwZGF0ZUxlYWZUYWdzKHtET01FcnJvcjp0cnVlLERPTUltcGxlbWVudGF0aW9uOnRy
-dWUsTWVkaWFFcnJvcjp0cnVlLE5hdmlnYXRvcjp0cnVlLE5hdmlnYXRvckNvbmN1cnJlbnRIYXJkd2Fy
-ZTp0cnVlLE5hdmlnYXRvclVzZXJNZWRpYUVycm9yOnRydWUsT3ZlcmNvbnN0cmFpbmVkRXJyb3I6dHJ1
-ZSxQb3NpdGlvbkVycm9yOnRydWUsUmFuZ2U6dHJ1ZSxTUUxFcnJvcjp0cnVlLERhdGFWaWV3OnRydWUs
-QXJyYXlCdWZmZXJWaWV3OmZhbHNlLEZsb2F0MzJBcnJheTp0cnVlLEZsb2F0NjRBcnJheTp0cnVlLElu
-dDE2QXJyYXk6dHJ1ZSxJbnQzMkFycmF5OnRydWUsSW50OEFycmF5OnRydWUsVWludDE2QXJyYXk6dHJ1
-ZSxVaW50MzJBcnJheTp0cnVlLFVpbnQ4Q2xhbXBlZEFycmF5OnRydWUsQ2FudmFzUGl4ZWxBcnJheTp0
-cnVlLFVpbnQ4QXJyYXk6ZmFsc2UsSFRNTEF1ZGlvRWxlbWVudDp0cnVlLEhUTUxCUkVsZW1lbnQ6dHJ1
-ZSxIVE1MQnV0dG9uRWxlbWVudDp0cnVlLEhUTUxDYW52YXNFbGVtZW50OnRydWUsSFRNTENvbnRlbnRF
-bGVtZW50OnRydWUsSFRNTERMaXN0RWxlbWVudDp0cnVlLEhUTUxEYXRhRWxlbWVudDp0cnVlLEhUTUxE
-YXRhTGlzdEVsZW1lbnQ6dHJ1ZSxIVE1MRGV0YWlsc0VsZW1lbnQ6dHJ1ZSxIVE1MRGlhbG9nRWxlbWVu
-dDp0cnVlLEhUTUxEaXZFbGVtZW50OnRydWUsSFRNTEVtYmVkRWxlbWVudDp0cnVlLEhUTUxGaWVsZFNl
-dEVsZW1lbnQ6dHJ1ZSxIVE1MSFJFbGVtZW50OnRydWUsSFRNTEhlYWRFbGVtZW50OnRydWUsSFRNTEhl
-YWRpbmdFbGVtZW50OnRydWUsSFRNTEh0bWxFbGVtZW50OnRydWUsSFRNTElGcmFtZUVsZW1lbnQ6dHJ1
-ZSxIVE1MSW1hZ2VFbGVtZW50OnRydWUsSFRNTElucHV0RWxlbWVudDp0cnVlLEhUTUxMSUVsZW1lbnQ6
-dHJ1ZSxIVE1MTGFiZWxFbGVtZW50OnRydWUsSFRNTExlZ2VuZEVsZW1lbnQ6dHJ1ZSxIVE1MTGlua0Vs
-ZW1lbnQ6dHJ1ZSxIVE1MTWFwRWxlbWVudDp0cnVlLEhUTUxNZWRpYUVsZW1lbnQ6dHJ1ZSxIVE1MTWVu
-dUVsZW1lbnQ6dHJ1ZSxIVE1MTWV0YUVsZW1lbnQ6dHJ1ZSxIVE1MTWV0ZXJFbGVtZW50OnRydWUsSFRN
-TE1vZEVsZW1lbnQ6dHJ1ZSxIVE1MT0xpc3RFbGVtZW50OnRydWUsSFRNTE9iamVjdEVsZW1lbnQ6dHJ1
-ZSxIVE1MT3B0R3JvdXBFbGVtZW50OnRydWUsSFRNTE9wdGlvbkVsZW1lbnQ6dHJ1ZSxIVE1MT3V0cHV0
-RWxlbWVudDp0cnVlLEhUTUxQYXJhbUVsZW1lbnQ6dHJ1ZSxIVE1MUGljdHVyZUVsZW1lbnQ6dHJ1ZSxI
-VE1MUHJlRWxlbWVudDp0cnVlLEhUTUxQcm9ncmVzc0VsZW1lbnQ6dHJ1ZSxIVE1MUXVvdGVFbGVtZW50
-OnRydWUsSFRNTFNjcmlwdEVsZW1lbnQ6dHJ1ZSxIVE1MU2hhZG93RWxlbWVudDp0cnVlLEhUTUxTbG90
-RWxlbWVudDp0cnVlLEhUTUxTb3VyY2VFbGVtZW50OnRydWUsSFRNTFNwYW5FbGVtZW50OnRydWUsSFRN
-TFN0eWxlRWxlbWVudDp0cnVlLEhUTUxUYWJsZUNhcHRpb25FbGVtZW50OnRydWUsSFRNTFRhYmxlQ2Vs
-bEVsZW1lbnQ6dHJ1ZSxIVE1MVGFibGVEYXRhQ2VsbEVsZW1lbnQ6dHJ1ZSxIVE1MVGFibGVIZWFkZXJD
-ZWxsRWxlbWVudDp0cnVlLEhUTUxUYWJsZUNvbEVsZW1lbnQ6dHJ1ZSxIVE1MVGV4dEFyZWFFbGVtZW50
-OnRydWUsSFRNTFRpbWVFbGVtZW50OnRydWUsSFRNTFRpdGxlRWxlbWVudDp0cnVlLEhUTUxUcmFja0Vs
-ZW1lbnQ6dHJ1ZSxIVE1MVUxpc3RFbGVtZW50OnRydWUsSFRNTFVua25vd25FbGVtZW50OnRydWUsSFRN
-TFZpZGVvRWxlbWVudDp0cnVlLEhUTUxEaXJlY3RvcnlFbGVtZW50OnRydWUsSFRNTEZvbnRFbGVtZW50
-OnRydWUsSFRNTEZyYW1lRWxlbWVudDp0cnVlLEhUTUxGcmFtZVNldEVsZW1lbnQ6dHJ1ZSxIVE1MTWFy
-cXVlZUVsZW1lbnQ6dHJ1ZSxIVE1MRWxlbWVudDpmYWxzZSxIVE1MQW5jaG9yRWxlbWVudDp0cnVlLEhU
-TUxBcmVhRWxlbWVudDp0cnVlLEhUTUxCYXNlRWxlbWVudDp0cnVlLEJsb2I6ZmFsc2UsSFRNTEJvZHlF
-bGVtZW50OnRydWUsQ0RBVEFTZWN0aW9uOnRydWUsQ2hhcmFjdGVyRGF0YTp0cnVlLENvbW1lbnQ6dHJ1
-ZSxQcm9jZXNzaW5nSW5zdHJ1Y3Rpb246dHJ1ZSxUZXh0OnRydWUsQ1NTU3R5bGVEZWNsYXJhdGlvbjp0
-cnVlLE1TU3R5bGVDU1NQcm9wZXJ0aWVzOnRydWUsQ1NTMlByb3BlcnRpZXM6dHJ1ZSxYTUxEb2N1bWVu
-dDp0cnVlLERvY3VtZW50OmZhbHNlLERPTUV4Y2VwdGlvbjp0cnVlLERPTVJlY3RSZWFkT25seTpmYWxz
-ZSxET01Ub2tlbkxpc3Q6dHJ1ZSxFbGVtZW50OmZhbHNlLEFib3J0UGF5bWVudEV2ZW50OnRydWUsQW5p
-bWF0aW9uRXZlbnQ6dHJ1ZSxBbmltYXRpb25QbGF5YmFja0V2ZW50OnRydWUsQXBwbGljYXRpb25DYWNo
-ZUVycm9yRXZlbnQ6dHJ1ZSxCYWNrZ3JvdW5kRmV0Y2hDbGlja0V2ZW50OnRydWUsQmFja2dyb3VuZEZl
-dGNoRXZlbnQ6dHJ1ZSxCYWNrZ3JvdW5kRmV0Y2hGYWlsRXZlbnQ6dHJ1ZSxCYWNrZ3JvdW5kRmV0Y2hl
-ZEV2ZW50OnRydWUsQmVmb3JlSW5zdGFsbFByb21wdEV2ZW50OnRydWUsQmVmb3JlVW5sb2FkRXZlbnQ6
-dHJ1ZSxCbG9iRXZlbnQ6dHJ1ZSxDYW5NYWtlUGF5bWVudEV2ZW50OnRydWUsQ2xpcGJvYXJkRXZlbnQ6
-dHJ1ZSxDbG9zZUV2ZW50OnRydWUsQ3VzdG9tRXZlbnQ6dHJ1ZSxEZXZpY2VNb3Rpb25FdmVudDp0cnVl
-LERldmljZU9yaWVudGF0aW9uRXZlbnQ6dHJ1ZSxFcnJvckV2ZW50OnRydWUsRXh0ZW5kYWJsZUV2ZW50
-OnRydWUsRXh0ZW5kYWJsZU1lc3NhZ2VFdmVudDp0cnVlLEZldGNoRXZlbnQ6dHJ1ZSxGb250RmFjZVNl
-dExvYWRFdmVudDp0cnVlLEZvcmVpZ25GZXRjaEV2ZW50OnRydWUsR2FtZXBhZEV2ZW50OnRydWUsSGFz
-aENoYW5nZUV2ZW50OnRydWUsSW5zdGFsbEV2ZW50OnRydWUsTWVkaWFFbmNyeXB0ZWRFdmVudDp0cnVl
-LE1lZGlhS2V5TWVzc2FnZUV2ZW50OnRydWUsTWVkaWFRdWVyeUxpc3RFdmVudDp0cnVlLE1lZGlhU3Ry
-ZWFtRXZlbnQ6dHJ1ZSxNZWRpYVN0cmVhbVRyYWNrRXZlbnQ6dHJ1ZSxNZXNzYWdlRXZlbnQ6dHJ1ZSxN
-SURJQ29ubmVjdGlvbkV2ZW50OnRydWUsTUlESU1lc3NhZ2VFdmVudDp0cnVlLE11dGF0aW9uRXZlbnQ6
-dHJ1ZSxOb3RpZmljYXRpb25FdmVudDp0cnVlLFBhZ2VUcmFuc2l0aW9uRXZlbnQ6dHJ1ZSxQYXltZW50
-UmVxdWVzdEV2ZW50OnRydWUsUGF5bWVudFJlcXVlc3RVcGRhdGVFdmVudDp0cnVlLFBvcFN0YXRlRXZl
-bnQ6dHJ1ZSxQcmVzZW50YXRpb25Db25uZWN0aW9uQXZhaWxhYmxlRXZlbnQ6dHJ1ZSxQcmVzZW50YXRp
-b25Db25uZWN0aW9uQ2xvc2VFdmVudDp0cnVlLFByb21pc2VSZWplY3Rpb25FdmVudDp0cnVlLFB1c2hF
-dmVudDp0cnVlLFJUQ0RhdGFDaGFubmVsRXZlbnQ6dHJ1ZSxSVENEVE1GVG9uZUNoYW5nZUV2ZW50OnRy
-dWUsUlRDUGVlckNvbm5lY3Rpb25JY2VFdmVudDp0cnVlLFJUQ1RyYWNrRXZlbnQ6dHJ1ZSxTZWN1cml0
-eVBvbGljeVZpb2xhdGlvbkV2ZW50OnRydWUsU2Vuc29yRXJyb3JFdmVudDp0cnVlLFNwZWVjaFJlY29n
-bml0aW9uRXJyb3I6dHJ1ZSxTcGVlY2hSZWNvZ25pdGlvbkV2ZW50OnRydWUsU3BlZWNoU3ludGhlc2lz
-RXZlbnQ6dHJ1ZSxTdG9yYWdlRXZlbnQ6dHJ1ZSxTeW5jRXZlbnQ6dHJ1ZSxUcmFja0V2ZW50OnRydWUs
-VHJhbnNpdGlvbkV2ZW50OnRydWUsV2ViS2l0VHJhbnNpdGlvbkV2ZW50OnRydWUsVlJEZXZpY2VFdmVu
-dDp0cnVlLFZSRGlzcGxheUV2ZW50OnRydWUsVlJTZXNzaW9uRXZlbnQ6dHJ1ZSxNb2pvSW50ZXJmYWNl
-UmVxdWVzdEV2ZW50OnRydWUsVVNCQ29ubmVjdGlvbkV2ZW50OnRydWUsSURCVmVyc2lvbkNoYW5nZUV2
-ZW50OnRydWUsQXVkaW9Qcm9jZXNzaW5nRXZlbnQ6dHJ1ZSxPZmZsaW5lQXVkaW9Db21wbGV0aW9uRXZl
-bnQ6dHJ1ZSxXZWJHTENvbnRleHRFdmVudDp0cnVlLEV2ZW50OmZhbHNlLElucHV0RXZlbnQ6ZmFsc2Us
-U3VibWl0RXZlbnQ6ZmFsc2UsRXZlbnRUYXJnZXQ6ZmFsc2UsRmlsZTp0cnVlLEhUTUxGb3JtRWxlbWVu
-dDp0cnVlLEhpc3Rvcnk6dHJ1ZSxIVE1MRG9jdW1lbnQ6dHJ1ZSxYTUxIdHRwUmVxdWVzdDp0cnVlLFhN
-TEh0dHBSZXF1ZXN0RXZlbnRUYXJnZXQ6ZmFsc2UsSW1hZ2VEYXRhOnRydWUsTG9jYXRpb246dHJ1ZSxN
-b3VzZUV2ZW50OnRydWUsRHJhZ0V2ZW50OnRydWUsUG9pbnRlckV2ZW50OnRydWUsV2hlZWxFdmVudDp0
-cnVlLERvY3VtZW50RnJhZ21lbnQ6dHJ1ZSxTaGFkb3dSb290OnRydWUsRG9jdW1lbnRUeXBlOnRydWUs
-Tm9kZTpmYWxzZSxOb2RlTGlzdDp0cnVlLFJhZGlvTm9kZUxpc3Q6dHJ1ZSxIVE1MUGFyYWdyYXBoRWxl
-bWVudDp0cnVlLFByb2dyZXNzRXZlbnQ6dHJ1ZSxSZXNvdXJjZVByb2dyZXNzRXZlbnQ6dHJ1ZSxIVE1M
-U2VsZWN0RWxlbWVudDp0cnVlLEhUTUxUYWJsZUVsZW1lbnQ6dHJ1ZSxIVE1MVGFibGVSb3dFbGVtZW50
-OnRydWUsSFRNTFRhYmxlU2VjdGlvbkVsZW1lbnQ6dHJ1ZSxIVE1MVGVtcGxhdGVFbGVtZW50OnRydWUs
-Q29tcG9zaXRpb25FdmVudDp0cnVlLEZvY3VzRXZlbnQ6dHJ1ZSxLZXlib2FyZEV2ZW50OnRydWUsVGV4
-dEV2ZW50OnRydWUsVG91Y2hFdmVudDp0cnVlLFVJRXZlbnQ6ZmFsc2UsV2luZG93OnRydWUsRE9NV2lu
-ZG93OnRydWUsRGVkaWNhdGVkV29ya2VyR2xvYmFsU2NvcGU6dHJ1ZSxTZXJ2aWNlV29ya2VyR2xvYmFs
-U2NvcGU6dHJ1ZSxTaGFyZWRXb3JrZXJHbG9iYWxTY29wZTp0cnVlLFdvcmtlckdsb2JhbFNjb3BlOnRy
-dWUsQXR0cjp0cnVlLENsaWVudFJlY3Q6dHJ1ZSxET01SZWN0OnRydWUsTmFtZWROb2RlTWFwOnRydWUs
-TW96TmFtZWRBdHRyTWFwOnRydWUsSURCS2V5UmFuZ2U6dHJ1ZSxTVkdTY3JpcHRFbGVtZW50OnRydWUs
-U1ZHQUVsZW1lbnQ6dHJ1ZSxTVkdBbmltYXRlRWxlbWVudDp0cnVlLFNWR0FuaW1hdGVNb3Rpb25FbGVt
-ZW50OnRydWUsU1ZHQW5pbWF0ZVRyYW5zZm9ybUVsZW1lbnQ6dHJ1ZSxTVkdBbmltYXRpb25FbGVtZW50
-OnRydWUsU1ZHQ2lyY2xlRWxlbWVudDp0cnVlLFNWR0NsaXBQYXRoRWxlbWVudDp0cnVlLFNWR0RlZnNF
-bGVtZW50OnRydWUsU1ZHRGVzY0VsZW1lbnQ6dHJ1ZSxTVkdEaXNjYXJkRWxlbWVudDp0cnVlLFNWR0Vs
-bGlwc2VFbGVtZW50OnRydWUsU1ZHRkVCbGVuZEVsZW1lbnQ6dHJ1ZSxTVkdGRUNvbG9yTWF0cml4RWxl
-bWVudDp0cnVlLFNWR0ZFQ29tcG9uZW50VHJhbnNmZXJFbGVtZW50OnRydWUsU1ZHRkVDb21wb3NpdGVF
-bGVtZW50OnRydWUsU1ZHRkVDb252b2x2ZU1hdHJpeEVsZW1lbnQ6dHJ1ZSxTVkdGRURpZmZ1c2VMaWdo
-dGluZ0VsZW1lbnQ6dHJ1ZSxTVkdGRURpc3BsYWNlbWVudE1hcEVsZW1lbnQ6dHJ1ZSxTVkdGRURpc3Rh
-bnRMaWdodEVsZW1lbnQ6dHJ1ZSxTVkdGRUZsb29kRWxlbWVudDp0cnVlLFNWR0ZFRnVuY0FFbGVtZW50
-OnRydWUsU1ZHRkVGdW5jQkVsZW1lbnQ6dHJ1ZSxTVkdGRUZ1bmNHRWxlbWVudDp0cnVlLFNWR0ZFRnVu
-Y1JFbGVtZW50OnRydWUsU1ZHRkVHYXVzc2lhbkJsdXJFbGVtZW50OnRydWUsU1ZHRkVJbWFnZUVsZW1l
-bnQ6dHJ1ZSxTVkdGRU1lcmdlRWxlbWVudDp0cnVlLFNWR0ZFTWVyZ2VOb2RlRWxlbWVudDp0cnVlLFNW
-R0ZFTW9ycGhvbG9neUVsZW1lbnQ6dHJ1ZSxTVkdGRU9mZnNldEVsZW1lbnQ6dHJ1ZSxTVkdGRVBvaW50
-TGlnaHRFbGVtZW50OnRydWUsU1ZHRkVTcGVjdWxhckxpZ2h0aW5nRWxlbWVudDp0cnVlLFNWR0ZFU3Bv
-dExpZ2h0RWxlbWVudDp0cnVlLFNWR0ZFVGlsZUVsZW1lbnQ6dHJ1ZSxTVkdGRVR1cmJ1bGVuY2VFbGVt
-ZW50OnRydWUsU1ZHRmlsdGVyRWxlbWVudDp0cnVlLFNWR0ZvcmVpZ25PYmplY3RFbGVtZW50OnRydWUs
-U1ZHR0VsZW1lbnQ6dHJ1ZSxTVkdHZW9tZXRyeUVsZW1lbnQ6dHJ1ZSxTVkdHcmFwaGljc0VsZW1lbnQ6
-dHJ1ZSxTVkdJbWFnZUVsZW1lbnQ6dHJ1ZSxTVkdMaW5lRWxlbWVudDp0cnVlLFNWR0xpbmVhckdyYWRp
-ZW50RWxlbWVudDp0cnVlLFNWR01hcmtlckVsZW1lbnQ6dHJ1ZSxTVkdNYXNrRWxlbWVudDp0cnVlLFNW
-R01ldGFkYXRhRWxlbWVudDp0cnVlLFNWR1BhdGhFbGVtZW50OnRydWUsU1ZHUGF0dGVybkVsZW1lbnQ6
-dHJ1ZSxTVkdQb2x5Z29uRWxlbWVudDp0cnVlLFNWR1BvbHlsaW5lRWxlbWVudDp0cnVlLFNWR1JhZGlh
-bEdyYWRpZW50RWxlbWVudDp0cnVlLFNWR1JlY3RFbGVtZW50OnRydWUsU1ZHU2V0RWxlbWVudDp0cnVl
-LFNWR1N0b3BFbGVtZW50OnRydWUsU1ZHU3R5bGVFbGVtZW50OnRydWUsU1ZHU1ZHRWxlbWVudDp0cnVl
-LFNWR1N3aXRjaEVsZW1lbnQ6dHJ1ZSxTVkdTeW1ib2xFbGVtZW50OnRydWUsU1ZHVFNwYW5FbGVtZW50
-OnRydWUsU1ZHVGV4dENvbnRlbnRFbGVtZW50OnRydWUsU1ZHVGV4dEVsZW1lbnQ6dHJ1ZSxTVkdUZXh0
-UGF0aEVsZW1lbnQ6dHJ1ZSxTVkdUZXh0UG9zaXRpb25pbmdFbGVtZW50OnRydWUsU1ZHVGl0bGVFbGVt
-ZW50OnRydWUsU1ZHVXNlRWxlbWVudDp0cnVlLFNWR1ZpZXdFbGVtZW50OnRydWUsU1ZHR3JhZGllbnRF
-bGVtZW50OnRydWUsU1ZHQ29tcG9uZW50VHJhbnNmZXJGdW5jdGlvbkVsZW1lbnQ6dHJ1ZSxTVkdGRURy
-b3BTaGFkb3dFbGVtZW50OnRydWUsU1ZHTVBhdGhFbGVtZW50OnRydWUsU1ZHRWxlbWVudDpmYWxzZX0p
-CkguTFouJG5hdGl2ZVN1cGVyY2xhc3NUYWc9IkFycmF5QnVmZmVyVmlldyIKSC5SRy4kbmF0aXZlU3Vw
-ZXJjbGFzc1RhZz0iQXJyYXlCdWZmZXJWaWV3IgpILlZQLiRuYXRpdmVTdXBlcmNsYXNzVGFnPSJBcnJh
-eUJ1ZmZlclZpZXciCkguRGcuJG5hdGl2ZVN1cGVyY2xhc3NUYWc9IkFycmF5QnVmZmVyVmlldyIKSC5X
-Qi4kbmF0aXZlU3VwZXJjbGFzc1RhZz0iQXJyYXlCdWZmZXJWaWV3IgpILlpHLiRuYXRpdmVTdXBlcmNs
-YXNzVGFnPSJBcnJheUJ1ZmZlclZpZXciCkguUGcuJG5hdGl2ZVN1cGVyY2xhc3NUYWc9IkFycmF5QnVm
-ZmVyVmlldyJ9KSgpCmNvbnZlcnRBbGxUb0Zhc3RPYmplY3QodykKY29udmVydFRvRmFzdE9iamVjdCgk
-KTsoZnVuY3Rpb24oYSl7aWYodHlwZW9mIGRvY3VtZW50PT09InVuZGVmaW5lZCIpe2EobnVsbCkKcmV0
-dXJufWlmKHR5cGVvZiBkb2N1bWVudC5jdXJyZW50U2NyaXB0IT0ndW5kZWZpbmVkJyl7YShkb2N1bWVu
-dC5jdXJyZW50U2NyaXB0KQpyZXR1cm59dmFyIHQ9ZG9jdW1lbnQuc2NyaXB0cwpmdW5jdGlvbiBvbkxv
-YWQoYil7Zm9yKHZhciByPTA7cjx0Lmxlbmd0aDsrK3IpdFtyXS5yZW1vdmVFdmVudExpc3RlbmVyKCJs
-b2FkIixvbkxvYWQsZmFsc2UpCmEoYi50YXJnZXQpfWZvcih2YXIgcz0wO3M8dC5sZW5ndGg7KytzKXRb
-c10uYWRkRXZlbnRMaXN0ZW5lcigibG9hZCIsb25Mb2FkLGZhbHNlKX0pKGZ1bmN0aW9uKGEpe3YuY3Vy
-cmVudFNjcmlwdD1hCmlmKHR5cGVvZiBkYXJ0TWFpblJ1bm5lcj09PSJmdW5jdGlvbiIpZGFydE1haW5S
-dW5uZXIoTC5JcSxbXSkKZWxzZSBMLklxKFtdKX0pfSkoKQovLyMgc291cmNlTWFwcGluZ1VSTD1taWdy
-YXRpb24uanMubWFwCg==
+cmlwdGlvbiIsdGhpcy5hLCJlbnRyaWVzIixxXSx1LlgsdS5fKX19ClUud2IucHJvdG90eXBlPXsKTHQ6
+ZnVuY3Rpb24oKXt2YXIgdCxzLHIscT10aGlzLHA9UC5GbCh1LlgsdS5fKQpwLlkoMCwiZGVzY3JpcHRp
+b24iLHEuYSkKdD1xLmIKaWYodCE9bnVsbClwLlkoMCwiZnVuY3Rpb24iLHQpCnQ9cS5jCmlmKHQhPW51
+bGwpcC5ZKDAsImxpbmsiLHQuTHQoKSkKdD1xLmQKaWYodC5sZW5ndGghPT0wKXtzPUgudDYodCkKcj1z
+LkMoImxKPDEsWjA8cVUqLE1oKj4qPiIpCnAuWSgwLCJoaW50QWN0aW9ucyIsUC5DSChuZXcgSC5sSih0
+LHMuQygiWjA8cVUqLE1oKj4qKDEpIikuYShuZXcgVS5iMCgpKSxyKSwhMCxyLkMoImFMLkUiKSkpfXJl
+dHVybiBwfX0KVS5hTi5wcm90b3R5cGU9ewokMTpmdW5jdGlvbihhKXtyZXR1cm4gVS5ueih1LnQuYShh
+KSl9LAokUzozOH0KVS5iMC5wcm90b3R5cGU9ewokMTpmdW5jdGlvbihhKXtyZXR1cm4gdS5hWC5hKGEp
+Lkx0KCl9LAokUzozOX0KQi5qOC5wcm90b3R5cGU9ewpMdDpmdW5jdGlvbigpe3JldHVybiBQLkVGKFsi
+bGluZSIsdGhpcy5hLCJleHBsYW5hdGlvbiIsdGhpcy5iLCJvZmZzZXQiLHRoaXMuY10sdS5YLHUuXyl9
+fQpCLnFwLnByb3RvdHlwZT17Ckx0OmZ1bmN0aW9uKCl7dmFyIHQscyxyLHEscCxvLG4sbT10aGlzLGw9
+dS5YLGs9UC5GbChsLHUuZHApCmZvcih0PW0uZCx0PXQuZ1B1KHQpLHQ9dC5na3oodCkscz11Ll8scj11
+Lkc7dC5GKCk7KXtxPXQuZ2woKQpwPXEuYQpvPUguVk0oW10scikKZm9yKHE9Si5JVChxLmIpO3EuRigp
+Oyl7bj1xLmdsKCkKQy5ObS5pKG8sUC5FRihbImxpbmUiLG4uYSwiZXhwbGFuYXRpb24iLG4uYiwib2Zm
+c2V0IixuLmNdLGwscykpfWsuWSgwLHAsbyl9cmV0dXJuIFAuRUYoWyJyZWdpb25zIixtLmEsIm5hdmln
+YXRpb25Db250ZW50IixtLmIsInNvdXJjZUNvZGUiLG0uYywiZWRpdHMiLGtdLGwscyl9fQpULm1RLnBy
+b3RvdHlwZT17fQpMLmUucHJvdG90eXBlPXsKJDE6ZnVuY3Rpb24oYSl7dmFyIHQscyxyLHEscCxvLG4K
+dS5hTC5hKGEpCnQ9d2luZG93LmxvY2F0aW9uLnBhdGhuYW1lCnM9TC5HNih3aW5kb3cubG9jYXRpb24u
+aHJlZikKcj1MLmFLKHdpbmRvdy5sb2NhdGlvbi5ocmVmKQpMLkdlKCkKaWYodCE9PSIvIiYmdCE9PUou
+VDAoZG9jdW1lbnQucXVlcnlTZWxlY3RvcigiLnJvb3QiKS50ZXh0Q29udGVudCkpTC5HNyh0LHMsciwh
+MCxuZXcgTC5WVyh0LHMscikpCnE9ZG9jdW1lbnQKcD1KLnFGKHEucXVlcnlTZWxlY3RvcigiLmFwcGx5
+LW1pZ3JhdGlvbiIpKQpvPXAuJHRpCm49by5DKCJ+KDEpPyIpLmEobmV3IEwub1ooKSkKdS5aLmEobnVs
+bCkKVy5KRShwLmEscC5iLG4sITEsby5jKQpvPUoucUYocS5xdWVyeVNlbGVjdG9yKCIucmVydW4tbWln
+cmF0aW9uIikpCm49by4kdGkKVy5KRShvLmEsby5iLG4uQygifigxKT8iKS5hKG5ldyBMLkhpKCkpLCEx
+LG4uYykKbj1KLnFGKHEucXVlcnlTZWxlY3RvcigiLnJlcG9ydC1wcm9ibGVtIikpCm89bi4kdGkKVy5K
+RShuLmEsbi5iLG8uQygifigxKT8iKS5hKG5ldyBMLkJUKCkpLCExLG8uYykKcT1KLnFGKHEucXVlcnlT
+ZWxlY3RvcigiLnBvcHVwLXBhbmUgLmNsb3NlIikpCm89cS4kdGkKVy5KRShxLmEscS5iLG8uQygifigx
+KT8iKS5hKG5ldyBMLlBZKCkpLCExLG8uYyl9LAokUzoxN30KTC5WVy5wcm90b3R5cGU9ewokMDpmdW5j
+dGlvbigpe0wuRnIodGhpcy5hLHRoaXMuYix0aGlzLmMpfSwKJFM6MH0KTC5vWi5wcm90b3R5cGU9ewok
+MTpmdW5jdGlvbihhKXt2YXIgdCxzLHIscQp1Lk8uYShhKQppZihILm9UKEMub2wudXMod2luZG93LCJU
+aGlzIHdpbGwgYXBwbHkgdGhlIGNoYW5nZXMgeW91J3ZlIHByZXZpZXdlZCB0byB5b3VyIHdvcmtpbmcg
+ZGlyZWN0b3J5LiBJdCBpcyByZWNvbW1lbmRlZCB5b3UgY29tbWl0IGFueSBjaGFuZ2VzIHlvdSBtYWRl
+IGJlZm9yZSBkb2luZyB0aGlzLiIpKSl7dD1MLnR5KCIvYXBwbHktbWlncmF0aW9uIixudWxsKS5XNyhu
+ZXcgTC5qcigpLHUuUCkKcz1uZXcgTC5xbCgpCnUuYjcuYShudWxsKQpyPXQuJHRpCnE9JC5YMwppZihx
+IT09Qy5OVSlzPVAuVkgocyxxKQp0LnhmKG5ldyBQLkZlKG5ldyBQLnZzKHEsciksMixudWxsLHMsci5D
+KCJAPDE+IikuS3Eoci5jKS5DKCJGZTwxLDI+IikpKX19LAokUzoyfQpMLmpyLnByb3RvdHlwZT17CiQx
+OmZ1bmN0aW9uKGEpe3ZhciB0CnUudC5hKGEpCnQ9ZG9jdW1lbnQuYm9keQp0LmNsYXNzTGlzdC5yZW1v
+dmUoInByb3Bvc2VkIikKdC5jbGFzc0xpc3QuYWRkKCJhcHBsaWVkIil9LAokUzo0Mn0KTC5xbC5wcm90
+b3R5cGU9ewokMjpmdW5jdGlvbihhLGIpe0wuQzIoIkNvdWxkIG5vdCBhcHBseSBtaWdyYXRpb24iLGEs
+Yil9LAokQzoiJDIiLAokUjoyLAokUzo2fQpMLkhpLnByb3RvdHlwZT17CiQxOmZ1bmN0aW9uKGEpe3Jl
+dHVybiB0aGlzLnhuKHUuTy5hKGEpKX0sCnhuOmZ1bmN0aW9uKGEpe3ZhciB0PTAscz1QLkZYKHUuUCks
+cj0xLHEscD1bXSxvLG4sbSxsCnZhciAkYXN5bmMkJDE9UC5seihmdW5jdGlvbihiLGMpe2lmKGI9PT0x
+KXtxPWMKdD1yfXdoaWxlKHRydWUpc3dpdGNoKHQpe2Nhc2UgMDpyPTMKZG9jdW1lbnQuYm9keS5jbGFz
+c0xpc3QuYWRkKCJyZXJ1bm5pbmciKQp0PTYKcmV0dXJuIFAualEoTC50eSgiL3JlcnVuLW1pZ3JhdGlv
+biIsbnVsbCksJGFzeW5jJCQxKQpjYXNlIDY6d2luZG93LmxvY2F0aW9uLnJlbG9hZCgpCnAucHVzaCg1
+KQp0PTQKYnJlYWsKY2FzZSAzOnI9MgpsPXEKbz1ILlJ1KGwpCm49SC50cyhsKQpMLkMyKCJGYWlsZWQg
+dG8gcmVydW4gbWlncmF0aW9uIixvLG4pCnAucHVzaCg1KQp0PTQKYnJlYWsKY2FzZSAyOnA9WzFdCmNh
+c2UgNDpyPTEKZG9jdW1lbnQuYm9keS5jbGFzc0xpc3QucmVtb3ZlKCJyZXJ1bm5pbmciKQp0PXAucG9w
+KCkKYnJlYWsKY2FzZSA1OnJldHVybiBQLnlDKG51bGwscykKY2FzZSAxOnJldHVybiBQLmYzKHEscyl9
+fSkKcmV0dXJuIFAuREkoJGFzeW5jJCQxLHMpfSwKJFM6MTh9CkwuQlQucHJvdG90eXBlPXsKJDE6ZnVu
+Y3Rpb24oYSl7dmFyIHQKdS5PLmEoYSkKdD11LlgKQy5vbC5Qbyh3aW5kb3csUC5YZCgiaHR0cHMiLCJn
+aXRodWIuY29tIiwiZGFydC1sYW5nL3Nkay9pc3N1ZXMvbmV3IixQLkVGKFsidGl0bGUiLCJDdXN0b21l
+ci1yZXBvcnRlZCBpc3N1ZSB3aXRoIE5OQkQgbWlncmF0aW9uIHRvb2wiLCJsYWJlbHMiLCJhcmVhLWFu
+YWx5emVyLGFuYWx5emVyLW5uYmQtbWlncmF0aW9uLHR5cGUtYnVnIiwiYm9keSIsIiMjIyMgU3RlcHMg
+dG8gcmVwcm9kdWNlXG5cbiMjIyMgV2hhdCBkaWQgeW91IGV4cGVjdCB0byBoYXBwZW4/XG5cbiMjIyMg
+V2hhdCBhY3R1YWxseSBoYXBwZW5lZD9cblxuX1NjcmVlbnNob3RzIGFyZSBhcHByZWNpYXRlZF9cblxu
+KipEYXJ0IFNESyB2ZXJzaW9uKio6ICIrSC5Faihkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgic2RrLXZl
+cnNpb24iKS50ZXh0Q29udGVudCkrIlxuXG5UaGFua3MgZm9yIGZpbGluZyFcbiJdLHQsdCkpLmduRCgp
+LCJyZXBvcnQtcHJvYmxlbSIpfSwKJFM6Mn0KTC5QWS5wcm90b3R5cGU9ewokMTpmdW5jdGlvbihhKXt2
+YXIgdAp1Lk8uYShhKQp0PWRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoIi5wb3B1cC1wYW5lIikuc3R5bGUK
+dC5kaXNwbGF5PSJub25lIgpyZXR1cm4ibm9uZSJ9LAokUzo0NH0KTC5MLnByb3RvdHlwZT17CiQxOmZ1
+bmN0aW9uKGEpe3ZhciB0LHMscgp1LmFMLmEoYSkKdD13aW5kb3cubG9jYXRpb24ucGF0aG5hbWUKcz1M
+Lkc2KHdpbmRvdy5sb2NhdGlvbi5ocmVmKQpyPUwuYUsod2luZG93LmxvY2F0aW9uLmhyZWYpCmlmKHQu
+bGVuZ3RoPjEpTC5HNyh0LHMsciwhMSxudWxsKQplbHNle0wuQkUodCxuZXcgQi5xcCgiIiwiIiwiIixD
+LkNNKSwhMCkKTC5CWCgiJm5ic3A7IixudWxsKX19LAokUzoxN30KTC5XeC5wcm90b3R5cGU9ewokMTpm
+dW5jdGlvbihhKXt2YXIgdCxzLHIscT0iY29sbGFwc2VkIgp1Lk8uYShhKQp0PXRoaXMuYQpzPUouWUUo
+dCkKcj10aGlzLmIKaWYoIXMuZ1AodCkudGcoMCxxKSl7cy5nUCh0KS5pKDAscSkKSi5kUihyKS5pKDAs
+cSl9ZWxzZXtzLmdQKHQpLlIoMCxxKQpKLmRSKHIpLlIoMCxxKX19LAokUzoyfQpMLkFPLnByb3RvdHlw
+ZT17CiQxOmZ1bmN0aW9uKGEpe3ZhciB0PUoucUYodS5nLmEoYSkpLHM9dC4kdGkscj1zLkMoIn4oMSk/
+IikuYShuZXcgTC5kTih0aGlzLmEpKQp1LlouYShudWxsKQpXLkpFKHQuYSx0LmIsciwhMSxzLmMpfSwK
+JFM6M30KTC5kTi5wcm90b3R5cGU9ewokMTpmdW5jdGlvbihhKXt2YXIgdAp1Lk8uYShhKQp0PWRvY3Vt
+ZW50LnF1ZXJ5U2VsZWN0b3IoInRhYmxlW2RhdGEtcGF0aF0iKQp0LnRvU3RyaW5nCkwudDIoYSx0aGlz
+LmEsdC5nZXRBdHRyaWJ1dGUoImRhdGEtIituZXcgVy5TeShuZXcgVy5pNyh0KSkuTygicGF0aCIpKSl9
+LAokUzoyfQpMLkhvLnByb3RvdHlwZT17CiQxOmZ1bmN0aW9uKGEpe3ZhciB0LHMscgp1LmcuYShhKQp0
+PUoucUYoYSkKcz10LiR0aQpyPXMuQygifigxKT8iKS5hKG5ldyBMLnh6KGEsdGhpcy5hKSkKdS5aLmEo
+bnVsbCkKVy5KRSh0LmEsdC5iLHIsITEscy5jKX0sCiRTOjN9CkwueHoucHJvdG90eXBlPXsKJDE6ZnVu
+Y3Rpb24oYSl7dmFyIHQKdS5PLmEoYSkKdD10aGlzLmEKTC5oWCh0aGlzLmIsUC5RQSh0LmdldEF0dHJp
+YnV0ZSgiZGF0YS0iK25ldyBXLlN5KG5ldyBXLmk3KHQpKS5PKCJvZmZzZXQiKSksbnVsbCksUC5RQSh0
+LmdldEF0dHJpYnV0ZSgiZGF0YS0iK25ldyBXLlN5KG5ldyBXLmk3KHQpKS5PKCJsaW5lIikpLG51bGwp
+KX0sCiRTOjJ9CkwuSUMucHJvdG90eXBlPXsKJDE6ZnVuY3Rpb24oYSl7dmFyIHQ9Si5xRih1LmcuYShh
+KSkscz10LiR0aQpzLkMoIn4oMSk/IikuYShMLmlTKCkpCnUuWi5hKG51bGwpClcuSkUodC5hLHQuYixM
+LmlTKCksITEscy5jKX0sCiRTOjN9CkwuZkMucHJvdG90eXBlPXsKJDE6ZnVuY3Rpb24oYSl7dS5lUS5h
+KGEpCnRoaXMuYS5hTSgwLHRoaXMuYil9LAokUzo0Nn0KTC5uVC5wcm90b3R5cGU9ewokMDpmdW5jdGlv
+bigpe0wuRnIodGhpcy5hLmEsdGhpcy5iLHRoaXMuYyl9LAokUzowfQpMLk5ZLnByb3RvdHlwZT17CiQw
+OmZ1bmN0aW9uKCl7TC5Gcih0aGlzLmEuYSxudWxsLG51bGwpfSwKJFM6MH0KTC5lWC5wcm90b3R5cGU9
+ewokMTpmdW5jdGlvbihhKXt1LmcuYShhKQokLnpCKCkudG9TdHJpbmcKdS5kSC5hKCQub3coKS5xKDAs
+ImhsanMiKSkuVjcoImhpZ2hsaWdodEJsb2NrIixbYV0pfSwKJFM6M30KTC5FRS5wcm90b3R5cGU9ewok
+MTpmdW5jdGlvbihhKXt2YXIgdCxzCnUuTy5hKGEpCnQ9dGhpcy5hCnM9dGhpcy5iCkwuYWYod2luZG93
+LmxvY2F0aW9uLnBhdGhuYW1lLHQscywhMCxuZXcgTC5RTCh0LHMpKQpMLmhYKHRoaXMuYyx0LHMpfSwK
+JFM6Mn0KTC5RTC5wcm90b3R5cGU9ewokMDpmdW5jdGlvbigpe0wuRnIod2luZG93LmxvY2F0aW9uLnBh
+dGhuYW1lLHRoaXMuYSx0aGlzLmIpfSwKJFM6MH0KTC5WUy5wcm90b3R5cGU9ewokMTpmdW5jdGlvbihh
+KXt2YXIgdCxzPSJzZWxlY3RlZC1maWxlIgp1LmcuYShhKQphLnRvU3RyaW5nCnQ9Si5ZRShhKQppZihh
+LmdldEF0dHJpYnV0ZSgiZGF0YS0iK25ldyBXLlN5KG5ldyBXLmk3KGEpKS5PKCJuYW1lIikpPT09dGhp
+cy5hLmEpdC5nUChhKS5pKDAscykKZWxzZSB0LmdQKGEpLlIoMCxzKX0sCiRTOjN9CkwuVEQucHJvdG90
+eXBlPXsKJDE6ZnVuY3Rpb24oYSl7cmV0dXJuIEwudDIodS5PLmEoYSksITAsbnVsbCl9LAokUzoxOX0K
+TC5BUy5wcm90b3R5cGU9ewokMTpmdW5jdGlvbihhKXtyZXR1cm4gdGhpcy5QTCh1Lk8uYShhKSl9LApQ
+TDpmdW5jdGlvbihhKXt2YXIgdD0wLHM9UC5GWCh1LlApLHI9MSxxLHA9W10sbz10aGlzLG4sbSxsLGss
+agp2YXIgJGFzeW5jJCQxPVAubHooZnVuY3Rpb24oYixjKXtpZihiPT09MSl7cT1jCnQ9cn13aGlsZSh0
+cnVlKXN3aXRjaCh0KXtjYXNlIDA6cj0zCmw9dS5YCnQ9NgpyZXR1cm4gUC5qUShMLnR5KEwuUTQoIi9h
+cHBseS1oaW50IixQLkZsKGwsbCkpLG8uYS5MdCgpKSwkYXN5bmMkJDEpCmNhc2UgNjpsPW8uYgpMLkc3
+KGwuYyxudWxsLGwuYiwhMSxudWxsKQpkb2N1bWVudC5ib2R5LmNsYXNzTGlzdC5hZGQoIm5lZWRzLXJl
+cnVuIikKcj0xCnQ9NQpicmVhawpjYXNlIDM6cj0yCmo9cQpuPUguUnUoaikKbT1ILnRzKGopCkwuQzIo
+IkNvdWxkIG5vdCBhcHBseSBoaW50IixuLG0pCnQ9NQpicmVhawpjYXNlIDI6dD0xCmJyZWFrCmNhc2Ug
+NTpyZXR1cm4gUC55QyhudWxsLHMpCmNhc2UgMTpyZXR1cm4gUC5mMyhxLHMpfX0pCnJldHVybiBQLkRJ
+KCRhc3luYyQkMSxzKX0sCiRTOjE4fQpMLlhBLnByb3RvdHlwZT17CkViOmZ1bmN0aW9uKGEsYixjKXty
+ZXR1cm4hMH0sCmkwOmZ1bmN0aW9uKGEpe3JldHVybiEwfSwKJGlrRjoxfQpMLlpaLnByb3RvdHlwZT17
+Ckx0OmZ1bmN0aW9uKCl7dmFyIHQscz10aGlzLHI9UC5GbCh1LlgsdS5fKQpyLlkoMCwidHlwZSIsTC52
+eShzLmEpKQpyLlkoMCwibmFtZSIscy5iKQp0PXMuYwppZih0IT1udWxsKXIuWSgwLCJzdWJ0cmVlIixM
+LlZEKHQpKQp0PXMuZAppZih0IT1udWxsKXIuWSgwLCJwYXRoIix0KQp0PXMuZQppZih0IT1udWxsKXIu
+WSgwLCJocmVmIix0KQp0PXMuZgppZih0IT1udWxsKXIuWSgwLCJlZGl0Q291bnQiLHQpCnJldHVybiBy
+fX0KTC5POS5wcm90b3R5cGU9ewp3OmZ1bmN0aW9uKGEpe3JldHVybiB0aGlzLmJ9fQpNLmxJLnByb3Rv
+dHlwZT17CldPOmZ1bmN0aW9uKGEsYil7dmFyIHQscz1udWxsCk0uWUYoImFic29sdXRlIixILlZNKFti
+LG51bGwsbnVsbCxudWxsLG51bGwsbnVsbCxudWxsXSx1LmkpKQp0PXRoaXMuYQp0PXQuWXIoYik+MCYm
+IXQuaEsoYikKaWYodClyZXR1cm4gYgp0PUQuUlgoKQpyZXR1cm4gdGhpcy5xNygwLHQsYixzLHMscyxz
+LHMscyl9LAp6ZjpmdW5jdGlvbihhKXt2YXIgdCxzLHI9WC5DTChhLHRoaXMuYSkKci5JVigpCnQ9ci5k
+CnM9dC5sZW5ndGgKaWYocz09PTApe3Q9ci5iCnJldHVybiB0PT1udWxsPyIuIjp0fWlmKHM9PT0xKXt0
+PXIuYgpyZXR1cm4gdD09bnVsbD8iLiI6dH1pZigwPj1zKXJldHVybiBILk9IKHQsLTEpCnQucG9wKCkK
+Qy5ObS5tdihyLmUpCnIuSVYoKQpyZXR1cm4gci53KDApfSwKcTc6ZnVuY3Rpb24oYSxiLGMsZCxlLGYs
+ZyxoLGkpe3ZhciB0PUguVk0oW2IsYyxkLGUsZixnLGgsaV0sdS5pKQpNLllGKCJqb2luIix0KQpyZXR1
+cm4gdGhpcy5JUChuZXcgSC5VNSh0LHUuZ2YuYShuZXcgTS5NaSgpKSx1LmZpKSl9LApJUDpmdW5jdGlv
+bihhKXt2YXIgdCxzLHIscSxwLG8sbixtLGwsawp1LmVTLmEoYSkKZm9yKHQ9YS4kdGkscz10LkMoImEy
+KGNYLkUpIikuYShuZXcgTS5xNygpKSxyPWEuZ2t6KGEpLHQ9bmV3IEguU08ocixzLHQuQygiU088Y1gu
+RT4iKSkscz10aGlzLmEscT0hMSxwPSExLG89IiI7dC5GKCk7KXtuPXIuZ2woKQppZihzLmhLKG4pJiZw
+KXttPVguQ0wobixzKQpsPW8uY2hhckNvZGVBdCgwKT09MD9vOm8Kbz1DLnhCLk5qKGwsMCxzLlNwKGws
+ITApKQptLmI9bwppZihzLmRzKG8pKUMuTm0uWShtLmUsMCxzLmdtSSgpKQpvPW0udygwKX1lbHNlIGlm
+KHMuWXIobik+MCl7cD0hcy5oSyhuKQpvPUguRWoobil9ZWxzZXtrPW4ubGVuZ3RoCmlmKGshPT0wKXtp
+ZigwPj1rKXJldHVybiBILk9IKG4sMCkKaz1zLlVkKG5bMF0pfWVsc2Ugaz0hMQppZighaylpZihxKW8r
+PXMuZ21JKCkKbys9bn1xPXMuZHMobil9cmV0dXJuIG8uY2hhckNvZGVBdCgwKT09MD9vOm99LApvNTpm
+dW5jdGlvbihhKXt2YXIgdAppZighdGhpcy55MyhhKSlyZXR1cm4gYQp0PVguQ0woYSx0aGlzLmEpCnQu
+clIoKQpyZXR1cm4gdC53KDApfSwKeTM6ZnVuY3Rpb24oYSl7dmFyIHQscyxyLHEscCxvLG4sbSxsLGsK
+YS50b1N0cmluZwp0PXRoaXMuYQpzPXQuWXIoYSkKaWYocyE9PTApe2lmKHQ9PT0kLktrKCkpZm9yKHI9
+MDtyPHM7KytyKWlmKEMueEIuVyhhLHIpPT09NDcpcmV0dXJuITAKcT1zCnA9NDd9ZWxzZXtxPTAKcD1u
+dWxsfWZvcihvPW5ldyBILnFqKGEpLmEsbj1vLmxlbmd0aCxyPXEsbT1udWxsO3I8bjsrK3IsbT1wLHA9
+bCl7bD1DLnhCLm0obyxyKQppZih0LnI0KGwpKXtpZih0PT09JC5LaygpJiZsPT09NDcpcmV0dXJuITAK
+aWYocCE9bnVsbCYmdC5yNChwKSlyZXR1cm4hMAppZihwPT09NDYpaz1tPT1udWxsfHxtPT09NDZ8fHQu
+cjQobSkKZWxzZSBrPSExCmlmKGspcmV0dXJuITB9fWlmKHA9PW51bGwpcmV0dXJuITAKaWYodC5yNChw
+KSlyZXR1cm4hMAppZihwPT09NDYpdD1tPT1udWxsfHx0LnI0KG0pfHxtPT09NDYKZWxzZSB0PSExCmlm
+KHQpcmV0dXJuITAKcmV0dXJuITF9LApIUDpmdW5jdGlvbihhLGIpe3ZhciB0LHMscixxLHAsbyxuLG09
+dGhpcyxsPSdVbmFibGUgdG8gZmluZCBhIHBhdGggdG8gIicKYj1tLldPKDAsYikKdD1tLmEKaWYodC5Z
+cihiKTw9MCYmdC5ZcihhKT4wKXJldHVybiBtLm81KGEpCmlmKHQuWXIoYSk8PTB8fHQuaEsoYSkpYT1t
+LldPKDAsYSkKaWYodC5ZcihhKTw9MCYmdC5ZcihiKT4wKXRocm93IEguYihYLkk3KGwrSC5FaihhKSsn
+IiBmcm9tICInK0guRWooYikrJyIuJykpCnM9WC5DTChiLHQpCnMuclIoKQpyPVguQ0woYSx0KQpyLnJS
+KCkKcT1zLmQKcD1xLmxlbmd0aAppZihwIT09MCl7aWYoMD49cClyZXR1cm4gSC5PSChxLDApCnE9Si5S
+TShxWzBdLCIuIil9ZWxzZSBxPSExCmlmKHEpcmV0dXJuIHIudygwKQpxPXMuYgpwPXIuYgppZihxIT1w
+KXE9cT09bnVsbHx8cD09bnVsbHx8IXQuTmMocSxwKQplbHNlIHE9ITEKaWYocSlyZXR1cm4gci53KDAp
+CndoaWxlKCEwKXtxPXMuZApwPXEubGVuZ3RoCmlmKHAhPT0wKXtvPXIuZApuPW8ubGVuZ3RoCmlmKG4h
+PT0wKXtpZigwPj1wKXJldHVybiBILk9IKHEsMCkKcT1xWzBdCmlmKDA+PW4pcmV0dXJuIEguT0gobyww
+KQpvPXQuTmMocSxvWzBdKQpxPW99ZWxzZSBxPSExfWVsc2UgcT0hMQppZighcSlicmVhawpDLk5tLlc0
+KHMuZCwwKQpDLk5tLlc0KHMuZSwxKQpDLk5tLlc0KHIuZCwwKQpDLk5tLlc0KHIuZSwxKX1xPXMuZApw
+PXEubGVuZ3RoCmlmKHAhPT0wKXtpZigwPj1wKXJldHVybiBILk9IKHEsMCkKcT1KLlJNKHFbMF0sIi4u
+Iil9ZWxzZSBxPSExCmlmKHEpdGhyb3cgSC5iKFguSTcobCtILkVqKGEpKyciIGZyb20gIicrSC5Faihi
+KSsnIi4nKSkKcT11LlgKQy5ObS5VRyhyLmQsMCxQLk84KHMuZC5sZW5ndGgsIi4uIiwhMSxxKSkKQy5O
+bS5ZKHIuZSwwLCIiKQpDLk5tLlVHKHIuZSwxLFAuTzgocy5kLmxlbmd0aCx0LmdtSSgpLCExLHEpKQp0
+PXIuZApxPXQubGVuZ3RoCmlmKHE9PT0wKXJldHVybiIuIgppZihxPjEmJkouUk0oQy5ObS5ncloodCks
+Ii4iKSl7dD1yLmQKaWYoMD49dC5sZW5ndGgpcmV0dXJuIEguT0godCwtMSkKdC5wb3AoKQp0PXIuZQpD
+Lk5tLm12KHQpCkMuTm0ubXYodCkKQy5ObS5pKHQsIiIpfXIuYj0iIgpyLklWKCkKcmV0dXJuIHIudygw
+KX19Ck0uTWkucHJvdG90eXBlPXsKJDE6ZnVuY3Rpb24oYSl7cmV0dXJuIEguaChhKSE9bnVsbH0sCiRT
+OjIwfQpNLnE3LnByb3RvdHlwZT17CiQxOmZ1bmN0aW9uKGEpe3JldHVybiBILmgoYSkhPT0iIn0sCiRT
+OjIwfQpNLk5vLnByb3RvdHlwZT17CiQxOmZ1bmN0aW9uKGEpe0guaChhKQpyZXR1cm4gYT09bnVsbD8i
+bnVsbCI6JyInK2ErJyInfSwKJFM6NDl9CkIuZnYucHJvdG90eXBlPXsKeFo6ZnVuY3Rpb24oYSl7dmFy
+IHQscz10aGlzLllyKGEpCmlmKHM+MClyZXR1cm4gSi5sZChhLDAscykKaWYodGhpcy5oSyhhKSl7aWYo
+MD49YS5sZW5ndGgpcmV0dXJuIEguT0goYSwwKQp0PWFbMF19ZWxzZSB0PW51bGwKcmV0dXJuIHR9LApO
+YzpmdW5jdGlvbihhLGIpe3JldHVybiBhPT1ifX0KWC5XRC5wcm90b3R5cGU9ewpJVjpmdW5jdGlvbigp
+e3ZhciB0LHMscj10aGlzCndoaWxlKCEwKXt0PXIuZAppZighKHQubGVuZ3RoIT09MCYmSi5STShDLk5t
+LmdyWih0KSwiIikpKWJyZWFrCnQ9ci5kCmlmKDA+PXQubGVuZ3RoKXJldHVybiBILk9IKHQsLTEpCnQu
+cG9wKCkKQy5ObS5tdihyLmUpfXQ9ci5lCnM9dC5sZW5ndGgKaWYocyE9PTApQy5ObS5ZKHQscy0xLCIi
+KX0sCnJSOmZ1bmN0aW9uKCl7dmFyIHQscyxyLHEscCxvLG49dGhpcyxtPUguVk0oW10sdS5pKQpmb3Io
+dD1uLmQscz10Lmxlbmd0aCxyPTAscT0wO3E8dC5sZW5ndGg7dC5sZW5ndGg9PT1zfHwoMCxILmxrKSh0
+KSwrK3Epe3A9dFtxXQpvPUouaWEocCkKaWYoIShvLkROKHAsIi4iKXx8by5ETihwLCIiKSkpaWYoby5E
+TihwLCIuLiIpKXtvPW0ubGVuZ3RoCmlmKG8hPT0wKXtpZigwPj1vKXJldHVybiBILk9IKG0sLTEpCm0u
+cG9wKCl9ZWxzZSArK3J9ZWxzZSBDLk5tLmkobSxwKX1pZihuLmI9PW51bGwpQy5ObS5VRyhtLDAsUC5P
+OChyLCIuLiIsITEsdS5YKSkKaWYobS5sZW5ndGg9PT0wJiZuLmI9PW51bGwpQy5ObS5pKG0sIi4iKQpu
+LnNuSihtKQp0PW4uYQpuLnNQaChQLk84KG0ubGVuZ3RoKzEsdC5nbUkoKSwhMCx1LlgpKQpzPW4uYgpp
+ZihzPT1udWxsfHxtLmxlbmd0aD09PTB8fCF0LmRzKHMpKUMuTm0uWShuLmUsMCwiIikKcz1uLmIKaWYo
+cyE9bnVsbCYmdD09PSQuS2soKSl7cy50b1N0cmluZwpuLmI9SC55cyhzLCIvIiwiXFwiKX1uLklWKCl9
+LAp3OmZ1bmN0aW9uKGEpe3ZhciB0LHMscj10aGlzLHE9ci5iCnE9cSE9bnVsbD9xOiIiCmZvcih0PTA7
+dDxyLmQubGVuZ3RoOysrdCl7cz1yLmUKaWYodD49cy5sZW5ndGgpcmV0dXJuIEguT0gocyx0KQpzPXEr
+SC5FaihzW3RdKQpxPXIuZAppZih0Pj1xLmxlbmd0aClyZXR1cm4gSC5PSChxLHQpCnE9cytILkVqKHFb
+dF0pfXErPUguRWooQy5ObS5nclooci5lKSkKcmV0dXJuIHEuY2hhckNvZGVBdCgwKT09MD9xOnF9LApz
+bko6ZnVuY3Rpb24oYSl7dGhpcy5kPXUuZUcuYShhKX0sCnNQaDpmdW5jdGlvbihhKXt0aGlzLmU9dS5l
+Ry5hKGEpfX0KWC5kdi5wcm90b3R5cGU9ewp3OmZ1bmN0aW9uKGEpe3JldHVybiJQYXRoRXhjZXB0aW9u
+OiAiK3RoaXMuYX0sCiRpUno6MX0KTy56TC5wcm90b3R5cGU9ewp3OmZ1bmN0aW9uKGEpe3JldHVybiB0
+aGlzLmdvYyh0aGlzKX19CkUuT0YucHJvdG90eXBlPXsKVWQ6ZnVuY3Rpb24oYSl7cmV0dXJuIEMueEIu
+dGcoYSwiLyIpfSwKcjQ6ZnVuY3Rpb24oYSl7cmV0dXJuIGE9PT00N30sCmRzOmZ1bmN0aW9uKGEpe3Zh
+ciB0PWEubGVuZ3RoCnJldHVybiB0IT09MCYmQy54Qi5tKGEsdC0xKSE9PTQ3fSwKU3A6ZnVuY3Rpb24o
+YSxiKXtpZihhLmxlbmd0aCE9PTAmJkMueEIuVyhhLDApPT09NDcpcmV0dXJuIDEKcmV0dXJuIDB9LApZ
+cjpmdW5jdGlvbihhKXtyZXR1cm4gdGhpcy5TcChhLCExKX0sCmhLOmZ1bmN0aW9uKGEpe3JldHVybiEx
+fSwKZ29jOmZ1bmN0aW9uKCl7cmV0dXJuInBvc2l4In0sCmdtSTpmdW5jdGlvbigpe3JldHVybiIvIn19
+CkYucnUucHJvdG90eXBlPXsKVWQ6ZnVuY3Rpb24oYSl7cmV0dXJuIEMueEIudGcoYSwiLyIpfSwKcjQ6
+ZnVuY3Rpb24oYSl7cmV0dXJuIGE9PT00N30sCmRzOmZ1bmN0aW9uKGEpe3ZhciB0PWEubGVuZ3RoCmlm
+KHQ9PT0wKXJldHVybiExCmlmKEMueEIubShhLHQtMSkhPT00NylyZXR1cm4hMApyZXR1cm4gQy54Qi5U
+YyhhLCI6Ly8iKSYmdGhpcy5ZcihhKT09PXR9LApTcDpmdW5jdGlvbihhLGIpe3ZhciB0LHMscixxLHA9
+YS5sZW5ndGgKaWYocD09PTApcmV0dXJuIDAKaWYoQy54Qi5XKGEsMCk9PT00NylyZXR1cm4gMQpmb3Io
+dD0wO3Q8cDsrK3Qpe3M9Qy54Qi5XKGEsdCkKaWYocz09PTQ3KXJldHVybiAwCmlmKHM9PT01OCl7aWYo
+dD09PTApcmV0dXJuIDAKcj1DLnhCLlhVKGEsIi8iLEMueEIuUWkoYSwiLy8iLHQrMSk/dCszOnQpCmlm
+KHI8PTApcmV0dXJuIHAKaWYoIWJ8fHA8ciszKXJldHVybiByCmlmKCFDLnhCLm4oYSwiZmlsZTovLyIp
+KXJldHVybiByCmlmKCFCLll1KGEscisxKSlyZXR1cm4gcgpxPXIrMwpyZXR1cm4gcD09PXE/cTpyKzR9
+fXJldHVybiAwfSwKWXI6ZnVuY3Rpb24oYSl7cmV0dXJuIHRoaXMuU3AoYSwhMSl9LApoSzpmdW5jdGlv
+bihhKXtyZXR1cm4gYS5sZW5ndGghPT0wJiZDLnhCLlcoYSwwKT09PTQ3fSwKZ29jOmZ1bmN0aW9uKCl7
+cmV0dXJuInVybCJ9LApnbUk6ZnVuY3Rpb24oKXtyZXR1cm4iLyJ9fQpMLklWLnByb3RvdHlwZT17ClVk
+OmZ1bmN0aW9uKGEpe3JldHVybiBDLnhCLnRnKGEsIi8iKX0sCnI0OmZ1bmN0aW9uKGEpe3JldHVybiBh
+PT09NDd8fGE9PT05Mn0sCmRzOmZ1bmN0aW9uKGEpe3ZhciB0PWEubGVuZ3RoCmlmKHQ9PT0wKXJldHVy
+biExCnQ9Qy54Qi5tKGEsdC0xKQpyZXR1cm4hKHQ9PT00N3x8dD09PTkyKX0sClNwOmZ1bmN0aW9uKGEs
+Yil7dmFyIHQscyxyPWEubGVuZ3RoCmlmKHI9PT0wKXJldHVybiAwCnQ9Qy54Qi5XKGEsMCkKaWYodD09
+PTQ3KXJldHVybiAxCmlmKHQ9PT05Mil7aWYocjwyfHxDLnhCLlcoYSwxKSE9PTkyKXJldHVybiAxCnM9
+Qy54Qi5YVShhLCJcXCIsMikKaWYocz4wKXtzPUMueEIuWFUoYSwiXFwiLHMrMSkKaWYocz4wKXJldHVy
+biBzfXJldHVybiByfWlmKHI8MylyZXR1cm4gMAppZighQi5PUyh0KSlyZXR1cm4gMAppZihDLnhCLlco
+YSwxKSE9PTU4KXJldHVybiAwCnI9Qy54Qi5XKGEsMikKaWYoIShyPT09NDd8fHI9PT05MikpcmV0dXJu
+IDAKcmV0dXJuIDN9LApZcjpmdW5jdGlvbihhKXtyZXR1cm4gdGhpcy5TcChhLCExKX0sCmhLOmZ1bmN0
+aW9uKGEpe3JldHVybiB0aGlzLllyKGEpPT09MX0sCk90OmZ1bmN0aW9uKGEsYil7dmFyIHQKaWYoYT09
+PWIpcmV0dXJuITAKaWYoYT09PTQ3KXJldHVybiBiPT09OTIKaWYoYT09PTkyKXJldHVybiBiPT09NDcK
+aWYoKGFeYikhPT0zMilyZXR1cm4hMQp0PWF8MzIKcmV0dXJuIHQ+PTk3JiZ0PD0xMjJ9LApOYzpmdW5j
+dGlvbihhLGIpe3ZhciB0LHMscgppZihhPT1iKXJldHVybiEwCnQ9YS5sZW5ndGgKaWYodCE9PWIubGVu
+Z3RoKXJldHVybiExCmZvcihzPUouclkoYikscj0wO3I8dDsrK3IpaWYoIXRoaXMuT3QoQy54Qi5XKGEs
+cikscy5XKGIscikpKXJldHVybiExCnJldHVybiEwfSwKZ29jOmZ1bmN0aW9uKCl7cmV0dXJuIndpbmRv
+d3MifSwKZ21JOmZ1bmN0aW9uKCl7cmV0dXJuIlxcIn19OyhmdW5jdGlvbiBhbGlhc2VzKCl7dmFyIHQ9
+Si52Qi5wcm90b3R5cGUKdC5VPXQudwp0LlNqPXQuZTcKdD1KLk1GLnByb3RvdHlwZQp0LnQ9dC53CnQ9
+UC5jWC5wcm90b3R5cGUKdC5HRz10LmV2CnQ9UC5NaC5wcm90b3R5cGUKdC54Yj10LncKdD1XLmN2LnBy
+b3RvdHlwZQp0LkRXPXQucjYKdD1XLm02LnByb3RvdHlwZQp0LmpGPXQuRWIKdD1QLkU0LnByb3RvdHlw
+ZQp0LlVyPXQucQp0LmU0PXQuWX0pKCk7KGZ1bmN0aW9uIGluc3RhbGxUZWFyT2Zmcygpe3ZhciB0PWh1
+bmtIZWxwZXJzLl9zdGF0aWNfMSxzPWh1bmtIZWxwZXJzLl9zdGF0aWNfMCxyPWh1bmtIZWxwZXJzLmlu
+c3RhbGxJbnN0YW5jZVRlYXJPZmYscT1odW5rSGVscGVycy5pbnN0YWxsU3RhdGljVGVhck9mZixwPWh1
+bmtIZWxwZXJzLl9pbnN0YW5jZV8xdQp0KFAsIkVYIiwiWlYiLDcpCnQoUCwieXQiLCJvQSIsNykKdChQ
+LCJxVyIsIkJ6Iiw3KQpzKFAsIlY5IiwiZU4iLDEpCnIoUC5QZi5wcm90b3R5cGUsImdZSiIsMCwxLG51
+bGwsWyIkMiIsIiQxIl0sWyJ3MCIsInBtIl0sMjksMCkKdChQLCJDeSIsIk5DIiw0KQp0KFAsIlBIIiwi
+TXQiLDUpCnEoVywicFMiLDQsbnVsbCxbIiQ0Il0sWyJxRCJdLDgsMCkKcShXLCJWNCIsNCxudWxsLFsi
+JDQiXSxbIlFXIl0sOCwwKQpwKFAuQXMucHJvdG90eXBlLCJndU0iLCJUIiw1KQp0KFAsImlHIiwid1ki
+LDUyKQp0KFAsIncwIiwiZFUiLDM1KQp0KEwsImlTIiwiaTYiLDE5KX0pKCk7KGZ1bmN0aW9uIGluaGVy
+aXRhbmNlKCl7dmFyIHQ9aHVua0hlbHBlcnMubWl4aW4scz1odW5rSGVscGVycy5pbmhlcml0LHI9aHVu
+a0hlbHBlcnMuaW5oZXJpdE1hbnkKcyhQLk1oLG51bGwpCnIoUC5NaCxbSC5GSyxKLnZCLEoubTEsUC5Y
+UyxQLm5ZLFAuY1gsSC5hNyxQLkFuLEguU1UsSC5SZSxILnd2LFAuUG4sSC5XVSxILkxJLEgudixILmY5
+LEgudGUsSC5icSxILlhPLEgua3IsUC5ZayxILmRiLEguTjYsSC5WUixILkVLLEguUGIsSC50USxILlNk
+LEguSmMsSC5HLFAuVzMsUC5paCxQLkZ5LFAuR1YsUC5iOCxQLlBmLFAuRmUsUC52cyxQLk9NLFAucWgs
+UC5NTyxQLmtULFAueEksUC5DdyxQLm0wLFAuWHYsUC5ibixQLmxtLFAubEQsUC5LUCxQLk1hLFAuV1ks
+UC5VayxQLlNoLFAuUncsUC5ieixQLmEyLFAuaVAsUC5sZixQLms1LFAuS1ksUC5DRCxQLmFFLFAuRUgs
+UC56TSxQLlowLFAuTjMsUC5jOCxQLk9kLFAuaWIsUC5HeixQLlpkLFAucVUsUC5SbixQLkdELFAuRG4s
+UC5QRSxQLlVmLFcuaWQsVy5GayxXLkpRLFcuR20sVy52RCxXLm02LFcuT3csVy5XOSxXLmRXLFcuRmIs
+Vy5rRixXLm1rLFcuS28sUC5pSixQLkU0LFAubjYsTS5INyxVLkxMLFUuZDIsVS5TZSxVLk1sLFUueUQs
+VS53YixCLmo4LEIucXAsVC5tUSxMLlhBLEwuWlosTC5POSxNLmxJLE8uekwsWC5XRCxYLmR2XSkKcihK
+LnZCLFtKLnlFLEoud2UsSi5NRixKLmpkLEoucUksSi5EcixILkVULFcuRDAsVy5BeixXLkxlLFcuTmgs
+Vy5hZSxXLklCLFcubjcsVy5lYSxXLmJyLFcuU2csVy51OCxXLks3LFcuWFcsUC5oRl0pCnIoSi5NRixb
+Si5pQyxKLmtkLEouYzVdKQpzKEouUG8sSi5qZCkKcihKLnFJLFtKLmJVLEouVkFdKQpyKFAuWFMsW0gu
+bmQsSC5XMCxILmF6LEgudlYsSC5FcSxQLkM2LEgudTksUC5VZCxQLm4sUC51LFAubXAsUC51YixQLmRz
+LFAubGosUC5VVixQLmNdKQpzKFAudXksUC5uWSkKcihQLnV5LFtILncyLFcud3osVy5lN10pCnMoSC5x
+aixILncyKQpyKFAuY1gsW0guYlEsSC5pMSxILlU1LEguWFIsUC5tVyxILnVuXSkKcihILmJRLFtILmFM
+LEguaTUsUC54dV0pCnIoSC5hTCxbSC5uSCxILmxKLFAuaThdKQpzKEgueHksSC5pMSkKcihQLkFuLFtI
+Lk1ILEguU09dKQpzKFAuUlUsUC5QbikKcyhQLkdqLFAuUlUpCnMoSC5QRCxQLkdqKQpzKEguTFAsSC5X
+VSkKcihILnYsW0guQ2osSC5sYyxILmRDLEgud04sSC5WWCxQLnRoLFAuaGEsUC5WcyxQLkZ0LFAueUgs
+UC5XTSxQLlNYLFAuR3MsUC5kYSxQLm9RLFAucFYsUC5VNyxQLnZyLFAucnQsUC5LRixQLlpMLFAuUlQs
+UC5qWixQLnJxLFAuUlcsUC5CNSxQLnVPLFAucEssUC5oaixQLlZwLFAuT1IsUC5yYSxQLnlRLFAucGcs
+UC5jMixQLnRpLFAuV0YsUC5uMSxQLmNTLFAuVkMsUC5KVCxQLlJaLFAuTUUsUC55NSxQLnEzLFAueUks
+UC5jNixQLnFkLFcuQ3YsVy5LUyxXLkEzLFcudk4sVy5VdixXLkVnLFcuRW8sVy5XayxXLklBLFcuZm0s
+UC5qZyxQLlRhLFAuR0UsUC5ONyxQLnVRLFAuUEMsUC5tdCxQLk56LFAuUVMsUC5ucCxVLk1ELFUuYU4s
+VS5iMCxMLmUsTC5WVyxMLm9aLEwuanIsTC5xbCxMLkhpLEwuQlQsTC5QWSxMLkwsTC5XeCxMLkFPLEwu
+ZE4sTC5IbyxMLnh6LEwuSUMsTC5mQyxMLm5ULEwuTlksTC5lWCxMLkVFLEwuUUwsTC5WUyxMLlRELEwu
+QVMsTS5NaSxNLnE3LE0uTm9dKQpyKEgubGMsW0guengsSC5yVF0pCnMoSC5rWSxQLkM2KQpzKFAuaWws
+UC5ZaykKcihQLmlsLFtILk41LFAudXcsVy5jZixXLlN5XSkKcihQLm1XLFtILktXLFAucTRdKQpzKEgu
+TFosSC5FVCkKcihILkxaLFtILlJHLEguV0JdKQpzKEguVlAsSC5SRykKcyhILkRnLEguVlApCnMoSC5a
+RyxILldCKQpzKEguUGcsSC5aRykKcihILlBnLFtILnhqLEguZEUsSC5aQSxILmRULEguUHEsSC5lRSxI
+LlY2XSkKcyhILmlNLEgudTkpCnMoUC5aZixQLlBmKQpzKFAuSmksUC5tMCkKcyhQLmI2LFAuWHYpCnMo
+UC5WaixQLldZKQpyKFAuVWssW1AuQ1YsUC5aaSxQLmJ5XSkKcyhQLndJLFAua1QpCnIoUC53SSxbUC5V
+OCxQLm9qLFAuTXgsUC5FMyxQLkdZXSkKcyhQLks4LFAuVWQpCnMoUC50dSxQLlNoKQpzKFAudTUsUC5a
+aSkKcihQLmxmLFtQLkNQLFAuSWZdKQpyKFAudSxbUC5iSixQLmVZXSkKcyhQLnFlLFAuRG4pCnIoVy5E
+MCxbVy51SCxXLndhLFcuSzUsVy5DbV0pCnIoVy51SCxbVy5jdixXLm54LFcuUUYsVy5DUV0pCnIoVy5j
+dixbVy5xRSxQLmQ1XSkKcihXLnFFLFtXLkdoLFcuZlksVy5uQixXLlFQLFcuaDQsVy5TTixXLmxwLFcu
+VGIsVy5JdixXLldQLFcueVldKQpzKFcub0osVy5MZSkKcyhXLmhILFcuQXopCnMoVy5WYixXLlFGKQpz
+KFcuZkosVy53YSkKcihXLmVhLFtXLnc2LFcuZXddKQpzKFcuQWosVy53NikKcyhXLnJCLFcuSzcpCnMo
+Vy5CSCxXLnJCKQpzKFcudzQsVy5JQikKcyhXLm9hLFcuWFcpCnMoVy5yaCxXLm9hKQpzKFcuaTcsVy5j
+ZikKcyhQLkFzLFAuVmopCnIoUC5BcyxbVy5JNCxQLktlXSkKcyhXLlJPLFAucWgpCnMoVy5ldSxXLlJP
+KQpzKFcueEMsUC5NTykKcyhXLmN0LFcubTYpCnMoUC5CZixQLmlKKQpyKFAuRTQsW1AucjcsUC5jb10p
+CnMoUC5UeixQLmNvKQpzKFAuYkIsUC5kNSkKcyhCLmZ2LE8uekwpCnIoQi5mdixbRS5PRixGLnJ1LEwu
+SVZdKQp0KEgudzIsSC5SZSkKdChILlJHLFAubEQpCnQoSC5WUCxILlNVKQp0KEguV0IsUC5sRCkKdChI
+LlpHLEguU1UpCnQoUC5uWSxQLmxEKQp0KFAuV1ksUC5NYSkKdChQLlJVLFAuS1ApCnQoVy5MZSxXLmlk
+KQp0KFcuSzcsUC5sRCkKdChXLnJCLFcuR20pCnQoVy5YVyxQLmxEKQp0KFcub2EsVy5HbSkKdChQLmNv
+LFAubEQpfSkoKQp2YXIgdj17dHlwZVVuaXZlcnNlOntlQzpuZXcgTWFwKCksdFI6e30sZVQ6e30sdFBW
+Ont9LHNFQTpbXX0sbWFuZ2xlZEdsb2JhbE5hbWVzOntJZjoiaW50IixDUDoiZG91YmxlIixsZjoibnVt
+IixxVToiU3RyaW5nIixhMjoiYm9vbCIsYzg6Ik51bGwiLHpNOiJMaXN0In0sbWFuZ2xlZE5hbWVzOnt9
+LGdldFR5cGVGcm9tTmFtZTpnZXRHbG9iYWxGcm9tTmFtZSxtZXRhZGF0YTpbXSx0eXBlczpbImM4KCki
+LCJ+KCkiLCJjOChBaiopIiwiYzgoY3YqKSIsIkAoQCkiLCJxVShxVSkiLCJjOChALEApIiwifih+KCkp
+IiwiYTIoY3YscVUscVUsSlEpIiwiYzgoQCkiLCJjOChxVSxxVSkiLCJjOChNaD8sTWg/KSIsIkAoKSIs
+ImM4KHFVLEApIiwiYTIoa0YpIiwiYTIocVUpIiwifih4dTxxVT4pIiwiYzgoZWEqKSIsImI4PGM4Pioo
+QWoqKSIsIn4oQWoqKSIsImEyKihxVSopIiwiWjA8cVUscVU+KFowPHFVLHFVPixxVSkiLCJ+KHFVLHFV
+PykiLCJuNihJZikiLCJuNihALEApIiwiYTIodUgpIiwiYzgoQCxHeikiLCJAKGVhKSIsImM4KElmLEAp
+IiwifihNaFtHej9dKSIsIn4odUgsdUg/KSIsImM4KE1oLEd6KSIsImEyKHh1PHFVPikiLCJ2czxAPihA
+KSIsInI3KEApIiwiTWg/KEApIiwiRTQoQCkiLCJhMiooSDcqKSIsIkxMKihAKSIsIlowPHFVKixNaCo+
+KihMTCopIiwiQChxVSkiLCJAKEAscVUpIiwiYzgoWjA8cVUqLE1oKj4qKSIsImM4KEdELEApIiwicVUq
+KEFqKikiLCJ+KEApIiwiYzgoZXcqKSIsIn4ocVUsSWYpIiwifihxVVtAXSkiLCJxVSoocVUqKSIsIklm
+KElmLElmKSIsImM4KH4oKSkiLCJNaD8oTWg/KSIsIlR6PEA+KEApIl0saW50ZXJjZXB0b3JzQnlUYWc6
+bnVsbCxsZWFmVGFnczpudWxsLGFycmF5UnRpOnR5cGVvZiBTeW1ib2w9PSJmdW5jdGlvbiImJnR5cGVv
+ZiBTeW1ib2woKT09InN5bWJvbCI/U3ltYm9sKCIkdGkiKToiJHRpIn0KSC54Yih2LnR5cGVVbml2ZXJz
+ZSxKU09OLnBhcnNlKCd7ImM1IjoiTUYiLCJpQyI6Ik1GIiwia2QiOiJNRiIsInJ4IjoiZWEiLCJlNSI6
+ImVhIiwiWTAiOiJkNSIsInRwIjoiZDUiLCJHOCI6ImV3IiwiTXIiOiJxRSIsImVMIjoicUUiLCJJMCI6
+InVIIiwiaHMiOiJ1SCIsIlhnIjoiUUYiLCJuciI6IkFqIiwieTQiOiJ3NiIsImFQIjoiQ20iLCJ4YyI6
+Im54Iiwia0oiOiJueCIsInpVIjoiRGciLCJkZiI6IkVUIiwieUUiOnsiYTIiOltdfSwid2UiOnsiYzgi
+OltdfSwiTUYiOnsidm0iOltdLCJFSCI6W119LCJqZCI6eyJ6TSI6WyIxIl0sImJRIjpbIjEiXSwiY1gi
+OlsiMSJdfSwiUG8iOnsiamQiOlsiMSJdLCJ6TSI6WyIxIl0sImJRIjpbIjEiXSwiY1giOlsiMSJdfSwi
+bTEiOnsiQW4iOlsiMSJdfSwicUkiOnsiQ1AiOltdLCJsZiI6W119LCJiVSI6eyJDUCI6W10sIklmIjpb
+XSwibGYiOltdfSwiVkEiOnsiQ1AiOltdLCJsZiI6W119LCJEciI6eyJxVSI6W10sInZYIjpbXX0sIm5k
+Ijp7IlhTIjpbXX0sInFqIjp7ImxEIjpbIklmIl0sIlJlIjpbIklmIl0sInpNIjpbIklmIl0sImJRIjpb
+IklmIl0sImNYIjpbIklmIl0sImxELkUiOiJJZiIsIlJlLkUiOiJJZiJ9LCJiUSI6eyJjWCI6WyIxIl19
+LCJhTCI6eyJiUSI6WyIxIl0sImNYIjpbIjEiXX0sIm5IIjp7ImFMIjpbIjEiXSwiYlEiOlsiMSJdLCJj
+WCI6WyIxIl0sImFMLkUiOiIxIiwiY1guRSI6IjEifSwiYTciOnsiQW4iOlsiMSJdfSwiaTEiOnsiY1gi
+OlsiMiJdLCJjWC5FIjoiMiJ9LCJ4eSI6eyJpMSI6WyIxIiwiMiJdLCJiUSI6WyIyIl0sImNYIjpbIjIi
+XSwiY1guRSI6IjIifSwiTUgiOnsiQW4iOlsiMiJdfSwibEoiOnsiYUwiOlsiMiJdLCJiUSI6WyIyIl0s
+ImNYIjpbIjIiXSwiYUwuRSI6IjIiLCJjWC5FIjoiMiJ9LCJVNSI6eyJjWCI6WyIxIl0sImNYLkUiOiIx
+In0sIlNPIjp7IkFuIjpbIjEiXX0sIncyIjp7ImxEIjpbIjEiXSwiUmUiOlsiMSJdLCJ6TSI6WyIxIl0s
+ImJRIjpbIjEiXSwiY1giOlsiMSJdfSwid3YiOnsiR0QiOltdfSwiUEQiOnsiR2oiOlsiMSIsIjIiXSwi
+UlUiOlsiMSIsIjIiXSwiUG4iOlsiMSIsIjIiXSwiS1AiOlsiMSIsIjIiXSwiWjAiOlsiMSIsIjIiXX0s
+IldVIjp7IlowIjpbIjEiLCIyIl19LCJMUCI6eyJXVSI6WyIxIiwiMiJdLCJaMCI6WyIxIiwiMiJdfSwi
+WFIiOnsiY1giOlsiMSJdLCJjWC5FIjoiMSJ9LCJMSSI6eyJ2USI6W119LCJXMCI6eyJYUyI6W119LCJh
+eiI6eyJYUyI6W119LCJ2ViI6eyJYUyI6W119LCJ0ZSI6eyJSeiI6W119LCJYTyI6eyJHeiI6W119LCJ2
+Ijp7IkVIIjpbXX0sImxjIjp7IkVIIjpbXX0sInp4Ijp7IkVIIjpbXX0sInJUIjp7IkVIIjpbXX0sIkVx
+Ijp7IlhTIjpbXX0sImtZIjp7IlhTIjpbXX0sIk41Ijp7IllrIjpbIjEiLCIyIl0sIkZvIjpbIjEiLCIy
+Il0sIlowIjpbIjEiLCIyIl0sIllrLksiOiIxIiwiWWsuViI6IjIifSwiaTUiOnsiYlEiOlsiMSJdLCJj
+WCI6WyIxIl0sImNYLkUiOiIxIn0sIk42Ijp7IkFuIjpbIjEiXX0sIlZSIjp7IndMIjpbXSwidlgiOltd
+fSwiRUsiOnsiaWIiOltdLCJPZCI6W119LCJLVyI6eyJjWCI6WyJpYiJdLCJjWC5FIjoiaWIifSwiUGIi
+OnsiQW4iOlsiaWIiXX0sInRRIjp7Ik9kIjpbXX0sInVuIjp7ImNYIjpbIk9kIl0sImNYLkUiOiJPZCJ9
+LCJTZCI6eyJBbiI6WyJPZCJdfSwiRVQiOnsiZXEiOltdfSwiTFoiOnsiWGoiOlsiMSJdLCJFVCI6W10s
+ImVxIjpbXX0sIkRnIjp7ImxEIjpbIkNQIl0sIlhqIjpbIkNQIl0sInpNIjpbIkNQIl0sIkVUIjpbXSwi
+YlEiOlsiQ1AiXSwiZXEiOltdLCJjWCI6WyJDUCJdLCJTVSI6WyJDUCJdLCJsRC5FIjoiQ1AifSwiUGci
+OnsibEQiOlsiSWYiXSwiWGoiOlsiSWYiXSwiek0iOlsiSWYiXSwiRVQiOltdLCJiUSI6WyJJZiJdLCJl
+cSI6W10sImNYIjpbIklmIl0sIlNVIjpbIklmIl19LCJ4aiI6eyJsRCI6WyJJZiJdLCJYaiI6WyJJZiJd
+LCJ6TSI6WyJJZiJdLCJFVCI6W10sImJRIjpbIklmIl0sImVxIjpbXSwiY1giOlsiSWYiXSwiU1UiOlsi
+SWYiXSwibEQuRSI6IklmIn0sImRFIjp7ImxEIjpbIklmIl0sIlhqIjpbIklmIl0sInpNIjpbIklmIl0s
+IkVUIjpbXSwiYlEiOlsiSWYiXSwiZXEiOltdLCJjWCI6WyJJZiJdLCJTVSI6WyJJZiJdLCJsRC5FIjoi
+SWYifSwiWkEiOnsibEQiOlsiSWYiXSwiWGoiOlsiSWYiXSwiek0iOlsiSWYiXSwiRVQiOltdLCJiUSI6
+WyJJZiJdLCJlcSI6W10sImNYIjpbIklmIl0sIlNVIjpbIklmIl0sImxELkUiOiJJZiJ9LCJkVCI6eyJs
+RCI6WyJJZiJdLCJYaiI6WyJJZiJdLCJ6TSI6WyJJZiJdLCJFVCI6W10sImJRIjpbIklmIl0sImVxIjpb
+XSwiY1giOlsiSWYiXSwiU1UiOlsiSWYiXSwibEQuRSI6IklmIn0sIlBxIjp7ImxEIjpbIklmIl0sIlhq
+IjpbIklmIl0sInpNIjpbIklmIl0sIkVUIjpbXSwiYlEiOlsiSWYiXSwiZXEiOltdLCJjWCI6WyJJZiJd
+LCJTVSI6WyJJZiJdLCJsRC5FIjoiSWYifSwiZUUiOnsibEQiOlsiSWYiXSwiWGoiOlsiSWYiXSwiek0i
+OlsiSWYiXSwiRVQiOltdLCJiUSI6WyJJZiJdLCJlcSI6W10sImNYIjpbIklmIl0sIlNVIjpbIklmIl0s
+ImxELkUiOiJJZiJ9LCJWNiI6eyJsRCI6WyJJZiJdLCJuNiI6W10sIlhqIjpbIklmIl0sInpNIjpbIklm
+Il0sIkVUIjpbXSwiYlEiOlsiSWYiXSwiZXEiOltdLCJjWCI6WyJJZiJdLCJTVSI6WyJJZiJdLCJsRC5F
+IjoiSWYifSwidTkiOnsiWFMiOltdfSwiaU0iOnsiWFMiOltdfSwiR1YiOnsiQW4iOlsiMSJdfSwicTQi
+OnsiY1giOlsiMSJdLCJjWC5FIjoiMSJ9LCJaZiI6eyJQZiI6WyIxIl19LCJ2cyI6eyJiOCI6WyIxIl19
+LCJDdyI6eyJYUyI6W119LCJtMCI6eyJKQiI6W119LCJKaSI6eyJtMCI6W10sIkpCIjpbXX0sImI2Ijp7
+Ilh2IjpbIjEiXSwieHUiOlsiMSJdLCJiUSI6WyIxIl0sImNYIjpbIjEiXX0sImxtIjp7IkFuIjpbIjEi
+XX0sIm1XIjp7ImNYIjpbIjEiXX0sInV5Ijp7ImxEIjpbIjEiXSwiek0iOlsiMSJdLCJiUSI6WyIxIl0s
+ImNYIjpbIjEiXX0sImlsIjp7IllrIjpbIjEiLCIyIl0sIlowIjpbIjEiLCIyIl19LCJZayI6eyJaMCI6
+WyIxIiwiMiJdfSwiUG4iOnsiWjAiOlsiMSIsIjIiXX0sIkdqIjp7IlJVIjpbIjEiLCIyIl0sIlBuIjpb
+IjEiLCIyIl0sIktQIjpbIjEiLCIyIl0sIlowIjpbIjEiLCIyIl19LCJWaiI6eyJNYSI6WyIxIl0sInh1
+IjpbIjEiXSwiYlEiOlsiMSJdLCJjWCI6WyIxIl19LCJYdiI6eyJ4dSI6WyIxIl0sImJRIjpbIjEiXSwi
+Y1giOlsiMSJdfSwidXciOnsiWWsiOlsicVUiLCJAIl0sIlowIjpbInFVIiwiQCJdLCJZay5LIjoicVUi
+LCJZay5WIjoiQCJ9LCJpOCI6eyJhTCI6WyJxVSJdLCJiUSI6WyJxVSJdLCJjWCI6WyJxVSJdLCJhTC5F
+IjoicVUiLCJjWC5FIjoicVUifSwiQ1YiOnsiVWsiOlsiek08SWY+IiwicVUiXSwiVWsuUyI6InpNPElm
+PiJ9LCJVOCI6eyJ3SSI6WyJ6TTxJZj4iLCJxVSJdfSwiWmkiOnsiVWsiOlsicVUiLCJ6TTxJZj4iXX0s
+IlVkIjp7IlhTIjpbXX0sIks4Ijp7IlhTIjpbXX0sImJ5Ijp7IlVrIjpbIk1oPyIsInFVIl0sIlVrLlMi
+OiJNaD8ifSwib2oiOnsid0kiOlsiTWg/IiwicVUiXX0sIk14Ijp7IndJIjpbInFVIiwiTWg/Il19LCJ1
+NSI6eyJVayI6WyJxVSIsInpNPElmPiJdLCJVay5TIjoicVUifSwiRTMiOnsid0kiOlsicVUiLCJ6TTxJ
+Zj4iXX0sIkdZIjp7IndJIjpbInpNPElmPiIsInFVIl19LCJDUCI6eyJsZiI6W119LCJDNiI6eyJYUyI6
+W119LCJuIjp7IlhTIjpbXX0sInUiOnsiWFMiOltdfSwiYkoiOnsiWFMiOltdfSwiZVkiOnsiWFMiOltd
+fSwibXAiOnsiWFMiOltdfSwidWIiOnsiWFMiOltdfSwiZHMiOnsiWFMiOltdfSwibGoiOnsiWFMiOltd
+fSwiVVYiOnsiWFMiOltdfSwiazUiOnsiWFMiOltdfSwiS1kiOnsiWFMiOltdfSwiYyI6eyJYUyI6W119
+LCJDRCI6eyJSeiI6W119LCJhRSI6eyJSeiI6W119LCJJZiI6eyJsZiI6W119LCJ6TSI6eyJiUSI6WyIx
+Il0sImNYIjpbIjEiXX0sImliIjp7Ik9kIjpbXX0sInh1Ijp7ImJRIjpbIjEiXSwiY1giOlsiMSJdfSwi
+WmQiOnsiR3oiOltdfSwicVUiOnsidlgiOltdfSwiUm4iOnsiQkwiOltdfSwiRG4iOnsiaUQiOltdfSwi
+VWYiOnsiaUQiOltdfSwicWUiOnsiaUQiOltdfSwicUUiOnsiY3YiOltdLCJ1SCI6W10sIkQwIjpbXX0s
+IkdoIjp7ImN2IjpbXSwidUgiOltdLCJEMCI6W119LCJmWSI6eyJjdiI6W10sInVIIjpbXSwiRDAiOltd
+fSwibkIiOnsiY3YiOltdLCJ1SCI6W10sIkQwIjpbXX0sIlFQIjp7ImN2IjpbXSwidUgiOltdLCJEMCI6
+W119LCJueCI6eyJ1SCI6W10sIkQwIjpbXX0sIlFGIjp7InVIIjpbXSwiRDAiOltdfSwiSUIiOnsidG4i
+OlsibGYiXX0sInd6Ijp7ImxEIjpbIjEiXSwiek0iOlsiMSJdLCJiUSI6WyIxIl0sImNYIjpbIjEiXSwi
+bEQuRSI6IjEifSwiY3YiOnsidUgiOltdLCJEMCI6W119LCJoSCI6eyJBeiI6W119LCJoNCI6eyJjdiI6
+W10sInVIIjpbXSwiRDAiOltdfSwiVmIiOnsidUgiOltdLCJEMCI6W119LCJmSiI6eyJEMCI6W119LCJ3
+YSI6eyJEMCI6W119LCJBaiI6eyJlYSI6W119LCJlNyI6eyJsRCI6WyJ1SCJdLCJ6TSI6WyJ1SCJdLCJi
+USI6WyJ1SCJdLCJjWCI6WyJ1SCJdLCJsRC5FIjoidUgifSwidUgiOnsiRDAiOltdfSwiQkgiOnsibEQi
+OlsidUgiXSwiR20iOlsidUgiXSwiek0iOlsidUgiXSwiWGoiOlsidUgiXSwiYlEiOlsidUgiXSwiY1gi
+OlsidUgiXSwibEQuRSI6InVIIiwiR20uRSI6InVIIn0sIlNOIjp7ImN2IjpbXSwidUgiOltdLCJEMCI6
+W119LCJldyI6eyJlYSI6W119LCJscCI6eyJjdiI6W10sInVIIjpbXSwiRDAiOltdfSwiVGIiOnsiY3Yi
+OltdLCJ1SCI6W10sIkQwIjpbXX0sIkl2Ijp7ImN2IjpbXSwidUgiOltdLCJEMCI6W119LCJXUCI6eyJj
+diI6W10sInVIIjpbXSwiRDAiOltdfSwieVkiOnsiY3YiOltdLCJ1SCI6W10sIkQwIjpbXX0sInc2Ijp7
+ImVhIjpbXX0sIks1Ijp7InY2IjpbXSwiRDAiOltdfSwiQ20iOnsiRDAiOltdfSwiQ1EiOnsidUgiOltd
+LCJEMCI6W119LCJ3NCI6eyJ0biI6WyJsZiJdfSwicmgiOnsibEQiOlsidUgiXSwiR20iOlsidUgiXSwi
+ek0iOlsidUgiXSwiWGoiOlsidUgiXSwiYlEiOlsidUgiXSwiY1giOlsidUgiXSwibEQuRSI6InVIIiwi
+R20uRSI6InVIIn0sImNmIjp7IllrIjpbInFVIiwicVUiXSwiWjAiOlsicVUiLCJxVSJdfSwiaTciOnsi
+WWsiOlsicVUiLCJxVSJdLCJaMCI6WyJxVSIsInFVIl0sIllrLksiOiJxVSIsIllrLlYiOiJxVSJ9LCJT
+eSI6eyJZayI6WyJxVSIsInFVIl0sIlowIjpbInFVIiwicVUiXSwiWWsuSyI6InFVIiwiWWsuViI6InFV
+In0sIkk0Ijp7Ik1hIjpbInFVIl0sInh1IjpbInFVIl0sImJRIjpbInFVIl0sImNYIjpbInFVIl19LCJS
+TyI6eyJxaCI6WyIxIl19LCJldSI6eyJSTyI6WyIxIl0sInFoIjpbIjEiXX0sInhDIjp7Ik1PIjpbIjEi
+XX0sIkpRIjp7ImtGIjpbXX0sInZEIjp7ImtGIjpbXX0sIm02Ijp7ImtGIjpbXX0sImN0Ijp7ImtGIjpb
+XX0sIk93Ijp7ImtGIjpbXX0sIlc5Ijp7IkFuIjpbIjEiXX0sImRXIjp7InY2IjpbXSwiRDAiOltdfSwi
+bWsiOnsieTAiOltdfSwiS28iOnsib24iOltdfSwiQXMiOnsiTWEiOlsicVUiXSwieHUiOlsicVUiXSwi
+YlEiOlsicVUiXSwiY1giOlsicVUiXX0sInI3Ijp7IkU0IjpbXX0sIlR6Ijp7ImxEIjpbIjEiXSwiek0i
+OlsiMSJdLCJiUSI6WyIxIl0sIkU0IjpbXSwiY1giOlsiMSJdLCJsRC5FIjoiMSJ9LCJiQiI6eyJkNSI6
+W10sImN2IjpbXSwidUgiOltdLCJEMCI6W119LCJLZSI6eyJNYSI6WyJxVSJdLCJ4dSI6WyJxVSJdLCJi
+USI6WyJxVSJdLCJjWCI6WyJxVSJdfSwiZDUiOnsiY3YiOltdLCJ1SCI6W10sIkQwIjpbXX0sIm42Ijp7
+InpNIjpbIklmIl0sImJRIjpbIklmIl0sImNYIjpbIklmIl0sImVxIjpbXX0sIlhBIjp7ImtGIjpbXX0s
+ImR2Ijp7IlJ6IjpbXX0sIk9GIjp7ImZ2IjpbXX0sInJ1Ijp7ImZ2IjpbXX0sIklWIjp7ImZ2IjpbXX19
+JykpCkguRkYodi50eXBlVW5pdmVyc2UsSlNPTi5wYXJzZSgneyJiUSI6MSwidzIiOjEsIkxaIjoxLCJr
+VCI6MiwibVciOjEsInV5IjoxLCJpbCI6MiwiVmoiOjEsIm5ZIjoxLCJXWSI6MSwiY28iOjF9JykpCnZh
+ciB1PShmdW5jdGlvbiBydGlpKCl7dmFyIHQ9SC5OMApyZXR1cm57bjp0KCJDdyIpLGNSOnQoIm5CIiks
+dzp0KCJBeiIpLGs6dCgiUVAiKSxnRjp0KCJQRDxHRCxAPiIpLGd3OnQoImJRPEA+IiksaDp0KCJjdiIp
+LG06dCgiWFMiKSxCOnQoImVhIiksYVM6dCgiRDAiKSxjODp0KCJoSCIpLFk6dCgiRUgiKSxkOnQoImI4
+PEA+IiksSTp0KCJTZyIpLG86dCgidlEiKSxlaDp0KCJjWDx1SD4iKSxROnQoImNYPHFVPiIpLHA6dCgi
+Y1g8QD4iKSxyOnQoImpkPGtGPiIpLHM6dCgiamQ8cVU+IiksdTp0KCJqZDxAPiIpLGE6dCgiamQ8SWY+
+IiksZDc6dCgiamQ8U2UqPiIpLGg0OnQoImpkPGo4Kj4iKSxHOnQoImpkPFowPHFVKixNaCo+Kj4iKSxj
+UTp0KCJqZDxaWio+IiksaTp0KCJqZDxxVSo+IiksYUE6dCgiamQ8eUQqPiIpLGFKOnQoImpkPHdiKj4i
+KSxWOnQoImpkPElmKj4iKSxUOnQoIndlIiksZUg6dCgidm0iKSx2OnQoImM1IiksYVU6dCgiWGo8QD4i
+KSxhbTp0KCJUejxAPiIpLGVvOnQoIk41PEdELEA+IiksZHo6dCgiaEYiKSxiOnQoInpNPEA+IiksTDp0
+KCJ6TTxJZj4iKSxKOnQoIlowPHFVLHFVPiIpLGY6dCgiWjA8QCxAPiIpLGRvOnQoImxKPHFVLEA+Iiks
+Zmo6dCgibEo8cVUqLHFVPiIpLGRFOnQoIkVUIiksYm06dCgiVjYiKSxBOnQoInVIIiksRDp0KCJrRiIp
+LFA6dCgiYzgiKSxLOnQoIk1oIikscTp0KCJ0bjxsZj4iKSxmdjp0KCJ3TCIpLGV3OnQoImJCIiksQzp0
+KCJ4dTxxVT4iKSxsOnQoIkd6IiksTjp0KCJxVSIpLGQwOnQoInFVKHFVKikiKSxnNzp0KCJkNSIpLGZv
+OnQoIkdEIiksYVc6dCgieVkiKSx4OnQoImVxIiksZ2M6dCgibjYiKSxhazp0KCJrZCIpLEU6dCgiR2o8
+cVUscVU+IiksZEQ6dCgiaUQiKSxmaTp0KCJVNTxxVSo+IiksZzQ6dCgiSzUiKSxjaTp0KCJ2NiIpLGcy
+OnQoIkNtIiksYkM6dCgiWmY8ZkoqPiIpLGg5OnQoIkNRIiksYWM6dCgiZTciKSxqOnQoImV1PEFqKj4i
+KSxSOnQoInd6PGN2Kj4iKSxjOnQoInZzPEA+IiksZko6dCgidnM8SWY+IiksZ1Y6dCgidnM8ZkoqPiIp
+LGNyOnQoIkpRIikseTp0KCJhMiIpLGFsOnQoImEyKE1oKSIpLGdmOnQoImEyKHFVKikiKSxnUjp0KCJD
+UCIpLHo6dCgiQCIpLGZPOnQoIkAoKSIpLGJJOnQoIkAoTWgpIiksYWc6dCgiQChNaCxHeikiKSxiVTp0
+KCJAKHh1PHFVPikiKSxkTzp0KCJAKHFVKSIpLGI4OnQoIkAoQCxAKSIpLFM6dCgiSWYiKSxkZDp0KCJH
+aCoiKSxnOnQoImN2KiIpLGFMOnQoImVhKiIpLGVqOnQoIlJ6KiIpLGFYOnQoIkxMKiIpLGZFOnQoIkg3
+KiIpLFU6dCgiY1g8QD4qIiksZVM6dCgiY1g8cVUqPioiKSxkSDp0KCJFNCoiKSxmSzp0KCJ6TTxAPioi
+KSxkXzp0KCJ6TTxqOCo+KiIpLGRwOnQoInpNPFowPHFVKixNaCo+Kj4qIiksZUU6dCgiek08TWgqPioi
+KSxlRzp0KCJ6TTxxVSo+KiIpLGJaOnQoInU4KiIpLHQ6dCgiWjA8cVUqLE1oKj4qIiksTzp0KCJBaioi
+KSxhdzp0KCIwJioiKSxfOnQoIk1oKiIpLGVROnQoImV3KiIpLFg6dCgicVUqIiksY2g6dCgiRDA/Iiks
+Ykc6dCgiYjg8Yzg+PyIpLGJrOnQoInpNPHFVPj8iKSxiTTp0KCJ6TTxAPj8iKSxjWjp0KCJaMDxxVSxx
+VT4/IiksYzk6dCgiWjA8cVUsQD4/IiksVzp0KCJNaD8iKSxGOnQoIkZlPEAsQD4/IiksZTp0KCJibj8i
+KSxiNzp0KCJhMihNaCk/IiksYnc6dCgiQChlYSk/IiksZlY6dCgiTWg/KE1oPyxNaD8pPyIpLGRBOnQo
+Ik1oPyhAKT8iKSxaOnQoIn4oKT8iKSxlYjp0KCJ+KGV3Kik/IiksZGk6dCgibGYiKSxIOnQoIn4iKSxN
+OnQoIn4oKSIpLGVBOnQoIn4ocVUscVUpIiksY0E6dCgifihxVSxAKSIpfX0pKCk7KGZ1bmN0aW9uIGNv
+bnN0YW50cygpe3ZhciB0PWh1bmtIZWxwZXJzLm1ha2VDb25zdExpc3QKQy5SWT1XLlFQLnByb3RvdHlw
+ZQpDLm1IPVcuYWUucHJvdG90eXBlCkMuQlo9Vy5WYi5wcm90b3R5cGUKQy5EdD1XLmZKLnByb3RvdHlw
+ZQpDLk9rPUoudkIucHJvdG90eXBlCkMuTm09Si5qZC5wcm90b3R5cGUKQy5qbj1KLmJVLnByb3RvdHlw
+ZQpDLmpOPUoud2UucHJvdG90eXBlCkMuQ0Q9Si5xSS5wcm90b3R5cGUKQy54Qj1KLkRyLnByb3RvdHlw
+ZQpDLkRHPUouYzUucHJvdG90eXBlCkMuRXg9Vy51OC5wcm90b3R5cGUKQy50NT1XLkJILnByb3RvdHlw
+ZQpDLkx0PVcuU04ucHJvdG90eXBlCkMuWlE9Si5pQy5wcm90b3R5cGUKQy5JZT1XLlRiLnByb3RvdHlw
+ZQpDLnZCPUoua2QucHJvdG90eXBlCkMub2w9Vy5LNS5wcm90b3R5cGUKQy55OD1uZXcgUC5VOCgpCkMu
+aDk9bmV3IFAuQ1YoKQpDLk80PWZ1bmN0aW9uIGdldFRhZ0ZhbGxiYWNrKG8pIHsKICB2YXIgcyA9IE9i
+amVjdC5wcm90b3R5cGUudG9TdHJpbmcuY2FsbChvKTsKICByZXR1cm4gcy5zdWJzdHJpbmcoOCwgcy5s
+ZW5ndGggLSAxKTsKfQpDLllxPWZ1bmN0aW9uKCkgewogIHZhciB0b1N0cmluZ0Z1bmN0aW9uID0gT2Jq
+ZWN0LnByb3RvdHlwZS50b1N0cmluZzsKICBmdW5jdGlvbiBnZXRUYWcobykgewogICAgdmFyIHMgPSB0
+b1N0cmluZ0Z1bmN0aW9uLmNhbGwobyk7CiAgICByZXR1cm4gcy5zdWJzdHJpbmcoOCwgcy5sZW5ndGgg
+LSAxKTsKICB9CiAgZnVuY3Rpb24gZ2V0VW5rbm93blRhZyhvYmplY3QsIHRhZykgewogICAgaWYgKC9e
+SFRNTFtBLVpdLipFbGVtZW50JC8udGVzdCh0YWcpKSB7CiAgICAgIHZhciBuYW1lID0gdG9TdHJpbmdG
+dW5jdGlvbi5jYWxsKG9iamVjdCk7CiAgICAgIGlmIChuYW1lID09ICJbb2JqZWN0IE9iamVjdF0iKSBy
+ZXR1cm4gbnVsbDsKICAgICAgcmV0dXJuICJIVE1MRWxlbWVudCI7CiAgICB9CiAgfQogIGZ1bmN0aW9u
+IGdldFVua25vd25UYWdHZW5lcmljQnJvd3NlcihvYmplY3QsIHRhZykgewogICAgaWYgKHNlbGYuSFRN
+TEVsZW1lbnQgJiYgb2JqZWN0IGluc3RhbmNlb2YgSFRNTEVsZW1lbnQpIHJldHVybiAiSFRNTEVsZW1l
+bnQiOwogICAgcmV0dXJuIGdldFVua25vd25UYWcob2JqZWN0LCB0YWcpOwogIH0KICBmdW5jdGlvbiBw
+cm90b3R5cGVGb3JUYWcodGFnKSB7CiAgICBpZiAodHlwZW9mIHdpbmRvdyA9PSAidW5kZWZpbmVkIikg
+cmV0dXJuIG51bGw7CiAgICBpZiAodHlwZW9mIHdpbmRvd1t0YWddID09ICJ1bmRlZmluZWQiKSByZXR1
+cm4gbnVsbDsKICAgIHZhciBjb25zdHJ1Y3RvciA9IHdpbmRvd1t0YWddOwogICAgaWYgKHR5cGVvZiBj
+b25zdHJ1Y3RvciAhPSAiZnVuY3Rpb24iKSByZXR1cm4gbnVsbDsKICAgIHJldHVybiBjb25zdHJ1Y3Rv
+ci5wcm90b3R5cGU7CiAgfQogIGZ1bmN0aW9uIGRpc2NyaW1pbmF0b3IodGFnKSB7IHJldHVybiBudWxs
+OyB9CiAgdmFyIGlzQnJvd3NlciA9IHR5cGVvZiBuYXZpZ2F0b3IgPT0gIm9iamVjdCI7CiAgcmV0dXJu
+IHsKICAgIGdldFRhZzogZ2V0VGFnLAogICAgZ2V0VW5rbm93blRhZzogaXNCcm93c2VyID8gZ2V0VW5r
+bm93blRhZ0dlbmVyaWNCcm93c2VyIDogZ2V0VW5rbm93blRhZywKICAgIHByb3RvdHlwZUZvclRhZzog
+cHJvdG90eXBlRm9yVGFnLAogICAgZGlzY3JpbWluYXRvcjogZGlzY3JpbWluYXRvciB9Owp9CkMud2I9
+ZnVuY3Rpb24oZ2V0VGFnRmFsbGJhY2spIHsKICByZXR1cm4gZnVuY3Rpb24oaG9va3MpIHsKICAgIGlm
+ICh0eXBlb2YgbmF2aWdhdG9yICE9ICJvYmplY3QiKSByZXR1cm4gaG9va3M7CiAgICB2YXIgdWEgPSBu
+YXZpZ2F0b3IudXNlckFnZW50OwogICAgaWYgKHVhLmluZGV4T2YoIkR1bXBSZW5kZXJUcmVlIikgPj0g
+MCkgcmV0dXJuIGhvb2tzOwogICAgaWYgKHVhLmluZGV4T2YoIkNocm9tZSIpID49IDApIHsKICAgICAg
+ZnVuY3Rpb24gY29uZmlybShwKSB7CiAgICAgICAgcmV0dXJuIHR5cGVvZiB3aW5kb3cgPT0gIm9iamVj
+dCIgJiYgd2luZG93W3BdICYmIHdpbmRvd1twXS5uYW1lID09IHA7CiAgICAgIH0KICAgICAgaWYgKGNv
+bmZpcm0oIldpbmRvdyIpICYmIGNvbmZpcm0oIkhUTUxFbGVtZW50IikpIHJldHVybiBob29rczsKICAg
+IH0KICAgIGhvb2tzLmdldFRhZyA9IGdldFRhZ0ZhbGxiYWNrOwogIH07Cn0KQy5LVT1mdW5jdGlvbiho
+b29rcykgewogIGlmICh0eXBlb2YgZGFydEV4cGVyaW1lbnRhbEZpeHVwR2V0VGFnICE9ICJmdW5jdGlv
+biIpIHJldHVybiBob29rczsKICBob29rcy5nZXRUYWcgPSBkYXJ0RXhwZXJpbWVudGFsRml4dXBHZXRU
+YWcoaG9va3MuZ2V0VGFnKTsKfQpDLmZRPWZ1bmN0aW9uKGhvb2tzKSB7CiAgdmFyIGdldFRhZyA9IGhv
+b2tzLmdldFRhZzsKICB2YXIgcHJvdG90eXBlRm9yVGFnID0gaG9va3MucHJvdG90eXBlRm9yVGFnOwog
+IGZ1bmN0aW9uIGdldFRhZ0ZpeGVkKG8pIHsKICAgIHZhciB0YWcgPSBnZXRUYWcobyk7CiAgICBpZiAo
+dGFnID09ICJEb2N1bWVudCIpIHsKICAgICAgaWYgKCEhby54bWxWZXJzaW9uKSByZXR1cm4gIiFEb2N1
+bWVudCI7CiAgICAgIHJldHVybiAiIUhUTUxEb2N1bWVudCI7CiAgICB9CiAgICByZXR1cm4gdGFnOwog
+IH0KICBmdW5jdGlvbiBwcm90b3R5cGVGb3JUYWdGaXhlZCh0YWcpIHsKICAgIGlmICh0YWcgPT0gIkRv
+Y3VtZW50IikgcmV0dXJuIG51bGw7CiAgICByZXR1cm4gcHJvdG90eXBlRm9yVGFnKHRhZyk7CiAgfQog
+IGhvb2tzLmdldFRhZyA9IGdldFRhZ0ZpeGVkOwogIGhvb2tzLnByb3RvdHlwZUZvclRhZyA9IHByb3Rv
+dHlwZUZvclRhZ0ZpeGVkOwp9CkMuZGs9ZnVuY3Rpb24oaG9va3MpIHsKICB2YXIgdXNlckFnZW50ID0g
+dHlwZW9mIG5hdmlnYXRvciA9PSAib2JqZWN0IiA/IG5hdmlnYXRvci51c2VyQWdlbnQgOiAiIjsKICBp
+ZiAodXNlckFnZW50LmluZGV4T2YoIkZpcmVmb3giKSA9PSAtMSkgcmV0dXJuIGhvb2tzOwogIHZhciBn
+ZXRUYWcgPSBob29rcy5nZXRUYWc7CiAgdmFyIHF1aWNrTWFwID0gewogICAgIkJlZm9yZVVubG9hZEV2
+ZW50IjogIkV2ZW50IiwKICAgICJEYXRhVHJhbnNmZXIiOiAiQ2xpcGJvYXJkIiwKICAgICJHZW9HZW9s
+b2NhdGlvbiI6ICJHZW9sb2NhdGlvbiIsCiAgICAiTG9jYXRpb24iOiAiIUxvY2F0aW9uIiwKICAgICJX
+b3JrZXJNZXNzYWdlRXZlbnQiOiAiTWVzc2FnZUV2ZW50IiwKICAgICJYTUxEb2N1bWVudCI6ICIhRG9j
+dW1lbnQifTsKICBmdW5jdGlvbiBnZXRUYWdGaXJlZm94KG8pIHsKICAgIHZhciB0YWcgPSBnZXRUYWco
+byk7CiAgICByZXR1cm4gcXVpY2tNYXBbdGFnXSB8fCB0YWc7CiAgfQogIGhvb2tzLmdldFRhZyA9IGdl
+dFRhZ0ZpcmVmb3g7Cn0KQy54aT1mdW5jdGlvbihob29rcykgewogIHZhciB1c2VyQWdlbnQgPSB0eXBl
+b2YgbmF2aWdhdG9yID09ICJvYmplY3QiID8gbmF2aWdhdG9yLnVzZXJBZ2VudCA6ICIiOwogIGlmICh1
+c2VyQWdlbnQuaW5kZXhPZigiVHJpZGVudC8iKSA9PSAtMSkgcmV0dXJuIGhvb2tzOwogIHZhciBnZXRU
+YWcgPSBob29rcy5nZXRUYWc7CiAgdmFyIHF1aWNrTWFwID0gewogICAgIkJlZm9yZVVubG9hZEV2ZW50
+IjogIkV2ZW50IiwKICAgICJEYXRhVHJhbnNmZXIiOiAiQ2xpcGJvYXJkIiwKICAgICJIVE1MRERFbGVt
+ZW50IjogIkhUTUxFbGVtZW50IiwKICAgICJIVE1MRFRFbGVtZW50IjogIkhUTUxFbGVtZW50IiwKICAg
+ICJIVE1MUGhyYXNlRWxlbWVudCI6ICJIVE1MRWxlbWVudCIsCiAgICAiUG9zaXRpb24iOiAiR2VvcG9z
+aXRpb24iCiAgfTsKICBmdW5jdGlvbiBnZXRUYWdJRShvKSB7CiAgICB2YXIgdGFnID0gZ2V0VGFnKG8p
+OwogICAgdmFyIG5ld1RhZyA9IHF1aWNrTWFwW3RhZ107CiAgICBpZiAobmV3VGFnKSByZXR1cm4gbmV3
+VGFnOwogICAgaWYgKHRhZyA9PSAiT2JqZWN0IikgewogICAgICBpZiAod2luZG93LkRhdGFWaWV3ICYm
+IChvIGluc3RhbmNlb2Ygd2luZG93LkRhdGFWaWV3KSkgcmV0dXJuICJEYXRhVmlldyI7CiAgICB9CiAg
+ICByZXR1cm4gdGFnOwogIH0KICBmdW5jdGlvbiBwcm90b3R5cGVGb3JUYWdJRSh0YWcpIHsKICAgIHZh
+ciBjb25zdHJ1Y3RvciA9IHdpbmRvd1t0YWddOwogICAgaWYgKGNvbnN0cnVjdG9yID09IG51bGwpIHJl
+dHVybiBudWxsOwogICAgcmV0dXJuIGNvbnN0cnVjdG9yLnByb3RvdHlwZTsKICB9CiAgaG9va3MuZ2V0
+VGFnID0gZ2V0VGFnSUU7CiAgaG9va3MucHJvdG90eXBlRm9yVGFnID0gcHJvdG90eXBlRm9yVGFnSUU7
+Cn0KQy5pNz1mdW5jdGlvbihob29rcykgeyByZXR1cm4gaG9va3M7IH0KCkMuQ3Q9bmV3IFAuYnkoKQpD
+LkVxPW5ldyBQLms1KCkKQy54TT1uZXcgUC51NSgpCkMuUWs9bmV3IFAuRTMoKQpDLk52PW5ldyBILmty
+KCkKQy5OVT1uZXcgUC5KaSgpCkMucGQ9bmV3IFAuWmQoKQpDLkFkPW5ldyBNLkg3KDAsIkhpbnRBY3Rp
+b25LaW5kLmFkZE51bGxhYmxlSGludCIpCkMubmU9bmV3IE0uSDcoMSwiSGludEFjdGlvbktpbmQuYWRk
+Tm9uTnVsbGFibGVIaW50IikKQy5teT1uZXcgTS5INygyLCJIaW50QWN0aW9uS2luZC5jaGFuZ2VUb051
+bGxhYmxlSGludCIpCkMucng9bmV3IE0uSDcoMywiSGludEFjdGlvbktpbmQuY2hhbmdlVG9Ob25OdWxs
+YWJsZUhpbnQiKQpDLndWPW5ldyBNLkg3KDQsIkhpbnRBY3Rpb25LaW5kLnJlbW92ZU51bGxhYmxlSGlu
+dCIpCkMuZlI9bmV3IE0uSDcoNSwiSGludEFjdGlvbktpbmQucmVtb3ZlTm9uTnVsbGFibGVIaW50IikK
+Qy5BMz1uZXcgUC5NeChudWxsKQpDLm5YPW5ldyBQLm9qKG51bGwpCkMuYWs9SC5WTSh0KFswLDAsMzI3
+NzYsMzM3OTIsMSwxMDI0MCwwLDBdKSx1LlYpCkMuY209SC5WTSh0KFsiKjo6Y2xhc3MiLCIqOjpkaXIi
+LCIqOjpkcmFnZ2FibGUiLCIqOjpoaWRkZW4iLCIqOjppZCIsIio6OmluZXJ0IiwiKjo6aXRlbXByb3Ai
+LCIqOjppdGVtcmVmIiwiKjo6aXRlbXNjb3BlIiwiKjo6bGFuZyIsIio6OnNwZWxsY2hlY2siLCIqOjp0
+aXRsZSIsIio6OnRyYW5zbGF0ZSIsIkE6OmFjY2Vzc2tleSIsIkE6OmNvb3JkcyIsIkE6OmhyZWZsYW5n
+IiwiQTo6bmFtZSIsIkE6OnNoYXBlIiwiQTo6dGFiaW5kZXgiLCJBOjp0YXJnZXQiLCJBOjp0eXBlIiwi
+QVJFQTo6YWNjZXNza2V5IiwiQVJFQTo6YWx0IiwiQVJFQTo6Y29vcmRzIiwiQVJFQTo6bm9ocmVmIiwi
+QVJFQTo6c2hhcGUiLCJBUkVBOjp0YWJpbmRleCIsIkFSRUE6OnRhcmdldCIsIkFVRElPOjpjb250cm9s
+cyIsIkFVRElPOjpsb29wIiwiQVVESU86Om1lZGlhZ3JvdXAiLCJBVURJTzo6bXV0ZWQiLCJBVURJTzo6
+cHJlbG9hZCIsIkJETzo6ZGlyIiwiQk9EWTo6YWxpbmsiLCJCT0RZOjpiZ2NvbG9yIiwiQk9EWTo6bGlu
+ayIsIkJPRFk6OnRleHQiLCJCT0RZOjp2bGluayIsIkJSOjpjbGVhciIsIkJVVFRPTjo6YWNjZXNza2V5
+IiwiQlVUVE9OOjpkaXNhYmxlZCIsIkJVVFRPTjo6bmFtZSIsIkJVVFRPTjo6dGFiaW5kZXgiLCJCVVRU
+T046OnR5cGUiLCJCVVRUT046OnZhbHVlIiwiQ0FOVkFTOjpoZWlnaHQiLCJDQU5WQVM6OndpZHRoIiwi
+Q0FQVElPTjo6YWxpZ24iLCJDT0w6OmFsaWduIiwiQ09MOjpjaGFyIiwiQ09MOjpjaGFyb2ZmIiwiQ09M
+OjpzcGFuIiwiQ09MOjp2YWxpZ24iLCJDT0w6OndpZHRoIiwiQ09MR1JPVVA6OmFsaWduIiwiQ09MR1JP
+VVA6OmNoYXIiLCJDT0xHUk9VUDo6Y2hhcm9mZiIsIkNPTEdST1VQOjpzcGFuIiwiQ09MR1JPVVA6OnZh
+bGlnbiIsIkNPTEdST1VQOjp3aWR0aCIsIkNPTU1BTkQ6OmNoZWNrZWQiLCJDT01NQU5EOjpjb21tYW5k
+IiwiQ09NTUFORDo6ZGlzYWJsZWQiLCJDT01NQU5EOjpsYWJlbCIsIkNPTU1BTkQ6OnJhZGlvZ3JvdXAi
+LCJDT01NQU5EOjp0eXBlIiwiREFUQTo6dmFsdWUiLCJERUw6OmRhdGV0aW1lIiwiREVUQUlMUzo6b3Bl
+biIsIkRJUjo6Y29tcGFjdCIsIkRJVjo6YWxpZ24iLCJETDo6Y29tcGFjdCIsIkZJRUxEU0VUOjpkaXNh
+YmxlZCIsIkZPTlQ6OmNvbG9yIiwiRk9OVDo6ZmFjZSIsIkZPTlQ6OnNpemUiLCJGT1JNOjphY2NlcHQi
+LCJGT1JNOjphdXRvY29tcGxldGUiLCJGT1JNOjplbmN0eXBlIiwiRk9STTo6bWV0aG9kIiwiRk9STTo6
+bmFtZSIsIkZPUk06Om5vdmFsaWRhdGUiLCJGT1JNOjp0YXJnZXQiLCJGUkFNRTo6bmFtZSIsIkgxOjph
+bGlnbiIsIkgyOjphbGlnbiIsIkgzOjphbGlnbiIsIkg0OjphbGlnbiIsIkg1OjphbGlnbiIsIkg2Ojph
+bGlnbiIsIkhSOjphbGlnbiIsIkhSOjpub3NoYWRlIiwiSFI6OnNpemUiLCJIUjo6d2lkdGgiLCJIVE1M
+Ojp2ZXJzaW9uIiwiSUZSQU1FOjphbGlnbiIsIklGUkFNRTo6ZnJhbWVib3JkZXIiLCJJRlJBTUU6Omhl
+aWdodCIsIklGUkFNRTo6bWFyZ2luaGVpZ2h0IiwiSUZSQU1FOjptYXJnaW53aWR0aCIsIklGUkFNRTo6
+d2lkdGgiLCJJTUc6OmFsaWduIiwiSU1HOjphbHQiLCJJTUc6OmJvcmRlciIsIklNRzo6aGVpZ2h0Iiwi
+SU1HOjpoc3BhY2UiLCJJTUc6OmlzbWFwIiwiSU1HOjpuYW1lIiwiSU1HOjp1c2VtYXAiLCJJTUc6OnZz
+cGFjZSIsIklNRzo6d2lkdGgiLCJJTlBVVDo6YWNjZXB0IiwiSU5QVVQ6OmFjY2Vzc2tleSIsIklOUFVU
+OjphbGlnbiIsIklOUFVUOjphbHQiLCJJTlBVVDo6YXV0b2NvbXBsZXRlIiwiSU5QVVQ6OmF1dG9mb2N1
+cyIsIklOUFVUOjpjaGVja2VkIiwiSU5QVVQ6OmRpc2FibGVkIiwiSU5QVVQ6OmlucHV0bW9kZSIsIklO
+UFVUOjppc21hcCIsIklOUFVUOjpsaXN0IiwiSU5QVVQ6Om1heCIsIklOUFVUOjptYXhsZW5ndGgiLCJJ
+TlBVVDo6bWluIiwiSU5QVVQ6Om11bHRpcGxlIiwiSU5QVVQ6Om5hbWUiLCJJTlBVVDo6cGxhY2Vob2xk
+ZXIiLCJJTlBVVDo6cmVhZG9ubHkiLCJJTlBVVDo6cmVxdWlyZWQiLCJJTlBVVDo6c2l6ZSIsIklOUFVU
+OjpzdGVwIiwiSU5QVVQ6OnRhYmluZGV4IiwiSU5QVVQ6OnR5cGUiLCJJTlBVVDo6dXNlbWFwIiwiSU5Q
+VVQ6OnZhbHVlIiwiSU5TOjpkYXRldGltZSIsIktFWUdFTjo6ZGlzYWJsZWQiLCJLRVlHRU46OmtleXR5
+cGUiLCJLRVlHRU46Om5hbWUiLCJMQUJFTDo6YWNjZXNza2V5IiwiTEFCRUw6OmZvciIsIkxFR0VORDo6
+YWNjZXNza2V5IiwiTEVHRU5EOjphbGlnbiIsIkxJOjp0eXBlIiwiTEk6OnZhbHVlIiwiTElOSzo6c2l6
+ZXMiLCJNQVA6Om5hbWUiLCJNRU5VOjpjb21wYWN0IiwiTUVOVTo6bGFiZWwiLCJNRU5VOjp0eXBlIiwi
+TUVURVI6OmhpZ2giLCJNRVRFUjo6bG93IiwiTUVURVI6Om1heCIsIk1FVEVSOjptaW4iLCJNRVRFUjo6
+dmFsdWUiLCJPQkpFQ1Q6OnR5cGVtdXN0bWF0Y2giLCJPTDo6Y29tcGFjdCIsIk9MOjpyZXZlcnNlZCIs
+Ik9MOjpzdGFydCIsIk9MOjp0eXBlIiwiT1BUR1JPVVA6OmRpc2FibGVkIiwiT1BUR1JPVVA6OmxhYmVs
+IiwiT1BUSU9OOjpkaXNhYmxlZCIsIk9QVElPTjo6bGFiZWwiLCJPUFRJT046OnNlbGVjdGVkIiwiT1BU
+SU9OOjp2YWx1ZSIsIk9VVFBVVDo6Zm9yIiwiT1VUUFVUOjpuYW1lIiwiUDo6YWxpZ24iLCJQUkU6Ondp
+ZHRoIiwiUFJPR1JFU1M6Om1heCIsIlBST0dSRVNTOjptaW4iLCJQUk9HUkVTUzo6dmFsdWUiLCJTRUxF
+Q1Q6OmF1dG9jb21wbGV0ZSIsIlNFTEVDVDo6ZGlzYWJsZWQiLCJTRUxFQ1Q6Om11bHRpcGxlIiwiU0VM
+RUNUOjpuYW1lIiwiU0VMRUNUOjpyZXF1aXJlZCIsIlNFTEVDVDo6c2l6ZSIsIlNFTEVDVDo6dGFiaW5k
+ZXgiLCJTT1VSQ0U6OnR5cGUiLCJUQUJMRTo6YWxpZ24iLCJUQUJMRTo6Ymdjb2xvciIsIlRBQkxFOjpi
+b3JkZXIiLCJUQUJMRTo6Y2VsbHBhZGRpbmciLCJUQUJMRTo6Y2VsbHNwYWNpbmciLCJUQUJMRTo6ZnJh
+bWUiLCJUQUJMRTo6cnVsZXMiLCJUQUJMRTo6c3VtbWFyeSIsIlRBQkxFOjp3aWR0aCIsIlRCT0RZOjph
+bGlnbiIsIlRCT0RZOjpjaGFyIiwiVEJPRFk6OmNoYXJvZmYiLCJUQk9EWTo6dmFsaWduIiwiVEQ6OmFi
+YnIiLCJURDo6YWxpZ24iLCJURDo6YXhpcyIsIlREOjpiZ2NvbG9yIiwiVEQ6OmNoYXIiLCJURDo6Y2hh
+cm9mZiIsIlREOjpjb2xzcGFuIiwiVEQ6OmhlYWRlcnMiLCJURDo6aGVpZ2h0IiwiVEQ6Om5vd3JhcCIs
+IlREOjpyb3dzcGFuIiwiVEQ6OnNjb3BlIiwiVEQ6OnZhbGlnbiIsIlREOjp3aWR0aCIsIlRFWFRBUkVB
+OjphY2Nlc3NrZXkiLCJURVhUQVJFQTo6YXV0b2NvbXBsZXRlIiwiVEVYVEFSRUE6OmNvbHMiLCJURVhU
+QVJFQTo6ZGlzYWJsZWQiLCJURVhUQVJFQTo6aW5wdXRtb2RlIiwiVEVYVEFSRUE6Om5hbWUiLCJURVhU
+QVJFQTo6cGxhY2Vob2xkZXIiLCJURVhUQVJFQTo6cmVhZG9ubHkiLCJURVhUQVJFQTo6cmVxdWlyZWQi
+LCJURVhUQVJFQTo6cm93cyIsIlRFWFRBUkVBOjp0YWJpbmRleCIsIlRFWFRBUkVBOjp3cmFwIiwiVEZP
+T1Q6OmFsaWduIiwiVEZPT1Q6OmNoYXIiLCJURk9PVDo6Y2hhcm9mZiIsIlRGT09UOjp2YWxpZ24iLCJU
+SDo6YWJiciIsIlRIOjphbGlnbiIsIlRIOjpheGlzIiwiVEg6OmJnY29sb3IiLCJUSDo6Y2hhciIsIlRI
+OjpjaGFyb2ZmIiwiVEg6OmNvbHNwYW4iLCJUSDo6aGVhZGVycyIsIlRIOjpoZWlnaHQiLCJUSDo6bm93
+cmFwIiwiVEg6OnJvd3NwYW4iLCJUSDo6c2NvcGUiLCJUSDo6dmFsaWduIiwiVEg6OndpZHRoIiwiVEhF
+QUQ6OmFsaWduIiwiVEhFQUQ6OmNoYXIiLCJUSEVBRDo6Y2hhcm9mZiIsIlRIRUFEOjp2YWxpZ24iLCJU
+Ujo6YWxpZ24iLCJUUjo6Ymdjb2xvciIsIlRSOjpjaGFyIiwiVFI6OmNoYXJvZmYiLCJUUjo6dmFsaWdu
+IiwiVFJBQ0s6OmRlZmF1bHQiLCJUUkFDSzo6a2luZCIsIlRSQUNLOjpsYWJlbCIsIlRSQUNLOjpzcmNs
+YW5nIiwiVUw6OmNvbXBhY3QiLCJVTDo6dHlwZSIsIlZJREVPOjpjb250cm9scyIsIlZJREVPOjpoZWln
+aHQiLCJWSURFTzo6bG9vcCIsIlZJREVPOjptZWRpYWdyb3VwIiwiVklERU86Om11dGVkIiwiVklERU86
+OnByZWxvYWQiLCJWSURFTzo6d2lkdGgiXSksdS5pKQpDLlZDPUguVk0odChbMCwwLDY1NDkwLDQ1MDU1
+LDY1NTM1LDM0ODE1LDY1NTM0LDE4NDMxXSksdS5WKQpDLm1LPUguVk0odChbMCwwLDI2NjI0LDEwMjMs
+NjU1MzQsMjA0Nyw2NTUzNCwyMDQ3XSksdS5WKQpDLlNxPUguVk0odChbIkhFQUQiLCJBUkVBIiwiQkFT
+RSIsIkJBU0VGT05UIiwiQlIiLCJDT0wiLCJDT0xHUk9VUCIsIkVNQkVEIiwiRlJBTUUiLCJGUkFNRVNF
+VCIsIkhSIiwiSU1BR0UiLCJJTUciLCJJTlBVVCIsIklTSU5ERVgiLCJMSU5LIiwiTUVUQSIsIlBBUkFN
+IiwiU09VUkNFIiwiU1RZTEUiLCJUSVRMRSIsIldCUiJdKSx1LmkpCkMuaFU9SC5WTSh0KFtdKSx1LnUp
+CkMuZG49SC5WTSh0KFtdKSxILk4wKCJqZDxMTCo+IikpCkMueEQ9SC5WTSh0KFtdKSx1LmkpCkMudG89
+SC5WTSh0KFswLDAsMzI3MjIsMTIyODcsNjU1MzQsMzQ4MTUsNjU1MzQsMTg0MzFdKSx1LlYpCkMucms9
+SC5WTSh0KFtDLkFkLEMubmUsQy5teSxDLnJ4LEMud1YsQy5mUl0pLEguTjAoImpkPEg3Kj4iKSkKQy5G
+Mz1ILlZNKHQoWzAsMCwyNDU3NiwxMDIzLDY1NTM0LDM0ODE1LDY1NTM0LDE4NDMxXSksdS5WKQpDLmVh
+PUguVk0odChbMCwwLDMyNzU0LDExMjYzLDY1NTM0LDM0ODE1LDY1NTM0LDE4NDMxXSksdS5WKQpDLlpK
+PUguVk0odChbMCwwLDMyNzIyLDEyMjg3LDY1NTM1LDM0ODE1LDY1NTM0LDE4NDMxXSksdS5WKQpDLldk
+PUguVk0odChbMCwwLDY1NDkwLDEyMjg3LDY1NTM1LDM0ODE1LDY1NTM0LDE4NDMxXSksdS5WKQpDLlF4
+PUguVk0odChbImJpbmQiLCJpZiIsInJlZiIsInJlcGVhdCIsInN5bnRheCJdKSx1LmkpCkMuQkk9SC5W
+TSh0KFsiQTo6aHJlZiIsIkFSRUE6OmhyZWYiLCJCTE9DS1FVT1RFOjpjaXRlIiwiQk9EWTo6YmFja2dy
+b3VuZCIsIkNPTU1BTkQ6Omljb24iLCJERUw6OmNpdGUiLCJGT1JNOjphY3Rpb24iLCJJTUc6OnNyYyIs
+IklOUFVUOjpzcmMiLCJJTlM6OmNpdGUiLCJROjpjaXRlIiwiVklERU86OnBvc3RlciJdKSx1LmkpCkMu
+Q009bmV3IEguTFAoMCx7fSxDLnhELEguTjAoIkxQPHFVKix6TTxqOCo+Kj4iKSkKQy5XTz1uZXcgSC5M
+UCgwLHt9LEMueEQsSC5OMCgiTFA8cVUqLHFVKj4iKSkKQy5pSD1ILlZNKHQoW10pLEguTjAoImpkPEdE
+Kj4iKSkKQy5EeD1uZXcgSC5MUCgwLHt9LEMuaUgsSC5OMCgiTFA8R0QqLEA+IikpCkMuWTI9bmV3IEwu
+TzkoIk5hdmlnYXRpb25UcmVlTm9kZVR5cGUuZGlyZWN0b3J5IikKQy5yZj1uZXcgTC5POSgiTmF2aWdh
+dGlvblRyZWVOb2RlVHlwZS5maWxlIikKQy5UZT1uZXcgSC53digiY2FsbCIpCkMub0U9bmV3IFAuR1ko
+ITEpCkMud1E9bmV3IFAuRnkobnVsbCwyKX0pKCk7KGZ1bmN0aW9uIHN0YXRpY0ZpZWxkcygpeyQuem09
+bnVsbAokLnlqPTAKJC5tSj1udWxsCiQuUDQ9bnVsbAokLk5GPW51bGwKJC5UWD1udWxsCiQueDc9bnVs
+bAokLm53PW51bGwKJC52dj1udWxsCiQuQnY9bnVsbAokLlM2PW51bGwKJC5rOD1udWxsCiQubWc9bnVs
+bAokLlVEPSExCiQuWDM9Qy5OVQokLnhnPUguVk0oW10sSC5OMCgiamQ8TWg+IikpCiQueG89bnVsbAok
+LkJPPW51bGwKJC5sdD1udWxsCiQuRVU9bnVsbAokLm9yPVAuRmwodS5OLHUuWSkKJC5JNj1udWxsCiQu
+RmY9bnVsbH0pKCk7KGZ1bmN0aW9uIGxhenlJbml0aWFsaXplcnMoKXt2YXIgdD1odW5rSGVscGVycy5s
+YXp5CnQoJCwiZmEiLCJ3IixmdW5jdGlvbigpe3JldHVybiBILllnKCJfJGRhcnRfZGFydENsb3N1cmUi
+KX0pCnQoJCwiVTIiLCJTbiIsZnVuY3Rpb24oKXtyZXR1cm4gSC5jTShILlM3KHsKdG9TdHJpbmc6ZnVu
+Y3Rpb24oKXtyZXR1cm4iJHJlY2VpdmVyJCJ9fSkpfSkKdCgkLCJ4cSIsImxxIixmdW5jdGlvbigpe3Jl
+dHVybiBILmNNKEguUzcoeyRtZXRob2QkOm51bGwsCnRvU3RyaW5nOmZ1bmN0aW9uKCl7cmV0dXJuIiRy
+ZWNlaXZlciQifX0pKX0pCnQoJCwiUjEiLCJOOSIsZnVuY3Rpb24oKXtyZXR1cm4gSC5jTShILlM3KG51
+bGwpKX0pCnQoJCwiZk4iLCJpSSIsZnVuY3Rpb24oKXtyZXR1cm4gSC5jTShmdW5jdGlvbigpe3ZhciAk
+YXJndW1lbnRzRXhwciQ9JyRhcmd1bWVudHMkJwp0cnl7bnVsbC4kbWV0aG9kJCgkYXJndW1lbnRzRXhw
+ciQpfWNhdGNoKHMpe3JldHVybiBzLm1lc3NhZ2V9fSgpKX0pCnQoJCwicWkiLCJVTiIsZnVuY3Rpb24o
+KXtyZXR1cm4gSC5jTShILlM3KHZvaWQgMCkpfSkKdCgkLCJyWiIsIlpoIixmdW5jdGlvbigpe3JldHVy
+biBILmNNKGZ1bmN0aW9uKCl7dmFyICRhcmd1bWVudHNFeHByJD0nJGFyZ3VtZW50cyQnCnRyeXsodm9p
+ZCAwKS4kbWV0aG9kJCgkYXJndW1lbnRzRXhwciQpfWNhdGNoKHMpe3JldHVybiBzLm1lc3NhZ2V9fSgp
+KX0pCnQoJCwia3EiLCJyTiIsZnVuY3Rpb24oKXtyZXR1cm4gSC5jTShILk1qKG51bGwpKX0pCnQoJCwi
+dHQiLCJjMyIsZnVuY3Rpb24oKXtyZXR1cm4gSC5jTShmdW5jdGlvbigpe3RyeXtudWxsLiRtZXRob2Qk
+fWNhdGNoKHMpe3JldHVybiBzLm1lc3NhZ2V9fSgpKX0pCnQoJCwiZHQiLCJISyIsZnVuY3Rpb24oKXty
+ZXR1cm4gSC5jTShILk1qKHZvaWQgMCkpfSkKdCgkLCJBNyIsInIxIixmdW5jdGlvbigpe3JldHVybiBI
+LmNNKGZ1bmN0aW9uKCl7dHJ5eyh2b2lkIDApLiRtZXRob2QkfWNhdGNoKHMpe3JldHVybiBzLm1lc3Nh
+Z2V9fSgpKX0pCnQoJCwiV2MiLCJ1dCIsZnVuY3Rpb24oKXtyZXR1cm4gUC5PaigpfSkKdCgkLCJraCIs
+InJmIixmdW5jdGlvbigpe3JldHVybiBuZXcgUC5wZygpLiQwKCl9KQp0KCQsImFYIiwiSEciLGZ1bmN0
+aW9uKCl7cmV0dXJuIG5ldyBQLmMyKCkuJDAoKX0pCnQoJCwiYnQiLCJWNyIsZnVuY3Rpb24oKXtyZXR1
+cm4gbmV3IEludDhBcnJheShILlhGKEguVk0oWy0yLC0yLC0yLC0yLC0yLC0yLC0yLC0yLC0yLC0yLC0y
+LC0yLC0yLC0yLC0yLC0yLC0yLC0yLC0yLC0yLC0yLC0yLC0yLC0yLC0yLC0yLC0yLC0yLC0yLC0yLC0y
+LC0yLC0yLC0yLC0yLC0yLC0yLC0xLC0yLC0yLC0yLC0yLC0yLDYyLC0yLDYyLC0yLDYzLDUyLDUzLDU0
+LDU1LDU2LDU3LDU4LDU5LDYwLDYxLC0yLC0yLC0yLC0xLC0yLC0yLC0yLDAsMSwyLDMsNCw1LDYsNyw4
+LDksMTAsMTEsMTIsMTMsMTQsMTUsMTYsMTcsMTgsMTksMjAsMjEsMjIsMjMsMjQsMjUsLTIsLTIsLTIs
+LTIsNjMsLTIsMjYsMjcsMjgsMjksMzAsMzEsMzIsMzMsMzQsMzUsMzYsMzcsMzgsMzksNDAsNDEsNDIs
+NDMsNDQsNDUsNDYsNDcsNDgsNDksNTAsNTEsLTIsLTIsLTIsLTIsLTJdLHUuYSkpKX0pCnQoJCwiTTUi
+LCJ3USIsZnVuY3Rpb24oKXtyZXR1cm4gdHlwZW9mIHByb2Nlc3MhPSJ1bmRlZmluZWQiJiZPYmplY3Qu
+cHJvdG90eXBlLnRvU3RyaW5nLmNhbGwocHJvY2Vzcyk9PSJbb2JqZWN0IHByb2Nlc3NdIiYmcHJvY2Vz
+cy5wbGF0Zm9ybT09IndpbjMyIn0pCnQoJCwibWYiLCJ6NCIsZnVuY3Rpb24oKXtyZXR1cm4gUC5udSgi
+XltcXC1cXC4wLTlBLVpfYS16fl0qJCIpfSkKdCgkLCJPUSIsInZaIixmdW5jdGlvbigpe3JldHVybiBQ
+LktOKCl9KQp0KCQsIlNDIiwiQU4iLGZ1bmN0aW9uKCl7cmV0dXJuIFAudE0oWyJBIiwiQUJCUiIsIkFD
+Uk9OWU0iLCJBRERSRVNTIiwiQVJFQSIsIkFSVElDTEUiLCJBU0lERSIsIkFVRElPIiwiQiIsIkJESSIs
+IkJETyIsIkJJRyIsIkJMT0NLUVVPVEUiLCJCUiIsIkJVVFRPTiIsIkNBTlZBUyIsIkNBUFRJT04iLCJD
+RU5URVIiLCJDSVRFIiwiQ09ERSIsIkNPTCIsIkNPTEdST1VQIiwiQ09NTUFORCIsIkRBVEEiLCJEQVRB
+TElTVCIsIkREIiwiREVMIiwiREVUQUlMUyIsIkRGTiIsIkRJUiIsIkRJViIsIkRMIiwiRFQiLCJFTSIs
+IkZJRUxEU0VUIiwiRklHQ0FQVElPTiIsIkZJR1VSRSIsIkZPTlQiLCJGT09URVIiLCJGT1JNIiwiSDEi
+LCJIMiIsIkgzIiwiSDQiLCJINSIsIkg2IiwiSEVBREVSIiwiSEdST1VQIiwiSFIiLCJJIiwiSUZSQU1F
+IiwiSU1HIiwiSU5QVVQiLCJJTlMiLCJLQkQiLCJMQUJFTCIsIkxFR0VORCIsIkxJIiwiTUFQIiwiTUFS
+SyIsIk1FTlUiLCJNRVRFUiIsIk5BViIsIk5PQlIiLCJPTCIsIk9QVEdST1VQIiwiT1BUSU9OIiwiT1VU
+UFVUIiwiUCIsIlBSRSIsIlBST0dSRVNTIiwiUSIsIlMiLCJTQU1QIiwiU0VDVElPTiIsIlNFTEVDVCIs
+IlNNQUxMIiwiU09VUkNFIiwiU1BBTiIsIlNUUklLRSIsIlNUUk9ORyIsIlNVQiIsIlNVTU1BUlkiLCJT
+VVAiLCJUQUJMRSIsIlRCT0RZIiwiVEQiLCJURVhUQVJFQSIsIlRGT09UIiwiVEgiLCJUSEVBRCIsIlRJ
+TUUiLCJUUiIsIlRSQUNLIiwiVFQiLCJVIiwiVUwiLCJWQVIiLCJWSURFTyIsIldCUiJdLHUuTil9KQp0
+KCQsIlg0IiwiaEciLGZ1bmN0aW9uKCl7cmV0dXJuIFAubnUoIl5cXFMrJCIpfSkKdCgkLCJ3TyIsIm93
+IixmdW5jdGlvbigpe3JldHVybiBQLk5EKHNlbGYpfSkKdCgkLCJrdCIsIlI4IixmdW5jdGlvbigpe3Jl
+dHVybiBILllnKCJfJGRhcnRfZGFydE9iamVjdCIpfSkKdCgkLCJmSyIsImtJIixmdW5jdGlvbigpe3Jl
+dHVybiBmdW5jdGlvbiBEYXJ0T2JqZWN0KGEpe3RoaXMubz1hfX0pCnQoJCwicXQiLCJ6QiIsZnVuY3Rp
+b24oKXtyZXR1cm4gbmV3IFQubVEoKX0pCnQoJCwiT2wiLCJVRSIsZnVuY3Rpb24oKXtyZXR1cm4gUC5o
+SyhDLm9sLmdtVyhXLngzKCkpLmhyZWYpLmdoWSgpLnEoMCwiYXV0aFRva2VuIil9KQp0KCQsImhUIiwi
+eVAiLGZ1bmN0aW9uKCl7cmV0dXJuIFcuWnIoKS5xdWVyeVNlbGVjdG9yKCIuZWRpdC1saXN0IC5wYW5l
+bC1jb250ZW50Iil9KQp0KCQsIlc2IiwiaEwiLGZ1bmN0aW9uKCl7cmV0dXJuIFcuWnIoKS5xdWVyeVNl
+bGVjdG9yKCIuZWRpdC1wYW5lbCAucGFuZWwtY29udGVudCIpfSkKdCgkLCJUUiIsIkRXIixmdW5jdGlv
+bigpe3JldHVybiBXLlpyKCkucXVlcnlTZWxlY3RvcigiZm9vdGVyIil9KQp0KCQsIkVZIiwiZmkiLGZ1
+bmN0aW9uKCl7cmV0dXJuIFcuWnIoKS5xdWVyeVNlbGVjdG9yKCJoZWFkZXIiKX0pCnQoJCwiYXYiLCJE
+OSIsZnVuY3Rpb24oKXtyZXR1cm4gVy5acigpLnF1ZXJ5U2VsZWN0b3IoIiN1bml0LW5hbWUiKX0pCnQo
+JCwiZmUiLCJLRyIsZnVuY3Rpb24oKXtyZXR1cm4gbmV3IEwuWEEoKX0pCnQoJCwiZW8iLCJuVSIsZnVu
+Y3Rpb24oKXtyZXR1cm4gbmV3IE0ubEkoJC5IaygpKX0pCnQoJCwieXIiLCJiRCIsZnVuY3Rpb24oKXty
+ZXR1cm4gbmV3IEUuT0YoUC5udSgiLyIpLFAubnUoIlteL10kIiksUC5udSgiXi8iKSl9KQp0KCQsIk1r
+IiwiS2siLGZ1bmN0aW9uKCl7cmV0dXJuIG5ldyBMLklWKFAubnUoIlsvXFxcXF0iKSxQLm51KCJbXi9c
+XFxcXSQiKSxQLm51KCJeKFxcXFxcXFxcW15cXFxcXStcXFxcW15cXFxcL10rfFthLXpBLVpdOlsvXFxc
+XF0pIiksUC5udSgiXlsvXFxcXF0oPyFbL1xcXFxdKSIpKX0pCnQoJCwiYWsiLCJFYiIsZnVuY3Rpb24o
+KXtyZXR1cm4gbmV3IEYucnUoUC5udSgiLyIpLFAubnUoIiheW2EtekEtWl1bLSsuYS16QS1aXFxkXSo6
+Ly98W14vXSkkIiksUC5udSgiW2EtekEtWl1bLSsuYS16QS1aXFxkXSo6Ly9bXi9dKiIpLFAubnUoIl4v
+IikpfSkKdCgkLCJscyIsIkhrIixmdW5jdGlvbigpe3JldHVybiBPLlJoKCl9KX0pKCk7KGZ1bmN0aW9u
+IG5hdGl2ZVN1cHBvcnQoKXshZnVuY3Rpb24oKXt2YXIgdD1mdW5jdGlvbihhKXt2YXIgbj17fQpuW2Fd
+PTEKcmV0dXJuIE9iamVjdC5rZXlzKGh1bmtIZWxwZXJzLmNvbnZlcnRUb0Zhc3RPYmplY3QobikpWzBd
+fQp2LmdldElzb2xhdGVUYWc9ZnVuY3Rpb24oYSl7cmV0dXJuIHQoIl9fX2RhcnRfIithK3YuaXNvbGF0
+ZVRhZyl9CnZhciBzPSJfX19kYXJ0X2lzb2xhdGVfdGFnc18iCnZhciByPU9iamVjdFtzXXx8KE9iamVj
+dFtzXT1PYmplY3QuY3JlYXRlKG51bGwpKQp2YXIgcT0iX1p4WXhYIgpmb3IodmFyIHA9MDs7cCsrKXt2
+YXIgbz10KHErIl8iK3ArIl8iKQppZighKG8gaW4gcikpe3Jbb109MQp2Lmlzb2xhdGVUYWc9bwpicmVh
+a319di5kaXNwYXRjaFByb3BlcnR5TmFtZT12LmdldElzb2xhdGVUYWcoImRpc3BhdGNoX3JlY29yZCIp
+fSgpCmh1bmtIZWxwZXJzLnNldE9yVXBkYXRlSW50ZXJjZXB0b3JzQnlUYWcoe0RPTUVycm9yOkoudkIs
+TWVkaWFFcnJvcjpKLnZCLE5hdmlnYXRvcjpKLnZCLE5hdmlnYXRvckNvbmN1cnJlbnRIYXJkd2FyZTpK
+LnZCLE5hdmlnYXRvclVzZXJNZWRpYUVycm9yOkoudkIsT3ZlcmNvbnN0cmFpbmVkRXJyb3I6Si52QixQ
+b3NpdGlvbkVycm9yOkoudkIsUmFuZ2U6Si52QixTUUxFcnJvcjpKLnZCLERhdGFWaWV3OkguRVQsQXJy
+YXlCdWZmZXJWaWV3OkguRVQsRmxvYXQzMkFycmF5OkguRGcsRmxvYXQ2NEFycmF5OkguRGcsSW50MTZB
+cnJheTpILnhqLEludDMyQXJyYXk6SC5kRSxJbnQ4QXJyYXk6SC5aQSxVaW50MTZBcnJheTpILmRULFVp
+bnQzMkFycmF5OkguUHEsVWludDhDbGFtcGVkQXJyYXk6SC5lRSxDYW52YXNQaXhlbEFycmF5OkguZUUs
+VWludDhBcnJheTpILlY2LEhUTUxBdWRpb0VsZW1lbnQ6Vy5xRSxIVE1MQlJFbGVtZW50OlcucUUsSFRN
+TEJ1dHRvbkVsZW1lbnQ6Vy5xRSxIVE1MQ2FudmFzRWxlbWVudDpXLnFFLEhUTUxDb250ZW50RWxlbWVu
+dDpXLnFFLEhUTUxETGlzdEVsZW1lbnQ6Vy5xRSxIVE1MRGF0YUVsZW1lbnQ6Vy5xRSxIVE1MRGF0YUxp
+c3RFbGVtZW50OlcucUUsSFRNTERldGFpbHNFbGVtZW50OlcucUUsSFRNTERpYWxvZ0VsZW1lbnQ6Vy5x
+RSxIVE1MRGl2RWxlbWVudDpXLnFFLEhUTUxFbWJlZEVsZW1lbnQ6Vy5xRSxIVE1MRmllbGRTZXRFbGVt
+ZW50OlcucUUsSFRNTEhSRWxlbWVudDpXLnFFLEhUTUxIZWFkRWxlbWVudDpXLnFFLEhUTUxIZWFkaW5n
+RWxlbWVudDpXLnFFLEhUTUxIdG1sRWxlbWVudDpXLnFFLEhUTUxJRnJhbWVFbGVtZW50OlcucUUsSFRN
+TEltYWdlRWxlbWVudDpXLnFFLEhUTUxJbnB1dEVsZW1lbnQ6Vy5xRSxIVE1MTElFbGVtZW50OlcucUUs
+SFRNTExhYmVsRWxlbWVudDpXLnFFLEhUTUxMZWdlbmRFbGVtZW50OlcucUUsSFRNTExpbmtFbGVtZW50
+OlcucUUsSFRNTE1hcEVsZW1lbnQ6Vy5xRSxIVE1MTWVkaWFFbGVtZW50OlcucUUsSFRNTE1lbnVFbGVt
+ZW50OlcucUUsSFRNTE1ldGFFbGVtZW50OlcucUUsSFRNTE1ldGVyRWxlbWVudDpXLnFFLEhUTUxNb2RF
+bGVtZW50OlcucUUsSFRNTE9MaXN0RWxlbWVudDpXLnFFLEhUTUxPYmplY3RFbGVtZW50OlcucUUsSFRN
+TE9wdEdyb3VwRWxlbWVudDpXLnFFLEhUTUxPcHRpb25FbGVtZW50OlcucUUsSFRNTE91dHB1dEVsZW1l
+bnQ6Vy5xRSxIVE1MUGFyYW1FbGVtZW50OlcucUUsSFRNTFBpY3R1cmVFbGVtZW50OlcucUUsSFRNTFBy
+ZUVsZW1lbnQ6Vy5xRSxIVE1MUHJvZ3Jlc3NFbGVtZW50OlcucUUsSFRNTFF1b3RlRWxlbWVudDpXLnFF
+LEhUTUxTY3JpcHRFbGVtZW50OlcucUUsSFRNTFNoYWRvd0VsZW1lbnQ6Vy5xRSxIVE1MU2xvdEVsZW1l
+bnQ6Vy5xRSxIVE1MU291cmNlRWxlbWVudDpXLnFFLEhUTUxTcGFuRWxlbWVudDpXLnFFLEhUTUxTdHls
+ZUVsZW1lbnQ6Vy5xRSxIVE1MVGFibGVDYXB0aW9uRWxlbWVudDpXLnFFLEhUTUxUYWJsZUNlbGxFbGVt
+ZW50OlcucUUsSFRNTFRhYmxlRGF0YUNlbGxFbGVtZW50OlcucUUsSFRNTFRhYmxlSGVhZGVyQ2VsbEVs
+ZW1lbnQ6Vy5xRSxIVE1MVGFibGVDb2xFbGVtZW50OlcucUUsSFRNTFRleHRBcmVhRWxlbWVudDpXLnFF
+LEhUTUxUaW1lRWxlbWVudDpXLnFFLEhUTUxUaXRsZUVsZW1lbnQ6Vy5xRSxIVE1MVHJhY2tFbGVtZW50
+OlcucUUsSFRNTFVMaXN0RWxlbWVudDpXLnFFLEhUTUxVbmtub3duRWxlbWVudDpXLnFFLEhUTUxWaWRl
+b0VsZW1lbnQ6Vy5xRSxIVE1MRGlyZWN0b3J5RWxlbWVudDpXLnFFLEhUTUxGb250RWxlbWVudDpXLnFF
+LEhUTUxGcmFtZUVsZW1lbnQ6Vy5xRSxIVE1MRnJhbWVTZXRFbGVtZW50OlcucUUsSFRNTE1hcnF1ZWVF
+bGVtZW50OlcucUUsSFRNTEVsZW1lbnQ6Vy5xRSxIVE1MQW5jaG9yRWxlbWVudDpXLkdoLEhUTUxBcmVh
+RWxlbWVudDpXLmZZLEhUTUxCYXNlRWxlbWVudDpXLm5CLEJsb2I6Vy5BeixIVE1MQm9keUVsZW1lbnQ6
+Vy5RUCxDREFUQVNlY3Rpb246Vy5ueCxDaGFyYWN0ZXJEYXRhOlcubngsQ29tbWVudDpXLm54LFByb2Nl
+c3NpbmdJbnN0cnVjdGlvbjpXLm54LFRleHQ6Vy5ueCxDU1NTdHlsZURlY2xhcmF0aW9uOlcub0osTVNT
+dHlsZUNTU1Byb3BlcnRpZXM6Vy5vSixDU1MyUHJvcGVydGllczpXLm9KLFhNTERvY3VtZW50OlcuUUYs
+RG9jdW1lbnQ6Vy5RRixET01FeGNlcHRpb246Vy5OaCxET01JbXBsZW1lbnRhdGlvbjpXLmFlLERPTVJl
+Y3RSZWFkT25seTpXLklCLERPTVRva2VuTGlzdDpXLm43LEVsZW1lbnQ6Vy5jdixBYm9ydFBheW1lbnRF
+dmVudDpXLmVhLEFuaW1hdGlvbkV2ZW50OlcuZWEsQW5pbWF0aW9uUGxheWJhY2tFdmVudDpXLmVhLEFw
+cGxpY2F0aW9uQ2FjaGVFcnJvckV2ZW50OlcuZWEsQmFja2dyb3VuZEZldGNoQ2xpY2tFdmVudDpXLmVh
+LEJhY2tncm91bmRGZXRjaEV2ZW50OlcuZWEsQmFja2dyb3VuZEZldGNoRmFpbEV2ZW50OlcuZWEsQmFj
+a2dyb3VuZEZldGNoZWRFdmVudDpXLmVhLEJlZm9yZUluc3RhbGxQcm9tcHRFdmVudDpXLmVhLEJlZm9y
+ZVVubG9hZEV2ZW50OlcuZWEsQmxvYkV2ZW50OlcuZWEsQ2FuTWFrZVBheW1lbnRFdmVudDpXLmVhLENs
+aXBib2FyZEV2ZW50OlcuZWEsQ2xvc2VFdmVudDpXLmVhLEN1c3RvbUV2ZW50OlcuZWEsRGV2aWNlTW90
+aW9uRXZlbnQ6Vy5lYSxEZXZpY2VPcmllbnRhdGlvbkV2ZW50OlcuZWEsRXJyb3JFdmVudDpXLmVhLEV4
+dGVuZGFibGVFdmVudDpXLmVhLEV4dGVuZGFibGVNZXNzYWdlRXZlbnQ6Vy5lYSxGZXRjaEV2ZW50Olcu
+ZWEsRm9udEZhY2VTZXRMb2FkRXZlbnQ6Vy5lYSxGb3JlaWduRmV0Y2hFdmVudDpXLmVhLEdhbWVwYWRF
+dmVudDpXLmVhLEhhc2hDaGFuZ2VFdmVudDpXLmVhLEluc3RhbGxFdmVudDpXLmVhLE1lZGlhRW5jcnlw
+dGVkRXZlbnQ6Vy5lYSxNZWRpYUtleU1lc3NhZ2VFdmVudDpXLmVhLE1lZGlhUXVlcnlMaXN0RXZlbnQ6
+Vy5lYSxNZWRpYVN0cmVhbUV2ZW50OlcuZWEsTWVkaWFTdHJlYW1UcmFja0V2ZW50OlcuZWEsTWVzc2Fn
+ZUV2ZW50OlcuZWEsTUlESUNvbm5lY3Rpb25FdmVudDpXLmVhLE1JRElNZXNzYWdlRXZlbnQ6Vy5lYSxN
+dXRhdGlvbkV2ZW50OlcuZWEsTm90aWZpY2F0aW9uRXZlbnQ6Vy5lYSxQYWdlVHJhbnNpdGlvbkV2ZW50
+OlcuZWEsUGF5bWVudFJlcXVlc3RFdmVudDpXLmVhLFBheW1lbnRSZXF1ZXN0VXBkYXRlRXZlbnQ6Vy5l
+YSxQb3BTdGF0ZUV2ZW50OlcuZWEsUHJlc2VudGF0aW9uQ29ubmVjdGlvbkF2YWlsYWJsZUV2ZW50Olcu
+ZWEsUHJlc2VudGF0aW9uQ29ubmVjdGlvbkNsb3NlRXZlbnQ6Vy5lYSxQcm9taXNlUmVqZWN0aW9uRXZl
+bnQ6Vy5lYSxQdXNoRXZlbnQ6Vy5lYSxSVENEYXRhQ2hhbm5lbEV2ZW50OlcuZWEsUlRDRFRNRlRvbmVD
+aGFuZ2VFdmVudDpXLmVhLFJUQ1BlZXJDb25uZWN0aW9uSWNlRXZlbnQ6Vy5lYSxSVENUcmFja0V2ZW50
+OlcuZWEsU2VjdXJpdHlQb2xpY3lWaW9sYXRpb25FdmVudDpXLmVhLFNlbnNvckVycm9yRXZlbnQ6Vy5l
+YSxTcGVlY2hSZWNvZ25pdGlvbkVycm9yOlcuZWEsU3BlZWNoUmVjb2duaXRpb25FdmVudDpXLmVhLFNw
+ZWVjaFN5bnRoZXNpc0V2ZW50OlcuZWEsU3RvcmFnZUV2ZW50OlcuZWEsU3luY0V2ZW50OlcuZWEsVHJh
+Y2tFdmVudDpXLmVhLFRyYW5zaXRpb25FdmVudDpXLmVhLFdlYktpdFRyYW5zaXRpb25FdmVudDpXLmVh
+LFZSRGV2aWNlRXZlbnQ6Vy5lYSxWUkRpc3BsYXlFdmVudDpXLmVhLFZSU2Vzc2lvbkV2ZW50OlcuZWEs
+TW9qb0ludGVyZmFjZVJlcXVlc3RFdmVudDpXLmVhLFVTQkNvbm5lY3Rpb25FdmVudDpXLmVhLElEQlZl
+cnNpb25DaGFuZ2VFdmVudDpXLmVhLEF1ZGlvUHJvY2Vzc2luZ0V2ZW50OlcuZWEsT2ZmbGluZUF1ZGlv
+Q29tcGxldGlvbkV2ZW50OlcuZWEsV2ViR0xDb250ZXh0RXZlbnQ6Vy5lYSxFdmVudDpXLmVhLElucHV0
+RXZlbnQ6Vy5lYSxTdWJtaXRFdmVudDpXLmVhLEV2ZW50VGFyZ2V0OlcuRDAsRmlsZTpXLmhILEhUTUxG
+b3JtRWxlbWVudDpXLmg0LEhpc3Rvcnk6Vy5icixIVE1MRG9jdW1lbnQ6Vy5WYixYTUxIdHRwUmVxdWVz
+dDpXLmZKLFhNTEh0dHBSZXF1ZXN0RXZlbnRUYXJnZXQ6Vy53YSxJbWFnZURhdGE6Vy5TZyxMb2NhdGlv
+bjpXLnU4LE1vdXNlRXZlbnQ6Vy5BaixEcmFnRXZlbnQ6Vy5BaixQb2ludGVyRXZlbnQ6Vy5BaixXaGVl
+bEV2ZW50OlcuQWosRG9jdW1lbnRGcmFnbWVudDpXLnVILFNoYWRvd1Jvb3Q6Vy51SCxEb2N1bWVudFR5
+cGU6Vy51SCxOb2RlOlcudUgsTm9kZUxpc3Q6Vy5CSCxSYWRpb05vZGVMaXN0OlcuQkgsSFRNTFBhcmFn
+cmFwaEVsZW1lbnQ6Vy5TTixQcm9ncmVzc0V2ZW50OlcuZXcsUmVzb3VyY2VQcm9ncmVzc0V2ZW50Olcu
+ZXcsSFRNTFNlbGVjdEVsZW1lbnQ6Vy5scCxIVE1MVGFibGVFbGVtZW50OlcuVGIsSFRNTFRhYmxlUm93
+RWxlbWVudDpXLkl2LEhUTUxUYWJsZVNlY3Rpb25FbGVtZW50OlcuV1AsSFRNTFRlbXBsYXRlRWxlbWVu
+dDpXLnlZLENvbXBvc2l0aW9uRXZlbnQ6Vy53NixGb2N1c0V2ZW50OlcudzYsS2V5Ym9hcmRFdmVudDpX
+Lnc2LFRleHRFdmVudDpXLnc2LFRvdWNoRXZlbnQ6Vy53NixVSUV2ZW50OlcudzYsV2luZG93OlcuSzUs
+RE9NV2luZG93OlcuSzUsRGVkaWNhdGVkV29ya2VyR2xvYmFsU2NvcGU6Vy5DbSxTZXJ2aWNlV29ya2Vy
+R2xvYmFsU2NvcGU6Vy5DbSxTaGFyZWRXb3JrZXJHbG9iYWxTY29wZTpXLkNtLFdvcmtlckdsb2JhbFNj
+b3BlOlcuQ20sQXR0cjpXLkNRLENsaWVudFJlY3Q6Vy53NCxET01SZWN0OlcudzQsTmFtZWROb2RlTWFw
+OlcucmgsTW96TmFtZWRBdHRyTWFwOlcucmgsSURCS2V5UmFuZ2U6UC5oRixTVkdTY3JpcHRFbGVtZW50
+OlAuYkIsU1ZHQUVsZW1lbnQ6UC5kNSxTVkdBbmltYXRlRWxlbWVudDpQLmQ1LFNWR0FuaW1hdGVNb3Rp
+b25FbGVtZW50OlAuZDUsU1ZHQW5pbWF0ZVRyYW5zZm9ybUVsZW1lbnQ6UC5kNSxTVkdBbmltYXRpb25F
+bGVtZW50OlAuZDUsU1ZHQ2lyY2xlRWxlbWVudDpQLmQ1LFNWR0NsaXBQYXRoRWxlbWVudDpQLmQ1LFNW
+R0RlZnNFbGVtZW50OlAuZDUsU1ZHRGVzY0VsZW1lbnQ6UC5kNSxTVkdEaXNjYXJkRWxlbWVudDpQLmQ1
+LFNWR0VsbGlwc2VFbGVtZW50OlAuZDUsU1ZHRkVCbGVuZEVsZW1lbnQ6UC5kNSxTVkdGRUNvbG9yTWF0
+cml4RWxlbWVudDpQLmQ1LFNWR0ZFQ29tcG9uZW50VHJhbnNmZXJFbGVtZW50OlAuZDUsU1ZHRkVDb21w
+b3NpdGVFbGVtZW50OlAuZDUsU1ZHRkVDb252b2x2ZU1hdHJpeEVsZW1lbnQ6UC5kNSxTVkdGRURpZmZ1
+c2VMaWdodGluZ0VsZW1lbnQ6UC5kNSxTVkdGRURpc3BsYWNlbWVudE1hcEVsZW1lbnQ6UC5kNSxTVkdG
+RURpc3RhbnRMaWdodEVsZW1lbnQ6UC5kNSxTVkdGRUZsb29kRWxlbWVudDpQLmQ1LFNWR0ZFRnVuY0FF
+bGVtZW50OlAuZDUsU1ZHRkVGdW5jQkVsZW1lbnQ6UC5kNSxTVkdGRUZ1bmNHRWxlbWVudDpQLmQ1LFNW
+R0ZFRnVuY1JFbGVtZW50OlAuZDUsU1ZHRkVHYXVzc2lhbkJsdXJFbGVtZW50OlAuZDUsU1ZHRkVJbWFn
+ZUVsZW1lbnQ6UC5kNSxTVkdGRU1lcmdlRWxlbWVudDpQLmQ1LFNWR0ZFTWVyZ2VOb2RlRWxlbWVudDpQ
+LmQ1LFNWR0ZFTW9ycGhvbG9neUVsZW1lbnQ6UC5kNSxTVkdGRU9mZnNldEVsZW1lbnQ6UC5kNSxTVkdG
+RVBvaW50TGlnaHRFbGVtZW50OlAuZDUsU1ZHRkVTcGVjdWxhckxpZ2h0aW5nRWxlbWVudDpQLmQ1LFNW
+R0ZFU3BvdExpZ2h0RWxlbWVudDpQLmQ1LFNWR0ZFVGlsZUVsZW1lbnQ6UC5kNSxTVkdGRVR1cmJ1bGVu
+Y2VFbGVtZW50OlAuZDUsU1ZHRmlsdGVyRWxlbWVudDpQLmQ1LFNWR0ZvcmVpZ25PYmplY3RFbGVtZW50
+OlAuZDUsU1ZHR0VsZW1lbnQ6UC5kNSxTVkdHZW9tZXRyeUVsZW1lbnQ6UC5kNSxTVkdHcmFwaGljc0Vs
+ZW1lbnQ6UC5kNSxTVkdJbWFnZUVsZW1lbnQ6UC5kNSxTVkdMaW5lRWxlbWVudDpQLmQ1LFNWR0xpbmVh
+ckdyYWRpZW50RWxlbWVudDpQLmQ1LFNWR01hcmtlckVsZW1lbnQ6UC5kNSxTVkdNYXNrRWxlbWVudDpQ
+LmQ1LFNWR01ldGFkYXRhRWxlbWVudDpQLmQ1LFNWR1BhdGhFbGVtZW50OlAuZDUsU1ZHUGF0dGVybkVs
+ZW1lbnQ6UC5kNSxTVkdQb2x5Z29uRWxlbWVudDpQLmQ1LFNWR1BvbHlsaW5lRWxlbWVudDpQLmQ1LFNW
+R1JhZGlhbEdyYWRpZW50RWxlbWVudDpQLmQ1LFNWR1JlY3RFbGVtZW50OlAuZDUsU1ZHU2V0RWxlbWVu
+dDpQLmQ1LFNWR1N0b3BFbGVtZW50OlAuZDUsU1ZHU3R5bGVFbGVtZW50OlAuZDUsU1ZHU1ZHRWxlbWVu
+dDpQLmQ1LFNWR1N3aXRjaEVsZW1lbnQ6UC5kNSxTVkdTeW1ib2xFbGVtZW50OlAuZDUsU1ZHVFNwYW5F
+bGVtZW50OlAuZDUsU1ZHVGV4dENvbnRlbnRFbGVtZW50OlAuZDUsU1ZHVGV4dEVsZW1lbnQ6UC5kNSxT
+VkdUZXh0UGF0aEVsZW1lbnQ6UC5kNSxTVkdUZXh0UG9zaXRpb25pbmdFbGVtZW50OlAuZDUsU1ZHVGl0
+bGVFbGVtZW50OlAuZDUsU1ZHVXNlRWxlbWVudDpQLmQ1LFNWR1ZpZXdFbGVtZW50OlAuZDUsU1ZHR3Jh
+ZGllbnRFbGVtZW50OlAuZDUsU1ZHQ29tcG9uZW50VHJhbnNmZXJGdW5jdGlvbkVsZW1lbnQ6UC5kNSxT
+VkdGRURyb3BTaGFkb3dFbGVtZW50OlAuZDUsU1ZHTVBhdGhFbGVtZW50OlAuZDUsU1ZHRWxlbWVudDpQ
+LmQ1fSkKaHVua0hlbHBlcnMuc2V0T3JVcGRhdGVMZWFmVGFncyh7RE9NRXJyb3I6dHJ1ZSxNZWRpYUVy
+cm9yOnRydWUsTmF2aWdhdG9yOnRydWUsTmF2aWdhdG9yQ29uY3VycmVudEhhcmR3YXJlOnRydWUsTmF2
+aWdhdG9yVXNlck1lZGlhRXJyb3I6dHJ1ZSxPdmVyY29uc3RyYWluZWRFcnJvcjp0cnVlLFBvc2l0aW9u
+RXJyb3I6dHJ1ZSxSYW5nZTp0cnVlLFNRTEVycm9yOnRydWUsRGF0YVZpZXc6dHJ1ZSxBcnJheUJ1ZmZl
+clZpZXc6ZmFsc2UsRmxvYXQzMkFycmF5OnRydWUsRmxvYXQ2NEFycmF5OnRydWUsSW50MTZBcnJheTp0
+cnVlLEludDMyQXJyYXk6dHJ1ZSxJbnQ4QXJyYXk6dHJ1ZSxVaW50MTZBcnJheTp0cnVlLFVpbnQzMkFy
+cmF5OnRydWUsVWludDhDbGFtcGVkQXJyYXk6dHJ1ZSxDYW52YXNQaXhlbEFycmF5OnRydWUsVWludDhB
+cnJheTpmYWxzZSxIVE1MQXVkaW9FbGVtZW50OnRydWUsSFRNTEJSRWxlbWVudDp0cnVlLEhUTUxCdXR0
+b25FbGVtZW50OnRydWUsSFRNTENhbnZhc0VsZW1lbnQ6dHJ1ZSxIVE1MQ29udGVudEVsZW1lbnQ6dHJ1
+ZSxIVE1MRExpc3RFbGVtZW50OnRydWUsSFRNTERhdGFFbGVtZW50OnRydWUsSFRNTERhdGFMaXN0RWxl
+bWVudDp0cnVlLEhUTUxEZXRhaWxzRWxlbWVudDp0cnVlLEhUTUxEaWFsb2dFbGVtZW50OnRydWUsSFRN
+TERpdkVsZW1lbnQ6dHJ1ZSxIVE1MRW1iZWRFbGVtZW50OnRydWUsSFRNTEZpZWxkU2V0RWxlbWVudDp0
+cnVlLEhUTUxIUkVsZW1lbnQ6dHJ1ZSxIVE1MSGVhZEVsZW1lbnQ6dHJ1ZSxIVE1MSGVhZGluZ0VsZW1l
+bnQ6dHJ1ZSxIVE1MSHRtbEVsZW1lbnQ6dHJ1ZSxIVE1MSUZyYW1lRWxlbWVudDp0cnVlLEhUTUxJbWFn
+ZUVsZW1lbnQ6dHJ1ZSxIVE1MSW5wdXRFbGVtZW50OnRydWUsSFRNTExJRWxlbWVudDp0cnVlLEhUTUxM
+YWJlbEVsZW1lbnQ6dHJ1ZSxIVE1MTGVnZW5kRWxlbWVudDp0cnVlLEhUTUxMaW5rRWxlbWVudDp0cnVl
+LEhUTUxNYXBFbGVtZW50OnRydWUsSFRNTE1lZGlhRWxlbWVudDp0cnVlLEhUTUxNZW51RWxlbWVudDp0
+cnVlLEhUTUxNZXRhRWxlbWVudDp0cnVlLEhUTUxNZXRlckVsZW1lbnQ6dHJ1ZSxIVE1MTW9kRWxlbWVu
+dDp0cnVlLEhUTUxPTGlzdEVsZW1lbnQ6dHJ1ZSxIVE1MT2JqZWN0RWxlbWVudDp0cnVlLEhUTUxPcHRH
+cm91cEVsZW1lbnQ6dHJ1ZSxIVE1MT3B0aW9uRWxlbWVudDp0cnVlLEhUTUxPdXRwdXRFbGVtZW50OnRy
+dWUsSFRNTFBhcmFtRWxlbWVudDp0cnVlLEhUTUxQaWN0dXJlRWxlbWVudDp0cnVlLEhUTUxQcmVFbGVt
+ZW50OnRydWUsSFRNTFByb2dyZXNzRWxlbWVudDp0cnVlLEhUTUxRdW90ZUVsZW1lbnQ6dHJ1ZSxIVE1M
+U2NyaXB0RWxlbWVudDp0cnVlLEhUTUxTaGFkb3dFbGVtZW50OnRydWUsSFRNTFNsb3RFbGVtZW50OnRy
+dWUsSFRNTFNvdXJjZUVsZW1lbnQ6dHJ1ZSxIVE1MU3BhbkVsZW1lbnQ6dHJ1ZSxIVE1MU3R5bGVFbGVt
+ZW50OnRydWUsSFRNTFRhYmxlQ2FwdGlvbkVsZW1lbnQ6dHJ1ZSxIVE1MVGFibGVDZWxsRWxlbWVudDp0
+cnVlLEhUTUxUYWJsZURhdGFDZWxsRWxlbWVudDp0cnVlLEhUTUxUYWJsZUhlYWRlckNlbGxFbGVtZW50
+OnRydWUsSFRNTFRhYmxlQ29sRWxlbWVudDp0cnVlLEhUTUxUZXh0QXJlYUVsZW1lbnQ6dHJ1ZSxIVE1M
+VGltZUVsZW1lbnQ6dHJ1ZSxIVE1MVGl0bGVFbGVtZW50OnRydWUsSFRNTFRyYWNrRWxlbWVudDp0cnVl
+LEhUTUxVTGlzdEVsZW1lbnQ6dHJ1ZSxIVE1MVW5rbm93bkVsZW1lbnQ6dHJ1ZSxIVE1MVmlkZW9FbGVt
+ZW50OnRydWUsSFRNTERpcmVjdG9yeUVsZW1lbnQ6dHJ1ZSxIVE1MRm9udEVsZW1lbnQ6dHJ1ZSxIVE1M
+RnJhbWVFbGVtZW50OnRydWUsSFRNTEZyYW1lU2V0RWxlbWVudDp0cnVlLEhUTUxNYXJxdWVlRWxlbWVu
+dDp0cnVlLEhUTUxFbGVtZW50OmZhbHNlLEhUTUxBbmNob3JFbGVtZW50OnRydWUsSFRNTEFyZWFFbGVt
+ZW50OnRydWUsSFRNTEJhc2VFbGVtZW50OnRydWUsQmxvYjpmYWxzZSxIVE1MQm9keUVsZW1lbnQ6dHJ1
+ZSxDREFUQVNlY3Rpb246dHJ1ZSxDaGFyYWN0ZXJEYXRhOnRydWUsQ29tbWVudDp0cnVlLFByb2Nlc3Np
+bmdJbnN0cnVjdGlvbjp0cnVlLFRleHQ6dHJ1ZSxDU1NTdHlsZURlY2xhcmF0aW9uOnRydWUsTVNTdHls
+ZUNTU1Byb3BlcnRpZXM6dHJ1ZSxDU1MyUHJvcGVydGllczp0cnVlLFhNTERvY3VtZW50OnRydWUsRG9j
+dW1lbnQ6ZmFsc2UsRE9NRXhjZXB0aW9uOnRydWUsRE9NSW1wbGVtZW50YXRpb246dHJ1ZSxET01SZWN0
+UmVhZE9ubHk6ZmFsc2UsRE9NVG9rZW5MaXN0OnRydWUsRWxlbWVudDpmYWxzZSxBYm9ydFBheW1lbnRF
+dmVudDp0cnVlLEFuaW1hdGlvbkV2ZW50OnRydWUsQW5pbWF0aW9uUGxheWJhY2tFdmVudDp0cnVlLEFw
+cGxpY2F0aW9uQ2FjaGVFcnJvckV2ZW50OnRydWUsQmFja2dyb3VuZEZldGNoQ2xpY2tFdmVudDp0cnVl
+LEJhY2tncm91bmRGZXRjaEV2ZW50OnRydWUsQmFja2dyb3VuZEZldGNoRmFpbEV2ZW50OnRydWUsQmFj
+a2dyb3VuZEZldGNoZWRFdmVudDp0cnVlLEJlZm9yZUluc3RhbGxQcm9tcHRFdmVudDp0cnVlLEJlZm9y
+ZVVubG9hZEV2ZW50OnRydWUsQmxvYkV2ZW50OnRydWUsQ2FuTWFrZVBheW1lbnRFdmVudDp0cnVlLENs
+aXBib2FyZEV2ZW50OnRydWUsQ2xvc2VFdmVudDp0cnVlLEN1c3RvbUV2ZW50OnRydWUsRGV2aWNlTW90
+aW9uRXZlbnQ6dHJ1ZSxEZXZpY2VPcmllbnRhdGlvbkV2ZW50OnRydWUsRXJyb3JFdmVudDp0cnVlLEV4
+dGVuZGFibGVFdmVudDp0cnVlLEV4dGVuZGFibGVNZXNzYWdlRXZlbnQ6dHJ1ZSxGZXRjaEV2ZW50OnRy
+dWUsRm9udEZhY2VTZXRMb2FkRXZlbnQ6dHJ1ZSxGb3JlaWduRmV0Y2hFdmVudDp0cnVlLEdhbWVwYWRF
+dmVudDp0cnVlLEhhc2hDaGFuZ2VFdmVudDp0cnVlLEluc3RhbGxFdmVudDp0cnVlLE1lZGlhRW5jcnlw
+dGVkRXZlbnQ6dHJ1ZSxNZWRpYUtleU1lc3NhZ2VFdmVudDp0cnVlLE1lZGlhUXVlcnlMaXN0RXZlbnQ6
+dHJ1ZSxNZWRpYVN0cmVhbUV2ZW50OnRydWUsTWVkaWFTdHJlYW1UcmFja0V2ZW50OnRydWUsTWVzc2Fn
+ZUV2ZW50OnRydWUsTUlESUNvbm5lY3Rpb25FdmVudDp0cnVlLE1JRElNZXNzYWdlRXZlbnQ6dHJ1ZSxN
+dXRhdGlvbkV2ZW50OnRydWUsTm90aWZpY2F0aW9uRXZlbnQ6dHJ1ZSxQYWdlVHJhbnNpdGlvbkV2ZW50
+OnRydWUsUGF5bWVudFJlcXVlc3RFdmVudDp0cnVlLFBheW1lbnRSZXF1ZXN0VXBkYXRlRXZlbnQ6dHJ1
+ZSxQb3BTdGF0ZUV2ZW50OnRydWUsUHJlc2VudGF0aW9uQ29ubmVjdGlvbkF2YWlsYWJsZUV2ZW50OnRy
+dWUsUHJlc2VudGF0aW9uQ29ubmVjdGlvbkNsb3NlRXZlbnQ6dHJ1ZSxQcm9taXNlUmVqZWN0aW9uRXZl
+bnQ6dHJ1ZSxQdXNoRXZlbnQ6dHJ1ZSxSVENEYXRhQ2hhbm5lbEV2ZW50OnRydWUsUlRDRFRNRlRvbmVD
+aGFuZ2VFdmVudDp0cnVlLFJUQ1BlZXJDb25uZWN0aW9uSWNlRXZlbnQ6dHJ1ZSxSVENUcmFja0V2ZW50
+OnRydWUsU2VjdXJpdHlQb2xpY3lWaW9sYXRpb25FdmVudDp0cnVlLFNlbnNvckVycm9yRXZlbnQ6dHJ1
+ZSxTcGVlY2hSZWNvZ25pdGlvbkVycm9yOnRydWUsU3BlZWNoUmVjb2duaXRpb25FdmVudDp0cnVlLFNw
+ZWVjaFN5bnRoZXNpc0V2ZW50OnRydWUsU3RvcmFnZUV2ZW50OnRydWUsU3luY0V2ZW50OnRydWUsVHJh
+Y2tFdmVudDp0cnVlLFRyYW5zaXRpb25FdmVudDp0cnVlLFdlYktpdFRyYW5zaXRpb25FdmVudDp0cnVl
+LFZSRGV2aWNlRXZlbnQ6dHJ1ZSxWUkRpc3BsYXlFdmVudDp0cnVlLFZSU2Vzc2lvbkV2ZW50OnRydWUs
+TW9qb0ludGVyZmFjZVJlcXVlc3RFdmVudDp0cnVlLFVTQkNvbm5lY3Rpb25FdmVudDp0cnVlLElEQlZl
+cnNpb25DaGFuZ2VFdmVudDp0cnVlLEF1ZGlvUHJvY2Vzc2luZ0V2ZW50OnRydWUsT2ZmbGluZUF1ZGlv
+Q29tcGxldGlvbkV2ZW50OnRydWUsV2ViR0xDb250ZXh0RXZlbnQ6dHJ1ZSxFdmVudDpmYWxzZSxJbnB1
+dEV2ZW50OmZhbHNlLFN1Ym1pdEV2ZW50OmZhbHNlLEV2ZW50VGFyZ2V0OmZhbHNlLEZpbGU6dHJ1ZSxI
+VE1MRm9ybUVsZW1lbnQ6dHJ1ZSxIaXN0b3J5OnRydWUsSFRNTERvY3VtZW50OnRydWUsWE1MSHR0cFJl
+cXVlc3Q6dHJ1ZSxYTUxIdHRwUmVxdWVzdEV2ZW50VGFyZ2V0OmZhbHNlLEltYWdlRGF0YTp0cnVlLExv
+Y2F0aW9uOnRydWUsTW91c2VFdmVudDp0cnVlLERyYWdFdmVudDp0cnVlLFBvaW50ZXJFdmVudDp0cnVl
+LFdoZWVsRXZlbnQ6dHJ1ZSxEb2N1bWVudEZyYWdtZW50OnRydWUsU2hhZG93Um9vdDp0cnVlLERvY3Vt
+ZW50VHlwZTp0cnVlLE5vZGU6ZmFsc2UsTm9kZUxpc3Q6dHJ1ZSxSYWRpb05vZGVMaXN0OnRydWUsSFRN
+TFBhcmFncmFwaEVsZW1lbnQ6dHJ1ZSxQcm9ncmVzc0V2ZW50OnRydWUsUmVzb3VyY2VQcm9ncmVzc0V2
+ZW50OnRydWUsSFRNTFNlbGVjdEVsZW1lbnQ6dHJ1ZSxIVE1MVGFibGVFbGVtZW50OnRydWUsSFRNTFRh
+YmxlUm93RWxlbWVudDp0cnVlLEhUTUxUYWJsZVNlY3Rpb25FbGVtZW50OnRydWUsSFRNTFRlbXBsYXRl
+RWxlbWVudDp0cnVlLENvbXBvc2l0aW9uRXZlbnQ6dHJ1ZSxGb2N1c0V2ZW50OnRydWUsS2V5Ym9hcmRF
+dmVudDp0cnVlLFRleHRFdmVudDp0cnVlLFRvdWNoRXZlbnQ6dHJ1ZSxVSUV2ZW50OmZhbHNlLFdpbmRv
+dzp0cnVlLERPTVdpbmRvdzp0cnVlLERlZGljYXRlZFdvcmtlckdsb2JhbFNjb3BlOnRydWUsU2Vydmlj
+ZVdvcmtlckdsb2JhbFNjb3BlOnRydWUsU2hhcmVkV29ya2VyR2xvYmFsU2NvcGU6dHJ1ZSxXb3JrZXJH
+bG9iYWxTY29wZTp0cnVlLEF0dHI6dHJ1ZSxDbGllbnRSZWN0OnRydWUsRE9NUmVjdDp0cnVlLE5hbWVk
+Tm9kZU1hcDp0cnVlLE1vek5hbWVkQXR0ck1hcDp0cnVlLElEQktleVJhbmdlOnRydWUsU1ZHU2NyaXB0
+RWxlbWVudDp0cnVlLFNWR0FFbGVtZW50OnRydWUsU1ZHQW5pbWF0ZUVsZW1lbnQ6dHJ1ZSxTVkdBbmlt
+YXRlTW90aW9uRWxlbWVudDp0cnVlLFNWR0FuaW1hdGVUcmFuc2Zvcm1FbGVtZW50OnRydWUsU1ZHQW5p
+bWF0aW9uRWxlbWVudDp0cnVlLFNWR0NpcmNsZUVsZW1lbnQ6dHJ1ZSxTVkdDbGlwUGF0aEVsZW1lbnQ6
+dHJ1ZSxTVkdEZWZzRWxlbWVudDp0cnVlLFNWR0Rlc2NFbGVtZW50OnRydWUsU1ZHRGlzY2FyZEVsZW1l
+bnQ6dHJ1ZSxTVkdFbGxpcHNlRWxlbWVudDp0cnVlLFNWR0ZFQmxlbmRFbGVtZW50OnRydWUsU1ZHRkVD
+b2xvck1hdHJpeEVsZW1lbnQ6dHJ1ZSxTVkdGRUNvbXBvbmVudFRyYW5zZmVyRWxlbWVudDp0cnVlLFNW
+R0ZFQ29tcG9zaXRlRWxlbWVudDp0cnVlLFNWR0ZFQ29udm9sdmVNYXRyaXhFbGVtZW50OnRydWUsU1ZH
+RkVEaWZmdXNlTGlnaHRpbmdFbGVtZW50OnRydWUsU1ZHRkVEaXNwbGFjZW1lbnRNYXBFbGVtZW50OnRy
+dWUsU1ZHRkVEaXN0YW50TGlnaHRFbGVtZW50OnRydWUsU1ZHRkVGbG9vZEVsZW1lbnQ6dHJ1ZSxTVkdG
+RUZ1bmNBRWxlbWVudDp0cnVlLFNWR0ZFRnVuY0JFbGVtZW50OnRydWUsU1ZHRkVGdW5jR0VsZW1lbnQ6
+dHJ1ZSxTVkdGRUZ1bmNSRWxlbWVudDp0cnVlLFNWR0ZFR2F1c3NpYW5CbHVyRWxlbWVudDp0cnVlLFNW
+R0ZFSW1hZ2VFbGVtZW50OnRydWUsU1ZHRkVNZXJnZUVsZW1lbnQ6dHJ1ZSxTVkdGRU1lcmdlTm9kZUVs
+ZW1lbnQ6dHJ1ZSxTVkdGRU1vcnBob2xvZ3lFbGVtZW50OnRydWUsU1ZHRkVPZmZzZXRFbGVtZW50OnRy
+dWUsU1ZHRkVQb2ludExpZ2h0RWxlbWVudDp0cnVlLFNWR0ZFU3BlY3VsYXJMaWdodGluZ0VsZW1lbnQ6
+dHJ1ZSxTVkdGRVNwb3RMaWdodEVsZW1lbnQ6dHJ1ZSxTVkdGRVRpbGVFbGVtZW50OnRydWUsU1ZHRkVU
+dXJidWxlbmNlRWxlbWVudDp0cnVlLFNWR0ZpbHRlckVsZW1lbnQ6dHJ1ZSxTVkdGb3JlaWduT2JqZWN0
+RWxlbWVudDp0cnVlLFNWR0dFbGVtZW50OnRydWUsU1ZHR2VvbWV0cnlFbGVtZW50OnRydWUsU1ZHR3Jh
+cGhpY3NFbGVtZW50OnRydWUsU1ZHSW1hZ2VFbGVtZW50OnRydWUsU1ZHTGluZUVsZW1lbnQ6dHJ1ZSxT
+VkdMaW5lYXJHcmFkaWVudEVsZW1lbnQ6dHJ1ZSxTVkdNYXJrZXJFbGVtZW50OnRydWUsU1ZHTWFza0Vs
+ZW1lbnQ6dHJ1ZSxTVkdNZXRhZGF0YUVsZW1lbnQ6dHJ1ZSxTVkdQYXRoRWxlbWVudDp0cnVlLFNWR1Bh
+dHRlcm5FbGVtZW50OnRydWUsU1ZHUG9seWdvbkVsZW1lbnQ6dHJ1ZSxTVkdQb2x5bGluZUVsZW1lbnQ6
+dHJ1ZSxTVkdSYWRpYWxHcmFkaWVudEVsZW1lbnQ6dHJ1ZSxTVkdSZWN0RWxlbWVudDp0cnVlLFNWR1Nl
+dEVsZW1lbnQ6dHJ1ZSxTVkdTdG9wRWxlbWVudDp0cnVlLFNWR1N0eWxlRWxlbWVudDp0cnVlLFNWR1NW
+R0VsZW1lbnQ6dHJ1ZSxTVkdTd2l0Y2hFbGVtZW50OnRydWUsU1ZHU3ltYm9sRWxlbWVudDp0cnVlLFNW
+R1RTcGFuRWxlbWVudDp0cnVlLFNWR1RleHRDb250ZW50RWxlbWVudDp0cnVlLFNWR1RleHRFbGVtZW50
+OnRydWUsU1ZHVGV4dFBhdGhFbGVtZW50OnRydWUsU1ZHVGV4dFBvc2l0aW9uaW5nRWxlbWVudDp0cnVl
+LFNWR1RpdGxlRWxlbWVudDp0cnVlLFNWR1VzZUVsZW1lbnQ6dHJ1ZSxTVkdWaWV3RWxlbWVudDp0cnVl
+LFNWR0dyYWRpZW50RWxlbWVudDp0cnVlLFNWR0NvbXBvbmVudFRyYW5zZmVyRnVuY3Rpb25FbGVtZW50
+OnRydWUsU1ZHRkVEcm9wU2hhZG93RWxlbWVudDp0cnVlLFNWR01QYXRoRWxlbWVudDp0cnVlLFNWR0Vs
+ZW1lbnQ6ZmFsc2V9KQpILkxaLiRuYXRpdmVTdXBlcmNsYXNzVGFnPSJBcnJheUJ1ZmZlclZpZXciCkgu
+UkcuJG5hdGl2ZVN1cGVyY2xhc3NUYWc9IkFycmF5QnVmZmVyVmlldyIKSC5WUC4kbmF0aXZlU3VwZXJj
+bGFzc1RhZz0iQXJyYXlCdWZmZXJWaWV3IgpILkRnLiRuYXRpdmVTdXBlcmNsYXNzVGFnPSJBcnJheUJ1
+ZmZlclZpZXciCkguV0IuJG5hdGl2ZVN1cGVyY2xhc3NUYWc9IkFycmF5QnVmZmVyVmlldyIKSC5aRy4k
+bmF0aXZlU3VwZXJjbGFzc1RhZz0iQXJyYXlCdWZmZXJWaWV3IgpILlBnLiRuYXRpdmVTdXBlcmNsYXNz
+VGFnPSJBcnJheUJ1ZmZlclZpZXcifSkoKQpjb252ZXJ0QWxsVG9GYXN0T2JqZWN0KHcpCmNvbnZlcnRU
+b0Zhc3RPYmplY3QoJCk7KGZ1bmN0aW9uKGEpe2lmKHR5cGVvZiBkb2N1bWVudD09PSJ1bmRlZmluZWQi
+KXthKG51bGwpCnJldHVybn1pZih0eXBlb2YgZG9jdW1lbnQuY3VycmVudFNjcmlwdCE9J3VuZGVmaW5l
+ZCcpe2EoZG9jdW1lbnQuY3VycmVudFNjcmlwdCkKcmV0dXJufXZhciB0PWRvY3VtZW50LnNjcmlwdHMK
+ZnVuY3Rpb24gb25Mb2FkKGIpe2Zvcih2YXIgcj0wO3I8dC5sZW5ndGg7KytyKXRbcl0ucmVtb3ZlRXZl
+bnRMaXN0ZW5lcigibG9hZCIsb25Mb2FkLGZhbHNlKQphKGIudGFyZ2V0KX1mb3IodmFyIHM9MDtzPHQu
+bGVuZ3RoOysrcyl0W3NdLmFkZEV2ZW50TGlzdGVuZXIoImxvYWQiLG9uTG9hZCxmYWxzZSl9KShmdW5j
+dGlvbihhKXt2LmN1cnJlbnRTY3JpcHQ9YQppZih0eXBlb2YgZGFydE1haW5SdW5uZXI9PT0iZnVuY3Rp
+b24iKWRhcnRNYWluUnVubmVyKEwuSXEsW10pCmVsc2UgTC5JcShbXSl9KX0pKCkKLy8jIHNvdXJjZU1h
+cHBpbmdVUkw9bWlncmF0aW9uLmpzLm1hcAo=
''';
diff --git a/pkg/nnbd_migration/lib/src/front_end/web/migration.dart b/pkg/nnbd_migration/lib/src/front_end/web/migration.dart
index 776dab7..6649147 100644
--- a/pkg/nnbd_migration/lib/src/front_end/web/migration.dart
+++ b/pkg/nnbd_migration/lib/src/front_end/web/migration.dart
@@ -678,12 +678,12 @@
arrow.innerHtml = '▼';
Element icon = document.createElement('span');
li.append(icon);
- icon.innerHtml = '📁';
+ icon.innerHtml = '<span class="material-icons">folder_open</span>';
li.append(Text(entity.name));
writeNavigationSubtree(li, entity.subtree);
addArrowClickHandler(arrow);
} else {
- li.innerHtml = '📄';
+ li.innerHtml = '<span class="material-icons">insert_drive_file</span>';
Element a = document.createElement('a');
li.append(a);
a.classes.add('nav-link');
diff --git a/pkg/nnbd_migration/lib/src/preview/dart_logo_page.dart b/pkg/nnbd_migration/lib/src/preview/dart_logo_page.dart
index b4f8b70..061ace1 100644
--- a/pkg/nnbd_migration/lib/src/preview/dart_logo_page.dart
+++ b/pkg/nnbd_migration/lib/src/preview/dart_logo_page.dart
@@ -1,4 +1,4 @@
-// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
+// 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.
diff --git a/pkg/nnbd_migration/lib/src/preview/http_preview_server.dart b/pkg/nnbd_migration/lib/src/preview/http_preview_server.dart
index 2f90a02..3afdadb 100644
--- a/pkg/nnbd_migration/lib/src/preview/http_preview_server.dart
+++ b/pkg/nnbd_migration/lib/src/preview/http_preview_server.dart
@@ -35,13 +35,15 @@
// A function which allows the migration to be rerun, taking changed paths.
final Future<MigrationState> Function() rerunFunction;
+ final String hostname;
+
/// Integer for a port to run the preview server on. If null or zero, allow
/// [HttpServer.bind] to pick one.
final int preferredPort;
/// Initialize a newly created HTTP server.
- HttpPreviewServer(
- this.migrationState, this.rerunFunction, this.preferredPort);
+ HttpPreviewServer(this.migrationState, this.rerunFunction, this.hostname,
+ this.preferredPort);
Future<String> get authToken async {
await _serverFuture;
@@ -50,6 +52,11 @@
}
/// Return the port this server is bound to.
+ Future<String> get boundHostname async {
+ return (await _serverFuture)?.address?.host;
+ }
+
+ /// Return the port this server is bound to.
Future<int> get boundPort async {
return (await _serverFuture)?.port;
}
@@ -67,8 +74,15 @@
}
try {
- _serverFuture =
- HttpServer.bind(InternetAddress.loopbackIPv4, preferredPort ?? 0);
+ if (hostname == 'localhost') {
+ _serverFuture =
+ HttpServer.bind(InternetAddress.loopbackIPv4, preferredPort ?? 0);
+ } else if (hostname == 'any') {
+ _serverFuture =
+ HttpServer.bind(InternetAddress.anyIPv6, preferredPort ?? 0);
+ } else {
+ _serverFuture = HttpServer.bind(hostname, preferredPort ?? 0);
+ }
var server = await _serverFuture;
_handleServer(server);
diff --git a/pkg/nnbd_migration/lib/src/preview/material_icons_page.dart b/pkg/nnbd_migration/lib/src/preview/material_icons_page.dart
new file mode 100644
index 0000000..0364582
--- /dev/null
+++ b/pkg/nnbd_migration/lib/src/preview/material_icons_page.dart
@@ -0,0 +1,31 @@
+// 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/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<String, String> params) {
+ throw UnimplementedError();
+ }
+
+ @override
+ Future<void> generatePage(Map<String, String> params) async {
+ buf.write(resources.MaterialIconsRegular_ttf);
+ }
+}
diff --git a/pkg/nnbd_migration/lib/src/preview/preview_site.dart b/pkg/nnbd_migration/lib/src/preview/preview_site.dart
index 6e67a2d..9e02a7b 100644
--- a/pkg/nnbd_migration/lib/src/preview/preview_site.dart
+++ b/pkg/nnbd_migration/lib/src/preview/preview_site.dart
@@ -20,11 +20,14 @@
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.
@@ -51,6 +54,15 @@
/// 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';
@@ -141,6 +153,18 @@
// 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 ==
@@ -369,6 +393,10 @@
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;
}
diff --git a/pkg/nnbd_migration/lib/src/preview/roboto_mono_page.dart b/pkg/nnbd_migration/lib/src/preview/roboto_mono_page.dart
new file mode 100644
index 0000000..913ba72
--- /dev/null
+++ b/pkg/nnbd_migration/lib/src/preview/roboto_mono_page.dart
@@ -0,0 +1,31 @@
+// 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/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<String, String> params) {
+ throw UnimplementedError();
+ }
+
+ @override
+ Future<void> generatePage(Map<String, String> 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
new file mode 100644
index 0000000..db4c92a
--- /dev/null
+++ b/pkg/nnbd_migration/lib/src/preview/roboto_page.dart
@@ -0,0 +1,31 @@
+// 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/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<String, String> params) {
+ throw UnimplementedError();
+ }
+
+ @override
+ Future<void> generatePage(Map<String, String> params) async {
+ buf.write(resources.RobotoRegular_ttf);
+ }
+}
diff --git a/pkg/nnbd_migration/test/api_test.dart b/pkg/nnbd_migration/test/api_test.dart
index a6f7f61..e14309d 100644
--- a/pkg/nnbd_migration/test/api_test.dart
+++ b/pkg/nnbd_migration/test/api_test.dart
@@ -5237,6 +5237,34 @@
await _checkSingleFileChanges(content, expected);
}
+ Future<void> test_regression_42374() async {
+ var content = '''
+class C<R> {
+ R m(dynamic x) {
+ assert(x is R);
+ return x as R;
+ }
+}
+
+void main() {
+ C<int/*!*/>().m(null!);
+}
+''';
+ var expected = '''
+class C<R> {
+ R m(dynamic x) {
+ assert(x is R);
+ return x as R;
+ }
+}
+
+void main() {
+ C<int>().m(null!);
+}
+''';
+ await _checkSingleFileChanges(content, expected);
+ }
+
Future<void> test_remove_question_from_question_dot() async {
var content = '_f(int/*!*/ i) => i?.isEven;';
var expected = '_f(int i) => i.isEven;';
@@ -6065,6 +6093,33 @@
}
Future<void>
+ 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<void>
test_unconditional_assert_statement_implies_non_null_intent() async {
var content = '''
void f(int i) {
diff --git a/pkg/nnbd_migration/test/edge_builder_test.dart b/pkg/nnbd_migration/test/edge_builder_test.dart
index 7df67db..bf2f75b 100644
--- a/pkg/nnbd_migration/test/edge_builder_test.dart
+++ b/pkg/nnbd_migration/test/edge_builder_test.dart
@@ -728,6 +728,18 @@
assertEdge(decoratedTypeAnnotation('int i').node, never, hard: true);
}
+ Future<void> 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<void> test_assign_bound_to_type_parameter() async {
await analyze('''
class C<T extends List<int>> {
@@ -6222,8 +6234,8 @@
Future<void> test_prefixedIdentifier_bangHint() async {
await analyze('''
import 'dart:math' as m;
-double f1() => m.PI;
-double f2() => m.PI/*!*/;
+double f1() => m.pi;
+double f2() => m.pi/*!*/;
''');
expect(
assertEdge(anyNode, decoratedTypeAnnotation('double f1').node,
@@ -6235,7 +6247,7 @@
hard: false)
.sourceNode,
never);
- expect(hasNullCheckHint(findNode.prefixed('m.PI/*!*/')), isTrue);
+ expect(hasNullCheckHint(findNode.prefixed('m.pi/*!*/')), isTrue);
}
Future<void> test_prefixedIdentifier_field_type() async {
diff --git a/pkg/nnbd_migration/test/migration_cli_test.dart b/pkg/nnbd_migration/test/migration_cli_test.dart
index daf4b38..1c43754 100644
--- a/pkg/nnbd_migration/test/migration_cli_test.dart
+++ b/pkg/nnbd_migration/test/migration_cli_test.dart
@@ -61,10 +61,12 @@
_ExceptionGeneratingNonNullableFix(DartFixListener listener,
ResourceProvider resourceProvider, LineInfo Function(String) getLineInfo,
{List<String> included = const <String>[],
+ String hostname,
int preferredPort,
String summaryPath})
: super(listener, resourceProvider, getLineInfo,
included: included,
+ hostname: hostname,
preferredPort: preferredPort,
summaryPath: summaryPath);
@@ -121,17 +123,20 @@
NonNullableFix createNonNullableFix(DartFixListener listener,
ResourceProvider resourceProvider, LineInfo getLineInfo(String path),
{List<String> included = const <String>[],
+ String hostname,
int preferredPort,
String summaryPath}) {
if (cli._test.injectArtificialException) {
return _ExceptionGeneratingNonNullableFix(
listener, resourceProvider, getLineInfo,
included: included,
+ hostname: hostname,
preferredPort: preferredPort,
summaryPath: summaryPath);
} else {
return super.createNonNullableFix(listener, resourceProvider, getLineInfo,
included: included,
+ hostname: hostname,
preferredPort: preferredPort,
summaryPath: summaryPath);
}
@@ -759,6 +764,7 @@
await runWithPreviewServer(cli, [projectDir], (url) async {
expect(
logger.stdoutBuffer.toString(), contains('No analysis issues found'));
+ expect(url, startsWith('http://127.0.0.1:'));
await assertPreviewServerResponsive(url);
});
// No changes should have been made.
@@ -872,6 +878,19 @@
});
}
+ test_lifecycle_preview_on_host_any() async {
+ var projectContents = simpleProject();
+ var projectDir = await 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 = await createProjectDir(projectContents);
@@ -1294,6 +1313,16 @@
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);
diff --git a/pkg/nnbd_migration/tool/codegen/generate_resources.dart b/pkg/nnbd_migration/tool/codegen/generate_resources.dart
index 8a342a1..18695ef 100644
--- a/pkg/nnbd_migration/tool/codegen/generate_resources.dart
+++ b/pkg/nnbd_migration/tool/codegen/generate_resources.dart
@@ -69,6 +69,7 @@
'.html',
'.js',
'.png',
+ '.ttf',
];
String base64Encode(List<int> bytes) {
@@ -173,7 +174,8 @@
var name = path.basename(resource.path).replaceAll('.', '_');
String source;
- if (path.extension(resource.path) == '.png') {
+ var extension = path.extension(resource.path);
+ if (extension == '.png' || extension == '.ttf') {
source = resource.readAsStringSync(encoding: latin1);
} else {
source = resource.readAsStringSync();
@@ -240,7 +242,9 @@
continue;
}
- if (name == 'migration.js' || name == 'dart_192.png') {
+ if (name == 'migration.js' ||
+ name == 'dart_192.png' ||
+ path.extension(name) == '.ttf') {
// skip the compiled js and logo
continue;
}
diff --git a/pkg/nnbd_migration/tool/src/package.dart b/pkg/nnbd_migration/tool/src/package.dart
index 6b67e50..612d9cd 100644
--- a/pkg/nnbd_migration/tool/src/package.dart
+++ b/pkg/nnbd_migration/tool/src/package.dart
@@ -4,7 +4,6 @@
/// Abstractions for the different sources of truth for different packages.
-import 'dart:convert';
import 'dart:io';
import 'package:nnbd_migration/src/utilities/subprocess_launcher.dart';
diff --git a/pkg/pkg.status b/pkg/pkg.status
index 94033c1..4cc5873 100644
--- a/pkg/pkg.status
+++ b/pkg/pkg.status
@@ -173,6 +173,7 @@
frontend_server/test/*: SkipByDesign # Only meant to run on vm
vm/test/*: SkipByDesign # Only meant to run on vm
vm_service/test/*: SkipByDesign # Uses dart:io
+vm_snapshot_analysis/test/*: SkipByDesign # Only meant to run on vm
[ $system == windows ]
analysis_server/test/integration/analysis/get_errors_nonStandard_sdk_test: Skip
diff --git a/pkg/smith/pubspec.yaml b/pkg/smith/pubspec.yaml
index abd81b3..f88a2d3 100644
--- a/pkg/smith/pubspec.yaml
+++ b/pkg/smith/pubspec.yaml
@@ -1,8 +1,6 @@
name: smith
-author: Dart Team <misc@dartlang.org>
description: Shared code for working with the Dart SDK's tests and test runner.
-homepage: http://www.dartlang.org
-# This package is not intended to be published.
+# This package is not intended for consumption on pub.dev. DO NOT publish.
publish_to: none
environment:
sdk: "^2.3.0"
diff --git a/pkg/sourcemap_testing/pubspec.yaml b/pkg/sourcemap_testing/pubspec.yaml
index 070ea67..f1d11f0 100644
--- a/pkg/sourcemap_testing/pubspec.yaml
+++ b/pkg/sourcemap_testing/pubspec.yaml
@@ -1,5 +1,6 @@
# Helper package for testing sourcemaps. Used by ddc and dart2js.
name: sourcemap_testing
+# This package is not intended for consumption on pub.dev. DO NOT publish.
publish_to: none
environment:
diff --git a/pkg/status_file/pubspec.yaml b/pkg/status_file/pubspec.yaml
index 4eb3bcc..ae78d40 100644
--- a/pkg/status_file/pubspec.yaml
+++ b/pkg/status_file/pubspec.yaml
@@ -1,5 +1,5 @@
name: status_file
-# This package is not intended to be published.
+# This package is not intended for consumption on pub.dev. DO NOT publish.
publish_to: none
environment:
sdk: "^2.3.0"
diff --git a/pkg/telemetry/lib/telemetry.dart b/pkg/telemetry/lib/telemetry.dart
index 2da8ecc..0c28c7d 100644
--- a/pkg/telemetry/lib/telemetry.dart
+++ b/pkg/telemetry/lib/telemetry.dart
@@ -179,8 +179,9 @@
return false;
}
- return env['BOT'] == 'true'
- // https://docs.travis-ci.com/user/environment-variables/#Default-Environment-Variables
+ return env.containsKey('BOT')
+ // https://docs.travis-ci.com/user/environment-variables/
+ // Example .travis.yml file: https://github.com/flutter/devtools/blob/master/.travis.yml
||
env['TRAVIS'] == 'true' ||
env['CONTINUOUS_INTEGRATION'] == 'true' ||
diff --git a/pkg/telemetry/pubspec.yaml b/pkg/telemetry/pubspec.yaml
index 8084a84..c3ba85f 100644
--- a/pkg/telemetry/pubspec.yaml
+++ b/pkg/telemetry/pubspec.yaml
@@ -1,5 +1,6 @@
name: telemetry
description: A library to facilitate reporting analytics and crash reports.
+# This package is not intended for consumption on pub.dev. DO NOT publish.
publish_to: none
author: Dart Team <misc@dartlang.org>
diff --git a/pkg/test_runner/pubspec.yaml b/pkg/test_runner/pubspec.yaml
index b927d5b..a840b28 100644
--- a/pkg/test_runner/pubspec.yaml
+++ b/pkg/test_runner/pubspec.yaml
@@ -3,6 +3,7 @@
# BSD-style license that can be found in the LICENSE file.
name: test_runner
+# This package is not intended for consumption on pub.dev. DO NOT publish.
publish_to: none
environment:
sdk: "^2.3.0"
diff --git a/pkg/testing/pubspec.yaml b/pkg/testing/pubspec.yaml
index 9811fa7..f012946 100644
--- a/pkg/testing/pubspec.yaml
+++ b/pkg/testing/pubspec.yaml
@@ -1,6 +1,7 @@
# This pubspec is currently mainly used to make it easier to develop by making
# it a package.
name: testing
+# This package is not intended for consumption on pub.dev. DO NOT publish.
publish_to: none
environment:
sdk: '>=2.0.0 <3.0.0'
diff --git a/pkg/vm/bin/dump_kernel.dart b/pkg/vm/bin/dump_kernel.dart
index 6419750..852c073 100644
--- a/pkg/vm/bin/dump_kernel.dart
+++ b/pkg/vm/bin/dump_kernel.dart
@@ -22,6 +22,8 @@
show UnreachableNodeMetadataRepository;
import 'package:vm/metadata/call_site_attributes.dart'
show CallSiteAttributesMetadataRepository;
+import 'package:vm/metadata/loading_units.dart'
+ show LoadingUnitsMetadataRepository;
final String _usage = '''
Usage: dump_kernel input.dill output.txt
@@ -48,6 +50,7 @@
component.addMetadataRepository(new UnreachableNodeMetadataRepository());
component.addMetadataRepository(new BytecodeMetadataRepository());
component.addMetadataRepository(new CallSiteAttributesMetadataRepository());
+ component.addMetadataRepository(new LoadingUnitsMetadataRepository());
final List<int> bytes = new File(input).readAsBytesSync();
new BinaryBuilderWithMetadata(bytes).readComponent(component);
diff --git a/pkg/vm/bin/protobuf_aware_treeshaker.dart b/pkg/vm/bin/protobuf_aware_treeshaker.dart
index 0f9bf55..143a4c6 100644
--- a/pkg/vm/bin/protobuf_aware_treeshaker.dart
+++ b/pkg/vm/bin/protobuf_aware_treeshaker.dart
@@ -123,6 +123,7 @@
const bool useGlobalTypeFlowAnalysis = true;
const bool enableAsserts = false;
const bool useProtobufAwareTreeShaker = true;
+ const bool useProtobufAwareTreeShakerV2 = false;
final nopErrorDetector = ErrorDetector();
runGlobalTransformations(
target,
@@ -130,6 +131,7 @@
useGlobalTypeFlowAnalysis,
enableAsserts,
useProtobufAwareTreeShaker,
+ useProtobufAwareTreeShakerV2,
nopErrorDetector,
);
} else {
diff --git a/pkg/vm/lib/dominators.dart b/pkg/vm/lib/dominators.dart
new file mode 100644
index 0000000..19c5d84
--- /dev/null
+++ b/pkg/vm/lib/dominators.dart
@@ -0,0 +1,126 @@
+// 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.
+
+library vm.dominators;
+
+class Vertex<T extends Vertex<T>> {
+ // Input: vertices directly reachable from this vertex.
+ final List<T> successors = <T>[];
+
+ // Output: the nearest vertex that all paths from the root must go through to
+ // reach this vertex.
+ T dominator;
+
+ bool isDominatedBy(T other) {
+ var d = this;
+ while (d != null) {
+ if (d == other) {
+ return true;
+ }
+ d = d.dominator;
+ }
+ return false;
+ }
+
+ // Temporaries. See Lengauer and Tarjan.
+ final List<T> _predecessors = <T>[];
+ int _semi = 0;
+ T _label;
+ T _ancestor;
+ T _parent;
+ List<T> _bucket;
+}
+
+// T. Lengauer and R. E. Tarjan. "A Fast Algorithm for Finding Dominators
+// in a Flowgraph."
+computeDominators<T extends Vertex<T>>(T root) {
+ // Lengauer and Tarjan Step 1.
+ final vertex = <T>[];
+ vertex.add(null);
+
+ var n = 0;
+ dfs(T v) {
+ n++;
+
+ vertex.add(v);
+ v._semi = n;
+ v._label = v;
+ v._ancestor = null;
+
+ for (final w in v.successors) {
+ if (w._semi == 0) {
+ w._parent = v;
+ dfs(w);
+ }
+ w._predecessors.add(v);
+ }
+ }
+
+ dfs(root);
+
+ forestCompress(T v) {
+ if (v._ancestor._ancestor != null) {
+ forestCompress(v._ancestor);
+ if (v._ancestor._label._semi < v._label._semi) {
+ v._label = v._ancestor._label;
+ }
+ v._ancestor = v._ancestor._ancestor;
+ }
+ }
+
+ forestEval(T v) {
+ if (v._ancestor == null) {
+ return v;
+ } else {
+ forestCompress(v);
+ return v._label;
+ }
+ }
+
+ forestLink(T v, T w) {
+ w._ancestor = v;
+ }
+
+ for (var i = vertex.length - 1; i > 1; i--) {
+ Vertex<T> w = vertex[i];
+
+ // Lengauer and Tarjan Step 2.
+ for (Vertex<T> v in w._predecessors) {
+ if (v._semi == 0) continue; // Unreachable
+
+ final u = forestEval(v);
+ if (u._semi < w._semi) {
+ w._semi = u._semi;
+ }
+ }
+
+ Vertex<T> z = vertex[w._semi];
+ var b = z._bucket;
+ if (b == null) {
+ z._bucket = b = <T>[];
+ }
+ b.add(w);
+ forestLink(w._parent, w);
+
+ // Lengauer and Tarjan Step 3.
+ z = w._parent;
+ assert(z != null);
+ b = z._bucket;
+ z._bucket = null;
+ if (b != null) {
+ for (final v in b) {
+ final u = forestEval(v);
+ v.dominator = u._semi < v._semi ? u : w._parent;
+ }
+ }
+ }
+
+ // Lengauer and Tarjan Step 4.
+ for (var i = 2; i < vertex.length; i++) {
+ final w = vertex[i];
+ if (w.dominator != vertex[w._semi]) {
+ w.dominator = w.dominator.dominator;
+ }
+ }
+}
diff --git a/pkg/vm/lib/kernel_front_end.dart b/pkg/vm/lib/kernel_front_end.dart
index 24760e7..06cc08b 100644
--- a/pkg/vm/lib/kernel_front_end.dart
+++ b/pkg/vm/lib/kernel_front_end.dart
@@ -68,6 +68,7 @@
import 'transformations/call_site_annotator.dart' as call_site_annotator;
import 'transformations/unreachable_code_elimination.dart'
as unreachable_code_elimination;
+import 'transformations/deferred_loading.dart' as deferred_loading;
/// Declare options consumed by [runCompiler].
void declareCompilerOptions(ArgParser args) {
@@ -110,6 +111,8 @@
args.addFlag('protobuf-tree-shaker',
help: 'Enable protobuf tree shaker transformation in AOT mode.',
defaultsTo: false);
+ args.addFlag('protobuf-tree-shaker-v2',
+ help: 'Enable protobuf tree shaker v2 in AOT mode.', defaultsTo: false);
args.addMultiOption('define',
abbr: 'D',
help: 'The values for the environment constants (e.g. -Dkey=value).');
@@ -140,6 +143,9 @@
help: 'Comma separated list of experimental features to enable.');
args.addFlag('help',
abbr: 'h', negatable: false, help: 'Print this help message.');
+ args.addFlag('track-widget-creation',
+ help: 'Run a kernel transformer to track creation locations for widgets.',
+ defaultsTo: false);
}
/// Create ArgParser and populate it with options consumed by [runCompiler].
@@ -185,6 +191,7 @@
final bool enableAsserts = options['enable-asserts'];
final bool nullSafety = options['null-safety'];
final bool useProtobufTreeShaker = options['protobuf-tree-shaker'];
+ final bool useProtobufTreeShakerV2 = options['protobuf-tree-shaker-v2'];
final bool splitOutputByPackages = options['split-output-by-packages'];
final String manifestFilename = options['manifest'];
final String dataDir = options['component-name'] ?? options['data-dir'];
@@ -255,8 +262,11 @@
await autoDetectNullSafetyMode(mainUri, compilerOptions);
}
- compilerOptions.target = createFrontEndTarget(targetName,
- nullSafety: compilerOptions.nnbdMode == NnbdMode.Strong);
+ compilerOptions.target = createFrontEndTarget(
+ targetName,
+ trackWidgetCreation: options['track-widget-creation'],
+ nullSafety: compilerOptions.nnbdMode == NnbdMode.Strong,
+ );
if (compilerOptions.target == null) {
print('Failed to create front-end target $targetName.');
return badUsageExitCode;
@@ -272,6 +282,7 @@
bytecodeOptions: bytecodeOptions,
dropAST: dropAST && !splitOutputByPackages,
useProtobufTreeShaker: useProtobufTreeShaker,
+ useProtobufTreeShakerV2: useProtobufTreeShakerV2,
minimalKernel: minimalKernel,
treeShakeWriteOnlyFields: treeShakeWriteOnlyFields,
fromDillFile: fromDillFile);
@@ -352,6 +363,7 @@
BytecodeOptions bytecodeOptions,
bool dropAST: false,
bool useProtobufTreeShaker: false,
+ bool useProtobufTreeShakerV2: false,
bool minimalKernel: false,
bool treeShakeWriteOnlyFields: false,
String fromDillFile: null}) async {
@@ -385,6 +397,7 @@
useGlobalTypeFlowAnalysis,
enableAsserts,
useProtobufTreeShaker,
+ useProtobufTreeShakerV2,
errorDetector,
minimalKernel: minimalKernel,
treeShakeWriteOnlyFields: treeShakeWriteOnlyFields);
@@ -462,6 +475,7 @@
bool useGlobalTypeFlowAnalysis,
bool enableAsserts,
bool useProtobufTreeShaker,
+ bool useProtobufTreeShakerV2,
ErrorDetector errorDetector,
{bool minimalKernel: false,
bool treeShakeWriteOnlyFields: false}) async {
@@ -481,10 +495,15 @@
// before type flow analysis so TFA won't take unreachable code into account.
unreachable_code_elimination.transformComponent(component, enableAsserts);
+ if (useProtobufTreeShaker && useProtobufTreeShakerV2) {
+ throw 'Cannot use both versions of protobuf tree shaker';
+ }
+
if (useGlobalTypeFlowAnalysis) {
globalTypeFlow.transformComponent(target, coreTypes, component,
treeShakeSignatures: !minimalKernel,
- treeShakeWriteOnlyFields: treeShakeWriteOnlyFields);
+ treeShakeWriteOnlyFields: treeShakeWriteOnlyFields,
+ treeShakeProtobufs: useProtobufTreeShakerV2);
} else {
devirtualization.transformComponent(coreTypes, component);
no_dynamic_invocations_annotator.transformComponent(component);
@@ -513,6 +532,8 @@
// We don't know yet whether gen_snapshot will want to do obfuscation, but if
// it does it will need the obfuscation prohibitions.
obfuscationProhibitions.transformComponent(component, coreTypes);
+
+ deferred_loading.transformComponent(component);
}
/// Runs given [action] with [CompilerContext]. This is needed to
diff --git a/pkg/vm/lib/metadata/loading_units.dart b/pkg/vm/lib/metadata/loading_units.dart
new file mode 100644
index 0000000..410e0d5
--- /dev/null
+++ b/pkg/vm/lib/metadata/loading_units.dart
@@ -0,0 +1,85 @@
+// 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.
+
+library vm.metadata.loading_units;
+
+import 'package:kernel/ast.dart';
+
+class LoadingUnit {
+ final int id;
+ final int parentId;
+ final List<String> libraryUris;
+
+ LoadingUnit(this.id, this.parentId, this.libraryUris);
+
+ String toString() {
+ var sb = new StringBuffer();
+ sb.writeln("LoadingUnit(id=$id, parent=${parentId},");
+ for (var uri in libraryUris) {
+ sb.writeln(" $uri");
+ }
+ sb.write(")");
+ return sb.toString();
+ }
+}
+
+class LoadingUnitsMetadata {
+ final List<LoadingUnit> loadingUnits;
+
+ LoadingUnitsMetadata(this.loadingUnits);
+
+ String toString() {
+ var sb = new StringBuffer();
+ sb.writeln("LoadingUnitsMetadata(");
+ for (var unit in loadingUnits) {
+ sb.writeln(unit.toString());
+ }
+ sb.write(")");
+ return sb.toString();
+ }
+}
+
+/// Repository for [LoadingUnitsMetadata].
+class LoadingUnitsMetadataRepository
+ extends MetadataRepository<LoadingUnitsMetadata> {
+ static final repositoryTag = 'vm.loading-units.metadata';
+
+ @override
+ final String tag = repositoryTag;
+
+ @override
+ final Map<TreeNode, LoadingUnitsMetadata> mapping =
+ <TreeNode, LoadingUnitsMetadata>{};
+
+ @override
+ void writeToBinary(
+ LoadingUnitsMetadata metadata, Node node, BinarySink sink) {
+ sink.writeUInt30(metadata.loadingUnits.length);
+ for (LoadingUnit unit in metadata.loadingUnits) {
+ sink.writeUInt30(unit.id);
+ sink.writeUInt30(unit.parentId);
+ sink.writeUInt30(unit.libraryUris.length);
+ for (String uri in unit.libraryUris) {
+ sink.writeStringReference(uri);
+ }
+ }
+ }
+
+ @override
+ LoadingUnitsMetadata readFromBinary(Node node, BinarySource source) {
+ int length = source.readUInt();
+ var units = <LoadingUnit>[];
+ for (int i = 0; i < length; i++) {
+ int id = source.readUInt();
+ int parentId = source.readUInt();
+ var libraryUris = <String>[];
+ int length = source.readUInt();
+ for (int i = 0; i < length; i++) {
+ libraryUris.add(source.readStringReference());
+ }
+ units.add(new LoadingUnit(id, parentId, libraryUris));
+ }
+ return new LoadingUnitsMetadata(units);
+ }
+}
diff --git a/pkg/vm/lib/snapshot/commands/compare.dart b/pkg/vm/lib/snapshot/commands/compare.dart
deleted file mode 100644
index 9e1598d..0000000
--- a/pkg/vm/lib/snapshot/commands/compare.dart
+++ /dev/null
@@ -1,139 +0,0 @@
-// Copyright (c) 2018, 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 tool compares two JSON size reports produced by
-/// --print-instructions-sizes-to and reports which symbols increased in size
-/// and which symbols decreased in size.
-library vm.snapshot.compare;
-
-import 'dart:io';
-
-import 'package:args/command_runner.dart';
-import 'package:vm/snapshot/ascii_table.dart';
-
-import 'package:vm/snapshot/program_info.dart';
-import 'package:vm/snapshot/utils.dart';
-
-class CompareCommand extends Command<void> {
- @override
- final String name = 'compare';
-
- @override
- final String description = '''
-Compare two instruction size outputs and report which symbols changed in size.
-
-This tool compares two JSON size reports produced by
---print-instructions-sizes-to and reports which symbols
-changed in size.
-
-Use --narrow flag to limit column widths.''';
-
- @override
- String get invocation =>
- super.invocation.replaceAll('[arguments]', '<old.json> <new.json>');
-
- CompareCommand() {
- argParser
- ..addOption('column-width',
- help: 'Truncate column content to the given width'
- ' (${AsciiTable.unlimitedWidth} means do not truncate).',
- defaultsTo: AsciiTable.unlimitedWidth.toString())
- ..addOption('granularity',
- help: 'Choose the granularity of the output.',
- allowed: ['method', 'class', 'library', 'package'],
- defaultsTo: 'method')
- ..addFlag('collapse-anonymous-closures', help: '''
-Collapse all anonymous closures from the same scope into a single entry.
-When comparing size of AOT snapshots for two different versions of a
-program there is no reliable way to precisely establish which two anonymous
-closures are the same and should be compared in size - so
-comparison might produce a noisy output. This option reduces confusion
-by collapsing different anonymous closures within the same scope into a
-single entry. Note that when comparing the same application compiled
-with two different versions of an AOT compiler closures can be distinguished
-precisely based on their source position (which is included in their name).
-''');
- }
-
- @override
- Future<void> run() async {
- if (argResults.rest.length != 2) {
- usageException('Need to provide path to old.json and new.json reports.');
- }
-
- final columnWidth = argResults['column-width'];
- final maxWidth = int.tryParse(columnWidth);
- if (maxWidth == null) {
- usageException(
- 'Specified column width (${columnWidth}) is not an integer');
- }
-
- final oldJsonPath = _checkExists(argResults.rest[0]);
- final newJsonPath = _checkExists(argResults.rest[1]);
- printComparison(oldJsonPath, newJsonPath,
- maxWidth: maxWidth,
- granularity: _parseHistogramType(argResults['granularity']),
- collapseAnonymousClosures: argResults['collapse-anonymous-closures']);
- }
-
- HistogramType _parseHistogramType(String value) {
- switch (value) {
- case 'method':
- return HistogramType.bySymbol;
- case 'class':
- return HistogramType.byClass;
- case 'library':
- return HistogramType.byLibrary;
- case 'package':
- return HistogramType.byPackage;
- }
- }
-
- File _checkExists(String path) {
- final file = File(path);
- if (!file.existsSync()) {
- usageException('File $path does not exist!');
- }
- return file;
- }
-}
-
-void printComparison(File oldJson, File newJson,
- {int maxWidth: 0,
- bool collapseAnonymousClosures = false,
- HistogramType granularity = HistogramType.bySymbol}) async {
- final oldSizes = await loadProgramInfo(oldJson,
- collapseAnonymousClosures: collapseAnonymousClosures);
- final newSizes = await loadProgramInfo(newJson,
- collapseAnonymousClosures: collapseAnonymousClosures);
- final diff = computeDiff(oldSizes, newSizes);
-
- // Compute total sizes.
- final totalOld = oldSizes.totalSize;
- final totalNew = newSizes.totalSize;
- final totalDiff = diff.totalSize;
-
- // Compute histogram.
- final histogram = SizesHistogram.from(diff, granularity);
-
- // Now produce the report table.
- const numLargerSymbolsToReport = 30;
- const numSmallerSymbolsToReport = 10;
- printHistogram(histogram,
- sizeHeader: 'Diff (Bytes)',
- prefix: histogram.bySize
- .where((k) => histogram.buckets[k] > 0)
- .take(numLargerSymbolsToReport),
- suffix: histogram.bySize.reversed
- .where((k) => histogram.buckets[k] < 0)
- .take(numSmallerSymbolsToReport)
- .toList()
- .reversed,
- maxWidth: maxWidth);
-
- print('Comparing ${oldJson.path} (old) to ${newJson.path} (new)');
- print('Old : ${totalOld} bytes.');
- print('New : ${totalNew} bytes.');
- print('Change: ${totalDiff > 0 ? '+' : ''}${totalDiff} bytes.');
-}
diff --git a/pkg/vm/lib/snapshot/utils.dart b/pkg/vm/lib/snapshot/utils.dart
deleted file mode 100644
index e57a188..0000000
--- a/pkg/vm/lib/snapshot/utils.dart
+++ /dev/null
@@ -1,85 +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.
-library vm.snapshot.utils;
-
-import 'dart:io';
-import 'dart:convert';
-
-import 'package:vm/snapshot/ascii_table.dart';
-import 'package:vm/snapshot/program_info.dart';
-import 'package:vm/snapshot/instruction_sizes.dart' as instruction_sizes;
-
-Future<Object> loadJson(File input) async {
- return await input
- .openRead()
- .transform(utf8.decoder)
- .transform(json.decoder)
- .first;
-}
-
-Future<ProgramInfo> loadProgramInfo(File input,
- {bool collapseAnonymousClosures = false}) async {
- final json = await loadJson(input);
- return instruction_sizes.loadProgramInfo(json,
- collapseAnonymousClosures: collapseAnonymousClosures);
-}
-
-void printHistogram(SizesHistogram histogram,
- {Iterable<String> prefix = const [],
- Iterable<String> suffix = const [],
- String sizeHeader = 'Size (Bytes)',
- int maxWidth = 0}) {
- final table = AsciiTable(header: [
- for (var col in histogram.bucketing.nameComponents) Text.left(col),
- Text.right(sizeHeader),
- Text.right('Percent'),
- ], maxWidth: maxWidth);
-
- String formatPercent(int value, int total) {
- final p = value / total * 100.0;
- return p.toStringAsFixed(2) + "%";
- }
-
- if (prefix.isNotEmpty) {
- for (var key in prefix) {
- table.addRow([
- ...histogram.bucketing.namesFromBucket(key),
- histogram.buckets[key].toString(),
- formatPercent(histogram.buckets[key], histogram.totalSize),
- ]);
- }
- table.addSeparator(
- prefix.length < histogram.length ? Separator.Wave : Separator.Line);
- }
-
- final numRestRows = histogram.length - (suffix.length + prefix.length);
- if (numRestRows > 0) {
- final totalRestBytes = histogram.totalSize -
- [prefix, suffix]
- .expand((l) => l)
- .fold(0, (sum, key) => sum + histogram.buckets[key]);
- table.addTextSeparator(
- '$numRestRows more rows accounting for ${totalRestBytes}'
- ' (${formatPercent(totalRestBytes, histogram.totalSize)}) bytes');
- final avg = (totalRestBytes / numRestRows).round();
- table.addTextSeparator(
- 'on average that is ${avg} (${formatPercent(avg, histogram.totalSize)})'
- ' bytes per row');
- table.addSeparator(suffix.isNotEmpty ? Separator.Wave : Separator.Line);
- }
-
- if (suffix.isNotEmpty) {
- for (var key in suffix) {
- table.addRow([
- ...histogram.bucketing.namesFromBucket(key),
- histogram.buckets[key].toString(),
- formatPercent(histogram.buckets[key], histogram.totalSize),
- ]);
- }
- table.addSeparator(Separator.Line);
- }
-
- table.render();
- print('Total: ${histogram.totalSize} bytes');
-}
diff --git a/pkg/vm/lib/transformations/deferred_loading.dart b/pkg/vm/lib/transformations/deferred_loading.dart
new file mode 100644
index 0000000..f2b0318
--- /dev/null
+++ b/pkg/vm/lib/transformations/deferred_loading.dart
@@ -0,0 +1,118 @@
+// 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.
+
+library vm.transformations.deferred_loading;
+
+import 'package:kernel/ast.dart';
+import '../dominators.dart';
+import '../metadata/loading_units.dart';
+
+class _LoadingUnitBuilder {
+ final int id;
+ final _LibraryVertex root;
+ final List<Library> members = <Library>[];
+
+ _LoadingUnitBuilder(this.id, this.root);
+
+ int get parentId =>
+ root.dominator == null ? 0 : root.dominator.loadingUnit.id;
+
+ LoadingUnit asLoadingUnit() {
+ return new LoadingUnit(
+ id, parentId, members.map((l) => l.importUri.toString()).toList());
+ }
+
+ String toString() =>
+ "_LoadingUnitBuilder(id=$id, parent=${parentId}, size=${members.length})";
+}
+
+class _LibraryVertex extends Vertex<_LibraryVertex> {
+ final Library library;
+ bool isLoadingRoot = true;
+ _LoadingUnitBuilder loadingUnit;
+ _LibraryVertex(this.library);
+
+ String toString() => "_LibraryVertex(${library.importUri})";
+}
+
+List<LoadingUnit> computeLoadingUnits(Component component) {
+ // 1. Build the dominator tree for the library import graph.
+ final map = <Library, _LibraryVertex>{};
+ for (final lib in component.libraries) {
+ map[lib] = new _LibraryVertex(lib);
+ }
+ for (final vertex in map.values) {
+ for (final dep in vertex.library.dependencies) {
+ final target = map[dep.targetLibrary];
+ vertex.successors.add(target);
+ }
+ }
+ final root = map[component.mainMethod.parent as Library];
+
+ // Fake imports from root library to every core library so they end up in
+ // the same loading unit attributed to the user's root library.
+ for (final vertex in map.values) {
+ if (vertex.library.importUri.scheme == "dart") {
+ root.successors.add(vertex);
+ vertex.isLoadingRoot = false;
+ }
+ }
+
+ computeDominators(root);
+
+ // 2. Find loading unit roots.
+ for (var importer in map.values) {
+ if ((importer != root) && (importer.dominator == null)) {
+ continue; // Unreachable library.
+ }
+ for (var dep in importer.library.dependencies) {
+ if (dep.isDeferred) {
+ continue;
+ }
+ var importee = map[dep.targetLibrary];
+ if (importer.isDominatedBy(importee)) {
+ continue;
+ }
+ importee.isLoadingRoot = false;
+ }
+ }
+ assert(root.isLoadingRoot);
+
+ var loadingUnits = <_LoadingUnitBuilder>[];
+ for (var vertex in map.values) {
+ if (vertex.isLoadingRoot) {
+ var unit = new _LoadingUnitBuilder(loadingUnits.length + 1, vertex);
+ vertex.loadingUnit = unit;
+ unit.members.add(vertex.library);
+ loadingUnits.add(unit);
+ }
+ }
+
+ // 3. Attribute every library to the dominating loading unit.
+ for (var vertex in map.values) {
+ if (vertex.isLoadingRoot) {
+ continue; // Already processed.
+ }
+ var dom = vertex.dominator;
+ if (dom == null) {
+ continue; // Unreachable library.
+ }
+ while (dom.loadingUnit == null) {
+ dom = dom.dominator;
+ }
+ vertex.loadingUnit = dom.loadingUnit;
+ vertex.loadingUnit.members.add(vertex.library);
+ }
+
+ return loadingUnits.map((u) => u.asLoadingUnit()).toList();
+}
+
+Component transformComponent(Component component) {
+ final metadata = new LoadingUnitsMetadata(computeLoadingUnits(component));
+ final repo = new LoadingUnitsMetadataRepository();
+ component.addMetadataRepository(repo);
+ repo.mapping[component] = metadata;
+
+ return component;
+}
diff --git a/pkg/vm/lib/transformations/type_flow/analysis.dart b/pkg/vm/lib/transformations/type_flow/analysis.dart
index 974b04c..218f4cc 100644
--- a/pkg/vm/lib/transformations/type_flow/analysis.dart
+++ b/pkg/vm/lib/transformations/type_flow/analysis.dart
@@ -18,6 +18,7 @@
import 'calls.dart';
import 'native_code.dart';
+import 'protobuf_handler.dart' show ProtobufHandler;
import 'summary.dart';
import 'summary_collector.dart';
import 'types.dart';
@@ -1222,8 +1223,34 @@
enqueueInvocation(invocation);
}
+ bool invalidateProtobufFields() {
+ if (_typeFlowAnalysis.protobufHandler == null) {
+ return false;
+ }
+ final fields = _typeFlowAnalysis.protobufHandler.getInvalidatedFields();
+ if (fields.isEmpty) {
+ return false;
+ }
+ // Protobuf handler replaced contents of static field initializers.
+ for (var field in fields) {
+ assertx(field.isStatic);
+ // Reset summary in order to rebuild it.
+ _typeFlowAnalysis._summaries[field] = null;
+ // Invalidate (and enqueue) field initializer invocation.
+ final initializerInvocation = _typeFlowAnalysis._invocationsCache
+ .getInvocation(
+ DirectSelector(field, callKind: CallKind.FieldInitializer),
+ Args<Type>(const <Type>[]));
+ invalidateInvocation(initializerInvocation);
+ }
+ return true;
+ }
+
void process() {
- while (pending.isNotEmpty) {
+ for (;;) {
+ if (pending.isEmpty && !invalidateProtobufFields()) {
+ break;
+ }
assertx(callStack.isEmpty && processing.isEmpty);
Statistics.iterationsOverInvocationsWorkList++;
processInvocation(pending.first);
@@ -1320,6 +1347,7 @@
final TypeEnvironment environment;
final LibraryIndex libraryIndex;
final PragmaAnnotationParser annotationMatcher;
+ final ProtobufHandler protobufHandler;
NativeCodeOracle nativeCodeOracle;
_ClassHierarchyCache hierarchyCache;
SummaryCollector summaryCollector;
@@ -1343,14 +1371,22 @@
this._genericInterfacesInfo,
this.environment,
this.libraryIndex,
- {PragmaAnnotationParser matcher})
+ this.protobufHandler,
+ PragmaAnnotationParser matcher)
: annotationMatcher =
matcher ?? new ConstantPragmaAnnotationParser(coreTypes) {
nativeCodeOracle = new NativeCodeOracle(libraryIndex, annotationMatcher);
hierarchyCache = new _ClassHierarchyCache(this, hierarchy,
_genericInterfacesInfo, environment, target.flags.enableNullSafety);
- summaryCollector = new SummaryCollector(target, environment, hierarchy,
- this, hierarchyCache, nativeCodeOracle, hierarchyCache);
+ summaryCollector = new SummaryCollector(
+ target,
+ environment,
+ hierarchy,
+ this,
+ hierarchyCache,
+ nativeCodeOracle,
+ hierarchyCache,
+ protobufHandler);
_invocationsCache = new _InvocationsCache(this);
workList = new _WorkList(this);
diff --git a/pkg/vm/lib/transformations/type_flow/protobuf_handler.dart b/pkg/vm/lib/transformations/type_flow/protobuf_handler.dart
new file mode 100644
index 0000000..1e98f46
--- /dev/null
+++ b/pkg/vm/lib/transformations/type_flow/protobuf_handler.dart
@@ -0,0 +1,200 @@
+// 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:kernel/ast.dart';
+import 'package:kernel/clone.dart' show CloneVisitorNotMembers;
+import 'package:kernel/core_types.dart' show CoreTypes;
+import 'package:kernel/library_index.dart' show LibraryIndex;
+
+import 'utils.dart';
+
+/// Tracks used getters and setters of generated protobuf message
+/// classes and prunes metadata declarations.
+class ProtobufHandler {
+ static const String protobufLibraryUri = 'package:protobuf/protobuf.dart';
+ static const String metadataFieldName = '_i';
+
+ // All of those methods have the dart field name as second positional
+ // parameter.
+ // Method names are defined in:
+ // https://github.com/dart-lang/protobuf/blob/master/protobuf/lib/src/protobuf/builder_info.dart
+ // The code is generated by:
+ // https://github.com/dart-lang/protobuf/blob/master/protoc_plugin/lib/protobuf_field.dart.
+ static const Set<String> fieldAddingMethods = const <String>{
+ 'a',
+ 'aOM',
+ 'aOS',
+ 'aQM',
+ 'pPS',
+ 'aQS',
+ 'aInt64',
+ 'aOB',
+ 'e',
+ 'p',
+ 'pc',
+ 'm',
+ };
+
+ final CoreTypes coreTypes;
+ final Class _generatedMessageClass;
+ final Class _tagNumberClass;
+ final Field _tagNumberField;
+ final Class _builderInfoClass;
+ final Procedure _builderInfoAddMethod;
+ final _messageClasses = <Class, _MessageClass>{};
+ final _invalidatedClasses = <_MessageClass>{};
+
+ /// Creates [ProtobufHandler] instance for [component].
+ /// Returns null if protobuf library is not used.
+ static ProtobufHandler forComponent(
+ Component component, CoreTypes coreTypes) {
+ final libraryIndex = LibraryIndex(component, [protobufLibraryUri]);
+ if (!libraryIndex.containsLibrary(protobufLibraryUri)) {
+ return null;
+ }
+ return ProtobufHandler._internal(libraryIndex, coreTypes);
+ }
+
+ ProtobufHandler._internal(LibraryIndex libraryIndex, this.coreTypes)
+ : _generatedMessageClass =
+ libraryIndex.getClass(protobufLibraryUri, 'GeneratedMessage'),
+ _tagNumberClass =
+ libraryIndex.getClass(protobufLibraryUri, 'TagNumber'),
+ _tagNumberField = libraryIndex.getMember(
+ protobufLibraryUri, 'TagNumber', 'tagNumber'),
+ _builderInfoClass =
+ libraryIndex.getClass(protobufLibraryUri, 'BuilderInfo'),
+ _builderInfoAddMethod =
+ libraryIndex.getMember(protobufLibraryUri, 'BuilderInfo', 'add');
+
+ /// This method is called from summary collector when analysis discovered
+ /// that [member] is called and needs to construct a summary for its body.
+ ///
+ /// At this point protobuf handler can
+ /// - modify static field initializer of metadata field;
+ /// - track used members of the generated message classes.
+ void beforeSummaryCreation(Member member) {
+ // Only interested in members of subclasses of GeneratedMessage class.
+ final cls = member.enclosingClass;
+ if (cls == null || cls.superclass != _generatedMessageClass) {
+ return;
+ }
+ final messageClass = (_messageClasses[cls] ??= _MessageClass());
+ if (member is Field && member.name.name == metadataFieldName) {
+ // Update contents of static field initializer of metadata field (_i).
+ // according to the used tag numbers.
+ assertx(member.isStatic);
+ if (messageClass._metadataField == null) {
+ messageClass._metadataField = member;
+ ++Statistics.protobufMessagesUsed;
+ } else {
+ assertx(messageClass._metadataField == member);
+ }
+ _updateMetadataField(messageClass);
+ return;
+ }
+ if (member is Procedure && !member.isStatic) {
+ // Track usage of accessors of protobuf fields: extract tag number
+ // from annotations and add tag number to the set of used tags.
+ // This may also add message class to the set of invalidated classes,
+ // so their metadata field initializers will be revisited.
+ for (var annotation in member.annotations) {
+ final constant = (annotation as ConstantExpression).constant;
+ if (constant is InstanceConstant &&
+ constant.classReference == _tagNumberClass.reference) {
+ if (messageClass._usedTags.add(
+ (constant.fieldValues[_tagNumberField.reference] as IntConstant)
+ .value)) {
+ _invalidatedClasses.add(messageClass);
+ }
+ }
+ }
+ }
+ }
+
+ List<Field> getInvalidatedFields() {
+ final fields = <Field>[];
+ for (var cls in _invalidatedClasses) {
+ if (cls._metadataField != null) {
+ fields.add(cls._metadataField);
+ }
+ }
+ _invalidatedClasses.clear();
+ return fields;
+ }
+
+ /// Updates initializer of metadata field of [cls] message class.
+ void _updateMetadataField(_MessageClass cls) {
+ ++Statistics.protobufMetadataInitializersUpdated;
+ Statistics.protobufMetadataFieldsPruned -= cls.numberOfFieldsPruned;
+
+ final field = cls._metadataField;
+ if (cls._originalInitializer == null) {
+ cls._originalInitializer = field.initializer;
+ }
+ final cloner = CloneVisitorNotMembers();
+ field.initializer = cloner.clone(cls._originalInitializer)..parent = field;
+ final transformer = _MetadataTransformer(this, cls);
+ field.initializer.accept(transformer);
+ _invalidatedClasses.remove(cls);
+
+ cls.numberOfFieldsPruned = transformer.numberOfFieldsPruned;
+ Statistics.protobufMetadataFieldsPruned += cls.numberOfFieldsPruned;
+ }
+
+ bool _isUnusedMetadata(_MessageClass cls, MethodInvocation node) {
+ if (node.interfaceTarget != null &&
+ node.interfaceTarget.enclosingClass == _builderInfoClass &&
+ fieldAddingMethods.contains(node.name.name)) {
+ final tagNumber = (node.arguments.positional[0] as IntLiteral).value;
+ return !cls._usedTags.contains(tagNumber);
+ }
+ return false;
+ }
+}
+
+class _MessageClass {
+ Field _metadataField;
+ Expression _originalInitializer;
+ final _usedTags = <int>{};
+ int numberOfFieldsPruned = 0;
+}
+
+class _MetadataTransformer extends Transformer {
+ final ProtobufHandler ph;
+ final _MessageClass cls;
+ int numberOfFieldsPruned = 0;
+
+ _MetadataTransformer(this.ph, this.cls);
+
+ @override
+ TreeNode visitMethodInvocation(MethodInvocation node) {
+ if (!ph._isUnusedMetadata(cls, node)) {
+ super.visitMethodInvocation(node);
+ return node;
+ }
+ // Replace the field metadata method with a dummy call to
+ // `BuilderInfo.add`. This is to preserve the index calculations when
+ // removing a field.
+ // Change the tag-number to 0. Otherwise the decoder will get confused.
+ ++numberOfFieldsPruned;
+ return MethodInvocation(
+ node.receiver,
+ ph._builderInfoAddMethod.name,
+ Arguments(
+ <Expression>[
+ IntLiteral(0), // tagNumber
+ NullLiteral(), // name
+ NullLiteral(), // fieldType
+ NullLiteral(), // defaultOrMaker
+ NullLiteral(), // subBuilder
+ NullLiteral(), // valueOf
+ NullLiteral(), // enumValues
+ ],
+ types: <DartType>[ph.coreTypes.nullType],
+ ),
+ ph._builderInfoAddMethod)
+ ..fileOffset = node.fileOffset;
+ }
+}
diff --git a/pkg/vm/lib/transformations/type_flow/summary_collector.dart b/pkg/vm/lib/transformations/type_flow/summary_collector.dart
index 8b6f850..43c9e3e 100644
--- a/pkg/vm/lib/transformations/type_flow/summary_collector.dart
+++ b/pkg/vm/lib/transformations/type_flow/summary_collector.dart
@@ -18,6 +18,7 @@
import 'calls.dart';
import 'native_code.dart';
+import 'protobuf_handler.dart' show ProtobufHandler;
import 'summary.dart';
import 'types.dart';
import 'utils.dart';
@@ -521,6 +522,7 @@
final TypesBuilder _typesBuilder;
final NativeCodeOracle _nativeCodeOracle;
final GenericInterfacesInfo _genericInterfacesInfo;
+ final ProtobufHandler _protobufHandler;
final Map<TreeNode, Call> callSites = <TreeNode, Call>{};
final Map<AsExpression, TypeCheck> explicitCasts =
@@ -580,7 +582,8 @@
this._entryPointsListener,
this._typesBuilder,
this._nativeCodeOracle,
- this._genericInterfacesInfo) {
+ this._genericInterfacesInfo,
+ this._protobufHandler) {
assertx(_genericInterfacesInfo != null);
constantAllocationCollector = new ConstantAllocationCollector(this);
_nullMethodsAndGetters.addAll(getSelectors(
@@ -598,6 +601,8 @@
assertx(!member.isAbstract);
assertx(!(member is Procedure && member.isRedirectingFactoryConstructor));
+ _protobufHandler?.beforeSummaryCreation(member);
+
_staticTypeContext = new StaticTypeContext(member, _environment);
_variablesInfo = new _VariablesInfoCollector(member);
_variableValues = new List<TypeExpr>(_variablesInfo.numVariables);
diff --git a/pkg/vm/lib/transformations/type_flow/transformer.dart b/pkg/vm/lib/transformations/type_flow/transformer.dart
index f592d17..ce7f06e 100644
--- a/pkg/vm/lib/transformations/type_flow/transformer.dart
+++ b/pkg/vm/lib/transformations/type_flow/transformer.dart
@@ -17,6 +17,7 @@
import 'analysis.dart';
import 'calls.dart';
+import 'protobuf_handler.dart' show ProtobufHandler;
import 'summary.dart';
import 'table_selector.dart';
import 'types.dart';
@@ -40,20 +41,31 @@
Target target, CoreTypes coreTypes, Component component,
{PragmaAnnotationParser matcher,
bool treeShakeSignatures: true,
- bool treeShakeWriteOnlyFields: true}) {
+ bool treeShakeWriteOnlyFields: true,
+ bool treeShakeProtobufs: false}) {
void ignoreAmbiguousSupertypes(Class cls, Supertype a, Supertype b) {}
final hierarchy = new ClassHierarchy(component, coreTypes,
onAmbiguousSupertypes: ignoreAmbiguousSupertypes);
final types = new TypeEnvironment(coreTypes, hierarchy);
final libraryIndex = new LibraryIndex.all(component);
final genericInterfacesInfo = new GenericInterfacesInfoImpl(hierarchy);
+ final protobufHandler = treeShakeProtobufs
+ ? ProtobufHandler.forComponent(component, coreTypes)
+ : null;
Statistics.reset();
final analysisStopWatch = new Stopwatch()..start();
- final typeFlowAnalysis = new TypeFlowAnalysis(target, component, coreTypes,
- hierarchy, genericInterfacesInfo, types, libraryIndex,
- matcher: matcher);
+ final typeFlowAnalysis = new TypeFlowAnalysis(
+ target,
+ component,
+ coreTypes,
+ hierarchy,
+ genericInterfacesInfo,
+ types,
+ libraryIndex,
+ protobufHandler,
+ matcher);
Procedure main = component.mainMethod;
diff --git a/pkg/vm/lib/transformations/type_flow/utils.dart b/pkg/vm/lib/transformations/type_flow/utils.dart
index 30ff278..0d68b35 100644
--- a/pkg/vm/lib/transformations/type_flow/utils.dart
+++ b/pkg/vm/lib/transformations/type_flow/utils.dart
@@ -155,6 +155,9 @@
static int constructorBodiesDropped = 0;
static int callsDropped = 0;
static int throwExpressionsPruned = 0;
+ static int protobufMessagesUsed = 0;
+ static int protobufMetadataInitializersUpdated = 0;
+ static int protobufMetadataFieldsPruned = 0;
/// Resets statistic counters.
static void reset() {
@@ -181,6 +184,9 @@
constructorBodiesDropped = 0;
callsDropped = 0;
throwExpressionsPruned = 0;
+ protobufMessagesUsed = 0;
+ protobufMetadataInitializersUpdated = 0;
+ protobufMetadataFieldsPruned = 0;
}
static void print(String caption) {
@@ -208,6 +214,9 @@
${constructorBodiesDropped} constructor bodies dropped
${callsDropped} calls dropped
${throwExpressionsPruned} throw expressions pruned
+ ${protobufMessagesUsed} protobuf messages used
+ ${protobufMetadataInitializersUpdated} protobuf metadata initializers updated
+ ${protobufMetadataFieldsPruned} protobuf metadata fields pruned
""");
}
}
diff --git a/pkg/vm/lib/v8_snapshot_profile.dart b/pkg/vm/lib/v8_snapshot_profile.dart
deleted file mode 100644
index 847836d..0000000
--- a/pkg/vm/lib/v8_snapshot_profile.dart
+++ /dev/null
@@ -1,299 +0,0 @@
-// Copyright (c) 2018, 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:expect/expect.dart";
-import "package:dart2js_info/src/graph.dart";
-
-class _NodeInfo {
- int type;
- int name;
- int id;
- int selfSize;
- int edgeCount;
- _NodeInfo(
- this.type,
- this.name,
- this.id,
- this.selfSize,
- this.edgeCount,
- );
-}
-
-const List<String> _kRequiredNodeFields = [
- "type",
- "name",
- "id",
- "self_size",
- "edge_count",
-];
-
-class _EdgeInfo {
- int type;
- int nameOrIndex;
- int nodeOffset;
- _EdgeInfo(
- this.type,
- this.nameOrIndex,
- this.nodeOffset,
- );
-}
-
-const List<String> _kRequiredEdgeFields = [
- "type",
- "name_or_index",
- "to_node",
-];
-
-class NodeInfo {
- final String type;
- final String name;
- final int id;
- final int selfSize;
- NodeInfo(
- this.type,
- this.name,
- this.id,
- this.selfSize,
- );
-}
-
-class EdgeInfo {
- final int target;
- final String type;
-
- // Either a string for property names or an int for array/context elements.
- final dynamic nameOrIndex;
-
- EdgeInfo(this.target, this.type, this.nameOrIndex);
-}
-
-class V8SnapshotProfile extends Graph<int> {
- // Indexed by node offset.
- final Map<int, _NodeInfo> _nodes = {};
-
- // Indexed by start node offset.
- final Map<int, List<_EdgeInfo>> _toEdges = {};
- final Map<int, List<_EdgeInfo>> _fromEdges = {};
-
- List<String> _nodeFields = [];
- List<String> _edgeFields = [];
-
- List<String> _nodeTypes = [];
- List<String> _edgeTypes = [];
-
- List<String> _strings = [];
-
- // Only used to ensure IDs are unique.
- Set<int> _ids = Set<int>();
-
- V8SnapshotProfile.fromJson(Map top) {
- final Map snapshot = top["snapshot"];
- _parseMetadata(snapshot["meta"]);
-
- _parseStrings(top["strings"]);
- Expect.equals(snapshot["node_count"], _parseNodes(top["nodes"]));
- Expect.equals(snapshot["edge_count"], _parseEdges(top["edges"]));
-
- _verifyRoot();
-
- _calculateFromEdges();
- }
-
- void _verifyRoot() {
- // HeapSnapshotWorker.HeapSnapshot.calculateDistances (from HeapSnapshot.js)
- // assumes that the root does not have more than one edge to any other node
- // (most likely an oversight).
- final Set<int> roots = <int>{};
- for (final edge in _toEdges[root]) {
- final int to = edge.nodeOffset;
- Expect.isTrue(!roots.contains(to), "multiple root edges to node ${to}");
- roots.add(to);
- }
-
- // Check that all nodes are reachable from the root (offset 0).
- final Set<int> enqueued = {root};
- final dfs = <int>[root];
- while (!dfs.isEmpty) {
- final next = dfs.removeLast();
- for (final edge in _toEdges[next]) {
- if (!enqueued.contains(edge.nodeOffset)) {
- enqueued.add(edge.nodeOffset);
- dfs.add(edge.nodeOffset);
- }
- }
- }
- Expect.equals(enqueued.length, nodeCount);
- }
-
- void _parseMetadata(Map meta) {
- final List nodeFields = meta["node_fields"];
- nodeFields.forEach(_nodeFields.add);
- for (final field in _kRequiredNodeFields) {
- Expect.isTrue(nodeFields.contains(field), "missing node field ${field}");
- }
-
- final List edgeFields = meta["edge_fields"];
- edgeFields.forEach(_edgeFields.add);
- for (final field in _kRequiredEdgeFields) {
- Expect.isTrue(edgeFields.contains(field), "missing edge field ${field}");
- }
-
- // First entry of "node_types" is an array with the actual node types. IDK
- // what the other entries are for.
- List nodeTypes = meta["node_types"];
- nodeTypes = nodeTypes[0];
- nodeTypes.forEach(_nodeTypes.add);
-
- // Same for edges.
- List edgeTypes = meta["edge_types"];
- edgeTypes = edgeTypes[0];
- edgeTypes.forEach(_edgeTypes.add);
- }
-
- int _parseNodes(List nodes) {
- final int typeIndex = _nodeFields.indexOf("type");
- final int nameIndex = _nodeFields.indexOf("name");
- final int idIndex = _nodeFields.indexOf("id");
- final int selfSizeIndex = _nodeFields.indexOf("self_size");
- final int edgeCountIndex = _nodeFields.indexOf("edge_count");
-
- int offset = 0;
- for (; offset < nodes.length; offset += _nodeFields.length) {
- final int type = nodes[offset + typeIndex];
- Expect.isTrue(0 <= type && type < _nodeTypes.length,
- "node type ${type} outside range [0, ${_nodeTypes.length})");
-
- final int name = nodes[offset + nameIndex];
- Expect.isTrue(0 <= name && name < _strings.length,
- "node name ${name} outside range [0, ${_strings.length})");
-
- final int id = nodes[offset + idIndex];
- Expect.isTrue(id >= 0, "negative node ID ${id}");
- Expect.isFalse(_ids.contains(id), "node ID ${id} already added");
- _ids.add(id);
-
- final int selfSize = nodes[offset + selfSizeIndex];
- Expect.isTrue(selfSize >= 0, "negative node selfSize ${selfSize}");
-
- final int edgeCount = nodes[offset + edgeCountIndex];
- Expect.isTrue(edgeCount >= 0, "negative node edgeCount ${edgeCount}");
-
- _nodes[offset] = _NodeInfo(type, name, id, selfSize, edgeCount);
- }
-
- Expect.equals(offset, nodes.length);
- return offset ~/ _nodeFields.length;
- }
-
- int _parseEdges(List edges) {
- final int typeIndex = _edgeFields.indexOf("type");
- final int nameOrIndexIndex = _edgeFields.indexOf("name_or_index");
- final int toNodeIndex = _edgeFields.indexOf("to_node");
-
- int edgeOffset = 0;
- for (int nodeOffset = 0;
- nodeOffset < _nodes.length * _nodeFields.length;
- nodeOffset += _nodeFields.length) {
- final int edgeCount = _nodes[nodeOffset].edgeCount;
- final List<_EdgeInfo> nodeEdges = List<_EdgeInfo>(edgeCount);
- for (int i = 0; i < edgeCount; ++i, edgeOffset += _edgeFields.length) {
- final int type = edges[edgeOffset + typeIndex];
- Expect.isTrue(0 <= type && type < _edgeTypes.length,
- "edge type ${type} outside range [0, ${_edgeTypes.length}");
-
- final int nameOrIndex = edges[edgeOffset + nameOrIndexIndex];
- if (_edgeTypes[type] == "property") {
- Expect.isTrue(0 <= nameOrIndex && nameOrIndex < _strings.length,
- "edge name ${nameOrIndex} outside range [0, ${_strings.length}");
- } else if (_edgeTypes[type] == "element" ||
- _edgeTypes[type] == "context") {
- Expect.isTrue(nameOrIndex >= 0, "negative edge index ${nameOrIndex}");
- }
-
- final int toNode = edges[edgeOffset + toNodeIndex];
- checkNode(toNode);
- nodeEdges[i] = _EdgeInfo(type, nameOrIndex, toNode);
- }
- _toEdges[nodeOffset] = nodeEdges;
- }
-
- Expect.equals(edgeOffset, edges.length);
- return edgeOffset ~/ _edgeFields.length;
- }
-
- void checkNode(int offset) {
- Expect.isTrue(offset >= 0, "negative offset ${offset}");
- Expect.isTrue(offset % _nodeFields.length == 0,
- "offset ${offset} not a multiple of ${_nodeFields.length}");
- Expect.isTrue(
- offset ~/ _nodeFields.length < _nodes.length,
- "offset ${offset} divided by ${_nodeFields.length} is greater than or "
- "equal to node count ${_nodes.length}");
- }
-
- void _calculateFromEdges() {
- for (final MapEntry<int, List<_EdgeInfo>> entry in _toEdges.entries) {
- final int fromNode = entry.key;
- for (final _EdgeInfo edge in entry.value) {
- final List<_EdgeInfo> backEdges =
- _fromEdges.putIfAbsent(edge.nodeOffset, () => <_EdgeInfo>[]);
- backEdges.add(_EdgeInfo(edge.type, edge.nameOrIndex, fromNode));
- }
- }
- }
-
- void _parseStrings(List strings) => strings.forEach(_strings.add);
-
- int get accountedBytes {
- int sum = 0;
- for (final _NodeInfo info in _nodes.values) {
- sum += info.selfSize;
- }
- return sum;
- }
-
- int get unknownCount {
- final int unknownType = _nodeTypes.indexOf("Unknown");
- Expect.isTrue(
- unknownType >= 0, 'negative type index for "Unknown": ${unknownType}');
-
- int count = 0;
- for (final MapEntry<int, _NodeInfo> entry in _nodes.entries) {
- if (entry.value.type == unknownType) {
- ++count;
- }
- }
- return count;
- }
-
- bool get isEmpty => _nodes.isEmpty;
- int get nodeCount => _nodes.length;
-
- Iterable<int> get nodes => _nodes.keys;
-
- Iterable<int> targetsOf(int source) {
- return _toEdges[source].map((_EdgeInfo i) => i.nodeOffset);
- }
-
- Iterable<int> sourcesOf(int source) {
- return _fromEdges[source].map((_EdgeInfo i) => i.nodeOffset);
- }
-
- int get root => 0;
-
- NodeInfo operator [](int node) {
- _NodeInfo info = _nodes[node];
- final type = info.type != null ? _nodeTypes[info.type] : null;
- final name = info.name != null ? _strings[info.name] : null;
- return NodeInfo(type, name, info.id, info.selfSize);
- }
-
- Iterable<EdgeInfo> targets(int node) sync* {
- for (final _EdgeInfo info in _toEdges[node]) {
- final String type = _edgeTypes[info.type];
- yield EdgeInfo(info.nodeOffset, type,
- type == "property" ? _strings[info.nameOrIndex] : info.nameOrIndex);
- }
- }
-}
diff --git a/pkg/vm/pubspec.yaml b/pkg/vm/pubspec.yaml
index 27d6379..ba7b719 100644
--- a/pkg/vm/pubspec.yaml
+++ b/pkg/vm/pubspec.yaml
@@ -1,7 +1,7 @@
-# This package is not intended to be published.
name: vm
-# version: do-not-upload
description: VM specific Dart code and helper scripts
+# This package is not intended for consumption on pub.dev. DO NOT publish.
+publish_to: none
environment:
sdk: "^2.7.0"
diff --git a/pkg/vm/test/dominators_test.dart b/pkg/vm/test/dominators_test.dart
new file mode 100644
index 0000000..f9e6ee6
--- /dev/null
+++ b/pkg/vm/test/dominators_test.dart
@@ -0,0 +1,75 @@
+// 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:expect/expect.dart';
+import 'package:vm/dominators.dart';
+
+class StringVertex extends Vertex<StringVertex> {
+ final String name;
+ StringVertex(this.name);
+ String toString() => name;
+}
+
+main() {
+ // small example from [Lenguaer & Tarjan 1979]
+ var R = new StringVertex("R");
+ var A = new StringVertex("A");
+ var B = new StringVertex("B");
+ var C = new StringVertex("C");
+ var D = new StringVertex("D");
+ var E = new StringVertex("E");
+ var F = new StringVertex("F");
+ var G = new StringVertex("G");
+ var H = new StringVertex("H");
+ var I = new StringVertex("I");
+ var J = new StringVertex("J");
+ var K = new StringVertex("K");
+ var L = new StringVertex("L");
+
+ R.successors.add(A);
+ R.successors.add(B);
+ R.successors.add(C);
+ A.successors.add(D);
+ B.successors.add(A);
+ B.successors.add(D);
+ B.successors.add(E);
+ C.successors.add(F);
+ C.successors.add(G);
+ D.successors.add(L);
+ E.successors.add(H);
+ F.successors.add(I);
+ G.successors.add(I);
+ G.successors.add(J);
+ H.successors.add(E);
+ H.successors.add(K);
+ I.successors.add(K);
+ J.successors.add(I);
+ K.successors.add(I);
+ K.successors.add(R);
+ L.successors.add(H);
+
+ computeDominators(R);
+
+ for (var x in [R, A, B, C, D, E, F, G, H, I, J, K, L]) {
+ print("dom($x) = ${x.dominator}");
+ }
+
+ Expect.equals(null, R.dominator);
+
+ Expect.equals(R, I.dominator);
+ Expect.equals(R, K.dominator);
+ Expect.equals(R, C.dominator);
+ Expect.equals(R, H.dominator);
+ Expect.equals(R, E.dominator);
+ Expect.equals(R, A.dominator);
+ Expect.equals(R, D.dominator);
+ Expect.equals(R, B.dominator);
+
+ Expect.equals(C, F.dominator);
+ Expect.equals(C, G.dominator);
+
+ Expect.equals(G, J.dominator);
+
+ Expect.equals(D, L.dominator);
+}
diff --git a/pkg/vm/test/modular_kernel_plus_aot_test.dart b/pkg/vm/test/modular_kernel_plus_aot_test.dart
index 594e11e..6d0dc45 100644
--- a/pkg/vm/test/modular_kernel_plus_aot_test.dart
+++ b/pkg/vm/test/modular_kernel_plus_aot_test.dart
@@ -57,12 +57,14 @@
const useGlobalTypeFlowAnalysis = true;
const enableAsserts = false;
const useProtobufTreeShaker = false;
+ const useProtobufTreeShakerV2 = false;
await runGlobalTransformations(
vmTarget,
component,
useGlobalTypeFlowAnalysis,
enableAsserts,
useProtobufTreeShaker,
+ useProtobufTreeShakerV2,
ErrorDetector());
// Verify after running global transformations.
diff --git a/pkg/vm/test/snapshot/instruction_sizes_test.dart b/pkg/vm/test/snapshot/instruction_sizes_test.dart
deleted file mode 100644
index a6ce2eb..0000000
--- a/pkg/vm/test/snapshot/instruction_sizes_test.dart
+++ /dev/null
@@ -1,493 +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:path/path.dart' as path;
-import 'package:test/test.dart';
-
-import 'package:vm/snapshot/instruction_sizes.dart' as instruction_sizes;
-import 'package:vm/snapshot/program_info.dart';
-import 'package:vm/snapshot/utils.dart';
-
-final dart2native = () {
- final sdkBin = path.dirname(Platform.executable);
- final dart2native =
- path.join(sdkBin, Platform.isWindows ? 'dart2native.bat' : 'dart2native');
-
- if (!File(dart2native).existsSync()) {
- throw 'Failed to locate dart2native in the SDK';
- }
-
- return path.canonicalize(dart2native);
-}();
-
-void main() async {
- if (!Platform.executable.contains('dart-sdk')) {
- // If we are not running from the prebuilt SDK then this test does nothing.
- return;
- }
-
- group('instruction-sizes', () {
- final testSource = {
- 'input.dart': """
-@pragma('vm:never-inline')
-dynamic makeSomeClosures() {
- return [
- () => true,
- () => false,
- () => 11,
- ];
-}
-
-class A {
- @pragma('vm:never-inline')
- dynamic tornOff() {
- return true;
- }
-}
-
-class B {
- @pragma('vm:never-inline')
- dynamic tornOff() {
- return false;
- }
-}
-
-class C {
- static dynamic tornOff() async {
- return true;
- }
-}
-
-@pragma('vm:never-inline')
-Function tearOff(dynamic o) {
- return o.tornOff;
-}
-
-void main(List<String> args) {
- for (var cl in makeSomeClosures()) {
- print(cl());
- }
- print(tearOff(args.isEmpty ? A() : B()));
- print(C.tornOff);
-}
-"""
- };
-
- // Almost exactly the same source as above, but with few modifications
- // marked with a 'modified' comment.
- final testSourceModified = {
- 'input.dart': """
-@pragma('vm:never-inline')
-dynamic makeSomeClosures() {
- return [
- () => true,
- () => false,
- () => 11,
- () => {}, // modified
- ];
-}
-
-class A {
- @pragma('vm:never-inline')
- dynamic tornOff() {
- for (var cl in makeSomeClosures()) { // modified
- print(cl()); // modified
- } // modified
- return true;
- }
-}
-
-class B {
- @pragma('vm:never-inline')
- dynamic tornOff() {
- return false;
- }
-}
-
-class C {
- static dynamic tornOff() async {
- return true;
- }
-}
-
-@pragma('vm:never-inline')
-Function tearOff(dynamic o) {
- return o.tornOff;
-}
-
-void main(List<String> args) {
- // modified
- print(tearOff(args.isEmpty ? A() : B()));
- print(C.tornOff);
-}
-"""
- };
-
- final testSourceModified2 = {
- 'input.dart': """
-@pragma('vm:never-inline')
-dynamic makeSomeClosures() {
- return [
- () => 0,
- ];
-}
-
-class A {
- @pragma('vm:never-inline')
- dynamic tornOff() {
- return true;
- }
-}
-
-class B {
- @pragma('vm:never-inline')
- dynamic tornOff() {
- return false;
- }
-}
-
-class C {
- static dynamic tornOff() async {
- return true;
- }
-}
-
-@pragma('vm:never-inline')
-Function tearOff(dynamic o) {
- return o.tornOff;
-}
-
-void main(List<String> args) {
- for (var cl in makeSomeClosures()) {
- print(cl());
- }
- print(tearOff(args.isEmpty ? A() : B()));
- print(C.tornOff);
-}
-"""
- };
-
- test('basic-parsing', () async {
- await withSymbolSizes('basic-parsing', testSource, (sizesJson) async {
- final symbols =
- instruction_sizes.fromJson(await loadJson(File(sizesJson)));
- expect(symbols, isNotNull,
- reason: 'Sizes file was successfully parsed');
- expect(symbols.length, greaterThan(0),
- reason: 'Sizes file is non-empty');
-
- // Check for duplicated symbols (using both raw and scrubbed names).
- // Maps below contain mappings library-uri -> class-name -> names.
- final symbolRawNames = <String, Map<String, Set<String>>>{};
- final symbolScrubbedNames = <String, Map<String, Set<String>>>{};
-
- Set<String> getSetOfNames(Map<String, Map<String, Set<String>>> map,
- String libraryUri, String className) {
- return map
- .putIfAbsent(libraryUri ?? '', () => {})
- .putIfAbsent(className ?? '', () => {});
- }
-
- for (var sym in symbols) {
- expect(
- getSetOfNames(symbolRawNames, sym.libraryUri, sym.className)
- .add(sym.name.raw),
- isTrue,
- reason:
- 'All symbols should have unique names (within libraries): ${sym.name.raw}');
- expect(
- getSetOfNames(symbolScrubbedNames, sym.libraryUri, sym.className)
- .add(sym.name.scrubbed),
- isTrue,
- reason: 'Scrubbing the name should not make it non-unique');
- }
-
- // Check for expected names which should appear in the output.
- final inputDartSymbolNames =
- symbolScrubbedNames['package:input/input.dart'];
- expect(inputDartSymbolNames, isNotNull,
- reason: 'Symbols from input.dart are included into sizes output');
-
- expect(inputDartSymbolNames[''], isNotNull,
- reason: 'Should include top-level members from input.dart');
- expect(inputDartSymbolNames[''], contains('makeSomeClosures'));
- final closures = inputDartSymbolNames[''].where(
- (name) => name.startsWith('makeSomeClosures.<anonymous closure'));
- expect(closures.length, 3,
- reason: 'There are three closures inside makeSomeClosure');
-
- expect(inputDartSymbolNames['A'], isNotNull,
- reason: 'Should include class A members from input.dart');
- expect(inputDartSymbolNames['A'], contains('tornOff'));
- expect(inputDartSymbolNames['A'], contains('[tear-off] tornOff'));
- expect(inputDartSymbolNames['A'],
- contains('[tear-off-extractor] get:tornOff'));
-
- expect(inputDartSymbolNames['B'], isNotNull,
- reason: 'Should include class B members from input.dart');
- expect(inputDartSymbolNames['B'], contains('tornOff'));
- expect(inputDartSymbolNames['B'], contains('[tear-off] tornOff'));
- expect(inputDartSymbolNames['B'],
- contains('[tear-off-extractor] get:tornOff'));
-
- // Presence of async modifier should not cause tear-off name to end
- // with {body}.
- expect(inputDartSymbolNames['C'], contains('[tear-off] tornOff'));
-
- // Check that output does not contain '[unknown stub]'
- expect(symbolRawNames[''][''], isNot(contains('[unknown stub]')),
- reason: 'All stubs must be named');
- });
- });
-
- test('program-info', () async {
- await withSymbolSizes('program-info', testSource, (sizesJson) async {
- final info = await loadProgramInfo(File(sizesJson));
- expect(info.root.children, contains('dart:core'));
- expect(info.root.children, contains('dart:typed_data'));
- expect(info.root.children, contains('package:input'));
-
- final inputLib = info.root.children['package:input']
- .children['package:input/input.dart'];
- expect(inputLib, isNotNull);
- expect(inputLib.children, contains('')); // Top-level class.
- expect(inputLib.children, contains('A'));
- expect(inputLib.children, contains('B'));
- expect(inputLib.children, contains('C'));
-
- final topLevel = inputLib.children[''];
- expect(topLevel.children, contains('makeSomeClosures'));
- expect(
- topLevel.children['makeSomeClosures'].children.length, equals(3));
-
- for (var name in [
- '[tear-off] tornOff',
- 'tornOff',
- 'Allocate A',
- '[tear-off-extractor] get:tornOff'
- ]) {
- expect(inputLib.children['A'].children, contains(name));
- expect(inputLib.children['A'].children[name].children, isEmpty);
- }
-
- for (var name in [
- '[tear-off] tornOff',
- 'tornOff',
- 'Allocate B',
- '[tear-off-extractor] get:tornOff'
- ]) {
- expect(inputLib.children['B'].children, contains(name));
- expect(inputLib.children['B'].children[name].children, isEmpty);
- }
-
- for (var name in ['tornOff{body}', 'tornOff', '[tear-off] tornOff']) {
- expect(inputLib.children['C'].children, contains(name));
- expect(inputLib.children['C'].children[name].children, isEmpty);
- }
- });
- });
-
- test('histograms', () async {
- await withSymbolSizes('histograms', testSource, (sizesJson) async {
- final info = await loadProgramInfo(File(sizesJson));
- final bySymbol = SizesHistogram.from(info, HistogramType.bySymbol);
- expect(
- bySymbol.buckets,
- contains(bySymbol.bucketing.bucketFor(
- 'package:input', 'package:input/input.dart', 'A', 'tornOff')));
- expect(
- bySymbol.buckets,
- contains(bySymbol.bucketing.bucketFor(
- 'package:input', 'package:input/input.dart', 'B', 'tornOff')));
- expect(
- bySymbol.buckets,
- contains(bySymbol.bucketing.bucketFor(
- 'package:input', 'package:input/input.dart', 'C', 'tornOff')));
-
- final byClass = SizesHistogram.from(info, HistogramType.byClass);
- expect(
- byClass.buckets,
- contains(byClass.bucketing.bucketFor('package:input',
- 'package:input/input.dart', 'A', 'does-not-matter')));
- expect(
- byClass.buckets,
- contains(byClass.bucketing.bucketFor('package:input',
- 'package:input/input.dart', 'B', 'does-not-matter')));
- expect(
- byClass.buckets,
- contains(byClass.bucketing.bucketFor('package:input',
- 'package:input/input.dart', 'C', 'does-not-matter')));
-
- final byLibrary = SizesHistogram.from(info, HistogramType.byLibrary);
- expect(
- byLibrary.buckets,
- contains(byLibrary.bucketing.bucketFor(
- 'package:input',
- 'package:input/input.dart',
- 'does-not-matter',
- 'does-not-matter')));
-
- final byPackage = SizesHistogram.from(info, HistogramType.byPackage);
- expect(
- byPackage.buckets,
- contains(byPackage.bucketing.bucketFor(
- 'package:input',
- 'package:input/does-not-matter.dart',
- 'does-not-matter',
- 'does-not-matter')));
- });
- });
-
- // On Windows there is some issue with interpreting entry point URI as a package URI
- // it instead gets interpreted as a file URI - which breaks comparison. So we
- // simply ignore entry point library (main.dart).
- Map<String, dynamic> diffToJson(ProgramInfo diff) {
- final diffJson = diff.toJson();
- diffJson.removeWhere((key, _) => key.startsWith('file:'));
- return diffJson;
- }
-
- test('diff', () async {
- await withSymbolSizes('diff-1', testSource, (sizesJson) async {
- await withSymbolSizes('diff-2', testSourceModified,
- (modifiedSizesJson) async {
- final info = await loadProgramInfo(File(sizesJson));
- final modifiedInfo = await loadProgramInfo(File(modifiedSizesJson));
- final diff = computeDiff(info, modifiedInfo);
-
- expect(
- diffToJson(diff),
- equals({
- '#type': 'library',
- '@stubs': {'#type': 'library'},
- '@unknown': {'#type': 'library'},
- 'package:input': {
- '#type': 'package',
- 'package:input/input.dart': {
- '#type': 'library',
- '': {
- '#type': 'class',
- 'makeSomeClosures': {
- '#type': 'function',
- '#size': greaterThan(0), // We added code here.
- '<anonymous closure @118>': {
- '#type': 'function',
- '#size': greaterThan(0),
- },
- },
- 'main': {
- '#type': 'function',
- '#size': lessThan(0), // We removed code from main.
- },
- },
- 'A': {
- '#type': 'class',
- 'tornOff': {
- '#type': 'function',
- '#size': greaterThan(0),
- },
- }
- }
- }
- }));
- });
- });
- });
-
- test('diff-collapsed', () async {
- await withSymbolSizes('diff-collapsed-1', testSource, (sizesJson) async {
- await withSymbolSizes('diff-collapsed-2', testSourceModified2,
- (modifiedSizesJson) async {
- final info = await loadProgramInfo(File(sizesJson),
- collapseAnonymousClosures: true);
- final modifiedInfo = await loadProgramInfo(File(modifiedSizesJson),
- collapseAnonymousClosures: true);
- final diff = computeDiff(info, modifiedInfo);
-
- expect(
- diffToJson(diff),
- equals({
- '#type': 'library',
- '@stubs': {'#type': 'library'},
- '@unknown': {'#type': 'library'},
- 'package:input': {
- '#type': 'package',
- 'package:input/input.dart': {
- '#type': 'library',
- '': {
- '#type': 'class',
- 'makeSomeClosures': {
- '#size': lessThan(0),
- '#type': 'function',
- '<anonymous closure>': {
- '#size': lessThan(0),
- '#type': 'function'
- }
- }
- }
- }
- }
- }));
- });
- });
- });
- });
-}
-
-Future withSymbolSizes(String prefix, Map<String, String> source,
- Future Function(String sizesJson) f) {
- return withTempDir(prefix, (dir) async {
- final outputBinary = path.join(dir, 'output.exe');
- final sizesJson = path.join(dir, 'sizes.json');
- final packages = path.join(dir, '.packages');
- final mainDart = path.join(dir, 'main.dart');
-
- // Create test input.
- for (var file in source.entries) {
- await File(path.join(dir, file.key)).writeAsString(file.value);
- }
- await File(packages).writeAsString('''
-input:./
-''');
- await File(mainDart).writeAsString('''
-import 'package:input/input.dart' as input;
-
-void main(List<String> args) => input.main(args);
-''');
-
- // Compile input.dart to native and output instruction sizes.
- final result = await Process.run(dart2native, [
- '-o',
- outputBinary,
- '--packages=$packages',
- '--extra-gen-snapshot-options=--print_instructions_sizes_to=$sizesJson',
- mainDart,
- ]);
-
- expect(result.exitCode, equals(0), reason: '''
-Compilation completed successfully.
-
-stdout: ${result.stdout}
-stderr: ${result.stderr}
-''');
- expect(File(outputBinary).existsSync(), isTrue,
- reason: 'Output binary exists');
- expect(File(sizesJson).existsSync(), isTrue,
- reason: 'Instruction sizes output exists');
-
- await f(sizesJson);
- });
-}
-
-Future withTempDir(String prefix, Future Function(String dir) f) async {
- final tempDir =
- Directory.systemTemp.createTempSync('instruction-sizes-test-${prefix}');
- try {
- await f(tempDir.path);
- } finally {
- tempDir.deleteSync(recursive: true);
- }
-}
diff --git a/pkg/vm/test/transformations/deferred_loading_test.dart b/pkg/vm/test/transformations/deferred_loading_test.dart
new file mode 100644
index 0000000..4b5f961
--- /dev/null
+++ b/pkg/vm/test/transformations/deferred_loading_test.dart
@@ -0,0 +1,50 @@
+// 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:kernel/target/targets.dart';
+import 'package:kernel/ast.dart';
+import 'package:kernel/kernel.dart';
+import 'package:test/test.dart';
+import 'package:vm/transformations/deferred_loading.dart'
+ show transformComponent;
+
+import '../common_test_utils.dart';
+
+final String pkgVmDir = Platform.script.resolve('../..').toFilePath();
+
+runTestCase(Uri source) async {
+ final target = new TestingVmTarget(new TargetFlags());
+ Component component =
+ await compileTestCaseToKernelProgram(source, target: target);
+
+ component = transformComponent(component);
+
+ // Remove core libraries so the expected output isn't enormous and broken by
+ // core libraries changes.
+ component.libraries.removeWhere((lib) => lib.importUri.scheme == "dart");
+
+ String actual = kernelComponentToString(component);
+
+ // Remove absolute library URIs.
+ actual = actual.replaceAll(new Uri.file(pkgVmDir).toString(), '#pkg/vm');
+
+ compareResultWithExpectationsFile(source, actual);
+}
+
+main() {
+ group('deferred-loading', () {
+ final testCasesDir =
+ new Directory(pkgVmDir + '/testcases/transformations/deferred_loading');
+
+ for (var entry in testCasesDir
+ .listSync(recursive: true, followLinks: false)
+ .reversed) {
+ if (entry.path.endsWith("main.dart")) {
+ test(entry.path, () => runTestCase(entry.uri));
+ }
+ }
+ });
+}
diff --git a/pkg/vm/test/transformations/type_flow/summary_collector_test.dart b/pkg/vm/test/transformations/type_flow/summary_collector_test.dart
index 26c85a6..969556a 100644
--- a/pkg/vm/test/transformations/type_flow/summary_collector_test.dart
+++ b/pkg/vm/test/transformations/type_flow/summary_collector_test.dart
@@ -73,7 +73,8 @@
typesBuilder,
new NativeCodeOracle(
null, new ConstantPragmaAnnotationParser(coreTypes)),
- new GenericInterfacesInfoImpl(hierarchy));
+ new GenericInterfacesInfoImpl(hierarchy),
+ /*_protobufHandler=*/ null);
}
String print(TreeNode node) {
diff --git a/pkg/vm/test/transformations/type_flow/transformer_test.dart b/pkg/vm/test/transformations/type_flow/transformer_test.dart
index 123bc44..810b646 100644
--- a/pkg/vm/test/transformations/type_flow/transformer_test.dart
+++ b/pkg/vm/test/transformations/type_flow/transformer_test.dart
@@ -28,9 +28,25 @@
final coreTypes = new CoreTypes(component);
component = transformComponent(target, coreTypes, component,
- matcher: new ConstantPragmaAnnotationParser(coreTypes));
+ matcher: new ConstantPragmaAnnotationParser(coreTypes),
+ treeShakeProtobufs: true);
- final actual = kernelLibraryToString(component.mainMethod.enclosingLibrary);
+ String actual = kernelLibraryToString(component.mainMethod.enclosingLibrary);
+
+ // Tests in /protobuf_handler consist of multiple libraries.
+ // Include libraries with protobuf generated messages into the result.
+ if (source.toString().contains('/protobuf_handler/')) {
+ for (var lib in component.libraries) {
+ if (lib.importUri
+ .toString()
+ .contains('/protobuf_handler/lib/generated/')) {
+ lib.name ??= lib.importUri.pathSegments.last;
+ actual += kernelLibraryToString(lib);
+ }
+ }
+ // Remove library paths.
+ actual = actual.replaceAll(Uri.file(pkgVmDir).toString(), 'file:pkg/vm');
+ }
compareResultWithExpectationsFile(source, actual);
@@ -42,19 +58,18 @@
final testCasesDir = new Directory(
pkgVmDir + '/testcases/transformations/type_flow/transformer');
- for (var entry in testCasesDir
- .listSync(recursive: true, followLinks: false)
- .reversed) {
- if (entry.path.endsWith('.dart')) {
- final bool enableNullSafety = entry.path.endsWith('_nnbd_strong.dart');
- final bool enableNNBD =
- enableNullSafety || entry.path.endsWith('_nnbd.dart');
+ for (var entry
+ in testCasesDir.listSync(recursive: true, followLinks: false)) {
+ final path = entry.path;
+ if (path.endsWith('.dart') && !path.endsWith('.pb.dart')) {
+ final bool enableNullSafety = path.endsWith('_nnbd_strong.dart');
+ final bool enableNNBD = enableNullSafety || path.endsWith('_nnbd.dart');
final List<String> experimentalFlags = [
if (enableNNBD) 'non-nullable',
];
- test(entry.path,
+ test(path,
() => runTestCase(entry.uri, experimentalFlags, enableNullSafety));
}
}
- });
+ }, timeout: Timeout.none);
}
diff --git a/pkg/vm/testcases/transformations/deferred_loading/a.dart b/pkg/vm/testcases/transformations/deferred_loading/a.dart
new file mode 100644
index 0000000..4fa8635
--- /dev/null
+++ b/pkg/vm/testcases/transformations/deferred_loading/a.dart
@@ -0,0 +1,13 @@
+// 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.
+
+library a;
+
+import 'd.dart' deferred as d;
+
+a() async {
+ print("A");
+ await d.loadLibrary();
+ return d.d();
+}
diff --git a/pkg/vm/testcases/transformations/deferred_loading/b.dart b/pkg/vm/testcases/transformations/deferred_loading/b.dart
new file mode 100644
index 0000000..c6cb3e2
--- /dev/null
+++ b/pkg/vm/testcases/transformations/deferred_loading/b.dart
@@ -0,0 +1,12 @@
+// 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.
+
+library b;
+
+import 'c.dart' as c;
+
+b() async {
+ print("B");
+ return c.c();
+}
diff --git a/pkg/vm/testcases/transformations/deferred_loading/c.dart b/pkg/vm/testcases/transformations/deferred_loading/c.dart
new file mode 100644
index 0000000..12892dc
--- /dev/null
+++ b/pkg/vm/testcases/transformations/deferred_loading/c.dart
@@ -0,0 +1,14 @@
+// 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.
+
+library c;
+
+import 'b.dart' as b;
+import 'f.dart' deferred as f;
+
+c() async {
+ print("C");
+ await f.loadLibrary();
+ return f.f();
+}
diff --git a/pkg/vm/testcases/transformations/deferred_loading/d.dart b/pkg/vm/testcases/transformations/deferred_loading/d.dart
new file mode 100644
index 0000000..410714e
--- /dev/null
+++ b/pkg/vm/testcases/transformations/deferred_loading/d.dart
@@ -0,0 +1,12 @@
+// 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.
+
+library d;
+
+import 'e.dart' as e;
+
+d() async {
+ print("D");
+ return e.e();
+}
diff --git a/pkg/vm/testcases/transformations/deferred_loading/e.dart b/pkg/vm/testcases/transformations/deferred_loading/e.dart
new file mode 100644
index 0000000..e512b6e
--- /dev/null
+++ b/pkg/vm/testcases/transformations/deferred_loading/e.dart
@@ -0,0 +1,13 @@
+// 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.
+
+library e;
+
+import 'g.dart' deferred as g;
+
+e() async {
+ print("E");
+ await g.loadLibrary();
+ return g.g();
+}
diff --git a/pkg/vm/testcases/transformations/deferred_loading/f.dart b/pkg/vm/testcases/transformations/deferred_loading/f.dart
new file mode 100644
index 0000000..5ba9511
--- /dev/null
+++ b/pkg/vm/testcases/transformations/deferred_loading/f.dart
@@ -0,0 +1,16 @@
+// 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.
+
+library f;
+
+import 'g.dart' deferred as g;
+import 'i.dart' deferred as i;
+
+f() async {
+ print("F");
+ await g.loadLibrary();
+ return g.g();
+ await i.loadLibrary();
+ return i.i();
+}
diff --git a/pkg/vm/testcases/transformations/deferred_loading/g.dart b/pkg/vm/testcases/transformations/deferred_loading/g.dart
new file mode 100644
index 0000000..b7c4bc5
--- /dev/null
+++ b/pkg/vm/testcases/transformations/deferred_loading/g.dart
@@ -0,0 +1,11 @@
+// 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.
+
+library g;
+
+import 'h.dart' as h;
+
+g() async {
+ print("G");
+}
diff --git a/pkg/vm/testcases/transformations/deferred_loading/h.dart b/pkg/vm/testcases/transformations/deferred_loading/h.dart
new file mode 100644
index 0000000..8870dd9
--- /dev/null
+++ b/pkg/vm/testcases/transformations/deferred_loading/h.dart
@@ -0,0 +1,11 @@
+// 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.
+
+library h;
+
+import 'g.dart' as g;
+
+h() async {
+ print("H");
+}
diff --git a/pkg/vm/testcases/transformations/deferred_loading/i.dart b/pkg/vm/testcases/transformations/deferred_loading/i.dart
new file mode 100644
index 0000000..2bbeb73
--- /dev/null
+++ b/pkg/vm/testcases/transformations/deferred_loading/i.dart
@@ -0,0 +1,14 @@
+// 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.
+
+library i;
+
+import 'j.dart' deferred as j;
+import 'b.dart' as b;
+
+i() async {
+ print("I");
+ await j.loadLibrary();
+ return j.j();
+}
diff --git a/pkg/vm/testcases/transformations/deferred_loading/j.dart b/pkg/vm/testcases/transformations/deferred_loading/j.dart
new file mode 100644
index 0000000..9392e67
--- /dev/null
+++ b/pkg/vm/testcases/transformations/deferred_loading/j.dart
@@ -0,0 +1,11 @@
+// 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.
+
+library j;
+
+import 'a.dart' as a;
+
+j() async {
+ print("J");
+}
diff --git a/pkg/vm/testcases/transformations/deferred_loading/main.dart b/pkg/vm/testcases/transformations/deferred_loading/main.dart
new file mode 100644
index 0000000..72703c4
--- /dev/null
+++ b/pkg/vm/testcases/transformations/deferred_loading/main.dart
@@ -0,0 +1,13 @@
+// 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.
+
+library main;
+
+import 'a.dart';
+import 'b.dart';
+
+main() async {
+ await a();
+ await b();
+}
diff --git a/pkg/vm/testcases/transformations/deferred_loading/main.dart.expect b/pkg/vm/testcases/transformations/deferred_loading/main.dart.expect
new file mode 100644
index 0000000..3dbc78e
--- /dev/null
+++ b/pkg/vm/testcases/transformations/deferred_loading/main.dart.expect
@@ -0,0 +1,426 @@
+main = #lib::main;
+ [@vm.loading-units.metadata=LoadingUnitsMetadata(
+LoadingUnit(id=1, parent=0,
+ #lib
+ #pkg/vm/testcases/transformations/deferred_loading/a.dart
+ #pkg/vm/testcases/transformations/deferred_loading/b.dart
+ #pkg/vm/testcases/transformations/deferred_loading/c.dart
+ dart:async
+ dart:collection
+ dart:convert
+ dart:developer
+ dart:ffi
+ dart:_internal
+ dart:isolate
+ dart:math
+ dart:mirrors
+ dart:typed_data
+ dart:vmservice_io
+ dart:_vmservice
+ dart:_builtin
+ dart:nativewrappers
+ dart:io
+ dart:cli
+ dart:wasm
+ dart:core
+ dart:_http
+)
+LoadingUnit(id=2, parent=1,
+ #pkg/vm/testcases/transformations/deferred_loading/d.dart
+ #pkg/vm/testcases/transformations/deferred_loading/e.dart
+)
+LoadingUnit(id=3, parent=1,
+ #pkg/vm/testcases/transformations/deferred_loading/f.dart
+)
+LoadingUnit(id=4, parent=1,
+ #pkg/vm/testcases/transformations/deferred_loading/g.dart
+ #pkg/vm/testcases/transformations/deferred_loading/h.dart
+)
+LoadingUnit(id=5, parent=3,
+ #pkg/vm/testcases/transformations/deferred_loading/i.dart
+)
+LoadingUnit(id=6, parent=5,
+ #pkg/vm/testcases/transformations/deferred_loading/j.dart
+)
+)]library #lib from "#lib" as #lib {
+
+ import "#pkg/vm/testcases/transformations/deferred_loading/a.dart";
+ import "#pkg/vm/testcases/transformations/deferred_loading/b.dart";
+
+ static method main() → dynamic /* originally async */ {
+ final dart.async::_AsyncAwaitCompleter<dynamic>* :async_completer = new dart.async::_AsyncAwaitCompleter::•<dynamic>();
+ FutureOr<dynamic>* :return_value;
+ dynamic :async_stack_trace;
+ (dynamic) →* dynamic :async_op_then;
+ (dart.core::Object*, dart.core::StackTrace*) →* dynamic :async_op_error;
+ dart.core::int* :await_jump_var = 0;
+ dynamic :await_ctx_var;
+ dynamic :saved_try_context_var0;
+ function :async_op([dynamic :result, dynamic :exception, dynamic :stack_trace]) → dynamic yielding
+ try {
+ #L1:
+ {
+ [yield] let dynamic #t1 = dart.async::_awaitHelper(a::a(), :async_op_then, :async_op_error, :async_op) in null;
+ :result;
+ [yield] let dynamic #t2 = dart.async::_awaitHelper(b::b(), :async_op_then, :async_op_error, :async_op) in null;
+ :result;
+ }
+ dart.async::_completeOnAsyncReturn(:async_completer, :return_value);
+ return;
+ }
+ on dynamic catch(dynamic exception, dart.core::StackTrace* stack_trace) {
+ :async_completer.{dart.async::Completer::completeError}(exception, stack_trace);
+ }
+ :async_stack_trace = dart.async::_asyncStackTraceHelper(:async_op);
+ :async_op_then = dart.async::_asyncThenWrapperHelper(:async_op);
+ :async_op_error = dart.async::_asyncErrorWrapperHelper(:async_op);
+ :async_completer.{dart.async::_AsyncAwaitCompleter::start}(:async_op);
+ return :async_completer.{dart.async::Completer::future};
+ }
+}
+library a from "#pkg/vm/testcases/transformations/deferred_loading/a.dart" as a {
+
+ import "#pkg/vm/testcases/transformations/deferred_loading/d.dart" deferred as d;
+
+ static method a() → dynamic /* originally async */ {
+ final dart.async::_AsyncAwaitCompleter<dynamic>* :async_completer = new dart.async::_AsyncAwaitCompleter::•<dynamic>();
+ FutureOr<dynamic>* :return_value;
+ dynamic :async_stack_trace;
+ (dynamic) →* dynamic :async_op_then;
+ (dart.core::Object*, dart.core::StackTrace*) →* dynamic :async_op_error;
+ dart.core::int* :await_jump_var = 0;
+ dynamic :await_ctx_var;
+ dynamic :saved_try_context_var0;
+ function :async_op([dynamic :result, dynamic :exception, dynamic :stack_trace]) → dynamic yielding
+ try {
+ #L2:
+ {
+ dart.core::print("A");
+ [yield] let dynamic #t3 = dart.async::_awaitHelper(LoadLibrary(d), :async_op_then, :async_op_error, :async_op) in null;
+ :result;
+ :return_value = let final dynamic #t4 = CheckLibraryIsLoaded(d) in d::d();
+ break #L2;
+ }
+ dart.async::_completeOnAsyncReturn(:async_completer, :return_value);
+ return;
+ }
+ on dynamic catch(dynamic exception, dart.core::StackTrace* stack_trace) {
+ :async_completer.{dart.async::Completer::completeError}(exception, stack_trace);
+ }
+ :async_stack_trace = dart.async::_asyncStackTraceHelper(:async_op);
+ :async_op_then = dart.async::_asyncThenWrapperHelper(:async_op);
+ :async_op_error = dart.async::_asyncErrorWrapperHelper(:async_op);
+ :async_completer.{dart.async::_AsyncAwaitCompleter::start}(:async_op);
+ return :async_completer.{dart.async::Completer::future};
+ }
+}
+library b from "#pkg/vm/testcases/transformations/deferred_loading/b.dart" as b {
+
+ import "#pkg/vm/testcases/transformations/deferred_loading/c.dart" as c;
+
+ static method b() → dynamic /* originally async */ {
+ final dart.async::_AsyncAwaitCompleter<dynamic>* :async_completer = new dart.async::_AsyncAwaitCompleter::•<dynamic>();
+ FutureOr<dynamic>* :return_value;
+ dynamic :async_stack_trace;
+ (dynamic) →* dynamic :async_op_then;
+ (dart.core::Object*, dart.core::StackTrace*) →* dynamic :async_op_error;
+ dart.core::int* :await_jump_var = 0;
+ dynamic :await_ctx_var;
+ function :async_op([dynamic :result, dynamic :exception, dynamic :stack_trace]) → dynamic yielding
+ try {
+ #L3:
+ {
+ dart.core::print("B");
+ :return_value = c::c();
+ break #L3;
+ }
+ dart.async::_completeOnAsyncReturn(:async_completer, :return_value);
+ return;
+ }
+ on dynamic catch(dynamic exception, dart.core::StackTrace* stack_trace) {
+ :async_completer.{dart.async::Completer::completeError}(exception, stack_trace);
+ }
+ :async_stack_trace = dart.async::_asyncStackTraceHelper(:async_op);
+ :async_op_then = dart.async::_asyncThenWrapperHelper(:async_op);
+ :async_op_error = dart.async::_asyncErrorWrapperHelper(:async_op);
+ :async_completer.{dart.async::_AsyncAwaitCompleter::start}(:async_op);
+ return :async_completer.{dart.async::Completer::future};
+ }
+}
+library d from "#pkg/vm/testcases/transformations/deferred_loading/d.dart" as d {
+
+ import "#pkg/vm/testcases/transformations/deferred_loading/e.dart" as e;
+
+ static method d() → dynamic /* originally async */ {
+ final dart.async::_AsyncAwaitCompleter<dynamic>* :async_completer = new dart.async::_AsyncAwaitCompleter::•<dynamic>();
+ FutureOr<dynamic>* :return_value;
+ dynamic :async_stack_trace;
+ (dynamic) →* dynamic :async_op_then;
+ (dart.core::Object*, dart.core::StackTrace*) →* dynamic :async_op_error;
+ dart.core::int* :await_jump_var = 0;
+ dynamic :await_ctx_var;
+ function :async_op([dynamic :result, dynamic :exception, dynamic :stack_trace]) → dynamic yielding
+ try {
+ #L4:
+ {
+ dart.core::print("D");
+ :return_value = e::e();
+ break #L4;
+ }
+ dart.async::_completeOnAsyncReturn(:async_completer, :return_value);
+ return;
+ }
+ on dynamic catch(dynamic exception, dart.core::StackTrace* stack_trace) {
+ :async_completer.{dart.async::Completer::completeError}(exception, stack_trace);
+ }
+ :async_stack_trace = dart.async::_asyncStackTraceHelper(:async_op);
+ :async_op_then = dart.async::_asyncThenWrapperHelper(:async_op);
+ :async_op_error = dart.async::_asyncErrorWrapperHelper(:async_op);
+ :async_completer.{dart.async::_AsyncAwaitCompleter::start}(:async_op);
+ return :async_completer.{dart.async::Completer::future};
+ }
+}
+library c from "#pkg/vm/testcases/transformations/deferred_loading/c.dart" as c {
+
+ import "#pkg/vm/testcases/transformations/deferred_loading/b.dart" as b;
+ import "#pkg/vm/testcases/transformations/deferred_loading/f.dart" deferred as f;
+
+ static method c() → dynamic /* originally async */ {
+ final dart.async::_AsyncAwaitCompleter<dynamic>* :async_completer = new dart.async::_AsyncAwaitCompleter::•<dynamic>();
+ FutureOr<dynamic>* :return_value;
+ dynamic :async_stack_trace;
+ (dynamic) →* dynamic :async_op_then;
+ (dart.core::Object*, dart.core::StackTrace*) →* dynamic :async_op_error;
+ dart.core::int* :await_jump_var = 0;
+ dynamic :await_ctx_var;
+ dynamic :saved_try_context_var0;
+ function :async_op([dynamic :result, dynamic :exception, dynamic :stack_trace]) → dynamic yielding
+ try {
+ #L5:
+ {
+ dart.core::print("C");
+ [yield] let dynamic #t5 = dart.async::_awaitHelper(LoadLibrary(f), :async_op_then, :async_op_error, :async_op) in null;
+ :result;
+ :return_value = let final dynamic #t6 = CheckLibraryIsLoaded(f) in f::f();
+ break #L5;
+ }
+ dart.async::_completeOnAsyncReturn(:async_completer, :return_value);
+ return;
+ }
+ on dynamic catch(dynamic exception, dart.core::StackTrace* stack_trace) {
+ :async_completer.{dart.async::Completer::completeError}(exception, stack_trace);
+ }
+ :async_stack_trace = dart.async::_asyncStackTraceHelper(:async_op);
+ :async_op_then = dart.async::_asyncThenWrapperHelper(:async_op);
+ :async_op_error = dart.async::_asyncErrorWrapperHelper(:async_op);
+ :async_completer.{dart.async::_AsyncAwaitCompleter::start}(:async_op);
+ return :async_completer.{dart.async::Completer::future};
+ }
+}
+library e from "#pkg/vm/testcases/transformations/deferred_loading/e.dart" as e {
+
+ import "#pkg/vm/testcases/transformations/deferred_loading/g.dart" deferred as g;
+
+ static method e() → dynamic /* originally async */ {
+ final dart.async::_AsyncAwaitCompleter<dynamic>* :async_completer = new dart.async::_AsyncAwaitCompleter::•<dynamic>();
+ FutureOr<dynamic>* :return_value;
+ dynamic :async_stack_trace;
+ (dynamic) →* dynamic :async_op_then;
+ (dart.core::Object*, dart.core::StackTrace*) →* dynamic :async_op_error;
+ dart.core::int* :await_jump_var = 0;
+ dynamic :await_ctx_var;
+ dynamic :saved_try_context_var0;
+ function :async_op([dynamic :result, dynamic :exception, dynamic :stack_trace]) → dynamic yielding
+ try {
+ #L6:
+ {
+ dart.core::print("E");
+ [yield] let dynamic #t7 = dart.async::_awaitHelper(LoadLibrary(g), :async_op_then, :async_op_error, :async_op) in null;
+ :result;
+ :return_value = let final dynamic #t8 = CheckLibraryIsLoaded(g) in g::g();
+ break #L6;
+ }
+ dart.async::_completeOnAsyncReturn(:async_completer, :return_value);
+ return;
+ }
+ on dynamic catch(dynamic exception, dart.core::StackTrace* stack_trace) {
+ :async_completer.{dart.async::Completer::completeError}(exception, stack_trace);
+ }
+ :async_stack_trace = dart.async::_asyncStackTraceHelper(:async_op);
+ :async_op_then = dart.async::_asyncThenWrapperHelper(:async_op);
+ :async_op_error = dart.async::_asyncErrorWrapperHelper(:async_op);
+ :async_completer.{dart.async::_AsyncAwaitCompleter::start}(:async_op);
+ return :async_completer.{dart.async::Completer::future};
+ }
+}
+library f from "#pkg/vm/testcases/transformations/deferred_loading/f.dart" as f {
+
+ import "#pkg/vm/testcases/transformations/deferred_loading/g.dart" deferred as g;
+ import "#pkg/vm/testcases/transformations/deferred_loading/i.dart" deferred as i;
+
+ static method f() → dynamic /* originally async */ {
+ final dart.async::_AsyncAwaitCompleter<dynamic>* :async_completer = new dart.async::_AsyncAwaitCompleter::•<dynamic>();
+ FutureOr<dynamic>* :return_value;
+ dynamic :async_stack_trace;
+ (dynamic) →* dynamic :async_op_then;
+ (dart.core::Object*, dart.core::StackTrace*) →* dynamic :async_op_error;
+ dart.core::int* :await_jump_var = 0;
+ dynamic :await_ctx_var;
+ dynamic :saved_try_context_var0;
+ function :async_op([dynamic :result, dynamic :exception, dynamic :stack_trace]) → dynamic yielding
+ try {
+ #L7:
+ {
+ dart.core::print("F");
+ [yield] let dynamic #t9 = dart.async::_awaitHelper(LoadLibrary(g), :async_op_then, :async_op_error, :async_op) in null;
+ :result;
+ :return_value = let final dynamic #t10 = CheckLibraryIsLoaded(g) in g::g();
+ break #L7;
+ [yield] let dynamic #t11 = dart.async::_awaitHelper(LoadLibrary(i), :async_op_then, :async_op_error, :async_op) in null;
+ :result;
+ :return_value = let final dynamic #t12 = CheckLibraryIsLoaded(i) in i::i();
+ break #L7;
+ }
+ dart.async::_completeOnAsyncReturn(:async_completer, :return_value);
+ return;
+ }
+ on dynamic catch(dynamic exception, dart.core::StackTrace* stack_trace) {
+ :async_completer.{dart.async::Completer::completeError}(exception, stack_trace);
+ }
+ :async_stack_trace = dart.async::_asyncStackTraceHelper(:async_op);
+ :async_op_then = dart.async::_asyncThenWrapperHelper(:async_op);
+ :async_op_error = dart.async::_asyncErrorWrapperHelper(:async_op);
+ :async_completer.{dart.async::_AsyncAwaitCompleter::start}(:async_op);
+ return :async_completer.{dart.async::Completer::future};
+ }
+}
+library g from "#pkg/vm/testcases/transformations/deferred_loading/g.dart" as g {
+
+ import "#pkg/vm/testcases/transformations/deferred_loading/h.dart" as h;
+
+ static method g() → dynamic /* originally async */ {
+ final dart.async::_AsyncAwaitCompleter<dynamic>* :async_completer = new dart.async::_AsyncAwaitCompleter::•<dynamic>();
+ FutureOr<dynamic>* :return_value;
+ dynamic :async_stack_trace;
+ (dynamic) →* dynamic :async_op_then;
+ (dart.core::Object*, dart.core::StackTrace*) →* dynamic :async_op_error;
+ dart.core::int* :await_jump_var = 0;
+ dynamic :await_ctx_var;
+ function :async_op([dynamic :result, dynamic :exception, dynamic :stack_trace]) → dynamic yielding
+ try {
+ #L8:
+ {
+ dart.core::print("G");
+ }
+ dart.async::_completeOnAsyncReturn(:async_completer, :return_value);
+ return;
+ }
+ on dynamic catch(dynamic exception, dart.core::StackTrace* stack_trace) {
+ :async_completer.{dart.async::Completer::completeError}(exception, stack_trace);
+ }
+ :async_stack_trace = dart.async::_asyncStackTraceHelper(:async_op);
+ :async_op_then = dart.async::_asyncThenWrapperHelper(:async_op);
+ :async_op_error = dart.async::_asyncErrorWrapperHelper(:async_op);
+ :async_completer.{dart.async::_AsyncAwaitCompleter::start}(:async_op);
+ return :async_completer.{dart.async::Completer::future};
+ }
+}
+library i from "#pkg/vm/testcases/transformations/deferred_loading/i.dart" as i {
+
+ import "#pkg/vm/testcases/transformations/deferred_loading/j.dart" deferred as j;
+ import "#pkg/vm/testcases/transformations/deferred_loading/b.dart" as b;
+
+ static method i() → dynamic /* originally async */ {
+ final dart.async::_AsyncAwaitCompleter<dynamic>* :async_completer = new dart.async::_AsyncAwaitCompleter::•<dynamic>();
+ FutureOr<dynamic>* :return_value;
+ dynamic :async_stack_trace;
+ (dynamic) →* dynamic :async_op_then;
+ (dart.core::Object*, dart.core::StackTrace*) →* dynamic :async_op_error;
+ dart.core::int* :await_jump_var = 0;
+ dynamic :await_ctx_var;
+ dynamic :saved_try_context_var0;
+ function :async_op([dynamic :result, dynamic :exception, dynamic :stack_trace]) → dynamic yielding
+ try {
+ #L9:
+ {
+ dart.core::print("I");
+ [yield] let dynamic #t13 = dart.async::_awaitHelper(LoadLibrary(j), :async_op_then, :async_op_error, :async_op) in null;
+ :result;
+ :return_value = let final dynamic #t14 = CheckLibraryIsLoaded(j) in j::j();
+ break #L9;
+ }
+ dart.async::_completeOnAsyncReturn(:async_completer, :return_value);
+ return;
+ }
+ on dynamic catch(dynamic exception, dart.core::StackTrace* stack_trace) {
+ :async_completer.{dart.async::Completer::completeError}(exception, stack_trace);
+ }
+ :async_stack_trace = dart.async::_asyncStackTraceHelper(:async_op);
+ :async_op_then = dart.async::_asyncThenWrapperHelper(:async_op);
+ :async_op_error = dart.async::_asyncErrorWrapperHelper(:async_op);
+ :async_completer.{dart.async::_AsyncAwaitCompleter::start}(:async_op);
+ return :async_completer.{dart.async::Completer::future};
+ }
+}
+library h from "#pkg/vm/testcases/transformations/deferred_loading/h.dart" as h {
+
+ import "#pkg/vm/testcases/transformations/deferred_loading/g.dart" as g;
+
+ static method h() → dynamic /* originally async */ {
+ final dart.async::_AsyncAwaitCompleter<dynamic>* :async_completer = new dart.async::_AsyncAwaitCompleter::•<dynamic>();
+ FutureOr<dynamic>* :return_value;
+ dynamic :async_stack_trace;
+ (dynamic) →* dynamic :async_op_then;
+ (dart.core::Object*, dart.core::StackTrace*) →* dynamic :async_op_error;
+ dart.core::int* :await_jump_var = 0;
+ dynamic :await_ctx_var;
+ function :async_op([dynamic :result, dynamic :exception, dynamic :stack_trace]) → dynamic yielding
+ try {
+ #L10:
+ {
+ dart.core::print("H");
+ }
+ dart.async::_completeOnAsyncReturn(:async_completer, :return_value);
+ return;
+ }
+ on dynamic catch(dynamic exception, dart.core::StackTrace* stack_trace) {
+ :async_completer.{dart.async::Completer::completeError}(exception, stack_trace);
+ }
+ :async_stack_trace = dart.async::_asyncStackTraceHelper(:async_op);
+ :async_op_then = dart.async::_asyncThenWrapperHelper(:async_op);
+ :async_op_error = dart.async::_asyncErrorWrapperHelper(:async_op);
+ :async_completer.{dart.async::_AsyncAwaitCompleter::start}(:async_op);
+ return :async_completer.{dart.async::Completer::future};
+ }
+}
+library j from "#pkg/vm/testcases/transformations/deferred_loading/j.dart" as j {
+
+ import "#pkg/vm/testcases/transformations/deferred_loading/a.dart" as a;
+
+ static method j() → dynamic /* originally async */ {
+ final dart.async::_AsyncAwaitCompleter<dynamic>* :async_completer = new dart.async::_AsyncAwaitCompleter::•<dynamic>();
+ FutureOr<dynamic>* :return_value;
+ dynamic :async_stack_trace;
+ (dynamic) →* dynamic :async_op_then;
+ (dart.core::Object*, dart.core::StackTrace*) →* dynamic :async_op_error;
+ dart.core::int* :await_jump_var = 0;
+ dynamic :await_ctx_var;
+ function :async_op([dynamic :result, dynamic :exception, dynamic :stack_trace]) → dynamic yielding
+ try {
+ #L11:
+ {
+ dart.core::print("J");
+ }
+ dart.async::_completeOnAsyncReturn(:async_completer, :return_value);
+ return;
+ }
+ on dynamic catch(dynamic exception, dart.core::StackTrace* stack_trace) {
+ :async_completer.{dart.async::Completer::completeError}(exception, stack_trace);
+ }
+ :async_stack_trace = dart.async::_asyncStackTraceHelper(:async_op);
+ :async_op_then = dart.async::_asyncThenWrapperHelper(:async_op);
+ :async_op_error = dart.async::_asyncErrorWrapperHelper(:async_op);
+ :async_completer.{dart.async::_AsyncAwaitCompleter::start}(:async_op);
+ return :async_completer.{dart.async::Completer::future};
+ }
+}
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/compile_protos.sh b/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/compile_protos.sh
new file mode 100755
index 0000000..8031891
--- /dev/null
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/compile_protos.sh
@@ -0,0 +1,19 @@
+#!/usr/bin/env bash
+# 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.
+
+# Running this script requires having protoc_plugin installed in your path.
+
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
+
+rm -rf $DIR/lib/generated
+mkdir $DIR/lib/generated
+
+# Directory of the script
+GENERATED_DIR=$DIR/lib/generated
+
+protoc --dart_out=$GENERATED_DIR -I$DIR/protos $DIR/protos/*.proto
+rm $GENERATED_DIR/*.pbenum.dart $GENERATED_DIR/*.pbjson.dart $GENERATED_DIR/*.pbserver.dart
+
+dartfmt -w $DIR/lib/generated
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/create_test.dart b/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/create_test.dart
new file mode 100644
index 0000000..1912459
--- /dev/null
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/create_test.dart
@@ -0,0 +1,21 @@
+// 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/test.dart';
+
+import 'generated/foo.pb.dart';
+
+main() {
+ FooKeep foo = FooKeep()
+ ..barKeep = (BarKeep()..aKeep = 5)
+ ..mapKeep['foo'] = (BarKeep()..aKeep = 2)
+ ..aKeep = 43;
+ test('retrieving values', () {
+ expect(foo.barKeep.aKeep, 5);
+ expect(foo.mapKeep['foo'].aKeep, 2);
+ expect(foo.hasHasKeep(), false);
+ expect(foo.aKeep, 43);
+ foo.clearClearKeep();
+ });
+}
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/create_test.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/create_test.dart.expect
new file mode 100644
index 0000000..f23f5f6
--- /dev/null
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/create_test.dart.expect
@@ -0,0 +1,133 @@
+library #lib;
+import self as self;
+import "generated/foo.pb.dart" as pb;
+import "dart:core" as core;
+import "package:test_core/test_core.dart" as tes;
+import "package:test_api/src/frontend/expect.dart" as exp;
+
+import "package:test/test.dart";
+import "file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart";
+
+static method main() → dynamic {
+ pb::FooKeep* foo = let final pb::FooKeep* #t1 = [@vm.inferred-type.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::FooKeep] pb::FooKeep::•() in block {
+ [@vm.direct-call.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::FooKeep.barKeep] [@vm.inferred-type.metadata=!? (skip check)] #t1.{pb::FooKeep::barKeep} = let final pb::BarKeep* #t2 = [@vm.inferred-type.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::BarKeep] pb::BarKeep::•() in block {
+ [@vm.direct-call.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::BarKeep.aKeep] [@vm.inferred-type.metadata=!? (skip check)] #t2.{pb::BarKeep::aKeep} = 5;
+ } =>#t2;
+ [@vm.call-site-attributes.metadata=receiverType:dart.core::Map<dart.core::String*, library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::BarKeep*>*] [@vm.direct-call.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::FooKeep.mapKeep] [@vm.inferred-type.metadata=!] #t1.{pb::FooKeep::mapKeep}.{core::Map::[]=}("foo", let final pb::BarKeep* #t3 = [@vm.inferred-type.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::BarKeep] pb::BarKeep::•() in block {
+ [@vm.direct-call.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::BarKeep.aKeep] [@vm.inferred-type.metadata=!? (skip check)] #t3.{pb::BarKeep::aKeep} = 2;
+ } =>#t3);
+ [@vm.direct-call.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::FooKeep.aKeep] [@vm.inferred-type.metadata=!? (skip check)] #t1.{pb::FooKeep::aKeep} = 43;
+ } =>#t1;
+ tes::test("retrieving values", () → core::Null? {
+ exp::expect([@vm.direct-call.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::BarKeep.aKeep??] [@vm.inferred-type.metadata=int] [@vm.direct-call.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::FooKeep.barKeep] [@vm.inferred-type.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::BarKeep?] foo.{pb::FooKeep::barKeep}.{pb::BarKeep::aKeep}, 5);
+ exp::expect([@vm.direct-call.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::BarKeep.aKeep??] [@vm.inferred-type.metadata=int] [@vm.inferred-type.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::BarKeep?] [@vm.direct-call.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::FooKeep.mapKeep] [@vm.inferred-type.metadata=!] foo.{pb::FooKeep::mapKeep}.{core::Map::[]}("foo").{pb::BarKeep::aKeep}, 2);
+ exp::expect([@vm.direct-call.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::FooKeep.hasHasKeep] [@vm.inferred-type.metadata=dart.core::bool (skip check)] foo.{pb::FooKeep::hasHasKeep}(), false);
+ exp::expect([@vm.direct-call.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::FooKeep.aKeep] [@vm.inferred-type.metadata=int] foo.{pb::FooKeep::aKeep}, 43);
+ [@vm.direct-call.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::FooKeep.clearClearKeep] [@vm.inferred-type.metadata=!? (skip check)] foo.{pb::FooKeep::clearClearKeep}();
+ });
+}
+library foo.pb.dart;
+import self as self;
+import "package:protobuf/protobuf.dart" as pro;
+import "dart:core" as core;
+
+import "dart:core" as $core;
+import "package:protobuf/protobuf.dart" as $pb;
+
+class FooKeep extends pro::GeneratedMessage {
+[@vm.inferred-type.metadata=protobuf::BuilderInfo?] static final field pro::BuilderInfo* _i = let final pro::BuilderInfo* #t1 = new pro::BuilderInfo::•("FooKeep", createEmptyInstance: #C1) in block {
+ [@vm.direct-call.metadata=protobuf::BuilderInfo.aOM] [@vm.inferred-type.metadata=!? (skip check)] #t1.{pro::BuilderInfo::aOM}<self::BarKeep*>(1, "barKeep", protoName: "barKeep", subBuilder: #C2);
+ [@vm.direct-call.metadata=protobuf::BuilderInfo.add] [@vm.inferred-type.metadata=!? (skip check)] #t1.{pro::BuilderInfo::add}<core::Null?>(0, null, null, null, null, null, null);
+ [@vm.direct-call.metadata=protobuf::BuilderInfo.m] [@vm.inferred-type.metadata=!? (skip check)] #t1.{pro::BuilderInfo::m}<core::String*, self::BarKeep*>(3, "mapKeep", protoName: "mapKeep", entryClassName: "FooKeep.MapKeepEntry", keyFieldType: #C3, valueFieldType: #C4, valueCreator: #C2);
+ [@vm.direct-call.metadata=protobuf::BuilderInfo.add] [@vm.inferred-type.metadata=!? (skip check)] #t1.{pro::BuilderInfo::add}<core::Null?>(0, null, null, null, null, null, null);
+ [@vm.direct-call.metadata=protobuf::BuilderInfo.a] [@vm.inferred-type.metadata=!? (skip check)] #t1.{pro::BuilderInfo::a}<core::int*>(5, "aKeep", #C5, protoName: "aKeep");
+ [@vm.direct-call.metadata=protobuf::BuilderInfo.aOM] [@vm.inferred-type.metadata=!? (skip check)] #t1.{pro::BuilderInfo::aOM}<self::HasKeep*>(6, "hasKeep", protoName: "hasKeep", subBuilder: #C6);
+ [@vm.direct-call.metadata=protobuf::BuilderInfo.aOM] [@vm.inferred-type.metadata=!? (skip check)] #t1.{pro::BuilderInfo::aOM}<self::ClearKeep*>(7, "clearKeep", protoName: "clearKeep", subBuilder: #C7);
+ [@vm.direct-call.metadata=protobuf::BuilderInfo.hasRequiredFields] [@vm.inferred-type.metadata=!? (skip check)] #t1.{pro::BuilderInfo::hasRequiredFields} = false;
+ } =>#t1;
+ constructor _() → self::FooKeep*
+ : super pro::GeneratedMessage::•()
+ ;
+ static factory •() → self::FooKeep*
+ return [@vm.inferred-type.metadata=foo.pb.dart::FooKeep] self::FooKeep::create();
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:3] get info_() → pro::BuilderInfo*
+ return [@vm.inferred-type.metadata=protobuf::BuilderInfo?] self::FooKeep::_i;
+ @#C10
+ static method create() → self::FooKeep*
+ return new self::FooKeep::_();
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:4] @#C12
+ get barKeep() → self::BarKeep*
+ return [@vm.direct-call.metadata=protobuf::GeneratedMessage.$_getN] [@vm.inferred-type.metadata=foo.pb.dart::BarKeep? (skip check)] this.{pro::GeneratedMessage::$_getN}<self::BarKeep*>(0);
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:5] @#C12
+ set barKeep([@vm.inferred-type.metadata=foo.pb.dart::BarKeep] self::BarKeep* v) → void {
+ [@vm.direct-call.metadata=protobuf::GeneratedMessage.setField] [@vm.inferred-type.metadata=!? (skip check)] this.{pro::GeneratedMessage::setField}(1, v);
+ }
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:6] @#C14
+ get mapKeep() → core::Map<core::String*, self::BarKeep*>*
+ return [@vm.direct-call.metadata=protobuf::GeneratedMessage.$_getMap] [@vm.inferred-type.metadata=! (skip check)] this.{pro::GeneratedMessage::$_getMap}<core::String*, self::BarKeep*>(2);
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:7] @#C16
+ get aKeep() → core::int*
+ return [@vm.direct-call.metadata=protobuf::GeneratedMessage.$_getIZ] [@vm.inferred-type.metadata=int (skip check)] this.{pro::GeneratedMessage::$_getIZ}(4);
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:8] @#C16
+ set aKeep([@vm.inferred-type.metadata=dart.core::_Smi (value: 43)] core::int* v) → void {
+ [@vm.direct-call.metadata=protobuf::GeneratedMessage.$_setSignedInt32] [@vm.inferred-type.metadata=!? (skip check)] this.{pro::GeneratedMessage::$_setSignedInt32}(4, v);
+ }
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:9,getterSelectorId:10] @#C18
+ method hasHasKeep() → core::bool*
+ return [@vm.direct-call.metadata=protobuf::GeneratedMessage.$_has] [@vm.inferred-type.metadata=dart.core::bool (skip check)] this.{pro::GeneratedMessage::$_has}(5);
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:11,getterSelectorId:12] @#C20
+ method clearClearKeep() → void
+ return [@vm.direct-call.metadata=protobuf::GeneratedMessage.clearField] [@vm.inferred-type.metadata=dart.core::Null? (skip check) (value: null)] this.{pro::GeneratedMessage::clearField}(7);
+}
+class BarKeep extends pro::GeneratedMessage {
+[@vm.inferred-type.metadata=protobuf::BuilderInfo?] static final field pro::BuilderInfo* _i = let final pro::BuilderInfo* #t2 = new pro::BuilderInfo::•("BarKeep", createEmptyInstance: #C2) in block {
+ [@vm.direct-call.metadata=protobuf::BuilderInfo.a] [@vm.inferred-type.metadata=!? (skip check)] #t2.{pro::BuilderInfo::a}<core::int*>(1, "aKeep", #C5, protoName: "aKeep");
+ [@vm.direct-call.metadata=protobuf::BuilderInfo.add] [@vm.inferred-type.metadata=!? (skip check)] #t2.{pro::BuilderInfo::add}<core::Null?>(0, null, null, null, null, null, null);
+ [@vm.direct-call.metadata=protobuf::BuilderInfo.hasRequiredFields] [@vm.inferred-type.metadata=!? (skip check)] #t2.{pro::BuilderInfo::hasRequiredFields} = false;
+ } =>#t2;
+ constructor _() → self::BarKeep*
+ : super pro::GeneratedMessage::•()
+ ;
+ static factory •() → self::BarKeep*
+ return [@vm.inferred-type.metadata=foo.pb.dart::BarKeep] self::BarKeep::create();
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:3] get info_() → pro::BuilderInfo*
+ return [@vm.inferred-type.metadata=protobuf::BuilderInfo?] self::BarKeep::_i;
+ @#C10
+ static method create() → self::BarKeep*
+ return new self::BarKeep::_();
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:7] @#C12
+ get aKeep() → core::int*
+ return [@vm.direct-call.metadata=protobuf::GeneratedMessage.$_getIZ] [@vm.inferred-type.metadata=int (skip check)] this.{pro::GeneratedMessage::$_getIZ}(0);
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:8] @#C12
+ set aKeep([@vm.inferred-type.metadata=dart.core::_Smi] core::int* v) → void {
+ [@vm.direct-call.metadata=protobuf::GeneratedMessage.$_setSignedInt32] [@vm.inferred-type.metadata=!? (skip check)] this.{pro::GeneratedMessage::$_setSignedInt32}(0, v);
+ }
+}
+class HasKeep extends pro::GeneratedMessage {
+[@vm.inferred-type.metadata=protobuf::BuilderInfo?] static final field pro::BuilderInfo* _i = let final pro::BuilderInfo* #t3 = new pro::BuilderInfo::•("HasKeep", createEmptyInstance: #C6) in block {
+ [@vm.direct-call.metadata=protobuf::BuilderInfo.add] [@vm.inferred-type.metadata=!? (skip check)] #t3.{pro::BuilderInfo::add}<core::Null?>(0, null, null, null, null, null, null);
+ [@vm.direct-call.metadata=protobuf::BuilderInfo.hasRequiredFields] [@vm.inferred-type.metadata=!? (skip check)] #t3.{pro::BuilderInfo::hasRequiredFields} = false;
+ } =>#t3;
+ constructor _() → self::HasKeep*
+ : super pro::GeneratedMessage::•()
+ ;
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:3] get info_() → pro::BuilderInfo*
+ return [@vm.inferred-type.metadata=protobuf::BuilderInfo?] self::HasKeep::_i;
+ @#C10
+ static method create() → self::HasKeep*
+ return new self::HasKeep::_();
+}
+class ClearKeep extends pro::GeneratedMessage {
+[@vm.inferred-type.metadata=protobuf::BuilderInfo?] static final field pro::BuilderInfo* _i = let final pro::BuilderInfo* #t4 = new pro::BuilderInfo::•("ClearKeep", createEmptyInstance: #C7) in block {
+ [@vm.direct-call.metadata=protobuf::BuilderInfo.add] [@vm.inferred-type.metadata=!? (skip check)] #t4.{pro::BuilderInfo::add}<core::Null?>(0, null, null, null, null, null, null);
+ [@vm.direct-call.metadata=protobuf::BuilderInfo.hasRequiredFields] [@vm.inferred-type.metadata=!? (skip check)] #t4.{pro::BuilderInfo::hasRequiredFields} = false;
+ } =>#t4;
+ constructor _() → self::ClearKeep*
+ : super pro::GeneratedMessage::•()
+ ;
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:3] get info_() → pro::BuilderInfo*
+ return [@vm.inferred-type.metadata=protobuf::BuilderInfo?] self::ClearKeep::_i;
+ @#C10
+ static method create() → self::ClearKeep*
+ return new self::ClearKeep::_();
+}
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/decode_test.dart b/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/decode_test.dart
new file mode 100644
index 0000000..d33289f
--- /dev/null
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/decode_test.dart
@@ -0,0 +1,28 @@
+// 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/test.dart';
+
+import 'generated/foo.pb.dart';
+
+List<int> buffer = <int>[
+ 10, 4, 8, 5, 16, //
+ 4, 26, 9, 10, 3,
+ 102, 111, 111, 18, 2,
+ 8, 42, 34, 9, 10,
+ 3, 122, 111, 112, 18,
+ 2, 8, 3, 40, 43,
+ 50, 0, 58, 0,
+];
+
+main() {
+ FooKeep foo = FooKeep.fromBuffer(buffer);
+ test('Kept values are restored correctly', () {
+ expect(foo.mapKeep['foo'].aKeep, 42);
+ expect(foo.barKeep.aKeep, 5);
+ expect(foo.aKeep, 43);
+ expect(foo.hasHasKeep(), true);
+ foo.clearClearKeep();
+ });
+}
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/decode_test.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/decode_test.dart.expect
new file mode 100644
index 0000000..6ffe522
--- /dev/null
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/decode_test.dart.expect
@@ -0,0 +1,138 @@
+library #lib;
+import self as self;
+import "generated/foo.pb.dart" as pb;
+import "package:test_core/test_core.dart" as tes;
+import "dart:core" as core;
+import "package:test_api/src/frontend/expect.dart" as exp;
+
+import "package:test/test.dart";
+import "file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart";
+
+[@vm.inferred-type.metadata=dart.core::_GrowableList?<dart.core::int*>]static field core::List<core::int*>* buffer = <core::int*>[10, 4, 8, 5, 16, 4, 26, 9, 10, 3, 102, 111, 111, 18, 2, 8, 42, 34, 9, 10, 3, 122, 111, 112, 18, 2, 8, 3, 40, 43, 50, 0, 58, 0];
+static method main() → dynamic {
+ pb::FooKeep* foo = [@vm.inferred-type.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::FooKeep] pb::FooKeep::fromBuffer([@vm.inferred-type.metadata=dart.core::_GrowableList?<dart.core::int*>] self::buffer);
+ tes::test("Kept values are restored correctly", () → core::Null? {
+ exp::expect([@vm.direct-call.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::BarKeep.aKeep??] [@vm.inferred-type.metadata=int] [@vm.inferred-type.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::BarKeep?] [@vm.direct-call.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::FooKeep.mapKeep] [@vm.inferred-type.metadata=!] foo.{pb::FooKeep::mapKeep}.{core::Map::[]}("foo").{pb::BarKeep::aKeep}, 42);
+ exp::expect([@vm.direct-call.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::BarKeep.aKeep??] [@vm.inferred-type.metadata=int] [@vm.direct-call.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::FooKeep.barKeep] [@vm.inferred-type.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::BarKeep?] foo.{pb::FooKeep::barKeep}.{pb::BarKeep::aKeep}, 5);
+ exp::expect([@vm.direct-call.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::FooKeep.aKeep] [@vm.inferred-type.metadata=int] foo.{pb::FooKeep::aKeep}, 43);
+ exp::expect([@vm.direct-call.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::FooKeep.hasHasKeep] [@vm.inferred-type.metadata=dart.core::bool (skip check)] foo.{pb::FooKeep::hasHasKeep}(), true);
+ [@vm.direct-call.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::FooKeep.clearClearKeep] [@vm.inferred-type.metadata=!? (skip check)] foo.{pb::FooKeep::clearClearKeep}();
+ });
+}
+library foo.pb.dart;
+import self as self;
+import "package:protobuf/protobuf.dart" as pro;
+import "dart:core" as core;
+
+import "dart:core" as $core;
+import "package:protobuf/protobuf.dart" as $pb;
+
+class FooKeep extends pro::GeneratedMessage {
+[@vm.inferred-type.metadata=protobuf::BuilderInfo?] static final field pro::BuilderInfo* _i = let final pro::BuilderInfo* #t1 = new pro::BuilderInfo::•("FooKeep", createEmptyInstance: #C1) in block {
+ [@vm.direct-call.metadata=protobuf::BuilderInfo.aOM] [@vm.inferred-type.metadata=!? (skip check)] #t1.{pro::BuilderInfo::aOM}<self::BarKeep*>(1, "barKeep", protoName: "barKeep", subBuilder: #C2);
+ [@vm.direct-call.metadata=protobuf::BuilderInfo.add] [@vm.inferred-type.metadata=!? (skip check)] #t1.{pro::BuilderInfo::add}<core::Null?>(0, null, null, null, null, null, null);
+ [@vm.direct-call.metadata=protobuf::BuilderInfo.m] [@vm.inferred-type.metadata=!? (skip check)] #t1.{pro::BuilderInfo::m}<core::String*, self::BarKeep*>(3, "mapKeep", protoName: "mapKeep", entryClassName: "FooKeep.MapKeepEntry", keyFieldType: #C3, valueFieldType: #C4, valueCreator: #C2);
+ [@vm.direct-call.metadata=protobuf::BuilderInfo.add] [@vm.inferred-type.metadata=!? (skip check)] #t1.{pro::BuilderInfo::add}<core::Null?>(0, null, null, null, null, null, null);
+ [@vm.direct-call.metadata=protobuf::BuilderInfo.a] [@vm.inferred-type.metadata=!? (skip check)] #t1.{pro::BuilderInfo::a}<core::int*>(5, "aKeep", #C5, protoName: "aKeep");
+ [@vm.direct-call.metadata=protobuf::BuilderInfo.aOM] [@vm.inferred-type.metadata=!? (skip check)] #t1.{pro::BuilderInfo::aOM}<self::HasKeep*>(6, "hasKeep", protoName: "hasKeep", subBuilder: #C6);
+ [@vm.direct-call.metadata=protobuf::BuilderInfo.aOM] [@vm.inferred-type.metadata=!? (skip check)] #t1.{pro::BuilderInfo::aOM}<self::ClearKeep*>(7, "clearKeep", protoName: "clearKeep", subBuilder: #C7);
+ [@vm.direct-call.metadata=protobuf::BuilderInfo.hasRequiredFields] [@vm.inferred-type.metadata=!? (skip check)] #t1.{pro::BuilderInfo::hasRequiredFields} = false;
+ } =>#t1;
+ constructor _() → self::FooKeep*
+ : super pro::GeneratedMessage::•()
+ ;
+ static factory •() → self::FooKeep*
+ return [@vm.inferred-type.metadata=foo.pb.dart::FooKeep] self::FooKeep::create();
+ static factory fromBuffer([@vm.inferred-type.metadata=dart.core::_GrowableList?<dart.core::int*>] core::List<core::int*>* i, [[@vm.inferred-type.metadata=protobuf::_EmptyExtensionRegistry (value: const protobuf::_EmptyExtensionRegistry{})] pro::ExtensionRegistry* r = #C8]) → self::FooKeep*
+ return let final self::FooKeep* #t2 = [@vm.inferred-type.metadata=foo.pb.dart::FooKeep] self::FooKeep::create() in block {
+ [@vm.direct-call.metadata=protobuf::GeneratedMessage.mergeFromBuffer] [@vm.inferred-type.metadata=!? (skip check)] #t2.{pro::GeneratedMessage::mergeFromBuffer}(i, r);
+ } =>#t2;
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:2,getterSelectorId:3] method clone() → self::FooKeep*
+ return let final self::FooKeep* #t3 = [@vm.inferred-type.metadata=foo.pb.dart::FooKeep] self::FooKeep::•() in block {
+ [@vm.direct-call.metadata=protobuf::GeneratedMessage.mergeFromMessage] [@vm.inferred-type.metadata=!? (skip check)] #t3.{pro::GeneratedMessage::mergeFromMessage}(this);
+ } =>#t3;
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:4] get info_() → pro::BuilderInfo*
+ return [@vm.inferred-type.metadata=protobuf::BuilderInfo?] self::FooKeep::_i;
+ @#C11
+ static method create() → self::FooKeep*
+ return new self::FooKeep::_();
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:5] @#C13
+ get barKeep() → self::BarKeep*
+ return [@vm.direct-call.metadata=protobuf::GeneratedMessage.$_getN] [@vm.inferred-type.metadata=foo.pb.dart::BarKeep? (skip check)] this.{pro::GeneratedMessage::$_getN}<self::BarKeep*>(0);
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:6] @#C15
+ get mapKeep() → core::Map<core::String*, self::BarKeep*>*
+ return [@vm.direct-call.metadata=protobuf::GeneratedMessage.$_getMap] [@vm.inferred-type.metadata=! (skip check)] this.{pro::GeneratedMessage::$_getMap}<core::String*, self::BarKeep*>(2);
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:7] @#C17
+ get aKeep() → core::int*
+ return [@vm.direct-call.metadata=protobuf::GeneratedMessage.$_getIZ] [@vm.inferred-type.metadata=int (skip check)] this.{pro::GeneratedMessage::$_getIZ}(4);
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:8,getterSelectorId:9] @#C19
+ method hasHasKeep() → core::bool*
+ return [@vm.direct-call.metadata=protobuf::GeneratedMessage.$_has] [@vm.inferred-type.metadata=dart.core::bool (skip check)] this.{pro::GeneratedMessage::$_has}(5);
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:10,getterSelectorId:11] @#C21
+ method clearClearKeep() → void
+ return [@vm.direct-call.metadata=protobuf::GeneratedMessage.clearField] [@vm.inferred-type.metadata=dart.core::Null? (skip check) (value: null)] this.{pro::GeneratedMessage::clearField}(7);
+}
+class BarKeep extends pro::GeneratedMessage {
+[@vm.inferred-type.metadata=protobuf::BuilderInfo?] static final field pro::BuilderInfo* _i = let final pro::BuilderInfo* #t4 = new pro::BuilderInfo::•("BarKeep", createEmptyInstance: #C2) in block {
+ [@vm.direct-call.metadata=protobuf::BuilderInfo.a] [@vm.inferred-type.metadata=!? (skip check)] #t4.{pro::BuilderInfo::a}<core::int*>(1, "aKeep", #C5, protoName: "aKeep");
+ [@vm.direct-call.metadata=protobuf::BuilderInfo.add] [@vm.inferred-type.metadata=!? (skip check)] #t4.{pro::BuilderInfo::add}<core::Null?>(0, null, null, null, null, null, null);
+ [@vm.direct-call.metadata=protobuf::BuilderInfo.hasRequiredFields] [@vm.inferred-type.metadata=!? (skip check)] #t4.{pro::BuilderInfo::hasRequiredFields} = false;
+ } =>#t4;
+ constructor _() → self::BarKeep*
+ : super pro::GeneratedMessage::•()
+ ;
+ static factory •() → self::BarKeep*
+ return [@vm.inferred-type.metadata=foo.pb.dart::BarKeep] self::BarKeep::create();
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:2,getterSelectorId:3] method clone() → self::BarKeep*
+ return let final self::BarKeep* #t5 = [@vm.inferred-type.metadata=foo.pb.dart::BarKeep] self::BarKeep::•() in block {
+ [@vm.direct-call.metadata=protobuf::GeneratedMessage.mergeFromMessage] [@vm.inferred-type.metadata=!? (skip check)] #t5.{pro::GeneratedMessage::mergeFromMessage}(this);
+ } =>#t5;
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:4] get info_() → pro::BuilderInfo*
+ return [@vm.inferred-type.metadata=protobuf::BuilderInfo?] self::BarKeep::_i;
+ @#C11
+ static method create() → self::BarKeep*
+ return new self::BarKeep::_();
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:7] @#C13
+ get aKeep() → core::int*
+ return [@vm.direct-call.metadata=protobuf::GeneratedMessage.$_getIZ] [@vm.inferred-type.metadata=int (skip check)] this.{pro::GeneratedMessage::$_getIZ}(0);
+}
+class HasKeep extends pro::GeneratedMessage {
+[@vm.inferred-type.metadata=protobuf::BuilderInfo?] static final field pro::BuilderInfo* _i = let final pro::BuilderInfo* #t6 = new pro::BuilderInfo::•("HasKeep", createEmptyInstance: #C6) in block {
+ [@vm.direct-call.metadata=protobuf::BuilderInfo.add] [@vm.inferred-type.metadata=!? (skip check)] #t6.{pro::BuilderInfo::add}<core::Null?>(0, null, null, null, null, null, null);
+ [@vm.direct-call.metadata=protobuf::BuilderInfo.hasRequiredFields] [@vm.inferred-type.metadata=!? (skip check)] #t6.{pro::BuilderInfo::hasRequiredFields} = false;
+ } =>#t6;
+ constructor _() → self::HasKeep*
+ : super pro::GeneratedMessage::•()
+ ;
+ static factory •() → self::HasKeep*
+ return [@vm.inferred-type.metadata=foo.pb.dart::HasKeep] self::HasKeep::create();
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:2,getterSelectorId:3] method clone() → self::HasKeep*
+ return let final self::HasKeep* #t7 = [@vm.inferred-type.metadata=foo.pb.dart::HasKeep] self::HasKeep::•() in block {
+ [@vm.direct-call.metadata=protobuf::GeneratedMessage.mergeFromMessage] [@vm.inferred-type.metadata=!? (skip check)] #t7.{pro::GeneratedMessage::mergeFromMessage}(this);
+ } =>#t7;
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:4] get info_() → pro::BuilderInfo*
+ return [@vm.inferred-type.metadata=protobuf::BuilderInfo?] self::HasKeep::_i;
+ @#C11
+ static method create() → self::HasKeep*
+ return new self::HasKeep::_();
+}
+class ClearKeep extends pro::GeneratedMessage {
+[@vm.inferred-type.metadata=protobuf::BuilderInfo?] static final field pro::BuilderInfo* _i = let final pro::BuilderInfo* #t8 = new pro::BuilderInfo::•("ClearKeep", createEmptyInstance: #C7) in block {
+ [@vm.direct-call.metadata=protobuf::BuilderInfo.add] [@vm.inferred-type.metadata=!? (skip check)] #t8.{pro::BuilderInfo::add}<core::Null?>(0, null, null, null, null, null, null);
+ [@vm.direct-call.metadata=protobuf::BuilderInfo.hasRequiredFields] [@vm.inferred-type.metadata=!? (skip check)] #t8.{pro::BuilderInfo::hasRequiredFields} = false;
+ } =>#t8;
+ constructor _() → self::ClearKeep*
+ : super pro::GeneratedMessage::•()
+ ;
+ static factory •() → self::ClearKeep*
+ return [@vm.inferred-type.metadata=foo.pb.dart::ClearKeep] self::ClearKeep::create();
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:2,getterSelectorId:3] method clone() → self::ClearKeep*
+ return let final self::ClearKeep* #t9 = [@vm.inferred-type.metadata=foo.pb.dart::ClearKeep] self::ClearKeep::•() in block {
+ [@vm.direct-call.metadata=protobuf::GeneratedMessage.mergeFromMessage] [@vm.inferred-type.metadata=!? (skip check)] #t9.{pro::GeneratedMessage::mergeFromMessage}(this);
+ } =>#t9;
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:4] get info_() → pro::BuilderInfo*
+ return [@vm.inferred-type.metadata=protobuf::BuilderInfo?] self::ClearKeep::_i;
+ @#C11
+ static method create() → self::ClearKeep*
+ return new self::ClearKeep::_();
+}
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/encode_all_fields.dart b/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/encode_all_fields.dart
new file mode 100644
index 0000000..de756cc
--- /dev/null
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/encode_all_fields.dart
@@ -0,0 +1,26 @@
+// 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';
+
+import 'generated/foo.pb.dart';
+
+main() {
+ FooKeep foo = FooKeep()
+ ..barKeep = (BarKeep()
+ ..aKeep = 5
+ ..bDrop = 4)
+ ..mapKeep['foo'] = (BarKeep()..aKeep = 42)
+ ..mapDrop['zop'] = (ZopDrop()..aDrop = 3)
+ ..aKeep = 43
+ ..hasKeep = HasKeep()
+ ..clearKeep = ClearKeep();
+ final buffer = foo.writeToBuffer();
+ print('List<int> buffer = <int>[');
+ for (int i = 0; i < buffer.length; i += 5) {
+ final numbers = buffer.sublist(i, min(buffer.length, i + 5)).join(', ');
+ print(' $numbers,${i == 0 ? ' //' : ''}');
+ }
+ print('];');
+}
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/encode_all_fields.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/encode_all_fields.dart.expect
new file mode 100644
index 0000000..5ac9e51
--- /dev/null
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/encode_all_fields.dart.expect
@@ -0,0 +1,164 @@
+library #lib;
+import self as self;
+import "generated/foo.pb.dart" as pb;
+import "dart:core" as core;
+import "dart:typed_data" as typ;
+import "package:protobuf/protobuf.dart" as pro;
+import "dart:math" as math;
+
+import "dart:math";
+import "file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart";
+
+static method main() → dynamic {
+ pb::FooKeep* foo = let final pb::FooKeep* #t1 = [@vm.inferred-type.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::FooKeep] pb::FooKeep::•() in block {
+ [@vm.direct-call.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::FooKeep.barKeep] [@vm.inferred-type.metadata=!? (skip check)] #t1.{pb::FooKeep::barKeep} = let final pb::BarKeep* #t2 = [@vm.inferred-type.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::BarKeep] pb::BarKeep::•() in block {
+ [@vm.direct-call.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::BarKeep.aKeep] [@vm.inferred-type.metadata=!? (skip check)] #t2.{pb::BarKeep::aKeep} = 5;
+ [@vm.direct-call.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::BarKeep.bDrop] [@vm.inferred-type.metadata=!? (skip check)] #t2.{pb::BarKeep::bDrop} = 4;
+ } =>#t2;
+ [@vm.call-site-attributes.metadata=receiverType:dart.core::Map<dart.core::String*, library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::BarKeep*>*] [@vm.direct-call.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::FooKeep.mapKeep] [@vm.inferred-type.metadata=!] #t1.{pb::FooKeep::mapKeep}.{core::Map::[]=}("foo", let final pb::BarKeep* #t3 = [@vm.inferred-type.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::BarKeep] pb::BarKeep::•() in block {
+ [@vm.direct-call.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::BarKeep.aKeep] [@vm.inferred-type.metadata=!? (skip check)] #t3.{pb::BarKeep::aKeep} = 42;
+ } =>#t3);
+ [@vm.call-site-attributes.metadata=receiverType:dart.core::Map<dart.core::String*, library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::ZopDrop*>*] [@vm.direct-call.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::FooKeep.mapDrop] [@vm.inferred-type.metadata=!] #t1.{pb::FooKeep::mapDrop}.{core::Map::[]=}("zop", let final pb::ZopDrop* #t4 = [@vm.inferred-type.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::ZopDrop] pb::ZopDrop::•() in block {
+ [@vm.direct-call.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::ZopDrop.aDrop] [@vm.inferred-type.metadata=!? (skip check)] #t4.{pb::ZopDrop::aDrop} = 3;
+ } =>#t4);
+ [@vm.direct-call.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::FooKeep.aKeep] [@vm.inferred-type.metadata=!? (skip check)] #t1.{pb::FooKeep::aKeep} = 43;
+ [@vm.direct-call.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::FooKeep.hasKeep] [@vm.inferred-type.metadata=!? (skip check)] #t1.{pb::FooKeep::hasKeep} = [@vm.inferred-type.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::HasKeep] pb::HasKeep::•();
+ [@vm.direct-call.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::FooKeep.clearKeep] [@vm.inferred-type.metadata=!? (skip check)] #t1.{pb::FooKeep::clearKeep} = [@vm.inferred-type.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::ClearKeep] pb::ClearKeep::•();
+ } =>#t1;
+ final typ::Uint8List* buffer = [@vm.direct-call.metadata=protobuf::GeneratedMessage.writeToBuffer] [@vm.inferred-type.metadata=dart.typed_data::_Uint8List (skip check)] foo.{pro::GeneratedMessage::writeToBuffer}();
+ core::print("List<int> buffer = <int>[");
+ for (core::int* i = 0; [@vm.direct-call.metadata=dart.core::_IntegerImplementation.<] [@vm.inferred-type.metadata=dart.core::bool (skip check)] i.{core::num::<}([@vm.direct-call.metadata=dart.typed_data::_TypedList.length] [@vm.inferred-type.metadata=dart.core::_Smi] buffer.{core::List::length}); i = [@vm.direct-call.metadata=dart.core::_IntegerImplementation.+] [@vm.inferred-type.metadata=int (skip check)] i.{core::num::+}(5)) {
+ final core::String* numbers = [@vm.direct-call.metadata=dart.typed_data::_TypedListBase.join] [@vm.inferred-type.metadata=!? (skip check)] [@vm.direct-call.metadata=dart.typed_data::__Uint8List&_TypedList&_IntListMixin&_TypedIntListMixin.sublist] [@vm.inferred-type.metadata=dart.typed_data::_Uint8List (skip check)] buffer.{typ::Uint8List::sublist}(i, [@vm.inferred-type.metadata=int] math::min<core::int*>([@vm.direct-call.metadata=dart.typed_data::_TypedList.length] [@vm.inferred-type.metadata=dart.core::_Smi] buffer.{core::List::length}, [@vm.direct-call.metadata=dart.core::_IntegerImplementation.+] [@vm.inferred-type.metadata=int (skip check)] i.{core::num::+}(5))).{core::Iterable::join}(", ");
+ core::print(" ${numbers},${[@vm.direct-call.metadata=dart.core::_IntegerImplementation.==] [@vm.inferred-type.metadata=dart.core::bool (skip check)] i.{core::num::==}(0) ?{core::String*} " //" : ""}");
+ }
+ core::print("];");
+}
+library foo.pb.dart;
+import self as self;
+import "package:protobuf/protobuf.dart" as pro;
+import "dart:core" as core;
+
+import "dart:core" as $core;
+import "package:protobuf/protobuf.dart" as $pb;
+
+class FooKeep extends pro::GeneratedMessage {
+[@vm.inferred-type.metadata=protobuf::BuilderInfo?] static final field pro::BuilderInfo* _i = let final pro::BuilderInfo* #t1 = new pro::BuilderInfo::•("FooKeep", createEmptyInstance: #C1) in block {
+ [@vm.direct-call.metadata=protobuf::BuilderInfo.aOM] [@vm.inferred-type.metadata=!? (skip check)] #t1.{pro::BuilderInfo::aOM}<self::BarKeep*>(1, "barKeep", protoName: "barKeep", subBuilder: #C2);
+ [@vm.direct-call.metadata=protobuf::BuilderInfo.add] [@vm.inferred-type.metadata=!? (skip check)] #t1.{pro::BuilderInfo::add}<core::Null?>(0, null, null, null, null, null, null);
+ [@vm.direct-call.metadata=protobuf::BuilderInfo.m] [@vm.inferred-type.metadata=!? (skip check)] #t1.{pro::BuilderInfo::m}<core::String*, self::BarKeep*>(3, "mapKeep", protoName: "mapKeep", entryClassName: "FooKeep.MapKeepEntry", keyFieldType: #C3, valueFieldType: #C4, valueCreator: #C2);
+ [@vm.direct-call.metadata=protobuf::BuilderInfo.m] [@vm.inferred-type.metadata=!? (skip check)] #t1.{pro::BuilderInfo::m}<core::String*, self::ZopDrop*>(4, "mapDrop", protoName: "mapDrop", entryClassName: "FooKeep.MapDropEntry", keyFieldType: #C3, valueFieldType: #C4, valueCreator: #C5);
+ [@vm.direct-call.metadata=protobuf::BuilderInfo.a] [@vm.inferred-type.metadata=!? (skip check)] #t1.{pro::BuilderInfo::a}<core::int*>(5, "aKeep", #C6, protoName: "aKeep");
+ [@vm.direct-call.metadata=protobuf::BuilderInfo.aOM] [@vm.inferred-type.metadata=!? (skip check)] #t1.{pro::BuilderInfo::aOM}<self::HasKeep*>(6, "hasKeep", protoName: "hasKeep", subBuilder: #C7);
+ [@vm.direct-call.metadata=protobuf::BuilderInfo.aOM] [@vm.inferred-type.metadata=!? (skip check)] #t1.{pro::BuilderInfo::aOM}<self::ClearKeep*>(7, "clearKeep", protoName: "clearKeep", subBuilder: #C8);
+ [@vm.direct-call.metadata=protobuf::BuilderInfo.hasRequiredFields] [@vm.inferred-type.metadata=!? (skip check)] #t1.{pro::BuilderInfo::hasRequiredFields} = false;
+ } =>#t1;
+ constructor _() → self::FooKeep*
+ : super pro::GeneratedMessage::•()
+ ;
+ static factory •() → self::FooKeep*
+ return [@vm.inferred-type.metadata=foo.pb.dart::FooKeep] self::FooKeep::create();
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:2] get info_() → pro::BuilderInfo*
+ return [@vm.inferred-type.metadata=protobuf::BuilderInfo?] self::FooKeep::_i;
+ @#C11
+ static method create() → self::FooKeep*
+ return new self::FooKeep::_();
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:3] @#C13
+ set barKeep([@vm.inferred-type.metadata=foo.pb.dart::BarKeep] self::BarKeep* v) → void {
+ [@vm.direct-call.metadata=protobuf::GeneratedMessage.setField] [@vm.inferred-type.metadata=!? (skip check)] this.{pro::GeneratedMessage::setField}(1, v);
+ }
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:4] @#C15
+ get mapKeep() → core::Map<core::String*, self::BarKeep*>*
+ return [@vm.direct-call.metadata=protobuf::GeneratedMessage.$_getMap] [@vm.inferred-type.metadata=! (skip check)] this.{pro::GeneratedMessage::$_getMap}<core::String*, self::BarKeep*>(2);
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:5] @#C17
+ get mapDrop() → core::Map<core::String*, self::ZopDrop*>*
+ return [@vm.direct-call.metadata=protobuf::GeneratedMessage.$_getMap] [@vm.inferred-type.metadata=! (skip check)] this.{pro::GeneratedMessage::$_getMap}<core::String*, self::ZopDrop*>(3);
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:6] @#C19
+ set aKeep([@vm.inferred-type.metadata=dart.core::_Smi (value: 43)] core::int* v) → void {
+ [@vm.direct-call.metadata=protobuf::GeneratedMessage.$_setSignedInt32] [@vm.inferred-type.metadata=!? (skip check)] this.{pro::GeneratedMessage::$_setSignedInt32}(4, v);
+ }
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:7] @#C21
+ set hasKeep([@vm.inferred-type.metadata=foo.pb.dart::HasKeep] self::HasKeep* v) → void {
+ [@vm.direct-call.metadata=protobuf::GeneratedMessage.setField] [@vm.inferred-type.metadata=!? (skip check)] this.{pro::GeneratedMessage::setField}(6, v);
+ }
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:8] @#C23
+ set clearKeep([@vm.inferred-type.metadata=foo.pb.dart::ClearKeep] self::ClearKeep* v) → void {
+ [@vm.direct-call.metadata=protobuf::GeneratedMessage.setField] [@vm.inferred-type.metadata=!? (skip check)] this.{pro::GeneratedMessage::setField}(7, v);
+ }
+}
+class BarKeep extends pro::GeneratedMessage {
+[@vm.inferred-type.metadata=protobuf::BuilderInfo?] static final field pro::BuilderInfo* _i = let final pro::BuilderInfo* #t2 = new pro::BuilderInfo::•("BarKeep", createEmptyInstance: #C2) in block {
+ [@vm.direct-call.metadata=protobuf::BuilderInfo.a] [@vm.inferred-type.metadata=!? (skip check)] #t2.{pro::BuilderInfo::a}<core::int*>(1, "aKeep", #C6, protoName: "aKeep");
+ [@vm.direct-call.metadata=protobuf::BuilderInfo.a] [@vm.inferred-type.metadata=!? (skip check)] #t2.{pro::BuilderInfo::a}<core::int*>(2, "bDrop", #C6, protoName: "bDrop");
+ [@vm.direct-call.metadata=protobuf::BuilderInfo.hasRequiredFields] [@vm.inferred-type.metadata=!? (skip check)] #t2.{pro::BuilderInfo::hasRequiredFields} = false;
+ } =>#t2;
+ constructor _() → self::BarKeep*
+ : super pro::GeneratedMessage::•()
+ ;
+ static factory •() → self::BarKeep*
+ return [@vm.inferred-type.metadata=foo.pb.dart::BarKeep] self::BarKeep::create();
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:2] get info_() → pro::BuilderInfo*
+ return [@vm.inferred-type.metadata=protobuf::BuilderInfo?] self::BarKeep::_i;
+ @#C11
+ static method create() → self::BarKeep*
+ return new self::BarKeep::_();
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:6] @#C13
+ set aKeep([@vm.inferred-type.metadata=dart.core::_Smi] core::int* v) → void {
+ [@vm.direct-call.metadata=protobuf::GeneratedMessage.$_setSignedInt32] [@vm.inferred-type.metadata=!? (skip check)] this.{pro::GeneratedMessage::$_setSignedInt32}(0, v);
+ }
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:9] @#C25
+ set bDrop([@vm.inferred-type.metadata=dart.core::_Smi (value: 4)] core::int* v) → void {
+ [@vm.direct-call.metadata=protobuf::GeneratedMessage.$_setSignedInt32] [@vm.inferred-type.metadata=!? (skip check)] this.{pro::GeneratedMessage::$_setSignedInt32}(1, v);
+ }
+}
+class HasKeep extends pro::GeneratedMessage {
+[@vm.inferred-type.metadata=protobuf::BuilderInfo?] static final field pro::BuilderInfo* _i = let final pro::BuilderInfo* #t3 = new pro::BuilderInfo::•("HasKeep", createEmptyInstance: #C7) in block {
+ [@vm.direct-call.metadata=protobuf::BuilderInfo.add] [@vm.inferred-type.metadata=!? (skip check)] #t3.{pro::BuilderInfo::add}<core::Null?>(0, null, null, null, null, null, null);
+ [@vm.direct-call.metadata=protobuf::BuilderInfo.hasRequiredFields] [@vm.inferred-type.metadata=!? (skip check)] #t3.{pro::BuilderInfo::hasRequiredFields} = false;
+ } =>#t3;
+ constructor _() → self::HasKeep*
+ : super pro::GeneratedMessage::•()
+ ;
+ static factory •() → self::HasKeep*
+ return [@vm.inferred-type.metadata=foo.pb.dart::HasKeep] self::HasKeep::create();
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:2] get info_() → pro::BuilderInfo*
+ return [@vm.inferred-type.metadata=protobuf::BuilderInfo?] self::HasKeep::_i;
+ @#C11
+ static method create() → self::HasKeep*
+ return new self::HasKeep::_();
+}
+class ClearKeep extends pro::GeneratedMessage {
+[@vm.inferred-type.metadata=protobuf::BuilderInfo?] static final field pro::BuilderInfo* _i = let final pro::BuilderInfo* #t4 = new pro::BuilderInfo::•("ClearKeep", createEmptyInstance: #C8) in block {
+ [@vm.direct-call.metadata=protobuf::BuilderInfo.add] [@vm.inferred-type.metadata=!? (skip check)] #t4.{pro::BuilderInfo::add}<core::Null?>(0, null, null, null, null, null, null);
+ [@vm.direct-call.metadata=protobuf::BuilderInfo.hasRequiredFields] [@vm.inferred-type.metadata=!? (skip check)] #t4.{pro::BuilderInfo::hasRequiredFields} = false;
+ } =>#t4;
+ constructor _() → self::ClearKeep*
+ : super pro::GeneratedMessage::•()
+ ;
+ static factory •() → self::ClearKeep*
+ return [@vm.inferred-type.metadata=foo.pb.dart::ClearKeep] self::ClearKeep::create();
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:2] get info_() → pro::BuilderInfo*
+ return [@vm.inferred-type.metadata=protobuf::BuilderInfo?] self::ClearKeep::_i;
+ @#C11
+ static method create() → self::ClearKeep*
+ return new self::ClearKeep::_();
+}
+class ZopDrop extends pro::GeneratedMessage {
+[@vm.inferred-type.metadata=protobuf::BuilderInfo?] static final field pro::BuilderInfo* _i = let final pro::BuilderInfo* #t5 = new pro::BuilderInfo::•("ZopDrop", createEmptyInstance: #C5) in block {
+ [@vm.direct-call.metadata=protobuf::BuilderInfo.a] [@vm.inferred-type.metadata=!? (skip check)] #t5.{pro::BuilderInfo::a}<core::int*>(1, "aDrop", #C6, protoName: "aDrop");
+ [@vm.direct-call.metadata=protobuf::BuilderInfo.hasRequiredFields] [@vm.inferred-type.metadata=!? (skip check)] #t5.{pro::BuilderInfo::hasRequiredFields} = false;
+ } =>#t5;
+ constructor _() → self::ZopDrop*
+ : super pro::GeneratedMessage::•()
+ ;
+ static factory •() → self::ZopDrop*
+ return [@vm.inferred-type.metadata=foo.pb.dart::ZopDrop] self::ZopDrop::create();
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:2] get info_() → pro::BuilderInfo*
+ return [@vm.inferred-type.metadata=protobuf::BuilderInfo?] self::ZopDrop::_i;
+ @#C11
+ static method create() → self::ZopDrop*
+ return new self::ZopDrop::_();
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:10] @#C13
+ set aDrop([@vm.inferred-type.metadata=dart.core::_Smi (value: 3)] core::int* v) → void {
+ [@vm.direct-call.metadata=protobuf::GeneratedMessage.$_setSignedInt32] [@vm.inferred-type.metadata=!? (skip check)] this.{pro::GeneratedMessage::$_setSignedInt32}(0, v);
+ }
+}
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/freeze_test.dart b/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/freeze_test.dart
new file mode 100644
index 0000000..0529e4a
--- /dev/null
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/freeze_test.dart
@@ -0,0 +1,23 @@
+// 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/test.dart';
+
+import 'generated/foo.pb.dart';
+
+main() {
+ FooKeep foo = FooKeep()
+ ..barKeep = (BarKeep()..aKeep = 5)
+ ..mapKeep['foo'] = (BarKeep()..aKeep = 2)
+ ..aKeep = 43;
+ test('Freezing a message works', () {
+ foo.freeze();
+ expect(foo.barKeep.aKeep, 5);
+ expect(foo.mapKeep['foo'].aKeep, 2);
+ expect(foo.hasHasKeep(), false);
+ expect(foo.aKeep, 43);
+ expect(() => foo.clearClearKeep(),
+ throwsA(const TypeMatcher<UnsupportedError>()));
+ });
+}
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/freeze_test.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/freeze_test.dart.expect
new file mode 100644
index 0000000..ed24036
--- /dev/null
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/freeze_test.dart.expect
@@ -0,0 +1,137 @@
+library #lib;
+import self as self;
+import "generated/foo.pb.dart" as pb;
+import "dart:core" as core;
+import "package:test_core/test_core.dart" as tes;
+import "package:protobuf/protobuf.dart" as pro;
+import "package:test_api/src/frontend/expect.dart" as exp;
+import "package:test_api/src/frontend/throws_matcher.dart" as thr;
+import "package:matcher/src/type_matcher.dart" as typ;
+
+import "package:test/test.dart";
+import "file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart";
+
+static method main() → dynamic {
+ pb::FooKeep* foo = let final pb::FooKeep* #t1 = [@vm.inferred-type.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::FooKeep] pb::FooKeep::•() in block {
+ [@vm.direct-call.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::FooKeep.barKeep] [@vm.inferred-type.metadata=!? (skip check)] #t1.{pb::FooKeep::barKeep} = let final pb::BarKeep* #t2 = [@vm.inferred-type.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::BarKeep] pb::BarKeep::•() in block {
+ [@vm.direct-call.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::BarKeep.aKeep] [@vm.inferred-type.metadata=!? (skip check)] #t2.{pb::BarKeep::aKeep} = 5;
+ } =>#t2;
+ [@vm.call-site-attributes.metadata=receiverType:dart.core::Map<dart.core::String*, library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::BarKeep*>*] [@vm.direct-call.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::FooKeep.mapKeep] [@vm.inferred-type.metadata=!] #t1.{pb::FooKeep::mapKeep}.{core::Map::[]=}("foo", let final pb::BarKeep* #t3 = [@vm.inferred-type.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::BarKeep] pb::BarKeep::•() in block {
+ [@vm.direct-call.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::BarKeep.aKeep] [@vm.inferred-type.metadata=!? (skip check)] #t3.{pb::BarKeep::aKeep} = 2;
+ } =>#t3);
+ [@vm.direct-call.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::FooKeep.aKeep] [@vm.inferred-type.metadata=!? (skip check)] #t1.{pb::FooKeep::aKeep} = 43;
+ } =>#t1;
+ tes::test("Freezing a message works", () → core::Null? {
+ [@vm.direct-call.metadata=protobuf::GeneratedMessage.freeze] [@vm.inferred-type.metadata=!? (skip check)] foo.{pro::GeneratedMessage::freeze}();
+ exp::expect([@vm.direct-call.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::BarKeep.aKeep??] [@vm.inferred-type.metadata=int] [@vm.direct-call.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::FooKeep.barKeep] [@vm.inferred-type.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::BarKeep?] foo.{pb::FooKeep::barKeep}.{pb::BarKeep::aKeep}, 5);
+ exp::expect([@vm.direct-call.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::BarKeep.aKeep??] [@vm.inferred-type.metadata=int] [@vm.inferred-type.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::BarKeep?] [@vm.direct-call.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::FooKeep.mapKeep] [@vm.inferred-type.metadata=!] foo.{pb::FooKeep::mapKeep}.{core::Map::[]}("foo").{pb::BarKeep::aKeep}, 2);
+ exp::expect([@vm.direct-call.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::FooKeep.hasHasKeep] [@vm.inferred-type.metadata=dart.core::bool (skip check)] foo.{pb::FooKeep::hasHasKeep}(), false);
+ exp::expect([@vm.direct-call.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::FooKeep.aKeep] [@vm.inferred-type.metadata=int] foo.{pb::FooKeep::aKeep}, 43);
+ exp::expect(() → void => [@vm.direct-call.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::FooKeep.clearClearKeep] [@vm.inferred-type.metadata=!? (skip check)] foo.{pb::FooKeep::clearClearKeep}(), [@vm.inferred-type.metadata=library package:test_api/src/frontend/throws_matcher.dart::Throws] thr::throwsA(#C2));
+ });
+}
+library foo.pb.dart;
+import self as self;
+import "package:protobuf/protobuf.dart" as pro;
+import "dart:core" as core;
+
+import "dart:core" as $core;
+import "package:protobuf/protobuf.dart" as $pb;
+
+class FooKeep extends pro::GeneratedMessage {
+[@vm.inferred-type.metadata=protobuf::BuilderInfo?] static final field pro::BuilderInfo* _i = let final pro::BuilderInfo* #t1 = new pro::BuilderInfo::•("FooKeep", createEmptyInstance: #C1) in block {
+ [@vm.direct-call.metadata=protobuf::BuilderInfo.aOM] [@vm.inferred-type.metadata=!? (skip check)] #t1.{pro::BuilderInfo::aOM}<self::BarKeep*>(1, "barKeep", protoName: "barKeep", subBuilder: #C2);
+ [@vm.direct-call.metadata=protobuf::BuilderInfo.add] [@vm.inferred-type.metadata=!? (skip check)] #t1.{pro::BuilderInfo::add}<core::Null?>(0, null, null, null, null, null, null);
+ [@vm.direct-call.metadata=protobuf::BuilderInfo.m] [@vm.inferred-type.metadata=!? (skip check)] #t1.{pro::BuilderInfo::m}<core::String*, self::BarKeep*>(3, "mapKeep", protoName: "mapKeep", entryClassName: "FooKeep.MapKeepEntry", keyFieldType: #C3, valueFieldType: #C4, valueCreator: #C2);
+ [@vm.direct-call.metadata=protobuf::BuilderInfo.add] [@vm.inferred-type.metadata=!? (skip check)] #t1.{pro::BuilderInfo::add}<core::Null?>(0, null, null, null, null, null, null);
+ [@vm.direct-call.metadata=protobuf::BuilderInfo.a] [@vm.inferred-type.metadata=!? (skip check)] #t1.{pro::BuilderInfo::a}<core::int*>(5, "aKeep", #C5, protoName: "aKeep");
+ [@vm.direct-call.metadata=protobuf::BuilderInfo.aOM] [@vm.inferred-type.metadata=!? (skip check)] #t1.{pro::BuilderInfo::aOM}<self::HasKeep*>(6, "hasKeep", protoName: "hasKeep", subBuilder: #C6);
+ [@vm.direct-call.metadata=protobuf::BuilderInfo.aOM] [@vm.inferred-type.metadata=!? (skip check)] #t1.{pro::BuilderInfo::aOM}<self::ClearKeep*>(7, "clearKeep", protoName: "clearKeep", subBuilder: #C7);
+ [@vm.direct-call.metadata=protobuf::BuilderInfo.hasRequiredFields] [@vm.inferred-type.metadata=!? (skip check)] #t1.{pro::BuilderInfo::hasRequiredFields} = false;
+ } =>#t1;
+ constructor _() → self::FooKeep*
+ : super pro::GeneratedMessage::•()
+ ;
+ static factory •() → self::FooKeep*
+ return [@vm.inferred-type.metadata=foo.pb.dart::FooKeep] self::FooKeep::create();
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:3] get info_() → pro::BuilderInfo*
+ return [@vm.inferred-type.metadata=protobuf::BuilderInfo?] self::FooKeep::_i;
+ @#C10
+ static method create() → self::FooKeep*
+ return new self::FooKeep::_();
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:4] @#C12
+ get barKeep() → self::BarKeep*
+ return [@vm.direct-call.metadata=protobuf::GeneratedMessage.$_getN] [@vm.inferred-type.metadata=foo.pb.dart::BarKeep? (skip check)] this.{pro::GeneratedMessage::$_getN}<self::BarKeep*>(0);
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:5] @#C12
+ set barKeep([@vm.inferred-type.metadata=foo.pb.dart::BarKeep] self::BarKeep* v) → void {
+ [@vm.direct-call.metadata=protobuf::GeneratedMessage.setField] [@vm.inferred-type.metadata=!? (skip check)] this.{pro::GeneratedMessage::setField}(1, v);
+ }
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:6] @#C14
+ get mapKeep() → core::Map<core::String*, self::BarKeep*>*
+ return [@vm.direct-call.metadata=protobuf::GeneratedMessage.$_getMap] [@vm.inferred-type.metadata=! (skip check)] this.{pro::GeneratedMessage::$_getMap}<core::String*, self::BarKeep*>(2);
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:7] @#C16
+ get aKeep() → core::int*
+ return [@vm.direct-call.metadata=protobuf::GeneratedMessage.$_getIZ] [@vm.inferred-type.metadata=int (skip check)] this.{pro::GeneratedMessage::$_getIZ}(4);
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:8] @#C16
+ set aKeep([@vm.inferred-type.metadata=dart.core::_Smi (value: 43)] core::int* v) → void {
+ [@vm.direct-call.metadata=protobuf::GeneratedMessage.$_setSignedInt32] [@vm.inferred-type.metadata=!? (skip check)] this.{pro::GeneratedMessage::$_setSignedInt32}(4, v);
+ }
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:9,getterSelectorId:10] @#C18
+ method hasHasKeep() → core::bool*
+ return [@vm.direct-call.metadata=protobuf::GeneratedMessage.$_has] [@vm.inferred-type.metadata=dart.core::bool (skip check)] this.{pro::GeneratedMessage::$_has}(5);
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:11,getterSelectorId:12] @#C20
+ method clearClearKeep() → void
+ return [@vm.direct-call.metadata=protobuf::GeneratedMessage.clearField] [@vm.inferred-type.metadata=dart.core::Null? (skip check) (value: null)] this.{pro::GeneratedMessage::clearField}(7);
+}
+class BarKeep extends pro::GeneratedMessage {
+[@vm.inferred-type.metadata=protobuf::BuilderInfo?] static final field pro::BuilderInfo* _i = let final pro::BuilderInfo* #t2 = new pro::BuilderInfo::•("BarKeep", createEmptyInstance: #C2) in block {
+ [@vm.direct-call.metadata=protobuf::BuilderInfo.a] [@vm.inferred-type.metadata=!? (skip check)] #t2.{pro::BuilderInfo::a}<core::int*>(1, "aKeep", #C5, protoName: "aKeep");
+ [@vm.direct-call.metadata=protobuf::BuilderInfo.add] [@vm.inferred-type.metadata=!? (skip check)] #t2.{pro::BuilderInfo::add}<core::Null?>(0, null, null, null, null, null, null);
+ [@vm.direct-call.metadata=protobuf::BuilderInfo.hasRequiredFields] [@vm.inferred-type.metadata=!? (skip check)] #t2.{pro::BuilderInfo::hasRequiredFields} = false;
+ } =>#t2;
+ constructor _() → self::BarKeep*
+ : super pro::GeneratedMessage::•()
+ ;
+ static factory •() → self::BarKeep*
+ return [@vm.inferred-type.metadata=foo.pb.dart::BarKeep] self::BarKeep::create();
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:3] get info_() → pro::BuilderInfo*
+ return [@vm.inferred-type.metadata=protobuf::BuilderInfo?] self::BarKeep::_i;
+ @#C10
+ static method create() → self::BarKeep*
+ return new self::BarKeep::_();
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:7] @#C12
+ get aKeep() → core::int*
+ return [@vm.direct-call.metadata=protobuf::GeneratedMessage.$_getIZ] [@vm.inferred-type.metadata=int (skip check)] this.{pro::GeneratedMessage::$_getIZ}(0);
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:8] @#C12
+ set aKeep([@vm.inferred-type.metadata=dart.core::_Smi] core::int* v) → void {
+ [@vm.direct-call.metadata=protobuf::GeneratedMessage.$_setSignedInt32] [@vm.inferred-type.metadata=!? (skip check)] this.{pro::GeneratedMessage::$_setSignedInt32}(0, v);
+ }
+}
+class HasKeep extends pro::GeneratedMessage {
+[@vm.inferred-type.metadata=protobuf::BuilderInfo?] static final field pro::BuilderInfo* _i = let final pro::BuilderInfo* #t3 = new pro::BuilderInfo::•("HasKeep", createEmptyInstance: #C6) in block {
+ [@vm.direct-call.metadata=protobuf::BuilderInfo.add] [@vm.inferred-type.metadata=!? (skip check)] #t3.{pro::BuilderInfo::add}<core::Null?>(0, null, null, null, null, null, null);
+ [@vm.direct-call.metadata=protobuf::BuilderInfo.hasRequiredFields] [@vm.inferred-type.metadata=!? (skip check)] #t3.{pro::BuilderInfo::hasRequiredFields} = false;
+ } =>#t3;
+ constructor _() → self::HasKeep*
+ : super pro::GeneratedMessage::•()
+ ;
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:3] get info_() → pro::BuilderInfo*
+ return [@vm.inferred-type.metadata=protobuf::BuilderInfo?] self::HasKeep::_i;
+ @#C10
+ static method create() → self::HasKeep*
+ return new self::HasKeep::_();
+}
+class ClearKeep extends pro::GeneratedMessage {
+[@vm.inferred-type.metadata=protobuf::BuilderInfo?] static final field pro::BuilderInfo* _i = let final pro::BuilderInfo* #t4 = new pro::BuilderInfo::•("ClearKeep", createEmptyInstance: #C7) in block {
+ [@vm.direct-call.metadata=protobuf::BuilderInfo.add] [@vm.inferred-type.metadata=!? (skip check)] #t4.{pro::BuilderInfo::add}<core::Null?>(0, null, null, null, null, null, null);
+ [@vm.direct-call.metadata=protobuf::BuilderInfo.hasRequiredFields] [@vm.inferred-type.metadata=!? (skip check)] #t4.{pro::BuilderInfo::hasRequiredFields} = false;
+ } =>#t4;
+ constructor _() → self::ClearKeep*
+ : super pro::GeneratedMessage::•()
+ ;
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:3] get info_() → pro::BuilderInfo*
+ return [@vm.inferred-type.metadata=protobuf::BuilderInfo?] self::ClearKeep::_i;
+ @#C10
+ static method create() → self::ClearKeep*
+ return new self::ClearKeep::_();
+}
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart b/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart
new file mode 100644
index 0000000..cab7883
--- /dev/null
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart
@@ -0,0 +1,345 @@
+///
+// Generated code. Do not modify.
+// source: foo.proto
+//
+// @dart = 2.3
+// ignore_for_file: camel_case_types,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type
+
+import 'dart:core' as $core;
+
+import 'package:protobuf/protobuf.dart' as $pb;
+
+class FooKeep extends $pb.GeneratedMessage {
+ static final $pb.BuilderInfo _i =
+ $pb.BuilderInfo('FooKeep', createEmptyInstance: create)
+ ..aOM<BarKeep>(1, 'barKeep',
+ protoName: 'barKeep', subBuilder: BarKeep.create)
+ ..aOM<BarKeep>(2, 'barDrop',
+ protoName: 'barDrop', subBuilder: BarKeep.create)
+ ..m<$core.String, BarKeep>(3, 'mapKeep',
+ protoName: 'mapKeep',
+ entryClassName: 'FooKeep.MapKeepEntry',
+ keyFieldType: $pb.PbFieldType.OS,
+ valueFieldType: $pb.PbFieldType.OM,
+ valueCreator: BarKeep.create)
+ ..m<$core.String, ZopDrop>(4, 'mapDrop',
+ protoName: 'mapDrop',
+ entryClassName: 'FooKeep.MapDropEntry',
+ keyFieldType: $pb.PbFieldType.OS,
+ valueFieldType: $pb.PbFieldType.OM,
+ valueCreator: ZopDrop.create)
+ ..a<$core.int>(5, 'aKeep', $pb.PbFieldType.O3, protoName: 'aKeep')
+ ..aOM<HasKeep>(6, 'hasKeep',
+ protoName: 'hasKeep', subBuilder: HasKeep.create)
+ ..aOM<ClearKeep>(7, 'clearKeep',
+ protoName: 'clearKeep', subBuilder: ClearKeep.create)
+ ..hasRequiredFields = false;
+
+ FooKeep._() : super();
+ factory FooKeep() => create();
+ factory FooKeep.fromBuffer($core.List<$core.int> i,
+ [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+ create()..mergeFromBuffer(i, r);
+ factory FooKeep.fromJson($core.String i,
+ [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+ create()..mergeFromJson(i, r);
+ FooKeep clone() => FooKeep()..mergeFromMessage(this);
+ FooKeep copyWith(void Function(FooKeep) updates) =>
+ super.copyWith((message) => updates(message as FooKeep));
+ $pb.BuilderInfo get info_ => _i;
+ @$core.pragma('dart2js:noInline')
+ static FooKeep create() => FooKeep._();
+ FooKeep createEmptyInstance() => create();
+ static $pb.PbList<FooKeep> createRepeated() => $pb.PbList<FooKeep>();
+ @$core.pragma('dart2js:noInline')
+ static FooKeep getDefault() =>
+ _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<FooKeep>(create);
+ static FooKeep _defaultInstance;
+
+ @$pb.TagNumber(1)
+ BarKeep get barKeep => $_getN(0);
+ @$pb.TagNumber(1)
+ set barKeep(BarKeep v) {
+ setField(1, v);
+ }
+
+ @$pb.TagNumber(1)
+ $core.bool hasBarKeep() => $_has(0);
+ @$pb.TagNumber(1)
+ void clearBarKeep() => clearField(1);
+ @$pb.TagNumber(1)
+ BarKeep ensureBarKeep() => $_ensure(0);
+
+ @$pb.TagNumber(2)
+ BarKeep get barDrop => $_getN(1);
+ @$pb.TagNumber(2)
+ set barDrop(BarKeep v) {
+ setField(2, v);
+ }
+
+ @$pb.TagNumber(2)
+ $core.bool hasBarDrop() => $_has(1);
+ @$pb.TagNumber(2)
+ void clearBarDrop() => clearField(2);
+ @$pb.TagNumber(2)
+ BarKeep ensureBarDrop() => $_ensure(1);
+
+ @$pb.TagNumber(3)
+ $core.Map<$core.String, BarKeep> get mapKeep => $_getMap(2);
+
+ @$pb.TagNumber(4)
+ $core.Map<$core.String, ZopDrop> get mapDrop => $_getMap(3);
+
+ @$pb.TagNumber(5)
+ $core.int get aKeep => $_getIZ(4);
+ @$pb.TagNumber(5)
+ set aKeep($core.int v) {
+ $_setSignedInt32(4, v);
+ }
+
+ @$pb.TagNumber(5)
+ $core.bool hasAKeep() => $_has(4);
+ @$pb.TagNumber(5)
+ void clearAKeep() => clearField(5);
+
+ @$pb.TagNumber(6)
+ HasKeep get hasKeep => $_getN(5);
+ @$pb.TagNumber(6)
+ set hasKeep(HasKeep v) {
+ setField(6, v);
+ }
+
+ @$pb.TagNumber(6)
+ $core.bool hasHasKeep() => $_has(5);
+ @$pb.TagNumber(6)
+ void clearHasKeep() => clearField(6);
+ @$pb.TagNumber(6)
+ HasKeep ensureHasKeep() => $_ensure(5);
+
+ @$pb.TagNumber(7)
+ ClearKeep get clearKeep => $_getN(6);
+ @$pb.TagNumber(7)
+ set clearKeep(ClearKeep v) {
+ setField(7, v);
+ }
+
+ @$pb.TagNumber(7)
+ $core.bool hasClearKeep() => $_has(6);
+ @$pb.TagNumber(7)
+ void clearClearKeep() => clearField(7);
+ @$pb.TagNumber(7)
+ ClearKeep ensureClearKeep() => $_ensure(6);
+}
+
+class BarKeep extends $pb.GeneratedMessage {
+ static final $pb.BuilderInfo _i =
+ $pb.BuilderInfo('BarKeep', createEmptyInstance: create)
+ ..a<$core.int>(1, 'aKeep', $pb.PbFieldType.O3, protoName: 'aKeep')
+ ..a<$core.int>(2, 'bDrop', $pb.PbFieldType.O3, protoName: 'bDrop')
+ ..hasRequiredFields = false;
+
+ BarKeep._() : super();
+ factory BarKeep() => create();
+ factory BarKeep.fromBuffer($core.List<$core.int> i,
+ [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+ create()..mergeFromBuffer(i, r);
+ factory BarKeep.fromJson($core.String i,
+ [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+ create()..mergeFromJson(i, r);
+ BarKeep clone() => BarKeep()..mergeFromMessage(this);
+ BarKeep copyWith(void Function(BarKeep) updates) =>
+ super.copyWith((message) => updates(message as BarKeep));
+ $pb.BuilderInfo get info_ => _i;
+ @$core.pragma('dart2js:noInline')
+ static BarKeep create() => BarKeep._();
+ BarKeep createEmptyInstance() => create();
+ static $pb.PbList<BarKeep> createRepeated() => $pb.PbList<BarKeep>();
+ @$core.pragma('dart2js:noInline')
+ static BarKeep getDefault() =>
+ _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<BarKeep>(create);
+ static BarKeep _defaultInstance;
+
+ @$pb.TagNumber(1)
+ $core.int get aKeep => $_getIZ(0);
+ @$pb.TagNumber(1)
+ set aKeep($core.int v) {
+ $_setSignedInt32(0, v);
+ }
+
+ @$pb.TagNumber(1)
+ $core.bool hasAKeep() => $_has(0);
+ @$pb.TagNumber(1)
+ void clearAKeep() => clearField(1);
+
+ @$pb.TagNumber(2)
+ $core.int get bDrop => $_getIZ(1);
+ @$pb.TagNumber(2)
+ set bDrop($core.int v) {
+ $_setSignedInt32(1, v);
+ }
+
+ @$pb.TagNumber(2)
+ $core.bool hasBDrop() => $_has(1);
+ @$pb.TagNumber(2)
+ void clearBDrop() => clearField(2);
+}
+
+class HasKeep extends $pb.GeneratedMessage {
+ static final $pb.BuilderInfo _i =
+ $pb.BuilderInfo('HasKeep', createEmptyInstance: create)
+ ..a<$core.int>(1, 'aDrop', $pb.PbFieldType.O3, protoName: 'aDrop')
+ ..hasRequiredFields = false;
+
+ HasKeep._() : super();
+ factory HasKeep() => create();
+ factory HasKeep.fromBuffer($core.List<$core.int> i,
+ [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+ create()..mergeFromBuffer(i, r);
+ factory HasKeep.fromJson($core.String i,
+ [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+ create()..mergeFromJson(i, r);
+ HasKeep clone() => HasKeep()..mergeFromMessage(this);
+ HasKeep copyWith(void Function(HasKeep) updates) =>
+ super.copyWith((message) => updates(message as HasKeep));
+ $pb.BuilderInfo get info_ => _i;
+ @$core.pragma('dart2js:noInline')
+ static HasKeep create() => HasKeep._();
+ HasKeep createEmptyInstance() => create();
+ static $pb.PbList<HasKeep> createRepeated() => $pb.PbList<HasKeep>();
+ @$core.pragma('dart2js:noInline')
+ static HasKeep getDefault() =>
+ _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<HasKeep>(create);
+ static HasKeep _defaultInstance;
+
+ @$pb.TagNumber(1)
+ $core.int get aDrop => $_getIZ(0);
+ @$pb.TagNumber(1)
+ set aDrop($core.int v) {
+ $_setSignedInt32(0, v);
+ }
+
+ @$pb.TagNumber(1)
+ $core.bool hasADrop() => $_has(0);
+ @$pb.TagNumber(1)
+ void clearADrop() => clearField(1);
+}
+
+class ClearKeep extends $pb.GeneratedMessage {
+ static final $pb.BuilderInfo _i =
+ $pb.BuilderInfo('ClearKeep', createEmptyInstance: create)
+ ..a<$core.int>(1, 'aDrop', $pb.PbFieldType.O3, protoName: 'aDrop')
+ ..hasRequiredFields = false;
+
+ ClearKeep._() : super();
+ factory ClearKeep() => create();
+ factory ClearKeep.fromBuffer($core.List<$core.int> i,
+ [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+ create()..mergeFromBuffer(i, r);
+ factory ClearKeep.fromJson($core.String i,
+ [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+ create()..mergeFromJson(i, r);
+ ClearKeep clone() => ClearKeep()..mergeFromMessage(this);
+ ClearKeep copyWith(void Function(ClearKeep) updates) =>
+ super.copyWith((message) => updates(message as ClearKeep));
+ $pb.BuilderInfo get info_ => _i;
+ @$core.pragma('dart2js:noInline')
+ static ClearKeep create() => ClearKeep._();
+ ClearKeep createEmptyInstance() => create();
+ static $pb.PbList<ClearKeep> createRepeated() => $pb.PbList<ClearKeep>();
+ @$core.pragma('dart2js:noInline')
+ static ClearKeep getDefault() =>
+ _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<ClearKeep>(create);
+ static ClearKeep _defaultInstance;
+
+ @$pb.TagNumber(1)
+ $core.int get aDrop => $_getIZ(0);
+ @$pb.TagNumber(1)
+ set aDrop($core.int v) {
+ $_setSignedInt32(0, v);
+ }
+
+ @$pb.TagNumber(1)
+ $core.bool hasADrop() => $_has(0);
+ @$pb.TagNumber(1)
+ void clearADrop() => clearField(1);
+}
+
+class ZopDrop extends $pb.GeneratedMessage {
+ static final $pb.BuilderInfo _i =
+ $pb.BuilderInfo('ZopDrop', createEmptyInstance: create)
+ ..a<$core.int>(1, 'aDrop', $pb.PbFieldType.O3, protoName: 'aDrop')
+ ..hasRequiredFields = false;
+
+ ZopDrop._() : super();
+ factory ZopDrop() => create();
+ factory ZopDrop.fromBuffer($core.List<$core.int> i,
+ [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+ create()..mergeFromBuffer(i, r);
+ factory ZopDrop.fromJson($core.String i,
+ [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+ create()..mergeFromJson(i, r);
+ ZopDrop clone() => ZopDrop()..mergeFromMessage(this);
+ ZopDrop copyWith(void Function(ZopDrop) updates) =>
+ super.copyWith((message) => updates(message as ZopDrop));
+ $pb.BuilderInfo get info_ => _i;
+ @$core.pragma('dart2js:noInline')
+ static ZopDrop create() => ZopDrop._();
+ ZopDrop createEmptyInstance() => create();
+ static $pb.PbList<ZopDrop> createRepeated() => $pb.PbList<ZopDrop>();
+ @$core.pragma('dart2js:noInline')
+ static ZopDrop getDefault() =>
+ _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<ZopDrop>(create);
+ static ZopDrop _defaultInstance;
+
+ @$pb.TagNumber(1)
+ $core.int get aDrop => $_getIZ(0);
+ @$pb.TagNumber(1)
+ set aDrop($core.int v) {
+ $_setSignedInt32(0, v);
+ }
+
+ @$pb.TagNumber(1)
+ $core.bool hasADrop() => $_has(0);
+ @$pb.TagNumber(1)
+ void clearADrop() => clearField(1);
+}
+
+class MobDrop extends $pb.GeneratedMessage {
+ static final $pb.BuilderInfo _i =
+ $pb.BuilderInfo('MobDrop', createEmptyInstance: create)
+ ..a<$core.int>(1, 'aDrop', $pb.PbFieldType.O3, protoName: 'aDrop')
+ ..hasRequiredFields = false;
+
+ MobDrop._() : super();
+ factory MobDrop() => create();
+ factory MobDrop.fromBuffer($core.List<$core.int> i,
+ [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+ create()..mergeFromBuffer(i, r);
+ factory MobDrop.fromJson($core.String i,
+ [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+ create()..mergeFromJson(i, r);
+ MobDrop clone() => MobDrop()..mergeFromMessage(this);
+ MobDrop copyWith(void Function(MobDrop) updates) =>
+ super.copyWith((message) => updates(message as MobDrop));
+ $pb.BuilderInfo get info_ => _i;
+ @$core.pragma('dart2js:noInline')
+ static MobDrop create() => MobDrop._();
+ MobDrop createEmptyInstance() => create();
+ static $pb.PbList<MobDrop> createRepeated() => $pb.PbList<MobDrop>();
+ @$core.pragma('dart2js:noInline')
+ static MobDrop getDefault() =>
+ _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<MobDrop>(create);
+ static MobDrop _defaultInstance;
+
+ @$pb.TagNumber(1)
+ $core.int get aDrop => $_getIZ(0);
+ @$pb.TagNumber(1)
+ set aDrop($core.int v) {
+ $_setSignedInt32(0, v);
+ }
+
+ @$pb.TagNumber(1)
+ $core.bool hasADrop() => $_has(0);
+ @$pb.TagNumber(1)
+ void clearADrop() => clearField(1);
+}
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/name_mangling.pb.dart b/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/name_mangling.pb.dart
new file mode 100644
index 0000000..bc76b27
--- /dev/null
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/name_mangling.pb.dart
@@ -0,0 +1,80 @@
+///
+// Generated code. Do not modify.
+// source: name_mangling.proto
+//
+// @dart = 2.3
+// ignore_for_file: camel_case_types,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type
+
+import 'dart:core' as $core;
+
+import 'package:protobuf/protobuf.dart' as $pb;
+
+class AKeep extends $pb.GeneratedMessage {
+ static final $pb.BuilderInfo _i =
+ $pb.BuilderInfo('AKeep', createEmptyInstance: create)
+ ..hasRequiredFields = false;
+
+ AKeep._() : super();
+ factory AKeep() => create();
+ factory AKeep.fromBuffer($core.List<$core.int> i,
+ [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+ create()..mergeFromBuffer(i, r);
+ factory AKeep.fromJson($core.String i,
+ [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+ create()..mergeFromJson(i, r);
+ AKeep clone() => AKeep()..mergeFromMessage(this);
+ AKeep copyWith(void Function(AKeep) updates) =>
+ super.copyWith((message) => updates(message as AKeep));
+ $pb.BuilderInfo get info_ => _i;
+ @$core.pragma('dart2js:noInline')
+ static AKeep create() => AKeep._();
+ AKeep createEmptyInstance() => create();
+ static $pb.PbList<AKeep> createRepeated() => $pb.PbList<AKeep>();
+ @$core.pragma('dart2js:noInline')
+ static AKeep getDefault() =>
+ _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<AKeep>(create);
+ static AKeep _defaultInstance;
+}
+
+class NameManglingKeep extends $pb.GeneratedMessage {
+ static final $pb.BuilderInfo _i =
+ $pb.BuilderInfo('NameManglingKeep', createEmptyInstance: create)
+ ..aOM<AKeep>(10, 'clone', subBuilder: AKeep.create)
+ ..hasRequiredFields = false;
+
+ NameManglingKeep._() : super();
+ factory NameManglingKeep() => create();
+ factory NameManglingKeep.fromBuffer($core.List<$core.int> i,
+ [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+ create()..mergeFromBuffer(i, r);
+ factory NameManglingKeep.fromJson($core.String i,
+ [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+ create()..mergeFromJson(i, r);
+ NameManglingKeep clone() => NameManglingKeep()..mergeFromMessage(this);
+ NameManglingKeep copyWith(void Function(NameManglingKeep) updates) =>
+ super.copyWith((message) => updates(message as NameManglingKeep));
+ $pb.BuilderInfo get info_ => _i;
+ @$core.pragma('dart2js:noInline')
+ static NameManglingKeep create() => NameManglingKeep._();
+ NameManglingKeep createEmptyInstance() => create();
+ static $pb.PbList<NameManglingKeep> createRepeated() =>
+ $pb.PbList<NameManglingKeep>();
+ @$core.pragma('dart2js:noInline')
+ static NameManglingKeep getDefault() => _defaultInstance ??=
+ $pb.GeneratedMessage.$_defaultFor<NameManglingKeep>(create);
+ static NameManglingKeep _defaultInstance;
+
+ @$pb.TagNumber(10)
+ AKeep get clone_10 => $_getN(0);
+ @$pb.TagNumber(10)
+ set clone_10(AKeep v) {
+ setField(10, v);
+ }
+
+ @$pb.TagNumber(10)
+ $core.bool hasClone_10() => $_has(0);
+ @$pb.TagNumber(10)
+ void clearClone_10() => clearField(10);
+ @$pb.TagNumber(10)
+ AKeep ensureClone_10() => $_ensure(0);
+}
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/name_mangling_test.dart b/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/name_mangling_test.dart
new file mode 100644
index 0000000..b52c5dc
--- /dev/null
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/name_mangling_test.dart
@@ -0,0 +1,14 @@
+// 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/test.dart';
+
+import 'generated/name_mangling.pb.dart';
+
+main() {
+ NameManglingKeep n = NameManglingKeep.fromBuffer([]);
+ if (n.hasClone_10()) {
+ print("Has clone field");
+ }
+}
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/name_mangling_test.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/name_mangling_test.dart.expect
new file mode 100644
index 0000000..c392e78
--- /dev/null
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/name_mangling_test.dart.expect
@@ -0,0 +1,68 @@
+library #lib;
+import self as self;
+import "generated/name_mangling.pb.dart" as pb;
+import "dart:core" as core;
+
+import "package:test/test.dart";
+import "file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/name_mangling.pb.dart";
+
+static method main() → dynamic {
+ pb::NameManglingKeep* n = [@vm.inferred-type.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/name_mangling.pb.dart::NameManglingKeep] pb::NameManglingKeep::fromBuffer(<core::int*>[]);
+ if([@vm.direct-call.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/name_mangling.pb.dart::NameManglingKeep.hasClone_10] [@vm.inferred-type.metadata=dart.core::bool (skip check)] n.{pb::NameManglingKeep::hasClone_10}()) {
+ core::print("Has clone field");
+ }
+}
+library name_mangling.pb.dart;
+import self as self;
+import "package:protobuf/protobuf.dart" as pro;
+import "dart:core" as core;
+
+import "dart:core" as $core;
+import "package:protobuf/protobuf.dart" as $pb;
+
+class AKeep extends pro::GeneratedMessage {
+[@vm.inferred-type.metadata=protobuf::BuilderInfo?] static final field pro::BuilderInfo* _i = let final pro::BuilderInfo* #t1 = let core::String* #t2 = "AKeep" in let () →* self::AKeep* #t3 = #C1 in new pro::BuilderInfo::•(#t2, #t3) in block {
+ [@vm.direct-call.metadata=protobuf::BuilderInfo.hasRequiredFields] [@vm.inferred-type.metadata=!? (skip check)] #t1.{pro::BuilderInfo::hasRequiredFields} = false;
+ } =>#t1;
+ constructor _() → self::AKeep*
+ : super pro::GeneratedMessage::•()
+ ;
+ static factory •() → self::AKeep*
+ return [@vm.inferred-type.metadata=name_mangling.pb.dart::AKeep] self::AKeep::create();
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:1,getterSelectorId:2] method clone() → self::AKeep*
+ return let final self::AKeep* #t4 = [@vm.inferred-type.metadata=name_mangling.pb.dart::AKeep] self::AKeep::•() in block {
+ [@vm.direct-call.metadata=protobuf::GeneratedMessage.mergeFromMessage] [@vm.inferred-type.metadata=!? (skip check)] #t4.{pro::GeneratedMessage::mergeFromMessage}(this);
+ } =>#t4;
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:3] get info_() → pro::BuilderInfo*
+ return [@vm.inferred-type.metadata=protobuf::BuilderInfo?] self::AKeep::_i;
+ @#C4
+ static method create() → self::AKeep*
+ return new self::AKeep::_();
+}
+class NameManglingKeep extends pro::GeneratedMessage {
+[@vm.inferred-type.metadata=protobuf::BuilderInfo?] static final field pro::BuilderInfo* _i = let final pro::BuilderInfo* #t5 = let core::String* #t6 = "NameManglingKeep" in let () →* self::NameManglingKeep* #t7 = #C5 in new pro::BuilderInfo::•(#t6, #t7) in block {
+ [@vm.direct-call.metadata=protobuf::BuilderInfo.aOM] [@vm.inferred-type.metadata=!? (skip check)] #t5.{pro::BuilderInfo::aOM}<self::AKeep*>(10, "clone", subBuilder: #C1);
+ [@vm.direct-call.metadata=protobuf::BuilderInfo.hasRequiredFields] [@vm.inferred-type.metadata=!? (skip check)] #t5.{pro::BuilderInfo::hasRequiredFields} = false;
+ } =>#t5;
+ constructor _() → self::NameManglingKeep*
+ : super pro::GeneratedMessage::•()
+ ;
+ static factory •() → self::NameManglingKeep*
+ return [@vm.inferred-type.metadata=name_mangling.pb.dart::NameManglingKeep] self::NameManglingKeep::create();
+ static factory fromBuffer([@vm.inferred-type.metadata=dart.core::_GrowableList<dart.core::int*>] core::List<core::int*>* i, [[@vm.inferred-type.metadata=protobuf::_EmptyExtensionRegistry (value: const protobuf::_EmptyExtensionRegistry{})] pro::ExtensionRegistry* r = #C6]) → self::NameManglingKeep*
+ return let final self::NameManglingKeep* #t8 = [@vm.inferred-type.metadata=name_mangling.pb.dart::NameManglingKeep] self::NameManglingKeep::create() in block {
+ [@vm.direct-call.metadata=protobuf::GeneratedMessage.mergeFromBuffer] [@vm.inferred-type.metadata=!? (skip check)] #t8.{pro::GeneratedMessage::mergeFromBuffer}(i, r);
+ } =>#t8;
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:1,getterSelectorId:2] method clone() → self::NameManglingKeep*
+ return let final self::NameManglingKeep* #t9 = [@vm.inferred-type.metadata=name_mangling.pb.dart::NameManglingKeep] self::NameManglingKeep::•() in block {
+ [@vm.direct-call.metadata=protobuf::GeneratedMessage.mergeFromMessage] [@vm.inferred-type.metadata=!? (skip check)] #t9.{pro::GeneratedMessage::mergeFromMessage}(this);
+ } =>#t9;
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:3] get info_() → pro::BuilderInfo*
+ return [@vm.inferred-type.metadata=protobuf::BuilderInfo?] self::NameManglingKeep::_i;
+ @#C4
+ static method create() → self::NameManglingKeep*
+ return new self::NameManglingKeep::_();
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:4,getterSelectorId:5] @#C8
+ method hasClone_10() → core::bool*
+ return [@vm.direct-call.metadata=protobuf::GeneratedMessage.$_has] [@vm.inferred-type.metadata=dart.core::bool (skip check)] this.{pro::GeneratedMessage::$_has}(0);
+}
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/nop_test.dart b/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/nop_test.dart
new file mode 100644
index 0000000..60997dc
--- /dev/null
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/nop_test.dart
@@ -0,0 +1,9 @@
+// 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.
+
+main() {
+ // Ensures the protobuf-aware tree shaker can also transform non-protobuf
+ // using code.
+ print("hello world");
+}
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/nop_test.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/nop_test.dart.expect
new file mode 100644
index 0000000..7512991
--- /dev/null
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/nop_test.dart.expect
@@ -0,0 +1,7 @@
+library #lib;
+import self as self;
+import "dart:core" as core;
+
+static method main() → dynamic {
+ core::print("hello world");
+}
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/protos/foo.proto b/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/protos/foo.proto
new file mode 100644
index 0000000..1f10872
--- /dev/null
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/protos/foo.proto
@@ -0,0 +1,36 @@
+// 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.
+
+syntax = "proto3";
+
+message FooKeep {
+ BarKeep barKeep = 1;
+ BarKeep barDrop = 2;
+ map<string, BarKeep> mapKeep = 3;
+ map<string, ZopDrop> mapDrop = 4;
+ int32 aKeep = 5;
+ HasKeep hasKeep = 6;
+ ClearKeep clearKeep = 7;
+}
+
+message BarKeep {
+ int32 aKeep = 1;
+ int32 bDrop = 2;
+}
+
+message HasKeep {
+ int32 aDrop = 1;
+}
+
+message ClearKeep {
+ int32 aDrop = 1;
+}
+
+message ZopDrop {
+ int32 aDrop = 1;
+}
+
+message MobDrop {
+ int32 aDrop = 1;
+}
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/protos/name_mangling.proto b/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/protos/name_mangling.proto
new file mode 100644
index 0000000..f25b2f4
--- /dev/null
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/protos/name_mangling.proto
@@ -0,0 +1,10 @@
+syntax = "proto3";
+
+message AKeep {}
+
+message NameManglingKeep {
+ // the name `clone` is mangled by the protoc_plugin to not conflict with
+ // `GeneratedMessage.clone`.
+ // Still we should be able to detect usages of this field.
+ AKeep clone = 10;
+}
\ No newline at end of file
diff --git a/pkg/vm/tool/precompiler2 b/pkg/vm/tool/precompiler2
index b76c8bb..dd5b9be 100755
--- a/pkg/vm/tool/precompiler2
+++ b/pkg/vm/tool/precompiler2
@@ -34,6 +34,8 @@
;;
--tfa | \
--no-tfa | \
+ --protobuf-tree-shaker | \
+ --protobuf-tree-shaker-v2 | \
--gen-bytecode | \
--no-gen-bytecode | \
--bytecode-options=* | \
diff --git a/pkg/vm_snapshot_analysis/.gitignore b/pkg/vm_snapshot_analysis/.gitignore
new file mode 100644
index 0000000..49ce72d
--- /dev/null
+++ b/pkg/vm_snapshot_analysis/.gitignore
@@ -0,0 +1,3 @@
+.dart_tool/
+.packages
+pubspec.lock
diff --git a/pkg/vm_snapshot_analysis/AUTHORS b/pkg/vm_snapshot_analysis/AUTHORS
new file mode 100644
index 0000000..846e4a1
--- /dev/null
+++ b/pkg/vm_snapshot_analysis/AUTHORS
@@ -0,0 +1,6 @@
+# Below is a list of people and organizations that have contributed
+# to the Dart project. Names should be added to the list like so:
+#
+# Name/Organization <email address>
+
+Google LLC
diff --git a/pkg/vm_snapshot_analysis/CHANGELOG.md b/pkg/vm_snapshot_analysis/CHANGELOG.md
new file mode 100644
index 0000000..3742d31
--- /dev/null
+++ b/pkg/vm_snapshot_analysis/CHANGELOG.md
@@ -0,0 +1,5 @@
+# Changelog
+
+## 0.1.0
+
+- Initial release
diff --git a/pkg/vm_snapshot_analysis/LICENSE b/pkg/vm_snapshot_analysis/LICENSE
new file mode 100644
index 0000000..18daf2b
--- /dev/null
+++ b/pkg/vm_snapshot_analysis/LICENSE
@@ -0,0 +1,26 @@
+Copyright 2020, the Dart project authors. All rights reserved.
+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 Inc. 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/vm_snapshot_analysis/README.md b/pkg/vm_snapshot_analysis/README.md
new file mode 100644
index 0000000..92cf613
--- /dev/null
+++ b/pkg/vm_snapshot_analysis/README.md
@@ -0,0 +1,137 @@
+# vm_snapshot_analysis
+
+This package provides libraries and a utility for analysing the size and
+contents of Dart VM AOT snapshots based on the output of
+`--print-instructions-sizes-to` and `--write-v8-snapshot-profile-to` VM flags.
+
+## AOT Snapshot Basics
+
+Dart VM AOT snapshot is simply a serialized representation of the Dart VM
+heap graph. It consists of two parts: data (e.g. strings, `const` instances,
+objects representing classes, libraries, functions and runtime metadata) and
+executable code (machine code generated from Dart sources). Some nodes in this
+graph have clean and direct relationship to the original program (e.g. objects
+representing libraries, classes, functions), while other nodes don't. Bitwise
+equivalent objects can be deduplicated and shared (e.g. two functions with the
+same body will end up using the same machine code object). This makes
+impossible to attribute of every single byte from the snapshot to a particular
+place in the program with a 100% accuracy.
+
+* `--print-instructions-sizes-to` attributes _executable code_ from the snapshot
+to a particular Dart function (or internal stub) from which this code
+originated (ignoring deduplication). Executable code usually constitutes around
+half of the snapshot, those this varies depending on the application.
+* `--write-v8-snapshot-profile-to` is a graph representation of the snapshot,
+it attributes bytes written into a snapshot to a node in the heap graph. This
+format covers both data and code sections of the snapshot.
+
+## CLI
+
+The command line interface to the tools in this package is provided by a single
+entry point `bin/analyse.dart`. It consumes output of
+`--print-instructions-sizes-to` and `--write-v8-snapshot-profile-to` flags and
+presents it in different human readable ways.
+
+This script can be intalled globally as `snapshot_analysis` using
+
+```console
+$ pub global activate vm_snapshot_analysis
+```
+
+`snapshot_analysis` supports the following subcommands:
+
+### `summary`
+
+```console
+$ snapshot_analysis summary [-b granularity] [-w filter] <input.json>
+```
+
+This command shows breakdown of snapshot bytes at the given `granularity` (e.g.
+`method`, `class`, `library` or `package`), filtered by the given substring
+`filter`.
+
+For example, here is a output showing how many bytes from a snapshot
+can be attributed to classes in the `dart:core` library:
+
+```console
+$ pkg/vm/bin/snapshot_analysis.dart summary -b class -w dart:core profile.json
++-----------+------------------------+--------------+---------+----------+
+| Library | Class | Size (Bytes) | Percent | Of total |
++-----------+------------------------+--------------+---------+----------+
+| dart:core | _Uri | 43563 | 15.53% | 5.70% |
+| dart:core | _StringBase | 28831 | 10.28% | 3.77% |
+| dart:core | :: | 27559 | 9.83% | 3.60% |
+| @other | | 25467 | 9.08% | 3.33% |
+| dart:core | Uri | 14936 | 5.33% | 1.95% |
+| dart:core | int | 12276 | 4.38% | 1.61% |
+| dart:core | NoSuchMethodError | 12222 | 4.36% | 1.60% |
+...
+```
+
+Here objects which can be attributed to `_Uri` take `5.7%` of the snapshot,
+at the same time objects which can be attributed to `dart:core` library
+but not to any specific class within this library take `3.33%` of the snapshot.
+
+
+### `compare`
+
+```console
+$ snapshot_analysis compare [-b granularity] <old.json> <new.json>
+```
+
+This command shows comparison between two size profiles, allowing to understand
+changes to which part of the program contributed most to the change in the
+overall snapshot size.
+
+```console
+$ pkg/vm/bin/snapshot_analysis.dart compare -b class old.json new.json
++------------------------+--------------------------+--------------+---------+
+| Library | Class | Diff (Bytes) | Percent |
++------------------------+--------------------------+--------------+---------+
+| dart:core | _SimpleUri | 11519 | 22.34% |
+| dart:core | _Uri | 6563 | 12.73% |
+| dart:io | _RandomAccessFile | 5337 | 10.35% |
+| @other | | 4009 | 7.78% |
+...
+```
+
+In this example `11519` more bytes can be attributed to `_SimpleUri` class in
+`new.json` compared to `old.json`.
+
+### `treemap`
+
+```console
+$ snapshot_analysis treemap <input.json> <output-dir>
+$ google-chrome <output-dir>/index.html
+```
+
+This command generates treemap representation of the information from the
+profile `input.json` and stores it in `output-dir` directory. Treemap can
+later be viewed by opening `<output-dir>/index.html` in the browser of your
+choice.
+
+## API
+
+This package can also be used as a building block for other packages which
+want to analyse VM AOT snapshots.
+
+* `package:vm_snapshot_analysis/instruction_sizes.dart` provides helpers to
+read output of `--print-instructions-sizes-to=...`
+* `package:vm_snapshot_analysis/v8_profile.dart` provides helpers to read
+output of `--write-v8-snapshot-profile-to=...`
+
+Both formats can be converted into a `ProgramInfo` structure which attempts
+to breakdown snapshot size into hierarchical representation of the program
+structure which can be understood by a Dart developer, attributing bytes
+to packages, libraries, classes and functions.
+
+* `package:vm_snapshot_analysis/utils.dart` contains helper method
+`loadProgramInfo` which automatically detects format of the input JSON file
+and creates `ProgramInfo` in an appropriate way, allowing to write code
+which works in the same way with both formats.
+
+## Features and bugs
+
+Please file feature requests and bugs at the [issue tracker][tracker].
+
+[tracker]: https://github.com/dart-lang/sdk/issues
diff --git a/pkg/vm_snapshot_analysis/analysis_options.yaml b/pkg/vm_snapshot_analysis/analysis_options.yaml
new file mode 100644
index 0000000..8981941
--- /dev/null
+++ b/pkg/vm_snapshot_analysis/analysis_options.yaml
@@ -0,0 +1,4 @@
+include: package:pedantic/analysis_options.1.8.0.yaml
+analyzer:
+ exclude:
+ - lib/src/assets/**
diff --git a/pkg/vm/bin/snapshot_analysis.dart b/pkg/vm_snapshot_analysis/bin/analyse.dart
similarity index 67%
rename from pkg/vm/bin/snapshot_analysis.dart
rename to pkg/vm_snapshot_analysis/bin/analyse.dart
index f3a037f..5123fd6 100755
--- a/pkg/vm/bin/snapshot_analysis.dart
+++ b/pkg/vm_snapshot_analysis/bin/analyse.dart
@@ -1,15 +1,17 @@
#!/usr/bin/env dart
+
// 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/command_runner.dart';
-import 'package:vm/snapshot/commands/compare.dart';
-import 'package:vm/snapshot/commands/summary.dart';
-import 'package:vm/snapshot/commands/treemap.dart';
+import 'package:vm_snapshot_analysis/src/commands/compare.dart';
+import 'package:vm_snapshot_analysis/src/commands/summary.dart';
+import 'package:vm_snapshot_analysis/src/commands/treemap.dart';
-final runner = CommandRunner('snapshot_analysis.dart',
+final runner = CommandRunner(Platform.script.pathSegments.last,
'Tools for binary size analysis of Dart VM AOT snapshots.')
..addCommand(TreemapCommand())
..addCommand(CompareCommand())
diff --git a/pkg/vm/lib/snapshot/ascii_table.dart b/pkg/vm_snapshot_analysis/lib/ascii_table.dart
similarity index 97%
rename from pkg/vm/lib/snapshot/ascii_table.dart
rename to pkg/vm_snapshot_analysis/lib/ascii_table.dart
index a1b1df1..a0e5404 100644
--- a/pkg/vm/lib/snapshot/ascii_table.dart
+++ b/pkg/vm_snapshot_analysis/lib/ascii_table.dart
@@ -2,7 +2,7 @@
// 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.
-library vm.snapshot.ascii_table;
+library vm_snapshot_analysis.ascii_table;
import 'dart:math' as math;
@@ -126,7 +126,7 @@
final List<Row> rows = <Row>[];
- AsciiTable({List<dynamic> header, this.maxWidth: unlimitedWidth}) {
+ AsciiTable({List<dynamic> header, this.maxWidth = unlimitedWidth}) {
if (header != null) {
addSeparator();
addRow(header);
diff --git a/pkg/vm/lib/snapshot/instruction_sizes.dart b/pkg/vm_snapshot_analysis/lib/instruction_sizes.dart
similarity index 96%
rename from pkg/vm/lib/snapshot/instruction_sizes.dart
rename to pkg/vm_snapshot_analysis/lib/instruction_sizes.dart
index c05bad8..635f8dd 100644
--- a/pkg/vm/lib/snapshot/instruction_sizes.dart
+++ b/pkg/vm_snapshot_analysis/lib/instruction_sizes.dart
@@ -3,10 +3,10 @@
// BSD-style license that can be found in the LICENSE file.
/// Helper functions for parsing output of `--print-instructions-sizes-to` flag.
-library vm.snapshot.instruction_sizes;
+library vm_snapshot_analysis.instruction_sizes;
-import 'package:vm/snapshot/name.dart';
-import 'package:vm/snapshot/program_info.dart';
+import 'package:vm_snapshot_analysis/name.dart';
+import 'package:vm_snapshot_analysis/program_info.dart';
/// Parse the output of `--print-instructions-sizes-to` saved in the given
/// file [input].
diff --git a/pkg/vm/lib/snapshot/name.dart b/pkg/vm_snapshot_analysis/lib/name.dart
similarity index 98%
rename from pkg/vm/lib/snapshot/name.dart
rename to pkg/vm_snapshot_analysis/lib/name.dart
index e636b34..09b7893 100644
--- a/pkg/vm/lib/snapshot/name.dart
+++ b/pkg/vm_snapshot_analysis/lib/name.dart
@@ -3,7 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
/// Helpers for parsing Code object name produced by Code::QualifiedName
-library vm.snapshot.name;
+library vm_snapshot_analysis.name;
// Wrapper around the name of a Code object produced by Code::QualifiedName.
//
diff --git a/pkg/vm/lib/snapshot/program_info.dart b/pkg/vm_snapshot_analysis/lib/program_info.dart
similarity index 69%
rename from pkg/vm/lib/snapshot/program_info.dart
rename to pkg/vm_snapshot_analysis/lib/program_info.dart
index 09f233c..56be4ac 100644
--- a/pkg/vm/lib/snapshot/program_info.dart
+++ b/pkg/vm_snapshot_analysis/lib/program_info.dart
@@ -3,10 +3,12 @@
// BSD-style license that can be found in the LICENSE file.
/// Classes for representing information about the program structure.
-library vm.snapshot.program_info;
+library vm_snapshot_analysis.program_info;
import 'package:meta/meta.dart';
+import 'package:vm_snapshot_analysis/v8_profile.dart';
+
/// Represents information about compiled program.
class ProgramInfo {
static const int rootId = 0;
@@ -18,6 +20,10 @@
final ProgramInfoNode unknown;
int _nextId = 3;
+ /// V8 snapshot profile if this [ProgramInfo] object was created from an
+ /// output of `--write-v8-snapshot-profile-to=...` flag.
+ SnapshotInfo snapshotInfo;
+
ProgramInfo._(this.root, this.stubs, this.unknown);
factory ProgramInfo() {
@@ -53,7 +59,8 @@
/// Recursively visit all function nodes, which have [FunctionInfo.info]
/// populated.
void visit(
- void Function(String pkg, String lib, String cls, String fun, int size)
+ void Function(
+ String pkg, String lib, String cls, String fun, ProgramInfoNode n)
callback) {
final context = List<String>(NodeType.values.length);
@@ -65,13 +72,11 @@
context[node._type] = node.name;
}
- if (node.size != null) {
- final String pkg = context[NodeType.packageNode.index];
- final String lib = context[NodeType.libraryNode.index];
- final String cls = context[NodeType.classNode.index];
- final String mem = context[NodeType.functionNode.index];
- callback(pkg, lib, cls, mem, node.size);
- }
+ final String pkg = context[NodeType.packageNode.index];
+ final String lib = context[NodeType.libraryNode.index];
+ final String cls = context[NodeType.classNode.index];
+ final String mem = context[NodeType.functionNode.index];
+ callback(pkg, lib, cls, mem, node);
for (var child in node.children.values) {
recurse(child);
@@ -85,8 +90,8 @@
int get totalSize {
var result = 0;
- visit((pkg, lib, cls, fun, size) {
- result += size;
+ visit((pkg, lib, cls, fun, node) {
+ result += node.size ?? 0;
});
return result;
}
@@ -175,13 +180,11 @@
return <T>{...?a?.keys, ...?b?.keys};
}
-/// Histogram of sizes based on a [ProgramInfo] bucketed using one of the
-/// [HistogramType] rules.
-class SizesHistogram {
+class Histogram {
/// Rule used to produce this histogram. Specifies how bucket names
/// are constructed given (library-uri,class-name,function-name) tuples and
/// how these bucket names can be deconstructed back into human readable form.
- final Bucketing bucketing;
+ final BucketInfo bucketInfo;
/// Histogram buckets.
final Map<String, int> buckets;
@@ -194,24 +197,77 @@
int get length => bySize.length;
- SizesHistogram._(this.bucketing, this.buckets, this.bySize, this.totalSize);
+ Histogram._(this.bucketInfo, this.buckets)
+ : bySize = buckets.keys.toList(growable: false)
+ ..sort((a, b) => buckets[b] - buckets[a]),
+ totalSize = buckets.values.fold(0, (sum, size) => sum + size);
- /// Construct the histogram of specific [type] given a [ProgramInfo].
- static SizesHistogram from(ProgramInfo info, HistogramType type) {
+ static Histogram fromIterable<T>(
+ Iterable<T> entries, {
+ @required int Function(T) sizeOf,
+ @required String Function(T) bucketFor,
+ @required BucketInfo bucketInfo,
+ }) {
+ final buckets = <String, int>{};
+
+ for (var e in entries) {
+ final bucket = bucketFor(e);
+ final size = sizeOf(e);
+ buckets[bucket] = (buckets[bucket] ?? 0) + size;
+ }
+
+ return Histogram._(bucketInfo, buckets);
+ }
+}
+
+/// Construct the histogram of specific [type] given a [ProgramInfo].
+Histogram computeHistogram(ProgramInfo info, HistogramType type,
+ {String filter}) {
+ bool Function(String, String, String) matchesFilter;
+
+ if (filter != null) {
+ final re = RegExp(filter.replaceAll('*', '.*'), caseSensitive: false);
+ matchesFilter = (lib, cls, fun) => re.hasMatch("${lib}::${cls}.${fun}");
+ } else {
+ matchesFilter = (_, __, ___) => true;
+ }
+
+ if (type == HistogramType.byNodeType) {
+ final Set<int> filteredNodes = {};
+ if (filter != null) {
+ info.visit((pkg, lib, cls, fun, node) {
+ if (matchesFilter(lib, cls, fun)) {
+ filteredNodes.add(node.id);
+ }
+ });
+ }
+
+ return Histogram.fromIterable<Node>(
+ info.snapshotInfo.snapshot.nodes.where((n) =>
+ filter == null ||
+ filteredNodes.contains(info.snapshotInfo.ownerOf(n).id)),
+ sizeOf: (n) {
+ return n.selfSize;
+ },
+ bucketFor: (n) => n.type,
+ bucketInfo: const BucketInfo(nameComponents: ['Type']));
+ } else {
final buckets = <String, int>{};
final bucketing = Bucketing._forType[type];
- var totalSize = 0;
- info.visit((pkg, lib, cls, fun, size) {
+ info.visit((pkg, lib, cls, fun, node) {
+ if (node.size == null || node.size == 0) {
+ return;
+ }
+
final bucket = bucketing.bucketFor(pkg, lib, cls, fun);
- buckets[bucket] = (buckets[bucket] ?? 0) + size;
- totalSize += size;
+ if (!matchesFilter(lib, cls, fun)) {
+ return;
+ }
+ buckets[bucket] = (buckets[bucket] ?? 0) + node.size;
});
- final bySize = buckets.keys.toList(growable: false);
- bySize.sort((a, b) => buckets[b] - buckets[a]);
-
- return SizesHistogram._(bucketing, buckets, bySize, totalSize);
+ return Histogram._(bucketing, buckets);
}
}
@@ -220,22 +276,28 @@
byClass,
byLibrary,
byPackage,
+ byNodeType,
}
-abstract class Bucketing {
+class BucketInfo {
/// Specifies which human readable name components can be extracted from
/// the bucket name.
- List<String> get nameComponents;
+ final List<String> nameComponents;
+ /// Deconstructs bucket name into human readable components (the order matches
+ /// one returned by [nameComponents]).
+ List<String> namesFromBucket(String bucket) => [bucket];
+
+ const BucketInfo({@required this.nameComponents});
+}
+
+abstract class Bucketing extends BucketInfo {
/// Constructs the bucket name from the given library name [lib], class name
/// [cls] and function name [fun].
String bucketFor(String pkg, String lib, String cls, String fun);
- /// Deconstructs bucket name into human readable components (the order matches
- /// one returned by [nameComponents]).
- List<String> namesFromBucket(String bucket);
-
- const Bucketing();
+ const Bucketing({@required List<String> nameComponents})
+ : super(nameComponents: nameComponents);
static const _forType = {
HistogramType.bySymbol: _BucketBySymbol(),
@@ -250,9 +312,6 @@
class _BucketBySymbol extends Bucketing {
@override
- List<String> get nameComponents => const ['Library', 'Symbol'];
-
- @override
String bucketFor(String pkg, String lib, String cls, String fun) {
if (fun == null) {
return '@other${_nameSeparator}';
@@ -263,14 +322,11 @@
@override
List<String> namesFromBucket(String bucket) => bucket.split(_nameSeparator);
- const _BucketBySymbol();
+ const _BucketBySymbol() : super(nameComponents: const ['Library', 'Symbol']);
}
class _BucketByClass extends Bucketing {
@override
- List<String> get nameComponents => ['Library', 'Class'];
-
- @override
String bucketFor(String pkg, String lib, String cls, String fun) {
if (cls == null) {
return '@other${_nameSeparator}';
@@ -281,34 +337,22 @@
@override
List<String> namesFromBucket(String bucket) => bucket.split(_nameSeparator);
- const _BucketByClass();
+ const _BucketByClass() : super(nameComponents: const ['Library', 'Class']);
}
class _BucketByLibrary extends Bucketing {
@override
- List<String> get nameComponents => ['Library'];
-
- @override
String bucketFor(String pkg, String lib, String cls, String fun) => '$lib';
- @override
- List<String> namesFromBucket(String bucket) => [bucket];
-
- const _BucketByLibrary();
+ const _BucketByLibrary() : super(nameComponents: const ['Library']);
}
class _BucketByPackage extends Bucketing {
@override
- List<String> get nameComponents => ['Package'];
-
- @override
String bucketFor(String pkg, String lib, String cls, String fun) =>
pkg ?? lib;
- @override
- List<String> namesFromBucket(String bucket) => [bucket];
-
- const _BucketByPackage();
+ const _BucketByPackage() : super(nameComponents: const ['Package']);
}
String packageOf(String lib) {
diff --git a/pkg/vm_snapshot_analysis/lib/src/assets/D3SymbolTreeMap.js b/pkg/vm_snapshot_analysis/lib/src/assets/D3SymbolTreeMap.js
new file mode 100644
index 0000000..88dd692
--- /dev/null
+++ b/pkg/vm_snapshot_analysis/lib/src/assets/D3SymbolTreeMap.js
@@ -0,0 +1,938 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// TODO:
+// 1. Visibility functions: base on boxPadding.t, not 15
+// 2. Track a maxDisplayDepth that is user-settable:
+// maxDepth == currentRoot.depth + maxDisplayDepth
+function D3SymbolTreeMap(mapWidth, mapHeight, levelsToShow) {
+ this._mapContainer = undefined;
+ this._mapWidth = mapWidth;
+ this._mapHeight = mapHeight;
+ this.boxPadding = {'l': 5, 'r': 5, 't': 20, 'b': 5};
+ this.infobox = undefined;
+ this._maskContainer = undefined;
+ this._highlightContainer = undefined;
+ // Transition in this order:
+ // 1. Exiting items go away.
+ // 2. Updated items move.
+ // 3. New items enter.
+ this._exitDuration=500;
+ this._updateDuration=500;
+ this._enterDuration=500;
+ this._firstTransition=true;
+ this._layout = undefined;
+ this._currentRoot = undefined;
+ this._currentNodes = undefined;
+ this._treeData = undefined;
+ this._maxLevelsToShow = levelsToShow;
+ this._currentMaxDepth = this._maxLevelsToShow;
+}
+
+/**
+ * Make a number pretty, with comma separators.
+ */
+D3SymbolTreeMap._pretty = function(num) {
+ var asString = String(num);
+ var result = '';
+ var counter = 0;
+ for (var x = asString.length - 1; x >= 0; x--) {
+ counter++;
+ if (counter === 4) {
+ result = ',' + result;
+ counter = 1;
+ }
+ result = asString.charAt(x) + result;
+ }
+ return result;
+}
+
+/**
+ * Express a number in terms of KiB, MiB, GiB, etc.
+ * Note that these are powers of 2, not of 10.
+ */
+D3SymbolTreeMap._byteify = function(num) {
+ var suffix;
+ if (num >= 1024) {
+ if (num >= 1024 * 1024 * 1024) {
+ suffix = 'GiB';
+ num = num / (1024 * 1024 * 1024);
+ } else if (num >= 1024 * 1024) {
+ suffix = 'MiB';
+ num = num / (1024 * 1024);
+ } else if (num >= 1024) {
+ suffix = 'KiB'
+ num = num / 1024;
+ }
+ return num.toFixed(2) + ' ' + suffix;
+ }
+ return num + ' B';
+}
+
+D3SymbolTreeMap._NM_SYMBOL_TYPE_DESCRIPTIONS = {
+ // Definitions concisely derived from the nm 'man' page
+ 'A': 'Global absolute (A)',
+ 'B': 'Global uninitialized data (B)',
+ 'b': 'Local uninitialized data (b)',
+ 'C': 'Global uninitialized common (C)',
+ 'D': 'Global initialized data (D)',
+ 'd': 'Local initialized data (d)',
+ 'G': 'Global small initialized data (G)',
+ 'g': 'Local small initialized data (g)',
+ 'i': 'Indirect function (i)',
+ 'N': 'Debugging (N)',
+ 'p': 'Stack unwind (p)',
+ 'R': 'Global read-only data (R)',
+ 'r': 'Local read-only data (r)',
+ 'S': 'Global small uninitialized data (S)',
+ 's': 'Local small uninitialized data (s)',
+ 'T': 'Global code (T)',
+ 't': 'Local code (t)',
+ 'U': 'Undefined (U)',
+ 'u': 'Unique (u)',
+ 'V': 'Global weak object (V)',
+ 'v': 'Local weak object (v)',
+ 'W': 'Global weak symbol (W)',
+ 'w': 'Local weak symbol (w)',
+ '@': 'Vtable entry (@)', // non-standard, hack.
+ '-': 'STABS debugging (-)',
+ '?': 'Unrecognized (?)',
+};
+D3SymbolTreeMap._NM_SYMBOL_TYPES = '';
+for (var symbol_type in D3SymbolTreeMap._NM_SYMBOL_TYPE_DESCRIPTIONS) {
+ D3SymbolTreeMap._NM_SYMBOL_TYPES += symbol_type;
+}
+
+/**
+ * Given a symbol type code, look up and return a human-readable description
+ * of that symbol type. If the symbol type does not match one of the known
+ * types, the unrecognized description (corresponding to symbol type '?') is
+ * returned instead of null or undefined.
+ */
+D3SymbolTreeMap._getSymbolDescription = function(type) {
+ var result = D3SymbolTreeMap._NM_SYMBOL_TYPE_DESCRIPTIONS[type];
+ if (result === undefined) {
+ result = D3SymbolTreeMap._NM_SYMBOL_TYPE_DESCRIPTIONS['?'];
+ }
+ return result;
+}
+
+// Qualitative 12-value pastel Brewer palette.
+D3SymbolTreeMap._colorArray = [
+ 'rgb(141,211,199)',
+ 'rgb(255,255,179)',
+ 'rgb(190,186,218)',
+ 'rgb(251,128,114)',
+ 'rgb(128,177,211)',
+ 'rgb(253,180,98)',
+ 'rgb(179,222,105)',
+ 'rgb(252,205,229)',
+ 'rgb(217,217,217)',
+ 'rgb(188,128,189)',
+ 'rgb(204,235,197)',
+ 'rgb(255,237,111)'];
+
+D3SymbolTreeMap._initColorMap = function() {
+ var map = {};
+ var numColors = D3SymbolTreeMap._colorArray.length;
+ var count = 0;
+ for (var key in D3SymbolTreeMap._NM_SYMBOL_TYPE_DESCRIPTIONS) {
+ var index = count++ % numColors;
+ map[key] = d3.rgb(D3SymbolTreeMap._colorArray[index]);
+ }
+ D3SymbolTreeMap._colorMap = map;
+}
+D3SymbolTreeMap._initColorMap();
+
+D3SymbolTreeMap.getColorForType = function(type) {
+ var result = D3SymbolTreeMap._colorMap[type];
+ if (result === undefined) return d3.rgb('rgb(255,255,255)');
+ return result;
+}
+
+D3SymbolTreeMap.prototype.init = function() {
+ this.infobox = this._createInfoBox();
+ this._mapContainer = d3.select('body').append('div')
+ .style('position', 'relative')
+ .style('width', this._mapWidth)
+ .style('height', this._mapHeight)
+ .style('padding', 0)
+ .style('margin', 0)
+ .style('box-shadow', '5px 5px 5px #888');
+ this._layout = this._createTreeMapLayout();
+ this._setData(tree_data); // TODO: Don't use global 'tree_data'
+}
+
+/**
+ * Sets the data displayed by the treemap and layint out the map.
+ */
+D3SymbolTreeMap.prototype._setData = function(data) {
+ this._treeData = data;
+ console.time('_crunchStats');
+ this._crunchStats(data);
+ console.timeEnd('_crunchStats');
+ this._currentRoot = this._treeData;
+ this._currentNodes = this._layout.nodes(this._currentRoot);
+ this._currentMaxDepth = this._maxLevelsToShow;
+ this._doLayout();
+}
+
+/**
+ * Recursively traverses the entire tree starting from the specified node,
+ * computing statistics and recording metadata as it goes. Call this method
+ * only once per imported tree.
+ */
+D3SymbolTreeMap.prototype._crunchStats = function(node) {
+ var stack = [];
+ stack.idCounter = 0;
+ this._crunchStatsHelper(stack, node);
+}
+
+/**
+ * Invoke the specified visitor function on all data elements currently shown
+ * in the treemap including any and all of their children, starting at the
+ * currently-displayed root and descending recursively. The function will be
+ * passed the datum element representing each node. No traversal guarantees
+ * are made.
+ */
+D3SymbolTreeMap.prototype.visitFromDisplayedRoot = function(visitor) {
+ this._visit(this._currentRoot, visitor);
+}
+
+/**
+ * Helper function for visit functions.
+ */
+D3SymbolTreeMap.prototype._visit = function(datum, visitor) {
+ visitor.call(this, datum);
+ if (datum.children) for (var i = 0; i < datum.children.length; i++) {
+ this._visit(datum.children[i], visitor);
+ }
+}
+
+D3SymbolTreeMap.prototype._crunchStatsHelper = function(stack, node) {
+ // Only overwrite the node ID if it isn't already set.
+ // This allows stats to be crunched multiple times on subsets of data
+ // without breaking the data-to-ID bindings. New nodes get new IDs.
+ if (node.id === undefined) node.id = stack.idCounter++;
+ if (node.children === undefined) {
+ // Leaf node (symbol); accumulate stats.
+ for (var i = 0; i < stack.length; i++) {
+ var ancestor = stack[i];
+ if (!ancestor.symbol_stats) ancestor.symbol_stats = {};
+ if (ancestor.symbol_stats[node.t] === undefined) {
+ // New symbol type we haven't seen before, just record.
+ ancestor.symbol_stats[node.t] = {'count': 1,
+ 'size': node.value};
+ } else {
+ // Existing symbol type, increment.
+ ancestor.symbol_stats[node.t].count++;
+ ancestor.symbol_stats[node.t].size += node.value;
+ }
+ }
+ } else for (var i = 0; i < node.children.length; i++) {
+ stack.push(node);
+ this._crunchStatsHelper(stack, node.children[i]);
+ stack.pop();
+ }
+}
+
+D3SymbolTreeMap.prototype._createTreeMapLayout = function() {
+ var result = d3.layout.treemap()
+ .padding([this.boxPadding.t, this.boxPadding.r,
+ this.boxPadding.b, this.boxPadding.l])
+ .size([this._mapWidth, this._mapHeight]);
+ return result;
+}
+
+D3SymbolTreeMap.prototype.resize = function(width, height) {
+ this._mapWidth = width;
+ this._mapHeight = height;
+ this._mapContainer.style('width', width).style('height', height);
+ this._layout.size([this._mapWidth, this._mapHeight]);
+ this._currentNodes = this._layout.nodes(this._currentRoot);
+ this._doLayout();
+}
+
+D3SymbolTreeMap.prototype._zoomDatum = function(datum) {
+ if (this._currentRoot === datum) return; // already here
+ this._hideHighlight(datum);
+ this._hideInfoBox(datum);
+ this._currentRoot = datum;
+ this._currentNodes = this._layout.nodes(this._currentRoot);
+ this._currentMaxDepth = this._currentRoot.depth + this._maxLevelsToShow;
+ console.log('zooming into datum ' + this._currentRoot.n);
+ this._doLayout();
+}
+
+D3SymbolTreeMap.prototype.setMaxLevels = function(levelsToShow) {
+ this._maxLevelsToShow = levelsToShow;
+ this._currentNodes = this._layout.nodes(this._currentRoot);
+ this._currentMaxDepth = this._currentRoot.depth + this._maxLevelsToShow;
+ console.log('setting max levels to show: ' + this._maxLevelsToShow);
+ this._doLayout();
+}
+
+/**
+ * Clone the specified tree, returning an independent copy of the data.
+ * Only the original attributes expected to exist prior to invoking
+ * _crunchStatsHelper are retained, with the exception of the 'id' attribute
+ * (which must be retained for proper transitions).
+ * If the optional filter parameter is provided, it will be called with 'this'
+ * set to this treemap instance and passed the 'datum' object as an argument.
+ * When specified, the copy will retain only the data for which the filter
+ * function returns true.
+ */
+D3SymbolTreeMap.prototype._clone = function(datum, filter) {
+ var trackingStats = false;
+ if (this.__cloneState === undefined) {
+ console.time('_clone');
+ trackingStats = true;
+ this.__cloneState = {'accepted': 0, 'rejected': 0,
+ 'forced': 0, 'pruned': 0};
+ }
+
+ // Must go depth-first. All parents of children that are accepted by the
+ // filter must be preserved!
+ var copy = {'n': datum.n, 'k': datum.k};
+ var childAccepted = false;
+ if (datum.children !== undefined) {
+ for (var i = 0; i < datum.children.length; i++) {
+ var copiedChild = this._clone(datum.children[i], filter);
+ if (copiedChild !== undefined) {
+ childAccepted = true; // parent must also be accepted.
+ if (copy.children === undefined) copy.children = [];
+ copy.children.push(copiedChild);
+ }
+ }
+ }
+
+ // Ignore nodes that don't match the filter, when present.
+ var accept = false;
+ if (childAccepted) {
+ // Parent of an accepted child must also be accepted.
+ this.__cloneState.forced++;
+ accept = true;
+ } else if (filter !== undefined && filter.call(this, datum) !== true) {
+ this.__cloneState.rejected++;
+ } else if (datum.children === undefined) {
+ // Accept leaf nodes that passed the filter
+ this.__cloneState.accepted++;
+ accept = true;
+ } else {
+ // Non-leaf node. If no children are accepted, prune it.
+ this.__cloneState.pruned++;
+ }
+
+ if (accept) {
+ if (datum.id !== undefined) copy.id = datum.id;
+ if (datum.lastPathElement !== undefined) {
+ copy.lastPathElement = datum.lastPathElement;
+ }
+ if (datum.t !== undefined) copy.t = datum.t;
+ if (datum.value !== undefined && datum.children === undefined) {
+ copy.value = datum.value;
+ }
+ } else {
+ // Discard the copy we were going to return
+ copy = undefined;
+ }
+
+ if (trackingStats === true) {
+ // We are the fist call in the recursive chain.
+ console.timeEnd('_clone');
+ var totalAccepted = this.__cloneState.accepted +
+ this.__cloneState.forced;
+ console.log(
+ totalAccepted + ' nodes retained (' +
+ this.__cloneState.forced + ' forced by accepted children, ' +
+ this.__cloneState.accepted + ' accepted on their own merits), ' +
+ this.__cloneState.rejected + ' nodes (and their children) ' +
+ 'filtered out,' +
+ this.__cloneState.pruned + ' nodes pruned because because no ' +
+ 'children remained.');
+ delete this.__cloneState;
+ }
+ return copy;
+}
+
+D3SymbolTreeMap.prototype.filter = function(filter) {
+ // Ensure we have a copy of the original root.
+ if (this._backupTree === undefined) this._backupTree = this._treeData;
+ this._mapContainer.selectAll('div').remove();
+ this._setData(this._clone(this._backupTree, filter));
+}
+
+D3SymbolTreeMap.prototype._doLayout = function() {
+ console.time('_doLayout');
+ this._handleInodes();
+ this._handleLeaves();
+ this._firstTransition = false;
+ console.timeEnd('_doLayout');
+}
+
+D3SymbolTreeMap.prototype._highlightElement = function(datum, selection) {
+ this._showHighlight(datum, selection);
+}
+
+D3SymbolTreeMap.prototype._unhighlightElement = function(datum, selection) {
+ this._hideHighlight(datum, selection);
+}
+
+D3SymbolTreeMap.prototype._handleInodes = function() {
+ console.time('_handleInodes');
+ var thisTreeMap = this;
+ var inodes = this._currentNodes.filter(function(datum){
+ return (datum.depth <= thisTreeMap._currentMaxDepth) &&
+ datum.children !== undefined;
+ });
+ var cellsEnter = this._mapContainer.selectAll('div.inode')
+ .data(inodes, function(datum) { return datum.id; })
+ .enter()
+ .append('div').attr('class', 'inode').attr('id', function(datum){
+ return 'node-' + datum.id;});
+
+
+ // Define enter/update/exit for inodes
+ cellsEnter
+ .append('div')
+ .attr('class', 'rect inode_rect_entering')
+ .style('z-index', function(datum) { return datum.id * 2; })
+ .style('position', 'absolute')
+ .style('left', function(datum) { return datum.x; })
+ .style('top', function(datum){ return datum.y; })
+ .style('width', function(datum){ return datum.dx; })
+ .style('height', function(datum){ return datum.dy; })
+ .style('opacity', '0')
+ .style('border', '1px solid black')
+ .style('background-image', function(datum) {
+ return thisTreeMap._makeSymbolBucketBackgroundImage.call(
+ thisTreeMap, datum);
+ })
+ .style('background-color', function(datum) {
+ if (datum.t === undefined) return 'rgb(220,220,220)';
+ return D3SymbolTreeMap.getColorForType(datum.t).toString();
+ })
+ .on('mouseover', function(datum){
+ thisTreeMap._highlightElement.call(
+ thisTreeMap, datum, d3.select(this));
+ thisTreeMap._showInfoBox.call(thisTreeMap, datum);
+ })
+ .on('mouseout', function(datum){
+ thisTreeMap._unhighlightElement.call(
+ thisTreeMap, datum, d3.select(this));
+ thisTreeMap._hideInfoBox.call(thisTreeMap, datum);
+ })
+ .on('mousemove', function(){
+ thisTreeMap._moveInfoBox.call(thisTreeMap, event);
+ })
+ .on('dblclick', function(datum){
+ if (datum !== thisTreeMap._currentRoot) {
+ // Zoom into the selection
+ thisTreeMap._zoomDatum(datum);
+ } else if (datum.parent) {
+ console.log('event.shiftKey=' + event.shiftKey);
+ if (event.shiftKey === true) {
+ // Back to root
+ thisTreeMap._zoomDatum(thisTreeMap._treeData);
+ } else {
+ // Zoom out of the selection
+ thisTreeMap._zoomDatum(datum.parent);
+ }
+ }
+ });
+ cellsEnter
+ .append('div')
+ .attr('class', 'label inode_label_entering')
+ .style('z-index', function(datum) { return (datum.id * 2) + 1; })
+ .style('position', 'absolute')
+ .style('left', function(datum){ return datum.x; })
+ .style('top', function(datum){ return datum.y; })
+ .style('width', function(datum) { return datum.dx; })
+ .style('height', function(datum) { return thisTreeMap.boxPadding.t; })
+ .style('opacity', '0')
+ .style('pointer-events', 'none')
+ .style('-webkit-user-select', 'none')
+ .style('overflow', 'hidden') // required for ellipsis
+ .style('white-space', 'nowrap') // required for ellipsis
+ .style('text-overflow', 'ellipsis')
+ .style('text-align', 'center')
+ .style('vertical-align', 'top')
+ .style('visibility', function(datum) {
+ return (datum.dx < 15 || datum.dy < 15) ? 'hidden' : 'visible';
+ })
+ .text(function(datum) {
+ var sizeish = ' [' + D3SymbolTreeMap._byteify(datum.value) + ']'
+ var text;
+ if (datum.k === 'b') { // bucket
+ if (datum === thisTreeMap._currentRoot) {
+ text = thisTreeMap.pathFor(datum) + ': '
+ + D3SymbolTreeMap._getSymbolDescription(datum.t)
+ } else {
+ text = D3SymbolTreeMap._getSymbolDescription(datum.t);
+ }
+ } else if (datum === thisTreeMap._currentRoot) {
+ // The top-most level should always show the complete path
+ text = thisTreeMap.pathFor(datum);
+ } else {
+ // Anything that isn't a bucket or a leaf (symbol) or the
+ // current root should just show its name.
+ text = datum.n;
+ }
+ return text + sizeish;
+ }
+ );
+
+ // Complicated transition logic:
+ // For nodes that are entering, we want to fade them in in-place AFTER
+ // any adjusting nodes have resized and moved around. That way, new nodes
+ // seamlessly appear in the right spot after their containers have resized
+ // and moved around.
+ // To do this we do some trickery:
+ // 1. Define a '_entering' class on the entering elements
+ // 2. Use this to select only the entering elements and apply the opacity
+ // transition.
+ // 3. Use the same transition to drop the '_entering' suffix, so that they
+ // will correctly update in later zoom/resize/whatever operations.
+ // 4. The update transition is achieved by selecting the elements without
+ // the '_entering_' suffix and applying movement and resizing transition
+ // effects.
+ this._mapContainer.selectAll('div.inode_rect_entering').transition()
+ .duration(thisTreeMap._enterDuration).delay(
+ this._firstTransition ? 0 : thisTreeMap._exitDuration +
+ thisTreeMap._updateDuration)
+ .attr('class', 'rect inode_rect')
+ .style('opacity', '1')
+ this._mapContainer.selectAll('div.inode_label_entering').transition()
+ .duration(thisTreeMap._enterDuration).delay(
+ this._firstTransition ? 0 : thisTreeMap._exitDuration +
+ thisTreeMap._updateDuration)
+ .attr('class', 'label inode_label')
+ .style('opacity', '1')
+ this._mapContainer.selectAll('div.inode_rect').transition()
+ .duration(thisTreeMap._updateDuration).delay(thisTreeMap._exitDuration)
+ .style('opacity', '1')
+ .style('background-image', function(datum) {
+ return thisTreeMap._makeSymbolBucketBackgroundImage.call(
+ thisTreeMap, datum);
+ })
+ .style('left', function(datum) { return datum.x; })
+ .style('top', function(datum){ return datum.y; })
+ .style('width', function(datum){ return datum.dx; })
+ .style('height', function(datum){ return datum.dy; });
+ this._mapContainer.selectAll('div.inode_label').transition()
+ .duration(thisTreeMap._updateDuration).delay(thisTreeMap._exitDuration)
+ .style('opacity', '1')
+ .style('visibility', function(datum) {
+ return (datum.dx < 15 || datum.dy < 15) ? 'hidden' : 'visible';
+ })
+ .style('left', function(datum){ return datum.x; })
+ .style('top', function(datum){ return datum.y; })
+ .style('width', function(datum) { return datum.dx; })
+ .style('height', function(datum) { return thisTreeMap.boxPadding.t; })
+ .text(function(datum) {
+ var sizeish = ' [' + D3SymbolTreeMap._byteify(datum.value) + ']'
+ var text;
+ if (datum.k === 'b') {
+ if (datum === thisTreeMap._currentRoot) {
+ text = thisTreeMap.pathFor(datum) + ': ' +
+ D3SymbolTreeMap._getSymbolDescription(datum.t)
+ } else {
+ text = D3SymbolTreeMap._getSymbolDescription(datum.t);
+ }
+ } else if (datum === thisTreeMap._currentRoot) {
+ // The top-most level should always show the complete path
+ text = thisTreeMap.pathFor(datum);
+ } else {
+ // Anything that isn't a bucket or a leaf (symbol) or the
+ // current root should just show its name.
+ text = datum.n;
+ }
+ return text + sizeish;
+ });
+ var exit = this._mapContainer.selectAll('div.inode')
+ .data(inodes, function(datum) { return 'inode-' + datum.id; })
+ .exit();
+ exit.selectAll('div.inode_rect').transition().duration(
+ thisTreeMap._exitDuration).style('opacity', 0);
+ exit.selectAll('div.inode_label').transition().duration(
+ thisTreeMap._exitDuration).style('opacity', 0);
+ exit.transition().delay(thisTreeMap._exitDuration + 1).remove();
+
+ console.log(inodes.length + ' inodes layed out.');
+ console.timeEnd('_handleInodes');
+}
+
+D3SymbolTreeMap.prototype._handleLeaves = function() {
+ console.time('_handleLeaves');
+ var color_fn = d3.scale.category10();
+ var thisTreeMap = this;
+ var leaves = this._currentNodes.filter(function(datum){
+ return (datum.depth <= thisTreeMap._currentMaxDepth) &&
+ datum.children === undefined; });
+ var cellsEnter = this._mapContainer.selectAll('div.leaf')
+ .data(leaves, function(datum) { return datum.id; })
+ .enter()
+ .append('div').attr('class', 'leaf').attr('id', function(datum){
+ return 'node-' + datum.id;
+ });
+
+ // Define enter/update/exit for leaves
+ cellsEnter
+ .append('div')
+ .attr('class', 'rect leaf_rect_entering')
+ .style('z-index', function(datum) { return datum.id * 2; })
+ .style('position', 'absolute')
+ .style('left', function(datum){ return datum.x; })
+ .style('top', function(datum){ return datum.y; })
+ .style('width', function(datum){ return datum.dx; })
+ .style('height', function(datum){ return datum.dy; })
+ .style('opacity', '0')
+ .style('background-color', function(datum) {
+ if (datum.t === undefined) return 'rgb(220,220,220)';
+ return D3SymbolTreeMap.getColorForType(datum.t)
+ .darker(0.3).toString();
+ })
+ .style('border', '1px solid black')
+ .on('mouseover', function(datum){
+ thisTreeMap._highlightElement.call(
+ thisTreeMap, datum, d3.select(this));
+ thisTreeMap._showInfoBox.call(thisTreeMap, datum);
+ })
+ .on('mouseout', function(datum){
+ thisTreeMap._unhighlightElement.call(
+ thisTreeMap, datum, d3.select(this));
+ thisTreeMap._hideInfoBox.call(thisTreeMap, datum);
+ })
+ .on('mousemove', function(){ thisTreeMap._moveInfoBox.call(
+ thisTreeMap, event);
+ });
+ cellsEnter
+ .append('div')
+ .attr('class', 'label leaf_label_entering')
+ .style('z-index', function(datum) { return (datum.id * 2) + 1; })
+ .style('position', 'absolute')
+ .style('left', function(datum){ return datum.x; })
+ .style('top', function(datum){ return datum.y; })
+ .style('width', function(datum) { return datum.dx; })
+ .style('height', function(datum) { return datum.dy; })
+ .style('opacity', '0')
+ .style('pointer-events', 'none')
+ .style('-webkit-user-select', 'none')
+ .style('overflow', 'hidden') // required for ellipsis
+ .style('white-space', 'nowrap') // required for ellipsis
+ .style('text-overflow', 'ellipsis')
+ .style('text-align', 'center')
+ .style('vertical-align', 'middle')
+ .style('visibility', function(datum) {
+ return (datum.dx < 15 || datum.dy < 15) ? 'hidden' : 'visible';
+ })
+ .text(function(datum) { return datum.n; });
+
+ // Complicated transition logic: See note in _handleInodes()
+ this._mapContainer.selectAll('div.leaf_rect_entering').transition()
+ .duration(thisTreeMap._enterDuration).delay(
+ this._firstTransition ? 0 : thisTreeMap._exitDuration +
+ thisTreeMap._updateDuration)
+ .attr('class', 'rect leaf_rect')
+ .style('opacity', '1')
+ this._mapContainer.selectAll('div.leaf_label_entering').transition()
+ .duration(thisTreeMap._enterDuration).delay(
+ this._firstTransition ? 0 : thisTreeMap._exitDuration +
+ thisTreeMap._updateDuration)
+ .attr('class', 'label leaf_label')
+ .style('opacity', '1')
+ this._mapContainer.selectAll('div.leaf_rect').transition()
+ .duration(thisTreeMap._updateDuration).delay(thisTreeMap._exitDuration)
+ .style('opacity', '1')
+ .style('left', function(datum){ return datum.x; })
+ .style('top', function(datum){ return datum.y; })
+ .style('width', function(datum){ return datum.dx; })
+ .style('height', function(datum){ return datum.dy; });
+ this._mapContainer.selectAll('div.leaf_label').transition()
+ .duration(thisTreeMap._updateDuration).delay(thisTreeMap._exitDuration)
+ .style('opacity', '1')
+ .style('visibility', function(datum) {
+ return (datum.dx < 15 || datum.dy < 15) ? 'hidden' : 'visible';
+ })
+ .style('left', function(datum){ return datum.x; })
+ .style('top', function(datum){ return datum.y; })
+ .style('width', function(datum) { return datum.dx; })
+ .style('height', function(datum) { return datum.dy; });
+ var exit = this._mapContainer.selectAll('div.leaf')
+ .data(leaves, function(datum) { return 'leaf-' + datum.id; })
+ .exit();
+ exit.selectAll('div.leaf_rect').transition()
+ .duration(thisTreeMap._exitDuration)
+ .style('opacity', 0);
+ exit.selectAll('div.leaf_label').transition()
+ .duration(thisTreeMap._exitDuration)
+ .style('opacity', 0);
+ exit.transition().delay(thisTreeMap._exitDuration + 1).remove();
+
+ console.log(leaves.length + ' leaves layed out.');
+ console.timeEnd('_handleLeaves');
+}
+
+D3SymbolTreeMap.prototype._makeSymbolBucketBackgroundImage = function(datum) {
+ if (!(datum.t === undefined && datum.depth == this._currentMaxDepth)) {
+ return 'none';
+ }
+ var text = '';
+ var lastStop = 0;
+ for (var x = 0; x < D3SymbolTreeMap._NM_SYMBOL_TYPES.length; x++) {
+ symbol_type = D3SymbolTreeMap._NM_SYMBOL_TYPES.charAt(x);
+ var stats = datum.symbol_stats[symbol_type];
+ if (stats !== undefined) {
+ if (text.length !== 0) {
+ text += ', ';
+ }
+ var percent = 100 * (stats.size / datum.value);
+ var nowStop = lastStop + percent;
+ var tempcolor = D3SymbolTreeMap.getColorForType(symbol_type);
+ var color = d3.rgb(tempcolor).toString();
+ text += color + ' ' + lastStop + '%, ' + color + ' ' +
+ nowStop + '%';
+ lastStop = nowStop;
+ }
+ }
+ return 'linear-gradient(' + (datum.dx > datum.dy ? 'to right' :
+ 'to bottom') + ', ' + text + ')';
+}
+
+D3SymbolTreeMap.prototype.pathFor = function(datum) {
+ if (datum.__path) return datum.__path;
+ parts=[];
+ node = datum;
+ while (node) {
+ if (node.k === 'p') { // path node
+ if(node.n !== '/') parts.unshift(node.n);
+ }
+ node = node.parent;
+ }
+ datum.__path = '/' + parts.join('/');
+ return datum.__path;
+}
+
+D3SymbolTreeMap.prototype._createHighlight = function(datum, selection) {
+ var x = parseInt(selection.style('left'));
+ var y = parseInt(selection.style('top'));
+ var w = parseInt(selection.style('width'));
+ var h = parseInt(selection.style('height'));
+ datum.highlight = this._mapContainer.append('div')
+ .attr('id', 'h-' + datum.id)
+ .attr('class', 'highlight')
+ .style('pointer-events', 'none')
+ .style('-webkit-user-select', 'none')
+ .style('z-index', '999999')
+ .style('position', 'absolute')
+ .style('top', y-2)
+ .style('left', x-2)
+ .style('width', w+4)
+ .style('height', h+4)
+ .style('margin', 0)
+ .style('padding', 0)
+ .style('border', '4px outset rgba(250,40,200,0.9)')
+ .style('box-sizing', 'border-box')
+ .style('opacity', 0.0);
+}
+
+D3SymbolTreeMap.prototype._showHighlight = function(datum, selection) {
+ if (datum === this._currentRoot) return;
+ if (datum.highlight === undefined) {
+ this._createHighlight(datum, selection);
+ }
+ datum.highlight.transition().duration(200).style('opacity', 1.0);
+}
+
+D3SymbolTreeMap.prototype._hideHighlight = function(datum, selection) {
+ if (datum.highlight === undefined) return;
+ datum.highlight.transition().duration(750)
+ .style('opacity', 0)
+ .each('end', function(){
+ if (datum.highlight) datum.highlight.remove();
+ delete datum.highlight;
+ });
+}
+
+D3SymbolTreeMap.prototype._createInfoBox = function() {
+ return d3.select('body')
+ .append('div')
+ .attr('id', 'infobox')
+ .style('z-index', '2147483647') // (2^31) - 1: Hopefully safe :)
+ .style('position', 'absolute')
+ .style('visibility', 'hidden')
+ .style('background-color', 'rgba(255,255,255, 0.9)')
+ .style('border', '1px solid black')
+ .style('padding', '10px')
+ .style('-webkit-user-select', 'none')
+ .style('box-shadow', '3px 3px rgba(70,70,70,0.5)')
+ .style('border-radius', '10px')
+ .style('white-space', 'nowrap');
+}
+
+D3SymbolTreeMap.prototype._showInfoBox = function(datum) {
+ this.infobox.text('');
+ var numSymbols = 0;
+ var sizeish = D3SymbolTreeMap._pretty(datum.value) + ' bytes (' +
+ D3SymbolTreeMap._byteify(datum.value) + ')';
+ if (datum.k === 'p' || datum.k === 'b') { // path or bucket
+ if (datum.symbol_stats) { // can be empty if filters are applied
+ for (var x = 0; x < D3SymbolTreeMap._NM_SYMBOL_TYPES.length; x++) {
+ symbol_type = D3SymbolTreeMap._NM_SYMBOL_TYPES.charAt(x);
+ var stats = datum.symbol_stats[symbol_type];
+ if (stats !== undefined) numSymbols += stats.count;
+ }
+ }
+ } else if (datum.k === 's') { // symbol
+ numSymbols = 1;
+ }
+
+ if (datum.k === 'p' && !datum.lastPathElement) {
+ this.infobox.append('div').text('Directory: ' + this.pathFor(datum))
+ this.infobox.append('div').text('Size: ' + sizeish);
+ } else {
+ if (datum.k === 'p') { // path
+ this.infobox.append('div').text('File: ' + this.pathFor(datum))
+ this.infobox.append('div').text('Size: ' + sizeish);
+ } else if (datum.k === 'b') { // bucket
+ this.infobox.append('div').text('Symbol Bucket: ' +
+ D3SymbolTreeMap._getSymbolDescription(datum.t));
+ this.infobox.append('div').text('Count: ' + numSymbols);
+ this.infobox.append('div').text('Size: ' + sizeish);
+ this.infobox.append('div').text('Location: ' + this.pathFor(datum))
+ } else if (datum.k === 's') { // symbol
+ this.infobox.append('div').text('Symbol: ' + datum.n);
+ this.infobox.append('div').text('Type: ' +
+ D3SymbolTreeMap._getSymbolDescription(datum.t));
+ this.infobox.append('div').text('Size: ' + sizeish);
+ this.infobox.append('div').text('Location: ' + this.pathFor(datum))
+ }
+ }
+ if (datum.k === 'p') {
+ this.infobox.append('div')
+ .text('Number of symbols: ' + D3SymbolTreeMap._pretty(numSymbols));
+ if (datum.symbol_stats) { // can be empty if filters are applied
+ var table = this.infobox.append('table')
+ .attr('border', 1).append('tbody');
+ var header = table.append('tr');
+ header.append('th').text('Type');
+ header.append('th').text('Count');
+ header.append('th')
+ .style('white-space', 'nowrap')
+ .text('Total Size (Bytes)');
+ for (var x = 0; x < D3SymbolTreeMap._NM_SYMBOL_TYPES.length; x++) {
+ symbol_type = D3SymbolTreeMap._NM_SYMBOL_TYPES.charAt(x);
+ var stats = datum.symbol_stats[symbol_type];
+ if (stats !== undefined) {
+ var tr = table.append('tr');
+ tr.append('td')
+ .style('white-space', 'nowrap')
+ .text(D3SymbolTreeMap._getSymbolDescription(
+ symbol_type));
+ tr.append('td').text(D3SymbolTreeMap._pretty(stats.count));
+ tr.append('td').text(D3SymbolTreeMap._pretty(stats.size));
+ }
+ }
+ }
+ }
+ this.infobox.style('visibility', 'visible');
+}
+
+D3SymbolTreeMap.prototype._hideInfoBox = function(datum) {
+ this.infobox.style('visibility', 'hidden');
+}
+
+D3SymbolTreeMap.prototype._moveInfoBox = function(event) {
+ var element = document.getElementById('infobox');
+ var w = element.offsetWidth;
+ var h = element.offsetHeight;
+ var offsetLeft = 10;
+ var offsetTop = 10;
+
+ var rightLimit = window.innerWidth;
+ var rightEdge = event.pageX + offsetLeft + w;
+ if (rightEdge > rightLimit) {
+ // Too close to screen edge, reflect around the cursor
+ offsetLeft = -1 * (w + offsetLeft);
+ }
+
+ var bottomLimit = window.innerHeight;
+ var bottomEdge = event.pageY + offsetTop + h;
+ if (bottomEdge > bottomLimit) {
+ // Too close ot screen edge, reflect around the cursor
+ offsetTop = -1 * (h + offsetTop);
+ }
+
+ this.infobox.style('top', (event.pageY + offsetTop) + 'px')
+ .style('left', (event.pageX + offsetLeft) + 'px');
+}
+
+D3SymbolTreeMap.prototype.biggestSymbols = function(maxRecords) {
+ var result = undefined;
+ var smallest = undefined;
+ var sortFunction = function(a,b) {
+ var result = b.value - a.value;
+ if (result !== 0) return result; // sort by size
+ var pathA = treemap.pathFor(a); // sort by path
+ var pathB = treemap.pathFor(b);
+ if (pathA > pathB) return 1;
+ if (pathB > pathA) return -1;
+ return a.n - b.n; // sort by symbol name
+ };
+ this.visitFromDisplayedRoot(function(datum) {
+ if (datum.children) return; // ignore non-leaves
+ if (!result) { // first element
+ result = [datum];
+ smallest = datum.value;
+ return;
+ }
+ if (result.length < maxRecords) { // filling the array
+ result.push(datum);
+ return;
+ }
+ if (datum.value > smallest) { // array is already full
+ result.push(datum);
+ result.sort(sortFunction);
+ result.pop(); // get rid of smallest element
+ smallest = result[maxRecords - 1].value; // new threshold for entry
+ }
+ });
+ result.sort(sortFunction);
+ return result;
+}
+
+D3SymbolTreeMap.prototype.biggestPaths = function(maxRecords) {
+ var result = undefined;
+ var smallest = undefined;
+ var sortFunction = function(a,b) {
+ var result = b.value - a.value;
+ if (result !== 0) return result; // sort by size
+ var pathA = treemap.pathFor(a); // sort by path
+ var pathB = treemap.pathFor(b);
+ if (pathA > pathB) return 1;
+ if (pathB > pathA) return -1;
+ console.log('warning, multiple entries for the same path: ' + pathA);
+ return 0; // should be impossible
+ };
+ this.visitFromDisplayedRoot(function(datum) {
+ if (!datum.lastPathElement) return; // ignore non-files
+ if (!result) { // first element
+ result = [datum];
+ smallest = datum.value;
+ return;
+ }
+ if (result.length < maxRecords) { // filling the array
+ result.push(datum);
+ return;
+ }
+ if (datum.value > smallest) { // array is already full
+ result.push(datum);
+ result.sort(sortFunction);
+ result.pop(); // get rid of smallest element
+ smallest = result[maxRecords - 1].value; // new threshold for entry
+ }
+ });
+ result.sort(sortFunction);
+ return result;
+}
diff --git a/pkg/vm_snapshot_analysis/lib/src/assets/d3/README.chromium b/pkg/vm_snapshot_analysis/lib/src/assets/d3/README.chromium
new file mode 100644
index 0000000..7697762
--- /dev/null
+++ b/pkg/vm_snapshot_analysis/lib/src/assets/d3/README.chromium
@@ -0,0 +1,31 @@
+Name: d3
+Short Name: d3
+URL: https://github.com/mbostock/d3
+Version: 3.4.4
+Date: Mon Mar 24 20:45:44 2014 -0700
+Revision: fa55eead411a3c1b01703cb1ddfd59ccc0b23124
+License: BSD 3-Clause
+License File: src/LICENSE
+Security Critical: No
+License Android Compatible: Yes
+
+Description:
+A JavaScript library for manipulating documents based on data.
+
+Subject to the security patch(es) described below, you MAY include d3 in web-facing content, such
+as in pages generated by bots or tools.
+
+
+Local Modifications:
+1. Deleted everything except for:
+* d3.js the standalone non-minified library
+* LICENSE the BSD-style 3-Clause license
+* README.md the readme file from github, for basic information
+
+2. Applied the following patches at the request of security:
+patches/001_no_html.patch Disables the html() convenience functions, which could be used to
+ inject arbitrary content into the page. Instead of using html(),
+ programmatically create the individual nodes and/or text that you
+ require.
+ The html() methods have been modified to throw exceptions that
+ make it obvious that this feature is disabled for security.
diff --git a/pkg/vm_snapshot_analysis/lib/src/assets/d3/README.dart b/pkg/vm_snapshot_analysis/lib/src/assets/d3/README.dart
new file mode 100644
index 0000000..6abfe09
--- /dev/null
+++ b/pkg/vm_snapshot_analysis/lib/src/assets/d3/README.dart
@@ -0,0 +1 @@
+A local copy of third_party/d3 from Chromium project.
\ No newline at end of file
diff --git a/pkg/vm_snapshot_analysis/lib/src/assets/d3/patches/001_no_html.patch b/pkg/vm_snapshot_analysis/lib/src/assets/d3/patches/001_no_html.patch
new file mode 100644
index 0000000..3c976b0
--- /dev/null
+++ b/pkg/vm_snapshot_analysis/lib/src/assets/d3/patches/001_no_html.patch
@@ -0,0 +1,24 @@
+diff --git a/third_party/d3/src/d3.js b/third_party/d3/src/d3.js
+index a3e4b95..8a98c4d 100644
+--- a/third_party/d3/src/d3.js
++++ b/third_party/d3/src/d3.js
+@@ -713,6 +713,7 @@
+ }) : this.node().textContent;
+ };
+ d3_selectionPrototype.html = function(value) {
++ throw "disallowed by chromium security";
+ return arguments.length ? this.each(typeof value === "function" ? function() {
+ var v = value.apply(this, arguments);
+ this.innerHTML = v == null ? "" : v;
+@@ -9274,9 +9275,11 @@
+ return JSON.parse(request.responseText);
+ }
+ d3.html = function(url, callback) {
++ throw "disallowed by chromium security";
+ return d3_xhr(url, "text/html", d3_html, callback);
+ };
+ function d3_html(request) {
++ throw "disallowed by chromium security";
+ var range = d3_document.createRange();
+ range.selectNode(d3_document.body);
+ return range.createContextualFragment(request.responseText);
diff --git a/pkg/vm_snapshot_analysis/lib/src/assets/d3/src/LICENSE b/pkg/vm_snapshot_analysis/lib/src/assets/d3/src/LICENSE
new file mode 100644
index 0000000..8301346
--- /dev/null
+++ b/pkg/vm_snapshot_analysis/lib/src/assets/d3/src/LICENSE
@@ -0,0 +1,26 @@
+Copyright (c) 2010-2014, Michael Bostock
+All rights reserved.
+
+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.
+
+* The name Michael Bostock may not 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 MICHAEL BOSTOCK 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/vm_snapshot_analysis/lib/src/assets/d3/src/README.md b/pkg/vm_snapshot_analysis/lib/src/assets/d3/src/README.md
new file mode 100644
index 0000000..eb334e2
--- /dev/null
+++ b/pkg/vm_snapshot_analysis/lib/src/assets/d3/src/README.md
@@ -0,0 +1,9 @@
+# Data-Driven Documents
+
+<a href="http://d3js.org"><img src="http://d3js.org/logo.svg" align="left" hspace="10" vspace="6"></a>
+
+**D3.js** is a JavaScript library for manipulating documents based on data. **D3** helps you bring data to life using HTML, SVG and CSS. D3’s emphasis on web standards gives you the full capabilities of modern browsers without tying yourself to a proprietary framework, combining powerful visualization components and a data-driven approach to DOM manipulation.
+
+Want to learn more? [See the wiki.](https://github.com/mbostock/d3/wiki)
+
+For examples, [see the gallery](https://github.com/mbostock/d3/wiki/Gallery) and [mbostock’s bl.ocks](http://bl.ocks.org/mbostock).
diff --git a/pkg/vm_snapshot_analysis/lib/src/assets/d3/src/d3.js b/pkg/vm_snapshot_analysis/lib/src/assets/d3/src/d3.js
new file mode 100644
index 0000000..6dcabdf
--- /dev/null
+++ b/pkg/vm_snapshot_analysis/lib/src/assets/d3/src/d3.js
@@ -0,0 +1,9297 @@
+!function() {
+ var d3 = {
+ version: "3.4.4"
+ };
+ if (!Date.now) Date.now = function() {
+ return +new Date();
+ };
+ var d3_arraySlice = [].slice, d3_array = function(list) {
+ return d3_arraySlice.call(list);
+ };
+ var d3_document = document, d3_documentElement = d3_document.documentElement, d3_window = window;
+ try {
+ d3_array(d3_documentElement.childNodes)[0].nodeType;
+ } catch (e) {
+ d3_array = function(list) {
+ var i = list.length, array = new Array(i);
+ while (i--) array[i] = list[i];
+ return array;
+ };
+ }
+ try {
+ d3_document.createElement("div").style.setProperty("opacity", 0, "");
+ } catch (error) {
+ var d3_element_prototype = d3_window.Element.prototype, d3_element_setAttribute = d3_element_prototype.setAttribute, d3_element_setAttributeNS = d3_element_prototype.setAttributeNS, d3_style_prototype = d3_window.CSSStyleDeclaration.prototype, d3_style_setProperty = d3_style_prototype.setProperty;
+ d3_element_prototype.setAttribute = function(name, value) {
+ d3_element_setAttribute.call(this, name, value + "");
+ };
+ d3_element_prototype.setAttributeNS = function(space, local, value) {
+ d3_element_setAttributeNS.call(this, space, local, value + "");
+ };
+ d3_style_prototype.setProperty = function(name, value, priority) {
+ d3_style_setProperty.call(this, name, value + "", priority);
+ };
+ }
+ d3.ascending = d3_ascending;
+ function d3_ascending(a, b) {
+ return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;
+ }
+ d3.descending = function(a, b) {
+ return b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN;
+ };
+ d3.min = function(array, f) {
+ var i = -1, n = array.length, a, b;
+ if (arguments.length === 1) {
+ while (++i < n && !((a = array[i]) != null && a <= a)) a = undefined;
+ while (++i < n) if ((b = array[i]) != null && a > b) a = b;
+ } else {
+ while (++i < n && !((a = f.call(array, array[i], i)) != null && a <= a)) a = undefined;
+ while (++i < n) if ((b = f.call(array, array[i], i)) != null && a > b) a = b;
+ }
+ return a;
+ };
+ d3.max = function(array, f) {
+ var i = -1, n = array.length, a, b;
+ if (arguments.length === 1) {
+ while (++i < n && !((a = array[i]) != null && a <= a)) a = undefined;
+ while (++i < n) if ((b = array[i]) != null && b > a) a = b;
+ } else {
+ while (++i < n && !((a = f.call(array, array[i], i)) != null && a <= a)) a = undefined;
+ while (++i < n) if ((b = f.call(array, array[i], i)) != null && b > a) a = b;
+ }
+ return a;
+ };
+ d3.extent = function(array, f) {
+ var i = -1, n = array.length, a, b, c;
+ if (arguments.length === 1) {
+ while (++i < n && !((a = c = array[i]) != null && a <= a)) a = c = undefined;
+ while (++i < n) if ((b = array[i]) != null) {
+ if (a > b) a = b;
+ if (c < b) c = b;
+ }
+ } else {
+ while (++i < n && !((a = c = f.call(array, array[i], i)) != null && a <= a)) a = undefined;
+ while (++i < n) if ((b = f.call(array, array[i], i)) != null) {
+ if (a > b) a = b;
+ if (c < b) c = b;
+ }
+ }
+ return [ a, c ];
+ };
+ d3.sum = function(array, f) {
+ var s = 0, n = array.length, a, i = -1;
+ if (arguments.length === 1) {
+ while (++i < n) if (!isNaN(a = +array[i])) s += a;
+ } else {
+ while (++i < n) if (!isNaN(a = +f.call(array, array[i], i))) s += a;
+ }
+ return s;
+ };
+ function d3_number(x) {
+ return x != null && !isNaN(x);
+ }
+ d3.mean = function(array, f) {
+ var n = array.length, a, m = 0, i = -1, j = 0;
+ if (arguments.length === 1) {
+ while (++i < n) if (d3_number(a = array[i])) m += (a - m) / ++j;
+ } else {
+ while (++i < n) if (d3_number(a = f.call(array, array[i], i))) m += (a - m) / ++j;
+ }
+ return j ? m : undefined;
+ };
+ d3.quantile = function(values, p) {
+ var H = (values.length - 1) * p + 1, h = Math.floor(H), v = +values[h - 1], e = H - h;
+ return e ? v + e * (values[h] - v) : v;
+ };
+ d3.median = function(array, f) {
+ if (arguments.length > 1) array = array.map(f);
+ array = array.filter(d3_number);
+ return array.length ? d3.quantile(array.sort(d3_ascending), .5) : undefined;
+ };
+ function d3_bisector(compare) {
+ return {
+ left: function(a, x, lo, hi) {
+ if (arguments.length < 3) lo = 0;
+ if (arguments.length < 4) hi = a.length;
+ while (lo < hi) {
+ var mid = lo + hi >>> 1;
+ if (compare(a[mid], x) < 0) lo = mid + 1; else hi = mid;
+ }
+ return lo;
+ },
+ right: function(a, x, lo, hi) {
+ if (arguments.length < 3) lo = 0;
+ if (arguments.length < 4) hi = a.length;
+ while (lo < hi) {
+ var mid = lo + hi >>> 1;
+ if (compare(a[mid], x) > 0) hi = mid; else lo = mid + 1;
+ }
+ return lo;
+ }
+ };
+ }
+ var d3_bisect = d3_bisector(d3_ascending);
+ d3.bisectLeft = d3_bisect.left;
+ d3.bisect = d3.bisectRight = d3_bisect.right;
+ d3.bisector = function(f) {
+ return d3_bisector(f.length === 1 ? function(d, x) {
+ return d3_ascending(f(d), x);
+ } : f);
+ };
+ d3.shuffle = function(array) {
+ var m = array.length, t, i;
+ while (m) {
+ i = Math.random() * m-- | 0;
+ t = array[m], array[m] = array[i], array[i] = t;
+ }
+ return array;
+ };
+ d3.permute = function(array, indexes) {
+ var i = indexes.length, permutes = new Array(i);
+ while (i--) permutes[i] = array[indexes[i]];
+ return permutes;
+ };
+ d3.pairs = function(array) {
+ var i = 0, n = array.length - 1, p0, p1 = array[0], pairs = new Array(n < 0 ? 0 : n);
+ while (i < n) pairs[i] = [ p0 = p1, p1 = array[++i] ];
+ return pairs;
+ };
+ d3.zip = function() {
+ if (!(n = arguments.length)) return [];
+ for (var i = -1, m = d3.min(arguments, d3_zipLength), zips = new Array(m); ++i < m; ) {
+ for (var j = -1, n, zip = zips[i] = new Array(n); ++j < n; ) {
+ zip[j] = arguments[j][i];
+ }
+ }
+ return zips;
+ };
+ function d3_zipLength(d) {
+ return d.length;
+ }
+ d3.transpose = function(matrix) {
+ return d3.zip.apply(d3, matrix);
+ };
+ d3.keys = function(map) {
+ var keys = [];
+ for (var key in map) keys.push(key);
+ return keys;
+ };
+ d3.values = function(map) {
+ var values = [];
+ for (var key in map) values.push(map[key]);
+ return values;
+ };
+ d3.entries = function(map) {
+ var entries = [];
+ for (var key in map) entries.push({
+ key: key,
+ value: map[key]
+ });
+ return entries;
+ };
+ d3.merge = function(arrays) {
+ var n = arrays.length, m, i = -1, j = 0, merged, array;
+ while (++i < n) j += arrays[i].length;
+ merged = new Array(j);
+ while (--n >= 0) {
+ array = arrays[n];
+ m = array.length;
+ while (--m >= 0) {
+ merged[--j] = array[m];
+ }
+ }
+ return merged;
+ };
+ var abs = Math.abs;
+ d3.range = function(start, stop, step) {
+ if (arguments.length < 3) {
+ step = 1;
+ if (arguments.length < 2) {
+ stop = start;
+ start = 0;
+ }
+ }
+ if ((stop - start) / step === Infinity) throw new Error("infinite range");
+ var range = [], k = d3_range_integerScale(abs(step)), i = -1, j;
+ start *= k, stop *= k, step *= k;
+ if (step < 0) while ((j = start + step * ++i) > stop) range.push(j / k); else while ((j = start + step * ++i) < stop) range.push(j / k);
+ return range;
+ };
+ function d3_range_integerScale(x) {
+ var k = 1;
+ while (x * k % 1) k *= 10;
+ return k;
+ }
+ function d3_class(ctor, properties) {
+ try {
+ for (var key in properties) {
+ Object.defineProperty(ctor.prototype, key, {
+ value: properties[key],
+ enumerable: false
+ });
+ }
+ } catch (e) {
+ ctor.prototype = properties;
+ }
+ }
+ d3.map = function(object) {
+ var map = new d3_Map();
+ if (object instanceof d3_Map) object.forEach(function(key, value) {
+ map.set(key, value);
+ }); else for (var key in object) map.set(key, object[key]);
+ return map;
+ };
+ function d3_Map() {}
+ d3_class(d3_Map, {
+ has: d3_map_has,
+ get: function(key) {
+ return this[d3_map_prefix + key];
+ },
+ set: function(key, value) {
+ return this[d3_map_prefix + key] = value;
+ },
+ remove: d3_map_remove,
+ keys: d3_map_keys,
+ values: function() {
+ var values = [];
+ this.forEach(function(key, value) {
+ values.push(value);
+ });
+ return values;
+ },
+ entries: function() {
+ var entries = [];
+ this.forEach(function(key, value) {
+ entries.push({
+ key: key,
+ value: value
+ });
+ });
+ return entries;
+ },
+ size: d3_map_size,
+ empty: d3_map_empty,
+ forEach: function(f) {
+ for (var key in this) if (key.charCodeAt(0) === d3_map_prefixCode) f.call(this, key.substring(1), this[key]);
+ }
+ });
+ var d3_map_prefix = "\x00", d3_map_prefixCode = d3_map_prefix.charCodeAt(0);
+ function d3_map_has(key) {
+ return d3_map_prefix + key in this;
+ }
+ function d3_map_remove(key) {
+ key = d3_map_prefix + key;
+ return key in this && delete this[key];
+ }
+ function d3_map_keys() {
+ var keys = [];
+ this.forEach(function(key) {
+ keys.push(key);
+ });
+ return keys;
+ }
+ function d3_map_size() {
+ var size = 0;
+ for (var key in this) if (key.charCodeAt(0) === d3_map_prefixCode) ++size;
+ return size;
+ }
+ function d3_map_empty() {
+ for (var key in this) if (key.charCodeAt(0) === d3_map_prefixCode) return false;
+ return true;
+ }
+ d3.nest = function() {
+ var nest = {}, keys = [], sortKeys = [], sortValues, rollup;
+ function map(mapType, array, depth) {
+ if (depth >= keys.length) return rollup ? rollup.call(nest, array) : sortValues ? array.sort(sortValues) : array;
+ var i = -1, n = array.length, key = keys[depth++], keyValue, object, setter, valuesByKey = new d3_Map(), values;
+ while (++i < n) {
+ if (values = valuesByKey.get(keyValue = key(object = array[i]))) {
+ values.push(object);
+ } else {
+ valuesByKey.set(keyValue, [ object ]);
+ }
+ }
+ if (mapType) {
+ object = mapType();
+ setter = function(keyValue, values) {
+ object.set(keyValue, map(mapType, values, depth));
+ };
+ } else {
+ object = {};
+ setter = function(keyValue, values) {
+ object[keyValue] = map(mapType, values, depth);
+ };
+ }
+ valuesByKey.forEach(setter);
+ return object;
+ }
+ function entries(map, depth) {
+ if (depth >= keys.length) return map;
+ var array = [], sortKey = sortKeys[depth++];
+ map.forEach(function(key, keyMap) {
+ array.push({
+ key: key,
+ values: entries(keyMap, depth)
+ });
+ });
+ return sortKey ? array.sort(function(a, b) {
+ return sortKey(a.key, b.key);
+ }) : array;
+ }
+ nest.map = function(array, mapType) {
+ return map(mapType, array, 0);
+ };
+ nest.entries = function(array) {
+ return entries(map(d3.map, array, 0), 0);
+ };
+ nest.key = function(d) {
+ keys.push(d);
+ return nest;
+ };
+ nest.sortKeys = function(order) {
+ sortKeys[keys.length - 1] = order;
+ return nest;
+ };
+ nest.sortValues = function(order) {
+ sortValues = order;
+ return nest;
+ };
+ nest.rollup = function(f) {
+ rollup = f;
+ return nest;
+ };
+ return nest;
+ };
+ d3.set = function(array) {
+ var set = new d3_Set();
+ if (array) for (var i = 0, n = array.length; i < n; ++i) set.add(array[i]);
+ return set;
+ };
+ function d3_Set() {}
+ d3_class(d3_Set, {
+ has: d3_map_has,
+ add: function(value) {
+ this[d3_map_prefix + value] = true;
+ return value;
+ },
+ remove: function(value) {
+ value = d3_map_prefix + value;
+ return value in this && delete this[value];
+ },
+ values: d3_map_keys,
+ size: d3_map_size,
+ empty: d3_map_empty,
+ forEach: function(f) {
+ for (var value in this) if (value.charCodeAt(0) === d3_map_prefixCode) f.call(this, value.substring(1));
+ }
+ });
+ d3.behavior = {};
+ d3.rebind = function(target, source) {
+ var i = 1, n = arguments.length, method;
+ while (++i < n) target[method = arguments[i]] = d3_rebind(target, source, source[method]);
+ return target;
+ };
+ function d3_rebind(target, source, method) {
+ return function() {
+ var value = method.apply(source, arguments);
+ return value === source ? target : value;
+ };
+ }
+ function d3_vendorSymbol(object, name) {
+ if (name in object) return name;
+ name = name.charAt(0).toUpperCase() + name.substring(1);
+ for (var i = 0, n = d3_vendorPrefixes.length; i < n; ++i) {
+ var prefixName = d3_vendorPrefixes[i] + name;
+ if (prefixName in object) return prefixName;
+ }
+ }
+ var d3_vendorPrefixes = [ "webkit", "ms", "moz", "Moz", "o", "O" ];
+ function d3_noop() {}
+ d3.dispatch = function() {
+ var dispatch = new d3_dispatch(), i = -1, n = arguments.length;
+ while (++i < n) dispatch[arguments[i]] = d3_dispatch_event(dispatch);
+ return dispatch;
+ };
+ function d3_dispatch() {}
+ d3_dispatch.prototype.on = function(type, listener) {
+ var i = type.indexOf("."), name = "";
+ if (i >= 0) {
+ name = type.substring(i + 1);
+ type = type.substring(0, i);
+ }
+ if (type) return arguments.length < 2 ? this[type].on(name) : this[type].on(name, listener);
+ if (arguments.length === 2) {
+ if (listener == null) for (type in this) {
+ if (this.hasOwnProperty(type)) this[type].on(name, null);
+ }
+ return this;
+ }
+ };
+ function d3_dispatch_event(dispatch) {
+ var listeners = [], listenerByName = new d3_Map();
+ function event() {
+ var z = listeners, i = -1, n = z.length, l;
+ while (++i < n) if (l = z[i].on) l.apply(this, arguments);
+ return dispatch;
+ }
+ event.on = function(name, listener) {
+ var l = listenerByName.get(name), i;
+ if (arguments.length < 2) return l && l.on;
+ if (l) {
+ l.on = null;
+ listeners = listeners.slice(0, i = listeners.indexOf(l)).concat(listeners.slice(i + 1));
+ listenerByName.remove(name);
+ }
+ if (listener) listeners.push(listenerByName.set(name, {
+ on: listener
+ }));
+ return dispatch;
+ };
+ return event;
+ }
+ d3.event = null;
+ function d3_eventPreventDefault() {
+ d3.event.preventDefault();
+ }
+ function d3_eventSource() {
+ var e = d3.event, s;
+ while (s = e.sourceEvent) e = s;
+ return e;
+ }
+ function d3_eventDispatch(target) {
+ var dispatch = new d3_dispatch(), i = 0, n = arguments.length;
+ while (++i < n) dispatch[arguments[i]] = d3_dispatch_event(dispatch);
+ dispatch.of = function(thiz, argumentz) {
+ return function(e1) {
+ try {
+ var e0 = e1.sourceEvent = d3.event;
+ e1.target = target;
+ d3.event = e1;
+ dispatch[e1.type].apply(thiz, argumentz);
+ } finally {
+ d3.event = e0;
+ }
+ };
+ };
+ return dispatch;
+ }
+ d3.requote = function(s) {
+ return s.replace(d3_requote_re, "\\$&");
+ };
+ var d3_requote_re = /[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g;
+ var d3_subclass = {}.__proto__ ? function(object, prototype) {
+ object.__proto__ = prototype;
+ } : function(object, prototype) {
+ for (var property in prototype) object[property] = prototype[property];
+ };
+ function d3_selection(groups) {
+ d3_subclass(groups, d3_selectionPrototype);
+ return groups;
+ }
+ var d3_select = function(s, n) {
+ return n.querySelector(s);
+ }, d3_selectAll = function(s, n) {
+ return n.querySelectorAll(s);
+ }, d3_selectMatcher = d3_documentElement[d3_vendorSymbol(d3_documentElement, "matchesSelector")], d3_selectMatches = function(n, s) {
+ return d3_selectMatcher.call(n, s);
+ };
+ if (typeof Sizzle === "function") {
+ d3_select = function(s, n) {
+ return Sizzle(s, n)[0] || null;
+ };
+ d3_selectAll = Sizzle;
+ d3_selectMatches = Sizzle.matchesSelector;
+ }
+ d3.selection = function() {
+ return d3_selectionRoot;
+ };
+ var d3_selectionPrototype = d3.selection.prototype = [];
+ d3_selectionPrototype.select = function(selector) {
+ var subgroups = [], subgroup, subnode, group, node;
+ selector = d3_selection_selector(selector);
+ for (var j = -1, m = this.length; ++j < m; ) {
+ subgroups.push(subgroup = []);
+ subgroup.parentNode = (group = this[j]).parentNode;
+ for (var i = -1, n = group.length; ++i < n; ) {
+ if (node = group[i]) {
+ subgroup.push(subnode = selector.call(node, node.__data__, i, j));
+ if (subnode && "__data__" in node) subnode.__data__ = node.__data__;
+ } else {
+ subgroup.push(null);
+ }
+ }
+ }
+ return d3_selection(subgroups);
+ };
+ function d3_selection_selector(selector) {
+ return typeof selector === "function" ? selector : function() {
+ return d3_select(selector, this);
+ };
+ }
+ d3_selectionPrototype.selectAll = function(selector) {
+ var subgroups = [], subgroup, node;
+ selector = d3_selection_selectorAll(selector);
+ for (var j = -1, m = this.length; ++j < m; ) {
+ for (var group = this[j], i = -1, n = group.length; ++i < n; ) {
+ if (node = group[i]) {
+ subgroups.push(subgroup = d3_array(selector.call(node, node.__data__, i, j)));
+ subgroup.parentNode = node;
+ }
+ }
+ }
+ return d3_selection(subgroups);
+ };
+ function d3_selection_selectorAll(selector) {
+ return typeof selector === "function" ? selector : function() {
+ return d3_selectAll(selector, this);
+ };
+ }
+ var d3_nsPrefix = {
+ svg: "http://www.w3.org/2000/svg",
+ xhtml: "http://www.w3.org/1999/xhtml",
+ xlink: "http://www.w3.org/1999/xlink",
+ xml: "http://www.w3.org/XML/1998/namespace",
+ xmlns: "http://www.w3.org/2000/xmlns/"
+ };
+ d3.ns = {
+ prefix: d3_nsPrefix,
+ qualify: function(name) {
+ var i = name.indexOf(":"), prefix = name;
+ if (i >= 0) {
+ prefix = name.substring(0, i);
+ name = name.substring(i + 1);
+ }
+ return d3_nsPrefix.hasOwnProperty(prefix) ? {
+ space: d3_nsPrefix[prefix],
+ local: name
+ } : name;
+ }
+ };
+ d3_selectionPrototype.attr = function(name, value) {
+ if (arguments.length < 2) {
+ if (typeof name === "string") {
+ var node = this.node();
+ name = d3.ns.qualify(name);
+ return name.local ? node.getAttributeNS(name.space, name.local) : node.getAttribute(name);
+ }
+ for (value in name) this.each(d3_selection_attr(value, name[value]));
+ return this;
+ }
+ return this.each(d3_selection_attr(name, value));
+ };
+ function d3_selection_attr(name, value) {
+ name = d3.ns.qualify(name);
+ function attrNull() {
+ this.removeAttribute(name);
+ }
+ function attrNullNS() {
+ this.removeAttributeNS(name.space, name.local);
+ }
+ function attrConstant() {
+ this.setAttribute(name, value);
+ }
+ function attrConstantNS() {
+ this.setAttributeNS(name.space, name.local, value);
+ }
+ function attrFunction() {
+ var x = value.apply(this, arguments);
+ if (x == null) this.removeAttribute(name); else this.setAttribute(name, x);
+ }
+ function attrFunctionNS() {
+ var x = value.apply(this, arguments);
+ if (x == null) this.removeAttributeNS(name.space, name.local); else this.setAttributeNS(name.space, name.local, x);
+ }
+ return value == null ? name.local ? attrNullNS : attrNull : typeof value === "function" ? name.local ? attrFunctionNS : attrFunction : name.local ? attrConstantNS : attrConstant;
+ }
+ function d3_collapse(s) {
+ return s.trim().replace(/\s+/g, " ");
+ }
+ d3_selectionPrototype.classed = function(name, value) {
+ if (arguments.length < 2) {
+ if (typeof name === "string") {
+ var node = this.node(), n = (name = d3_selection_classes(name)).length, i = -1;
+ if (value = node.classList) {
+ while (++i < n) if (!value.contains(name[i])) return false;
+ } else {
+ value = node.getAttribute("class");
+ while (++i < n) if (!d3_selection_classedRe(name[i]).test(value)) return false;
+ }
+ return true;
+ }
+ for (value in name) this.each(d3_selection_classed(value, name[value]));
+ return this;
+ }
+ return this.each(d3_selection_classed(name, value));
+ };
+ function d3_selection_classedRe(name) {
+ return new RegExp("(?:^|\\s+)" + d3.requote(name) + "(?:\\s+|$)", "g");
+ }
+ function d3_selection_classes(name) {
+ return name.trim().split(/^|\s+/);
+ }
+ function d3_selection_classed(name, value) {
+ name = d3_selection_classes(name).map(d3_selection_classedName);
+ var n = name.length;
+ function classedConstant() {
+ var i = -1;
+ while (++i < n) name[i](this, value);
+ }
+ function classedFunction() {
+ var i = -1, x = value.apply(this, arguments);
+ while (++i < n) name[i](this, x);
+ }
+ return typeof value === "function" ? classedFunction : classedConstant;
+ }
+ function d3_selection_classedName(name) {
+ var re = d3_selection_classedRe(name);
+ return function(node, value) {
+ if (c = node.classList) return value ? c.add(name) : c.remove(name);
+ var c = node.getAttribute("class") || "";
+ if (value) {
+ re.lastIndex = 0;
+ if (!re.test(c)) node.setAttribute("class", d3_collapse(c + " " + name));
+ } else {
+ node.setAttribute("class", d3_collapse(c.replace(re, " ")));
+ }
+ };
+ }
+ d3_selectionPrototype.style = function(name, value, priority) {
+ var n = arguments.length;
+ if (n < 3) {
+ if (typeof name !== "string") {
+ if (n < 2) value = "";
+ for (priority in name) this.each(d3_selection_style(priority, name[priority], value));
+ return this;
+ }
+ if (n < 2) return d3_window.getComputedStyle(this.node(), null).getPropertyValue(name);
+ priority = "";
+ }
+ return this.each(d3_selection_style(name, value, priority));
+ };
+ function d3_selection_style(name, value, priority) {
+ function styleNull() {
+ this.style.removeProperty(name);
+ }
+ function styleConstant() {
+ this.style.setProperty(name, value, priority);
+ }
+ function styleFunction() {
+ var x = value.apply(this, arguments);
+ if (x == null) this.style.removeProperty(name); else this.style.setProperty(name, x, priority);
+ }
+ return value == null ? styleNull : typeof value === "function" ? styleFunction : styleConstant;
+ }
+ d3_selectionPrototype.property = function(name, value) {
+ if (arguments.length < 2) {
+ if (typeof name === "string") return this.node()[name];
+ for (value in name) this.each(d3_selection_property(value, name[value]));
+ return this;
+ }
+ return this.each(d3_selection_property(name, value));
+ };
+ function d3_selection_property(name, value) {
+ function propertyNull() {
+ delete this[name];
+ }
+ function propertyConstant() {
+ this[name] = value;
+ }
+ function propertyFunction() {
+ var x = value.apply(this, arguments);
+ if (x == null) delete this[name]; else this[name] = x;
+ }
+ return value == null ? propertyNull : typeof value === "function" ? propertyFunction : propertyConstant;
+ }
+ d3_selectionPrototype.text = function(value) {
+ return arguments.length ? this.each(typeof value === "function" ? function() {
+ var v = value.apply(this, arguments);
+ this.textContent = v == null ? "" : v;
+ } : value == null ? function() {
+ this.textContent = "";
+ } : function() {
+ this.textContent = value;
+ }) : this.node().textContent;
+ };
+ d3_selectionPrototype.html = function(value) {
+ throw "disallowed by chromium security";
+ return arguments.length ? this.each(typeof value === "function" ? function() {
+ var v = value.apply(this, arguments);
+ this.innerHTML = v == null ? "" : v;
+ } : value == null ? function() {
+ this.innerHTML = "";
+ } : function() {
+ this.innerHTML = value;
+ }) : this.node().innerHTML;
+ };
+ d3_selectionPrototype.append = function(name) {
+ name = d3_selection_creator(name);
+ return this.select(function() {
+ return this.appendChild(name.apply(this, arguments));
+ });
+ };
+ function d3_selection_creator(name) {
+ return typeof name === "function" ? name : (name = d3.ns.qualify(name)).local ? function() {
+ return this.ownerDocument.createElementNS(name.space, name.local);
+ } : function() {
+ return this.ownerDocument.createElementNS(this.namespaceURI, name);
+ };
+ }
+ d3_selectionPrototype.insert = function(name, before) {
+ name = d3_selection_creator(name);
+ before = d3_selection_selector(before);
+ return this.select(function() {
+ return this.insertBefore(name.apply(this, arguments), before.apply(this, arguments) || null);
+ });
+ };
+ d3_selectionPrototype.remove = function() {
+ return this.each(function() {
+ var parent = this.parentNode;
+ if (parent) parent.removeChild(this);
+ });
+ };
+ d3_selectionPrototype.data = function(value, key) {
+ var i = -1, n = this.length, group, node;
+ if (!arguments.length) {
+ value = new Array(n = (group = this[0]).length);
+ while (++i < n) {
+ if (node = group[i]) {
+ value[i] = node.__data__;
+ }
+ }
+ return value;
+ }
+ function bind(group, groupData) {
+ var i, n = group.length, m = groupData.length, n0 = Math.min(n, m), updateNodes = new Array(m), enterNodes = new Array(m), exitNodes = new Array(n), node, nodeData;
+ if (key) {
+ var nodeByKeyValue = new d3_Map(), dataByKeyValue = new d3_Map(), keyValues = [], keyValue;
+ for (i = -1; ++i < n; ) {
+ keyValue = key.call(node = group[i], node.__data__, i);
+ if (nodeByKeyValue.has(keyValue)) {
+ exitNodes[i] = node;
+ } else {
+ nodeByKeyValue.set(keyValue, node);
+ }
+ keyValues.push(keyValue);
+ }
+ for (i = -1; ++i < m; ) {
+ keyValue = key.call(groupData, nodeData = groupData[i], i);
+ if (node = nodeByKeyValue.get(keyValue)) {
+ updateNodes[i] = node;
+ node.__data__ = nodeData;
+ } else if (!dataByKeyValue.has(keyValue)) {
+ enterNodes[i] = d3_selection_dataNode(nodeData);
+ }
+ dataByKeyValue.set(keyValue, nodeData);
+ nodeByKeyValue.remove(keyValue);
+ }
+ for (i = -1; ++i < n; ) {
+ if (nodeByKeyValue.has(keyValues[i])) {
+ exitNodes[i] = group[i];
+ }
+ }
+ } else {
+ for (i = -1; ++i < n0; ) {
+ node = group[i];
+ nodeData = groupData[i];
+ if (node) {
+ node.__data__ = nodeData;
+ updateNodes[i] = node;
+ } else {
+ enterNodes[i] = d3_selection_dataNode(nodeData);
+ }
+ }
+ for (;i < m; ++i) {
+ enterNodes[i] = d3_selection_dataNode(groupData[i]);
+ }
+ for (;i < n; ++i) {
+ exitNodes[i] = group[i];
+ }
+ }
+ enterNodes.update = updateNodes;
+ enterNodes.parentNode = updateNodes.parentNode = exitNodes.parentNode = group.parentNode;
+ enter.push(enterNodes);
+ update.push(updateNodes);
+ exit.push(exitNodes);
+ }
+ var enter = d3_selection_enter([]), update = d3_selection([]), exit = d3_selection([]);
+ if (typeof value === "function") {
+ while (++i < n) {
+ bind(group = this[i], value.call(group, group.parentNode.__data__, i));
+ }
+ } else {
+ while (++i < n) {
+ bind(group = this[i], value);
+ }
+ }
+ update.enter = function() {
+ return enter;
+ };
+ update.exit = function() {
+ return exit;
+ };
+ return update;
+ };
+ function d3_selection_dataNode(data) {
+ return {
+ __data__: data
+ };
+ }
+ d3_selectionPrototype.datum = function(value) {
+ return arguments.length ? this.property("__data__", value) : this.property("__data__");
+ };
+ d3_selectionPrototype.filter = function(filter) {
+ var subgroups = [], subgroup, group, node;
+ if (typeof filter !== "function") filter = d3_selection_filter(filter);
+ for (var j = 0, m = this.length; j < m; j++) {
+ subgroups.push(subgroup = []);
+ subgroup.parentNode = (group = this[j]).parentNode;
+ for (var i = 0, n = group.length; i < n; i++) {
+ if ((node = group[i]) && filter.call(node, node.__data__, i, j)) {
+ subgroup.push(node);
+ }
+ }
+ }
+ return d3_selection(subgroups);
+ };
+ function d3_selection_filter(selector) {
+ return function() {
+ return d3_selectMatches(this, selector);
+ };
+ }
+ d3_selectionPrototype.order = function() {
+ for (var j = -1, m = this.length; ++j < m; ) {
+ for (var group = this[j], i = group.length - 1, next = group[i], node; --i >= 0; ) {
+ if (node = group[i]) {
+ if (next && next !== node.nextSibling) next.parentNode.insertBefore(node, next);
+ next = node;
+ }
+ }
+ }
+ return this;
+ };
+ d3_selectionPrototype.sort = function(comparator) {
+ comparator = d3_selection_sortComparator.apply(this, arguments);
+ for (var j = -1, m = this.length; ++j < m; ) this[j].sort(comparator);
+ return this.order();
+ };
+ function d3_selection_sortComparator(comparator) {
+ if (!arguments.length) comparator = d3_ascending;
+ return function(a, b) {
+ return a && b ? comparator(a.__data__, b.__data__) : !a - !b;
+ };
+ }
+ d3_selectionPrototype.each = function(callback) {
+ return d3_selection_each(this, function(node, i, j) {
+ callback.call(node, node.__data__, i, j);
+ });
+ };
+ function d3_selection_each(groups, callback) {
+ for (var j = 0, m = groups.length; j < m; j++) {
+ for (var group = groups[j], i = 0, n = group.length, node; i < n; i++) {
+ if (node = group[i]) callback(node, i, j);
+ }
+ }
+ return groups;
+ }
+ d3_selectionPrototype.call = function(callback) {
+ var args = d3_array(arguments);
+ callback.apply(args[0] = this, args);
+ return this;
+ };
+ d3_selectionPrototype.empty = function() {
+ return !this.node();
+ };
+ d3_selectionPrototype.node = function() {
+ for (var j = 0, m = this.length; j < m; j++) {
+ for (var group = this[j], i = 0, n = group.length; i < n; i++) {
+ var node = group[i];
+ if (node) return node;
+ }
+ }
+ return null;
+ };
+ d3_selectionPrototype.size = function() {
+ var n = 0;
+ this.each(function() {
+ ++n;
+ });
+ return n;
+ };
+ function d3_selection_enter(selection) {
+ d3_subclass(selection, d3_selection_enterPrototype);
+ return selection;
+ }
+ var d3_selection_enterPrototype = [];
+ d3.selection.enter = d3_selection_enter;
+ d3.selection.enter.prototype = d3_selection_enterPrototype;
+ d3_selection_enterPrototype.append = d3_selectionPrototype.append;
+ d3_selection_enterPrototype.empty = d3_selectionPrototype.empty;
+ d3_selection_enterPrototype.node = d3_selectionPrototype.node;
+ d3_selection_enterPrototype.call = d3_selectionPrototype.call;
+ d3_selection_enterPrototype.size = d3_selectionPrototype.size;
+ d3_selection_enterPrototype.select = function(selector) {
+ var subgroups = [], subgroup, subnode, upgroup, group, node;
+ for (var j = -1, m = this.length; ++j < m; ) {
+ upgroup = (group = this[j]).update;
+ subgroups.push(subgroup = []);
+ subgroup.parentNode = group.parentNode;
+ for (var i = -1, n = group.length; ++i < n; ) {
+ if (node = group[i]) {
+ subgroup.push(upgroup[i] = subnode = selector.call(group.parentNode, node.__data__, i, j));
+ subnode.__data__ = node.__data__;
+ } else {
+ subgroup.push(null);
+ }
+ }
+ }
+ return d3_selection(subgroups);
+ };
+ d3_selection_enterPrototype.insert = function(name, before) {
+ if (arguments.length < 2) before = d3_selection_enterInsertBefore(this);
+ return d3_selectionPrototype.insert.call(this, name, before);
+ };
+ function d3_selection_enterInsertBefore(enter) {
+ var i0, j0;
+ return function(d, i, j) {
+ var group = enter[j].update, n = group.length, node;
+ if (j != j0) j0 = j, i0 = 0;
+ if (i >= i0) i0 = i + 1;
+ while (!(node = group[i0]) && ++i0 < n) ;
+ return node;
+ };
+ }
+ d3_selectionPrototype.transition = function() {
+ var id = d3_transitionInheritId || ++d3_transitionId, subgroups = [], subgroup, node, transition = d3_transitionInherit || {
+ time: Date.now(),
+ ease: d3_ease_cubicInOut,
+ delay: 0,
+ duration: 250
+ };
+ for (var j = -1, m = this.length; ++j < m; ) {
+ subgroups.push(subgroup = []);
+ for (var group = this[j], i = -1, n = group.length; ++i < n; ) {
+ if (node = group[i]) d3_transitionNode(node, i, id, transition);
+ subgroup.push(node);
+ }
+ }
+ return d3_transition(subgroups, id);
+ };
+ d3_selectionPrototype.interrupt = function() {
+ return this.each(d3_selection_interrupt);
+ };
+ function d3_selection_interrupt() {
+ var lock = this.__transition__;
+ if (lock) ++lock.active;
+ }
+ d3.select = function(node) {
+ var group = [ typeof node === "string" ? d3_select(node, d3_document) : node ];
+ group.parentNode = d3_documentElement;
+ return d3_selection([ group ]);
+ };
+ d3.selectAll = function(nodes) {
+ var group = d3_array(typeof nodes === "string" ? d3_selectAll(nodes, d3_document) : nodes);
+ group.parentNode = d3_documentElement;
+ return d3_selection([ group ]);
+ };
+ var d3_selectionRoot = d3.select(d3_documentElement);
+ d3_selectionPrototype.on = function(type, listener, capture) {
+ var n = arguments.length;
+ if (n < 3) {
+ if (typeof type !== "string") {
+ if (n < 2) listener = false;
+ for (capture in type) this.each(d3_selection_on(capture, type[capture], listener));
+ return this;
+ }
+ if (n < 2) return (n = this.node()["__on" + type]) && n._;
+ capture = false;
+ }
+ return this.each(d3_selection_on(type, listener, capture));
+ };
+ function d3_selection_on(type, listener, capture) {
+ var name = "__on" + type, i = type.indexOf("."), wrap = d3_selection_onListener;
+ if (i > 0) type = type.substring(0, i);
+ var filter = d3_selection_onFilters.get(type);
+ if (filter) type = filter, wrap = d3_selection_onFilter;
+ function onRemove() {
+ var l = this[name];
+ if (l) {
+ this.removeEventListener(type, l, l.$);
+ delete this[name];
+ }
+ }
+ function onAdd() {
+ var l = wrap(listener, d3_array(arguments));
+ onRemove.call(this);
+ this.addEventListener(type, this[name] = l, l.$ = capture);
+ l._ = listener;
+ }
+ function removeAll() {
+ var re = new RegExp("^__on([^.]+)" + d3.requote(type) + "$"), match;
+ for (var name in this) {
+ if (match = name.match(re)) {
+ var l = this[name];
+ this.removeEventListener(match[1], l, l.$);
+ delete this[name];
+ }
+ }
+ }
+ return i ? listener ? onAdd : onRemove : listener ? d3_noop : removeAll;
+ }
+ var d3_selection_onFilters = d3.map({
+ mouseenter: "mouseover",
+ mouseleave: "mouseout"
+ });
+ d3_selection_onFilters.forEach(function(k) {
+ if ("on" + k in d3_document) d3_selection_onFilters.remove(k);
+ });
+ function d3_selection_onListener(listener, argumentz) {
+ return function(e) {
+ var o = d3.event;
+ d3.event = e;
+ argumentz[0] = this.__data__;
+ try {
+ listener.apply(this, argumentz);
+ } finally {
+ d3.event = o;
+ }
+ };
+ }
+ function d3_selection_onFilter(listener, argumentz) {
+ var l = d3_selection_onListener(listener, argumentz);
+ return function(e) {
+ var target = this, related = e.relatedTarget;
+ if (!related || related !== target && !(related.compareDocumentPosition(target) & 8)) {
+ l.call(target, e);
+ }
+ };
+ }
+ var d3_event_dragSelect = "onselectstart" in d3_document ? null : d3_vendorSymbol(d3_documentElement.style, "userSelect"), d3_event_dragId = 0;
+ function d3_event_dragSuppress() {
+ var name = ".dragsuppress-" + ++d3_event_dragId, click = "click" + name, w = d3.select(d3_window).on("touchmove" + name, d3_eventPreventDefault).on("dragstart" + name, d3_eventPreventDefault).on("selectstart" + name, d3_eventPreventDefault);
+ if (d3_event_dragSelect) {
+ var style = d3_documentElement.style, select = style[d3_event_dragSelect];
+ style[d3_event_dragSelect] = "none";
+ }
+ return function(suppressClick) {
+ w.on(name, null);
+ if (d3_event_dragSelect) style[d3_event_dragSelect] = select;
+ if (suppressClick) {
+ function off() {
+ w.on(click, null);
+ }
+ w.on(click, function() {
+ d3_eventPreventDefault();
+ off();
+ }, true);
+ setTimeout(off, 0);
+ }
+ };
+ }
+ d3.mouse = function(container) {
+ return d3_mousePoint(container, d3_eventSource());
+ };
+ function d3_mousePoint(container, e) {
+ if (e.changedTouches) e = e.changedTouches[0];
+ var svg = container.ownerSVGElement || container;
+ if (svg.createSVGPoint) {
+ var point = svg.createSVGPoint();
+ point.x = e.clientX, point.y = e.clientY;
+ point = point.matrixTransform(container.getScreenCTM().inverse());
+ return [ point.x, point.y ];
+ }
+ var rect = container.getBoundingClientRect();
+ return [ e.clientX - rect.left - container.clientLeft, e.clientY - rect.top - container.clientTop ];
+ }
+ d3.touches = function(container, touches) {
+ if (arguments.length < 2) touches = d3_eventSource().touches;
+ return touches ? d3_array(touches).map(function(touch) {
+ var point = d3_mousePoint(container, touch);
+ point.identifier = touch.identifier;
+ return point;
+ }) : [];
+ };
+ d3.behavior.drag = function() {
+ var event = d3_eventDispatch(drag, "drag", "dragstart", "dragend"), origin = null, mousedown = dragstart(d3_noop, d3.mouse, d3_behavior_dragMouseSubject, "mousemove", "mouseup"), touchstart = dragstart(d3_behavior_dragTouchId, d3.touch, d3_behavior_dragTouchSubject, "touchmove", "touchend");
+ function drag() {
+ this.on("mousedown.drag", mousedown).on("touchstart.drag", touchstart);
+ }
+ function dragstart(id, position, subject, move, end) {
+ return function() {
+ var that = this, target = d3.event.target, parent = that.parentNode, dispatch = event.of(that, arguments), dragged = 0, dragId = id(), dragName = ".drag" + (dragId == null ? "" : "-" + dragId), dragOffset, dragSubject = d3.select(subject()).on(move + dragName, moved).on(end + dragName, ended), dragRestore = d3_event_dragSuppress(), position0 = position(parent, dragId);
+ if (origin) {
+ dragOffset = origin.apply(that, arguments);
+ dragOffset = [ dragOffset.x - position0[0], dragOffset.y - position0[1] ];
+ } else {
+ dragOffset = [ 0, 0 ];
+ }
+ dispatch({
+ type: "dragstart"
+ });
+ function moved() {
+ var position1 = position(parent, dragId), dx, dy;
+ if (!position1) return;
+ dx = position1[0] - position0[0];
+ dy = position1[1] - position0[1];
+ dragged |= dx | dy;
+ position0 = position1;
+ dispatch({
+ type: "drag",
+ x: position1[0] + dragOffset[0],
+ y: position1[1] + dragOffset[1],
+ dx: dx,
+ dy: dy
+ });
+ }
+ function ended() {
+ if (!position(parent, dragId)) return;
+ dragSubject.on(move + dragName, null).on(end + dragName, null);
+ dragRestore(dragged && d3.event.target === target);
+ dispatch({
+ type: "dragend"
+ });
+ }
+ };
+ }
+ drag.origin = function(x) {
+ if (!arguments.length) return origin;
+ origin = x;
+ return drag;
+ };
+ return d3.rebind(drag, event, "on");
+ };
+ function d3_behavior_dragTouchId() {
+ return d3.event.changedTouches[0].identifier;
+ }
+ function d3_behavior_dragTouchSubject() {
+ return d3.event.target;
+ }
+ function d3_behavior_dragMouseSubject() {
+ return d3_window;
+ }
+ var π = Math.PI, τ = 2 * π, halfπ = π / 2, ε = 1e-6, ε2 = ε * ε, d3_radians = π / 180, d3_degrees = 180 / π;
+ function d3_sgn(x) {
+ return x > 0 ? 1 : x < 0 ? -1 : 0;
+ }
+ function d3_cross2d(a, b, c) {
+ return (b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0]);
+ }
+ function d3_acos(x) {
+ return x > 1 ? 0 : x < -1 ? π : Math.acos(x);
+ }
+ function d3_asin(x) {
+ return x > 1 ? halfπ : x < -1 ? -halfπ : Math.asin(x);
+ }
+ function d3_sinh(x) {
+ return ((x = Math.exp(x)) - 1 / x) / 2;
+ }
+ function d3_cosh(x) {
+ return ((x = Math.exp(x)) + 1 / x) / 2;
+ }
+ function d3_tanh(x) {
+ return ((x = Math.exp(2 * x)) - 1) / (x + 1);
+ }
+ function d3_haversin(x) {
+ return (x = Math.sin(x / 2)) * x;
+ }
+ var ρ = Math.SQRT2, ρ2 = 2, ρ4 = 4;
+ d3.interpolateZoom = function(p0, p1) {
+ var ux0 = p0[0], uy0 = p0[1], w0 = p0[2], ux1 = p1[0], uy1 = p1[1], w1 = p1[2];
+ var dx = ux1 - ux0, dy = uy1 - uy0, d2 = dx * dx + dy * dy, d1 = Math.sqrt(d2), b0 = (w1 * w1 - w0 * w0 + ρ4 * d2) / (2 * w0 * ρ2 * d1), b1 = (w1 * w1 - w0 * w0 - ρ4 * d2) / (2 * w1 * ρ2 * d1), r0 = Math.log(Math.sqrt(b0 * b0 + 1) - b0), r1 = Math.log(Math.sqrt(b1 * b1 + 1) - b1), dr = r1 - r0, S = (dr || Math.log(w1 / w0)) / ρ;
+ function interpolate(t) {
+ var s = t * S;
+ if (dr) {
+ var coshr0 = d3_cosh(r0), u = w0 / (ρ2 * d1) * (coshr0 * d3_tanh(ρ * s + r0) - d3_sinh(r0));
+ return [ ux0 + u * dx, uy0 + u * dy, w0 * coshr0 / d3_cosh(ρ * s + r0) ];
+ }
+ return [ ux0 + t * dx, uy0 + t * dy, w0 * Math.exp(ρ * s) ];
+ }
+ interpolate.duration = S * 1e3;
+ return interpolate;
+ };
+ d3.behavior.zoom = function() {
+ var view = {
+ x: 0,
+ y: 0,
+ k: 1
+ }, translate0, center, size = [ 960, 500 ], scaleExtent = d3_behavior_zoomInfinity, mousedown = "mousedown.zoom", mousemove = "mousemove.zoom", mouseup = "mouseup.zoom", mousewheelTimer, touchstart = "touchstart.zoom", touchtime, event = d3_eventDispatch(zoom, "zoomstart", "zoom", "zoomend"), x0, x1, y0, y1;
+ function zoom(g) {
+ g.on(mousedown, mousedowned).on(d3_behavior_zoomWheel + ".zoom", mousewheeled).on(mousemove, mousewheelreset).on("dblclick.zoom", dblclicked).on(touchstart, touchstarted);
+ }
+ zoom.event = function(g) {
+ g.each(function() {
+ var dispatch = event.of(this, arguments), view1 = view;
+ if (d3_transitionInheritId) {
+ d3.select(this).transition().each("start.zoom", function() {
+ view = this.__chart__ || {
+ x: 0,
+ y: 0,
+ k: 1
+ };
+ zoomstarted(dispatch);
+ }).tween("zoom:zoom", function() {
+ var dx = size[0], dy = size[1], cx = dx / 2, cy = dy / 2, i = d3.interpolateZoom([ (cx - view.x) / view.k, (cy - view.y) / view.k, dx / view.k ], [ (cx - view1.x) / view1.k, (cy - view1.y) / view1.k, dx / view1.k ]);
+ return function(t) {
+ var l = i(t), k = dx / l[2];
+ this.__chart__ = view = {
+ x: cx - l[0] * k,
+ y: cy - l[1] * k,
+ k: k
+ };
+ zoomed(dispatch);
+ };
+ }).each("end.zoom", function() {
+ zoomended(dispatch);
+ });
+ } else {
+ this.__chart__ = view;
+ zoomstarted(dispatch);
+ zoomed(dispatch);
+ zoomended(dispatch);
+ }
+ });
+ };
+ zoom.translate = function(_) {
+ if (!arguments.length) return [ view.x, view.y ];
+ view = {
+ x: +_[0],
+ y: +_[1],
+ k: view.k
+ };
+ rescale();
+ return zoom;
+ };
+ zoom.scale = function(_) {
+ if (!arguments.length) return view.k;
+ view = {
+ x: view.x,
+ y: view.y,
+ k: +_
+ };
+ rescale();
+ return zoom;
+ };
+ zoom.scaleExtent = function(_) {
+ if (!arguments.length) return scaleExtent;
+ scaleExtent = _ == null ? d3_behavior_zoomInfinity : [ +_[0], +_[1] ];
+ return zoom;
+ };
+ zoom.center = function(_) {
+ if (!arguments.length) return center;
+ center = _ && [ +_[0], +_[1] ];
+ return zoom;
+ };
+ zoom.size = function(_) {
+ if (!arguments.length) return size;
+ size = _ && [ +_[0], +_[1] ];
+ return zoom;
+ };
+ zoom.x = function(z) {
+ if (!arguments.length) return x1;
+ x1 = z;
+ x0 = z.copy();
+ view = {
+ x: 0,
+ y: 0,
+ k: 1
+ };
+ return zoom;
+ };
+ zoom.y = function(z) {
+ if (!arguments.length) return y1;
+ y1 = z;
+ y0 = z.copy();
+ view = {
+ x: 0,
+ y: 0,
+ k: 1
+ };
+ return zoom;
+ };
+ function location(p) {
+ return [ (p[0] - view.x) / view.k, (p[1] - view.y) / view.k ];
+ }
+ function point(l) {
+ return [ l[0] * view.k + view.x, l[1] * view.k + view.y ];
+ }
+ function scaleTo(s) {
+ view.k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], s));
+ }
+ function translateTo(p, l) {
+ l = point(l);
+ view.x += p[0] - l[0];
+ view.y += p[1] - l[1];
+ }
+ function rescale() {
+ if (x1) x1.domain(x0.range().map(function(x) {
+ return (x - view.x) / view.k;
+ }).map(x0.invert));
+ if (y1) y1.domain(y0.range().map(function(y) {
+ return (y - view.y) / view.k;
+ }).map(y0.invert));
+ }
+ function zoomstarted(dispatch) {
+ dispatch({
+ type: "zoomstart"
+ });
+ }
+ function zoomed(dispatch) {
+ rescale();
+ dispatch({
+ type: "zoom",
+ scale: view.k,
+ translate: [ view.x, view.y ]
+ });
+ }
+ function zoomended(dispatch) {
+ dispatch({
+ type: "zoomend"
+ });
+ }
+ function mousedowned() {
+ var that = this, target = d3.event.target, dispatch = event.of(that, arguments), dragged = 0, subject = d3.select(d3_window).on(mousemove, moved).on(mouseup, ended), location0 = location(d3.mouse(that)), dragRestore = d3_event_dragSuppress();
+ d3_selection_interrupt.call(that);
+ zoomstarted(dispatch);
+ function moved() {
+ dragged = 1;
+ translateTo(d3.mouse(that), location0);
+ zoomed(dispatch);
+ }
+ function ended() {
+ subject.on(mousemove, d3_window === that ? mousewheelreset : null).on(mouseup, null);
+ dragRestore(dragged && d3.event.target === target);
+ zoomended(dispatch);
+ }
+ }
+ function touchstarted() {
+ var that = this, dispatch = event.of(that, arguments), locations0 = {}, distance0 = 0, scale0, zoomName = ".zoom-" + d3.event.changedTouches[0].identifier, touchmove = "touchmove" + zoomName, touchend = "touchend" + zoomName, target = d3.select(d3.event.target).on(touchmove, moved).on(touchend, ended), subject = d3.select(that).on(mousedown, null).on(touchstart, started), dragRestore = d3_event_dragSuppress();
+ d3_selection_interrupt.call(that);
+ started();
+ zoomstarted(dispatch);
+ function relocate() {
+ var touches = d3.touches(that);
+ scale0 = view.k;
+ touches.forEach(function(t) {
+ if (t.identifier in locations0) locations0[t.identifier] = location(t);
+ });
+ return touches;
+ }
+ function started() {
+ var changed = d3.event.changedTouches;
+ for (var i = 0, n = changed.length; i < n; ++i) {
+ locations0[changed[i].identifier] = null;
+ }
+ var touches = relocate(), now = Date.now();
+ if (touches.length === 1) {
+ if (now - touchtime < 500) {
+ var p = touches[0], l = locations0[p.identifier];
+ scaleTo(view.k * 2);
+ translateTo(p, l);
+ d3_eventPreventDefault();
+ zoomed(dispatch);
+ }
+ touchtime = now;
+ } else if (touches.length > 1) {
+ var p = touches[0], q = touches[1], dx = p[0] - q[0], dy = p[1] - q[1];
+ distance0 = dx * dx + dy * dy;
+ }
+ }
+ function moved() {
+ var touches = d3.touches(that), p0, l0, p1, l1;
+ for (var i = 0, n = touches.length; i < n; ++i, l1 = null) {
+ p1 = touches[i];
+ if (l1 = locations0[p1.identifier]) {
+ if (l0) break;
+ p0 = p1, l0 = l1;
+ }
+ }
+ if (l1) {
+ var distance1 = (distance1 = p1[0] - p0[0]) * distance1 + (distance1 = p1[1] - p0[1]) * distance1, scale1 = distance0 && Math.sqrt(distance1 / distance0);
+ p0 = [ (p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2 ];
+ l0 = [ (l0[0] + l1[0]) / 2, (l0[1] + l1[1]) / 2 ];
+ scaleTo(scale1 * scale0);
+ }
+ touchtime = null;
+ translateTo(p0, l0);
+ zoomed(dispatch);
+ }
+ function ended() {
+ if (d3.event.touches.length) {
+ var changed = d3.event.changedTouches;
+ for (var i = 0, n = changed.length; i < n; ++i) {
+ delete locations0[changed[i].identifier];
+ }
+ for (var identifier in locations0) {
+ return void relocate();
+ }
+ }
+ target.on(zoomName, null);
+ subject.on(mousedown, mousedowned).on(touchstart, touchstarted);
+ dragRestore();
+ zoomended(dispatch);
+ }
+ }
+ function mousewheeled() {
+ var dispatch = event.of(this, arguments);
+ if (mousewheelTimer) clearTimeout(mousewheelTimer); else d3_selection_interrupt.call(this),
+ zoomstarted(dispatch);
+ mousewheelTimer = setTimeout(function() {
+ mousewheelTimer = null;
+ zoomended(dispatch);
+ }, 50);
+ d3_eventPreventDefault();
+ var point = center || d3.mouse(this);
+ if (!translate0) translate0 = location(point);
+ scaleTo(Math.pow(2, d3_behavior_zoomDelta() * .002) * view.k);
+ translateTo(point, translate0);
+ zoomed(dispatch);
+ }
+ function mousewheelreset() {
+ translate0 = null;
+ }
+ function dblclicked() {
+ var dispatch = event.of(this, arguments), p = d3.mouse(this), l = location(p), k = Math.log(view.k) / Math.LN2;
+ zoomstarted(dispatch);
+ scaleTo(Math.pow(2, d3.event.shiftKey ? Math.ceil(k) - 1 : Math.floor(k) + 1));
+ translateTo(p, l);
+ zoomed(dispatch);
+ zoomended(dispatch);
+ }
+ return d3.rebind(zoom, event, "on");
+ };
+ var d3_behavior_zoomInfinity = [ 0, Infinity ];
+ var d3_behavior_zoomDelta, d3_behavior_zoomWheel = "onwheel" in d3_document ? (d3_behavior_zoomDelta = function() {
+ return -d3.event.deltaY * (d3.event.deltaMode ? 120 : 1);
+ }, "wheel") : "onmousewheel" in d3_document ? (d3_behavior_zoomDelta = function() {
+ return d3.event.wheelDelta;
+ }, "mousewheel") : (d3_behavior_zoomDelta = function() {
+ return -d3.event.detail;
+ }, "MozMousePixelScroll");
+ function d3_Color() {}
+ d3_Color.prototype.toString = function() {
+ return this.rgb() + "";
+ };
+ d3.hsl = function(h, s, l) {
+ return arguments.length === 1 ? h instanceof d3_Hsl ? d3_hsl(h.h, h.s, h.l) : d3_rgb_parse("" + h, d3_rgb_hsl, d3_hsl) : d3_hsl(+h, +s, +l);
+ };
+ function d3_hsl(h, s, l) {
+ return new d3_Hsl(h, s, l);
+ }
+ function d3_Hsl(h, s, l) {
+ this.h = h;
+ this.s = s;
+ this.l = l;
+ }
+ var d3_hslPrototype = d3_Hsl.prototype = new d3_Color();
+ d3_hslPrototype.brighter = function(k) {
+ k = Math.pow(.7, arguments.length ? k : 1);
+ return d3_hsl(this.h, this.s, this.l / k);
+ };
+ d3_hslPrototype.darker = function(k) {
+ k = Math.pow(.7, arguments.length ? k : 1);
+ return d3_hsl(this.h, this.s, k * this.l);
+ };
+ d3_hslPrototype.rgb = function() {
+ return d3_hsl_rgb(this.h, this.s, this.l);
+ };
+ function d3_hsl_rgb(h, s, l) {
+ var m1, m2;
+ h = isNaN(h) ? 0 : (h %= 360) < 0 ? h + 360 : h;
+ s = isNaN(s) ? 0 : s < 0 ? 0 : s > 1 ? 1 : s;
+ l = l < 0 ? 0 : l > 1 ? 1 : l;
+ m2 = l <= .5 ? l * (1 + s) : l + s - l * s;
+ m1 = 2 * l - m2;
+ function v(h) {
+ if (h > 360) h -= 360; else if (h < 0) h += 360;
+ if (h < 60) return m1 + (m2 - m1) * h / 60;
+ if (h < 180) return m2;
+ if (h < 240) return m1 + (m2 - m1) * (240 - h) / 60;
+ return m1;
+ }
+ function vv(h) {
+ return Math.round(v(h) * 255);
+ }
+ return d3_rgb(vv(h + 120), vv(h), vv(h - 120));
+ }
+ d3.hcl = function(h, c, l) {
+ return arguments.length === 1 ? h instanceof d3_Hcl ? d3_hcl(h.h, h.c, h.l) : h instanceof d3_Lab ? d3_lab_hcl(h.l, h.a, h.b) : d3_lab_hcl((h = d3_rgb_lab((h = d3.rgb(h)).r, h.g, h.b)).l, h.a, h.b) : d3_hcl(+h, +c, +l);
+ };
+ function d3_hcl(h, c, l) {
+ return new d3_Hcl(h, c, l);
+ }
+ function d3_Hcl(h, c, l) {
+ this.h = h;
+ this.c = c;
+ this.l = l;
+ }
+ var d3_hclPrototype = d3_Hcl.prototype = new d3_Color();
+ d3_hclPrototype.brighter = function(k) {
+ return d3_hcl(this.h, this.c, Math.min(100, this.l + d3_lab_K * (arguments.length ? k : 1)));
+ };
+ d3_hclPrototype.darker = function(k) {
+ return d3_hcl(this.h, this.c, Math.max(0, this.l - d3_lab_K * (arguments.length ? k : 1)));
+ };
+ d3_hclPrototype.rgb = function() {
+ return d3_hcl_lab(this.h, this.c, this.l).rgb();
+ };
+ function d3_hcl_lab(h, c, l) {
+ if (isNaN(h)) h = 0;
+ if (isNaN(c)) c = 0;
+ return d3_lab(l, Math.cos(h *= d3_radians) * c, Math.sin(h) * c);
+ }
+ d3.lab = function(l, a, b) {
+ return arguments.length === 1 ? l instanceof d3_Lab ? d3_lab(l.l, l.a, l.b) : l instanceof d3_Hcl ? d3_hcl_lab(l.l, l.c, l.h) : d3_rgb_lab((l = d3.rgb(l)).r, l.g, l.b) : d3_lab(+l, +a, +b);
+ };
+ function d3_lab(l, a, b) {
+ return new d3_Lab(l, a, b);
+ }
+ function d3_Lab(l, a, b) {
+ this.l = l;
+ this.a = a;
+ this.b = b;
+ }
+ var d3_lab_K = 18;
+ var d3_lab_X = .95047, d3_lab_Y = 1, d3_lab_Z = 1.08883;
+ var d3_labPrototype = d3_Lab.prototype = new d3_Color();
+ d3_labPrototype.brighter = function(k) {
+ return d3_lab(Math.min(100, this.l + d3_lab_K * (arguments.length ? k : 1)), this.a, this.b);
+ };
+ d3_labPrototype.darker = function(k) {
+ return d3_lab(Math.max(0, this.l - d3_lab_K * (arguments.length ? k : 1)), this.a, this.b);
+ };
+ d3_labPrototype.rgb = function() {
+ return d3_lab_rgb(this.l, this.a, this.b);
+ };
+ function d3_lab_rgb(l, a, b) {
+ var y = (l + 16) / 116, x = y + a / 500, z = y - b / 200;
+ x = d3_lab_xyz(x) * d3_lab_X;
+ y = d3_lab_xyz(y) * d3_lab_Y;
+ z = d3_lab_xyz(z) * d3_lab_Z;
+ return d3_rgb(d3_xyz_rgb(3.2404542 * x - 1.5371385 * y - .4985314 * z), d3_xyz_rgb(-.969266 * x + 1.8760108 * y + .041556 * z), d3_xyz_rgb(.0556434 * x - .2040259 * y + 1.0572252 * z));
+ }
+ function d3_lab_hcl(l, a, b) {
+ return l > 0 ? d3_hcl(Math.atan2(b, a) * d3_degrees, Math.sqrt(a * a + b * b), l) : d3_hcl(NaN, NaN, l);
+ }
+ function d3_lab_xyz(x) {
+ return x > .206893034 ? x * x * x : (x - 4 / 29) / 7.787037;
+ }
+ function d3_xyz_lab(x) {
+ return x > .008856 ? Math.pow(x, 1 / 3) : 7.787037 * x + 4 / 29;
+ }
+ function d3_xyz_rgb(r) {
+ return Math.round(255 * (r <= .00304 ? 12.92 * r : 1.055 * Math.pow(r, 1 / 2.4) - .055));
+ }
+ d3.rgb = function(r, g, b) {
+ return arguments.length === 1 ? r instanceof d3_Rgb ? d3_rgb(r.r, r.g, r.b) : d3_rgb_parse("" + r, d3_rgb, d3_hsl_rgb) : d3_rgb(~~r, ~~g, ~~b);
+ };
+ function d3_rgbNumber(value) {
+ return d3_rgb(value >> 16, value >> 8 & 255, value & 255);
+ }
+ function d3_rgbString(value) {
+ return d3_rgbNumber(value) + "";
+ }
+ function d3_rgb(r, g, b) {
+ return new d3_Rgb(r, g, b);
+ }
+ function d3_Rgb(r, g, b) {
+ this.r = r;
+ this.g = g;
+ this.b = b;
+ }
+ var d3_rgbPrototype = d3_Rgb.prototype = new d3_Color();
+ d3_rgbPrototype.brighter = function(k) {
+ k = Math.pow(.7, arguments.length ? k : 1);
+ var r = this.r, g = this.g, b = this.b, i = 30;
+ if (!r && !g && !b) return d3_rgb(i, i, i);
+ if (r && r < i) r = i;
+ if (g && g < i) g = i;
+ if (b && b < i) b = i;
+ return d3_rgb(Math.min(255, ~~(r / k)), Math.min(255, ~~(g / k)), Math.min(255, ~~(b / k)));
+ };
+ d3_rgbPrototype.darker = function(k) {
+ k = Math.pow(.7, arguments.length ? k : 1);
+ return d3_rgb(~~(k * this.r), ~~(k * this.g), ~~(k * this.b));
+ };
+ d3_rgbPrototype.hsl = function() {
+ return d3_rgb_hsl(this.r, this.g, this.b);
+ };
+ d3_rgbPrototype.toString = function() {
+ return "#" + d3_rgb_hex(this.r) + d3_rgb_hex(this.g) + d3_rgb_hex(this.b);
+ };
+ function d3_rgb_hex(v) {
+ return v < 16 ? "0" + Math.max(0, v).toString(16) : Math.min(255, v).toString(16);
+ }
+ function d3_rgb_parse(format, rgb, hsl) {
+ var r = 0, g = 0, b = 0, m1, m2, color;
+ m1 = /([a-z]+)\((.*)\)/i.exec(format);
+ if (m1) {
+ m2 = m1[2].split(",");
+ switch (m1[1]) {
+ case "hsl":
+ {
+ return hsl(parseFloat(m2[0]), parseFloat(m2[1]) / 100, parseFloat(m2[2]) / 100);
+ }
+
+ case "rgb":
+ {
+ return rgb(d3_rgb_parseNumber(m2[0]), d3_rgb_parseNumber(m2[1]), d3_rgb_parseNumber(m2[2]));
+ }
+ }
+ }
+ if (color = d3_rgb_names.get(format)) return rgb(color.r, color.g, color.b);
+ if (format != null && format.charAt(0) === "#" && !isNaN(color = parseInt(format.substring(1), 16))) {
+ if (format.length === 4) {
+ r = (color & 3840) >> 4;
+ r = r >> 4 | r;
+ g = color & 240;
+ g = g >> 4 | g;
+ b = color & 15;
+ b = b << 4 | b;
+ } else if (format.length === 7) {
+ r = (color & 16711680) >> 16;
+ g = (color & 65280) >> 8;
+ b = color & 255;
+ }
+ }
+ return rgb(r, g, b);
+ }
+ function d3_rgb_hsl(r, g, b) {
+ var min = Math.min(r /= 255, g /= 255, b /= 255), max = Math.max(r, g, b), d = max - min, h, s, l = (max + min) / 2;
+ if (d) {
+ s = l < .5 ? d / (max + min) : d / (2 - max - min);
+ if (r == max) h = (g - b) / d + (g < b ? 6 : 0); else if (g == max) h = (b - r) / d + 2; else h = (r - g) / d + 4;
+ h *= 60;
+ } else {
+ h = NaN;
+ s = l > 0 && l < 1 ? 0 : h;
+ }
+ return d3_hsl(h, s, l);
+ }
+ function d3_rgb_lab(r, g, b) {
+ r = d3_rgb_xyz(r);
+ g = d3_rgb_xyz(g);
+ b = d3_rgb_xyz(b);
+ var x = d3_xyz_lab((.4124564 * r + .3575761 * g + .1804375 * b) / d3_lab_X), y = d3_xyz_lab((.2126729 * r + .7151522 * g + .072175 * b) / d3_lab_Y), z = d3_xyz_lab((.0193339 * r + .119192 * g + .9503041 * b) / d3_lab_Z);
+ return d3_lab(116 * y - 16, 500 * (x - y), 200 * (y - z));
+ }
+ function d3_rgb_xyz(r) {
+ return (r /= 255) <= .04045 ? r / 12.92 : Math.pow((r + .055) / 1.055, 2.4);
+ }
+ function d3_rgb_parseNumber(c) {
+ var f = parseFloat(c);
+ return c.charAt(c.length - 1) === "%" ? Math.round(f * 2.55) : f;
+ }
+ var d3_rgb_names = d3.map({
+ aliceblue: 15792383,
+ antiquewhite: 16444375,
+ aqua: 65535,
+ aquamarine: 8388564,
+ azure: 15794175,
+ beige: 16119260,
+ bisque: 16770244,
+ black: 0,
+ blanchedalmond: 16772045,
+ blue: 255,
+ blueviolet: 9055202,
+ brown: 10824234,
+ burlywood: 14596231,
+ cadetblue: 6266528,
+ chartreuse: 8388352,
+ chocolate: 13789470,
+ coral: 16744272,
+ cornflowerblue: 6591981,
+ cornsilk: 16775388,
+ crimson: 14423100,
+ cyan: 65535,
+ darkblue: 139,
+ darkcyan: 35723,
+ darkgoldenrod: 12092939,
+ darkgray: 11119017,
+ darkgreen: 25600,
+ darkgrey: 11119017,
+ darkkhaki: 12433259,
+ darkmagenta: 9109643,
+ darkolivegreen: 5597999,
+ darkorange: 16747520,
+ darkorchid: 10040012,
+ darkred: 9109504,
+ darksalmon: 15308410,
+ darkseagreen: 9419919,
+ darkslateblue: 4734347,
+ darkslategray: 3100495,
+ darkslategrey: 3100495,
+ darkturquoise: 52945,
+ darkviolet: 9699539,
+ deeppink: 16716947,
+ deepskyblue: 49151,
+ dimgray: 6908265,
+ dimgrey: 6908265,
+ dodgerblue: 2003199,
+ firebrick: 11674146,
+ floralwhite: 16775920,
+ forestgreen: 2263842,
+ fuchsia: 16711935,
+ gainsboro: 14474460,
+ ghostwhite: 16316671,
+ gold: 16766720,
+ goldenrod: 14329120,
+ gray: 8421504,
+ green: 32768,
+ greenyellow: 11403055,
+ grey: 8421504,
+ honeydew: 15794160,
+ hotpink: 16738740,
+ indianred: 13458524,
+ indigo: 4915330,
+ ivory: 16777200,
+ khaki: 15787660,
+ lavender: 15132410,
+ lavenderblush: 16773365,
+ lawngreen: 8190976,
+ lemonchiffon: 16775885,
+ lightblue: 11393254,
+ lightcoral: 15761536,
+ lightcyan: 14745599,
+ lightgoldenrodyellow: 16448210,
+ lightgray: 13882323,
+ lightgreen: 9498256,
+ lightgrey: 13882323,
+ lightpink: 16758465,
+ lightsalmon: 16752762,
+ lightseagreen: 2142890,
+ lightskyblue: 8900346,
+ lightslategray: 7833753,
+ lightslategrey: 7833753,
+ lightsteelblue: 11584734,
+ lightyellow: 16777184,
+ lime: 65280,
+ limegreen: 3329330,
+ linen: 16445670,
+ magenta: 16711935,
+ maroon: 8388608,
+ mediumaquamarine: 6737322,
+ mediumblue: 205,
+ mediumorchid: 12211667,
+ mediumpurple: 9662683,
+ mediumseagreen: 3978097,
+ mediumslateblue: 8087790,
+ mediumspringgreen: 64154,
+ mediumturquoise: 4772300,
+ mediumvioletred: 13047173,
+ midnightblue: 1644912,
+ mintcream: 16121850,
+ mistyrose: 16770273,
+ moccasin: 16770229,
+ navajowhite: 16768685,
+ navy: 128,
+ oldlace: 16643558,
+ olive: 8421376,
+ olivedrab: 7048739,
+ orange: 16753920,
+ orangered: 16729344,
+ orchid: 14315734,
+ palegoldenrod: 15657130,
+ palegreen: 10025880,
+ paleturquoise: 11529966,
+ palevioletred: 14381203,
+ papayawhip: 16773077,
+ peachpuff: 16767673,
+ peru: 13468991,
+ pink: 16761035,
+ plum: 14524637,
+ powderblue: 11591910,
+ purple: 8388736,
+ red: 16711680,
+ rosybrown: 12357519,
+ royalblue: 4286945,
+ saddlebrown: 9127187,
+ salmon: 16416882,
+ sandybrown: 16032864,
+ seagreen: 3050327,
+ seashell: 16774638,
+ sienna: 10506797,
+ silver: 12632256,
+ skyblue: 8900331,
+ slateblue: 6970061,
+ slategray: 7372944,
+ slategrey: 7372944,
+ snow: 16775930,
+ springgreen: 65407,
+ steelblue: 4620980,
+ tan: 13808780,
+ teal: 32896,
+ thistle: 14204888,
+ tomato: 16737095,
+ turquoise: 4251856,
+ violet: 15631086,
+ wheat: 16113331,
+ white: 16777215,
+ whitesmoke: 16119285,
+ yellow: 16776960,
+ yellowgreen: 10145074
+ });
+ d3_rgb_names.forEach(function(key, value) {
+ d3_rgb_names.set(key, d3_rgbNumber(value));
+ });
+ function d3_functor(v) {
+ return typeof v === "function" ? v : function() {
+ return v;
+ };
+ }
+ d3.functor = d3_functor;
+ function d3_identity(d) {
+ return d;
+ }
+ d3.xhr = d3_xhrType(d3_identity);
+ function d3_xhrType(response) {
+ return function(url, mimeType, callback) {
+ if (arguments.length === 2 && typeof mimeType === "function") callback = mimeType,
+ mimeType = null;
+ return d3_xhr(url, mimeType, response, callback);
+ };
+ }
+ function d3_xhr(url, mimeType, response, callback) {
+ var xhr = {}, dispatch = d3.dispatch("beforesend", "progress", "load", "error"), headers = {}, request = new XMLHttpRequest(), responseType = null;
+ if (d3_window.XDomainRequest && !("withCredentials" in request) && /^(http(s)?:)?\/\//.test(url)) request = new XDomainRequest();
+ "onload" in request ? request.onload = request.onerror = respond : request.onreadystatechange = function() {
+ request.readyState > 3 && respond();
+ };
+ function respond() {
+ var status = request.status, result;
+ if (!status && request.responseText || status >= 200 && status < 300 || status === 304) {
+ try {
+ result = response.call(xhr, request);
+ } catch (e) {
+ dispatch.error.call(xhr, e);
+ return;
+ }
+ dispatch.load.call(xhr, result);
+ } else {
+ dispatch.error.call(xhr, request);
+ }
+ }
+ request.onprogress = function(event) {
+ var o = d3.event;
+ d3.event = event;
+ try {
+ dispatch.progress.call(xhr, request);
+ } finally {
+ d3.event = o;
+ }
+ };
+ xhr.header = function(name, value) {
+ name = (name + "").toLowerCase();
+ if (arguments.length < 2) return headers[name];
+ if (value == null) delete headers[name]; else headers[name] = value + "";
+ return xhr;
+ };
+ xhr.mimeType = function(value) {
+ if (!arguments.length) return mimeType;
+ mimeType = value == null ? null : value + "";
+ return xhr;
+ };
+ xhr.responseType = function(value) {
+ if (!arguments.length) return responseType;
+ responseType = value;
+ return xhr;
+ };
+ xhr.response = function(value) {
+ response = value;
+ return xhr;
+ };
+ [ "get", "post" ].forEach(function(method) {
+ xhr[method] = function() {
+ return xhr.send.apply(xhr, [ method ].concat(d3_array(arguments)));
+ };
+ });
+ xhr.send = function(method, data, callback) {
+ if (arguments.length === 2 && typeof data === "function") callback = data, data = null;
+ request.open(method, url, true);
+ if (mimeType != null && !("accept" in headers)) headers["accept"] = mimeType + ",*/*";
+ if (request.setRequestHeader) for (var name in headers) request.setRequestHeader(name, headers[name]);
+ if (mimeType != null && request.overrideMimeType) request.overrideMimeType(mimeType);
+ if (responseType != null) request.responseType = responseType;
+ if (callback != null) xhr.on("error", callback).on("load", function(request) {
+ callback(null, request);
+ });
+ dispatch.beforesend.call(xhr, request);
+ request.send(data == null ? null : data);
+ return xhr;
+ };
+ xhr.abort = function() {
+ request.abort();
+ return xhr;
+ };
+ d3.rebind(xhr, dispatch, "on");
+ return callback == null ? xhr : xhr.get(d3_xhr_fixCallback(callback));
+ }
+ function d3_xhr_fixCallback(callback) {
+ return callback.length === 1 ? function(error, request) {
+ callback(error == null ? request : null);
+ } : callback;
+ }
+ d3.dsv = function(delimiter, mimeType) {
+ var reFormat = new RegExp('["' + delimiter + "\n]"), delimiterCode = delimiter.charCodeAt(0);
+ function dsv(url, row, callback) {
+ if (arguments.length < 3) callback = row, row = null;
+ var xhr = d3_xhr(url, mimeType, row == null ? response : typedResponse(row), callback);
+ xhr.row = function(_) {
+ return arguments.length ? xhr.response((row = _) == null ? response : typedResponse(_)) : row;
+ };
+ return xhr;
+ }
+ function response(request) {
+ return dsv.parse(request.responseText);
+ }
+ function typedResponse(f) {
+ return function(request) {
+ return dsv.parse(request.responseText, f);
+ };
+ }
+ dsv.parse = function(text, f) {
+ var o;
+ return dsv.parseRows(text, function(row, i) {
+ if (o) return o(row, i - 1);
+ var a = new Function("d", "return {" + row.map(function(name, i) {
+ return JSON.stringify(name) + ": d[" + i + "]";
+ }).join(",") + "}");
+ o = f ? function(row, i) {
+ return f(a(row), i);
+ } : a;
+ });
+ };
+ dsv.parseRows = function(text, f) {
+ var EOL = {}, EOF = {}, rows = [], N = text.length, I = 0, n = 0, t, eol;
+ function token() {
+ if (I >= N) return EOF;
+ if (eol) return eol = false, EOL;
+ var j = I;
+ if (text.charCodeAt(j) === 34) {
+ var i = j;
+ while (i++ < N) {
+ if (text.charCodeAt(i) === 34) {
+ if (text.charCodeAt(i + 1) !== 34) break;
+ ++i;
+ }
+ }
+ I = i + 2;
+ var c = text.charCodeAt(i + 1);
+ if (c === 13) {
+ eol = true;
+ if (text.charCodeAt(i + 2) === 10) ++I;
+ } else if (c === 10) {
+ eol = true;
+ }
+ return text.substring(j + 1, i).replace(/""/g, '"');
+ }
+ while (I < N) {
+ var c = text.charCodeAt(I++), k = 1;
+ if (c === 10) eol = true; else if (c === 13) {
+ eol = true;
+ if (text.charCodeAt(I) === 10) ++I, ++k;
+ } else if (c !== delimiterCode) continue;
+ return text.substring(j, I - k);
+ }
+ return text.substring(j);
+ }
+ while ((t = token()) !== EOF) {
+ var a = [];
+ while (t !== EOL && t !== EOF) {
+ a.push(t);
+ t = token();
+ }
+ if (f && !(a = f(a, n++))) continue;
+ rows.push(a);
+ }
+ return rows;
+ };
+ dsv.format = function(rows) {
+ if (Array.isArray(rows[0])) return dsv.formatRows(rows);
+ var fieldSet = new d3_Set(), fields = [];
+ rows.forEach(function(row) {
+ for (var field in row) {
+ if (!fieldSet.has(field)) {
+ fields.push(fieldSet.add(field));
+ }
+ }
+ });
+ return [ fields.map(formatValue).join(delimiter) ].concat(rows.map(function(row) {
+ return fields.map(function(field) {
+ return formatValue(row[field]);
+ }).join(delimiter);
+ })).join("\n");
+ };
+ dsv.formatRows = function(rows) {
+ return rows.map(formatRow).join("\n");
+ };
+ function formatRow(row) {
+ return row.map(formatValue).join(delimiter);
+ }
+ function formatValue(text) {
+ return reFormat.test(text) ? '"' + text.replace(/\"/g, '""') + '"' : text;
+ }
+ return dsv;
+ };
+ d3.csv = d3.dsv(",", "text/csv");
+ d3.tsv = d3.dsv(" ", "text/tab-separated-values");
+ d3.touch = function(container, touches, identifier) {
+ if (arguments.length < 3) identifier = touches, touches = d3_eventSource().changedTouches;
+ if (touches) for (var i = 0, n = touches.length, touch; i < n; ++i) {
+ if ((touch = touches[i]).identifier === identifier) {
+ return d3_mousePoint(container, touch);
+ }
+ }
+ };
+ var d3_timer_queueHead, d3_timer_queueTail, d3_timer_interval, d3_timer_timeout, d3_timer_active, d3_timer_frame = d3_window[d3_vendorSymbol(d3_window, "requestAnimationFrame")] || function(callback) {
+ setTimeout(callback, 17);
+ };
+ d3.timer = function(callback, delay, then) {
+ var n = arguments.length;
+ if (n < 2) delay = 0;
+ if (n < 3) then = Date.now();
+ var time = then + delay, timer = {
+ c: callback,
+ t: time,
+ f: false,
+ n: null
+ };
+ if (d3_timer_queueTail) d3_timer_queueTail.n = timer; else d3_timer_queueHead = timer;
+ d3_timer_queueTail = timer;
+ if (!d3_timer_interval) {
+ d3_timer_timeout = clearTimeout(d3_timer_timeout);
+ d3_timer_interval = 1;
+ d3_timer_frame(d3_timer_step);
+ }
+ };
+ function d3_timer_step() {
+ var now = d3_timer_mark(), delay = d3_timer_sweep() - now;
+ if (delay > 24) {
+ if (isFinite(delay)) {
+ clearTimeout(d3_timer_timeout);
+ d3_timer_timeout = setTimeout(d3_timer_step, delay);
+ }
+ d3_timer_interval = 0;
+ } else {
+ d3_timer_interval = 1;
+ d3_timer_frame(d3_timer_step);
+ }
+ }
+ d3.timer.flush = function() {
+ d3_timer_mark();
+ d3_timer_sweep();
+ };
+ function d3_timer_mark() {
+ var now = Date.now();
+ d3_timer_active = d3_timer_queueHead;
+ while (d3_timer_active) {
+ if (now >= d3_timer_active.t) d3_timer_active.f = d3_timer_active.c(now - d3_timer_active.t);
+ d3_timer_active = d3_timer_active.n;
+ }
+ return now;
+ }
+ function d3_timer_sweep() {
+ var t0, t1 = d3_timer_queueHead, time = Infinity;
+ while (t1) {
+ if (t1.f) {
+ t1 = t0 ? t0.n = t1.n : d3_timer_queueHead = t1.n;
+ } else {
+ if (t1.t < time) time = t1.t;
+ t1 = (t0 = t1).n;
+ }
+ }
+ d3_timer_queueTail = t0;
+ return time;
+ }
+ function d3_format_precision(x, p) {
+ return p - (x ? Math.ceil(Math.log(x) / Math.LN10) : 1);
+ }
+ d3.round = function(x, n) {
+ return n ? Math.round(x * (n = Math.pow(10, n))) / n : Math.round(x);
+ };
+ var d3_formatPrefixes = [ "y", "z", "a", "f", "p", "n", "µ", "m", "", "k", "M", "G", "T", "P", "E", "Z", "Y" ].map(d3_formatPrefix);
+ d3.formatPrefix = function(value, precision) {
+ var i = 0;
+ if (value) {
+ if (value < 0) value *= -1;
+ if (precision) value = d3.round(value, d3_format_precision(value, precision));
+ i = 1 + Math.floor(1e-12 + Math.log(value) / Math.LN10);
+ i = Math.max(-24, Math.min(24, Math.floor((i - 1) / 3) * 3));
+ }
+ return d3_formatPrefixes[8 + i / 3];
+ };
+ function d3_formatPrefix(d, i) {
+ var k = Math.pow(10, abs(8 - i) * 3);
+ return {
+ scale: i > 8 ? function(d) {
+ return d / k;
+ } : function(d) {
+ return d * k;
+ },
+ symbol: d
+ };
+ }
+ function d3_locale_numberFormat(locale) {
+ var locale_decimal = locale.decimal, locale_thousands = locale.thousands, locale_grouping = locale.grouping, locale_currency = locale.currency, formatGroup = locale_grouping ? function(value) {
+ var i = value.length, t = [], j = 0, g = locale_grouping[0];
+ while (i > 0 && g > 0) {
+ t.push(value.substring(i -= g, i + g));
+ g = locale_grouping[j = (j + 1) % locale_grouping.length];
+ }
+ return t.reverse().join(locale_thousands);
+ } : d3_identity;
+ return function(specifier) {
+ var match = d3_format_re.exec(specifier), fill = match[1] || " ", align = match[2] || ">", sign = match[3] || "", symbol = match[4] || "", zfill = match[5], width = +match[6], comma = match[7], precision = match[8], type = match[9], scale = 1, prefix = "", suffix = "", integer = false;
+ if (precision) precision = +precision.substring(1);
+ if (zfill || fill === "0" && align === "=") {
+ zfill = fill = "0";
+ align = "=";
+ if (comma) width -= Math.floor((width - 1) / 4);
+ }
+ switch (type) {
+ case "n":
+ comma = true;
+ type = "g";
+ break;
+
+ case "%":
+ scale = 100;
+ suffix = "%";
+ type = "f";
+ break;
+
+ case "p":
+ scale = 100;
+ suffix = "%";
+ type = "r";
+ break;
+
+ case "b":
+ case "o":
+ case "x":
+ case "X":
+ if (symbol === "#") prefix = "0" + type.toLowerCase();
+
+ case "c":
+ case "d":
+ integer = true;
+ precision = 0;
+ break;
+
+ case "s":
+ scale = -1;
+ type = "r";
+ break;
+ }
+ if (symbol === "$") prefix = locale_currency[0], suffix = locale_currency[1];
+ if (type == "r" && !precision) type = "g";
+ if (precision != null) {
+ if (type == "g") precision = Math.max(1, Math.min(21, precision)); else if (type == "e" || type == "f") precision = Math.max(0, Math.min(20, precision));
+ }
+ type = d3_format_types.get(type) || d3_format_typeDefault;
+ var zcomma = zfill && comma;
+ return function(value) {
+ var fullSuffix = suffix;
+ if (integer && value % 1) return "";
+ var negative = value < 0 || value === 0 && 1 / value < 0 ? (value = -value, "-") : sign;
+ if (scale < 0) {
+ var unit = d3.formatPrefix(value, precision);
+ value = unit.scale(value);
+ fullSuffix = unit.symbol + suffix;
+ } else {
+ value *= scale;
+ }
+ value = type(value, precision);
+ var i = value.lastIndexOf("."), before = i < 0 ? value : value.substring(0, i), after = i < 0 ? "" : locale_decimal + value.substring(i + 1);
+ if (!zfill && comma) before = formatGroup(before);
+ var length = prefix.length + before.length + after.length + (zcomma ? 0 : negative.length), padding = length < width ? new Array(length = width - length + 1).join(fill) : "";
+ if (zcomma) before = formatGroup(padding + before);
+ negative += prefix;
+ value = before + after;
+ return (align === "<" ? negative + value + padding : align === ">" ? padding + negative + value : align === "^" ? padding.substring(0, length >>= 1) + negative + value + padding.substring(length) : negative + (zcomma ? value : padding + value)) + fullSuffix;
+ };
+ };
+ }
+ var d3_format_re = /(?:([^{])?([<>=^]))?([+\- ])?([$#])?(0)?(\d+)?(,)?(\.-?\d+)?([a-z%])?/i;
+ var d3_format_types = d3.map({
+ b: function(x) {
+ return x.toString(2);
+ },
+ c: function(x) {
+ return String.fromCharCode(x);
+ },
+ o: function(x) {
+ return x.toString(8);
+ },
+ x: function(x) {
+ return x.toString(16);
+ },
+ X: function(x) {
+ return x.toString(16).toUpperCase();
+ },
+ g: function(x, p) {
+ return x.toPrecision(p);
+ },
+ e: function(x, p) {
+ return x.toExponential(p);
+ },
+ f: function(x, p) {
+ return x.toFixed(p);
+ },
+ r: function(x, p) {
+ return (x = d3.round(x, d3_format_precision(x, p))).toFixed(Math.max(0, Math.min(20, d3_format_precision(x * (1 + 1e-15), p))));
+ }
+ });
+ function d3_format_typeDefault(x) {
+ return x + "";
+ }
+ var d3_time = d3.time = {}, d3_date = Date;
+ function d3_date_utc() {
+ this._ = new Date(arguments.length > 1 ? Date.UTC.apply(this, arguments) : arguments[0]);
+ }
+ d3_date_utc.prototype = {
+ getDate: function() {
+ return this._.getUTCDate();
+ },
+ getDay: function() {
+ return this._.getUTCDay();
+ },
+ getFullYear: function() {
+ return this._.getUTCFullYear();
+ },
+ getHours: function() {
+ return this._.getUTCHours();
+ },
+ getMilliseconds: function() {
+ return this._.getUTCMilliseconds();
+ },
+ getMinutes: function() {
+ return this._.getUTCMinutes();
+ },
+ getMonth: function() {
+ return this._.getUTCMonth();
+ },
+ getSeconds: function() {
+ return this._.getUTCSeconds();
+ },
+ getTime: function() {
+ return this._.getTime();
+ },
+ getTimezoneOffset: function() {
+ return 0;
+ },
+ valueOf: function() {
+ return this._.valueOf();
+ },
+ setDate: function() {
+ d3_time_prototype.setUTCDate.apply(this._, arguments);
+ },
+ setDay: function() {
+ d3_time_prototype.setUTCDay.apply(this._, arguments);
+ },
+ setFullYear: function() {
+ d3_time_prototype.setUTCFullYear.apply(this._, arguments);
+ },
+ setHours: function() {
+ d3_time_prototype.setUTCHours.apply(this._, arguments);
+ },
+ setMilliseconds: function() {
+ d3_time_prototype.setUTCMilliseconds.apply(this._, arguments);
+ },
+ setMinutes: function() {
+ d3_time_prototype.setUTCMinutes.apply(this._, arguments);
+ },
+ setMonth: function() {
+ d3_time_prototype.setUTCMonth.apply(this._, arguments);
+ },
+ setSeconds: function() {
+ d3_time_prototype.setUTCSeconds.apply(this._, arguments);
+ },
+ setTime: function() {
+ d3_time_prototype.setTime.apply(this._, arguments);
+ }
+ };
+ var d3_time_prototype = Date.prototype;
+ function d3_time_interval(local, step, number) {
+ function round(date) {
+ var d0 = local(date), d1 = offset(d0, 1);
+ return date - d0 < d1 - date ? d0 : d1;
+ }
+ function ceil(date) {
+ step(date = local(new d3_date(date - 1)), 1);
+ return date;
+ }
+ function offset(date, k) {
+ step(date = new d3_date(+date), k);
+ return date;
+ }
+ function range(t0, t1, dt) {
+ var time = ceil(t0), times = [];
+ if (dt > 1) {
+ while (time < t1) {
+ if (!(number(time) % dt)) times.push(new Date(+time));
+ step(time, 1);
+ }
+ } else {
+ while (time < t1) times.push(new Date(+time)), step(time, 1);
+ }
+ return times;
+ }
+ function range_utc(t0, t1, dt) {
+ try {
+ d3_date = d3_date_utc;
+ var utc = new d3_date_utc();
+ utc._ = t0;
+ return range(utc, t1, dt);
+ } finally {
+ d3_date = Date;
+ }
+ }
+ local.floor = local;
+ local.round = round;
+ local.ceil = ceil;
+ local.offset = offset;
+ local.range = range;
+ var utc = local.utc = d3_time_interval_utc(local);
+ utc.floor = utc;
+ utc.round = d3_time_interval_utc(round);
+ utc.ceil = d3_time_interval_utc(ceil);
+ utc.offset = d3_time_interval_utc(offset);
+ utc.range = range_utc;
+ return local;
+ }
+ function d3_time_interval_utc(method) {
+ return function(date, k) {
+ try {
+ d3_date = d3_date_utc;
+ var utc = new d3_date_utc();
+ utc._ = date;
+ return method(utc, k)._;
+ } finally {
+ d3_date = Date;
+ }
+ };
+ }
+ d3_time.year = d3_time_interval(function(date) {
+ date = d3_time.day(date);
+ date.setMonth(0, 1);
+ return date;
+ }, function(date, offset) {
+ date.setFullYear(date.getFullYear() + offset);
+ }, function(date) {
+ return date.getFullYear();
+ });
+ d3_time.years = d3_time.year.range;
+ d3_time.years.utc = d3_time.year.utc.range;
+ d3_time.day = d3_time_interval(function(date) {
+ var day = new d3_date(2e3, 0);
+ day.setFullYear(date.getFullYear(), date.getMonth(), date.getDate());
+ return day;
+ }, function(date, offset) {
+ date.setDate(date.getDate() + offset);
+ }, function(date) {
+ return date.getDate() - 1;
+ });
+ d3_time.days = d3_time.day.range;
+ d3_time.days.utc = d3_time.day.utc.range;
+ d3_time.dayOfYear = function(date) {
+ var year = d3_time.year(date);
+ return Math.floor((date - year - (date.getTimezoneOffset() - year.getTimezoneOffset()) * 6e4) / 864e5);
+ };
+ [ "sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday" ].forEach(function(day, i) {
+ i = 7 - i;
+ var interval = d3_time[day] = d3_time_interval(function(date) {
+ (date = d3_time.day(date)).setDate(date.getDate() - (date.getDay() + i) % 7);
+ return date;
+ }, function(date, offset) {
+ date.setDate(date.getDate() + Math.floor(offset) * 7);
+ }, function(date) {
+ var day = d3_time.year(date).getDay();
+ return Math.floor((d3_time.dayOfYear(date) + (day + i) % 7) / 7) - (day !== i);
+ });
+ d3_time[day + "s"] = interval.range;
+ d3_time[day + "s"].utc = interval.utc.range;
+ d3_time[day + "OfYear"] = function(date) {
+ var day = d3_time.year(date).getDay();
+ return Math.floor((d3_time.dayOfYear(date) + (day + i) % 7) / 7);
+ };
+ });
+ d3_time.week = d3_time.sunday;
+ d3_time.weeks = d3_time.sunday.range;
+ d3_time.weeks.utc = d3_time.sunday.utc.range;
+ d3_time.weekOfYear = d3_time.sundayOfYear;
+ function d3_locale_timeFormat(locale) {
+ var locale_dateTime = locale.dateTime, locale_date = locale.date, locale_time = locale.time, locale_periods = locale.periods, locale_days = locale.days, locale_shortDays = locale.shortDays, locale_months = locale.months, locale_shortMonths = locale.shortMonths;
+ function d3_time_format(template) {
+ var n = template.length;
+ function format(date) {
+ var string = [], i = -1, j = 0, c, p, f;
+ while (++i < n) {
+ if (template.charCodeAt(i) === 37) {
+ string.push(template.substring(j, i));
+ if ((p = d3_time_formatPads[c = template.charAt(++i)]) != null) c = template.charAt(++i);
+ if (f = d3_time_formats[c]) c = f(date, p == null ? c === "e" ? " " : "0" : p);
+ string.push(c);
+ j = i + 1;
+ }
+ }
+ string.push(template.substring(j, i));
+ return string.join("");
+ }
+ format.parse = function(string) {
+ var d = {
+ y: 1900,
+ m: 0,
+ d: 1,
+ H: 0,
+ M: 0,
+ S: 0,
+ L: 0,
+ Z: null
+ }, i = d3_time_parse(d, template, string, 0);
+ if (i != string.length) return null;
+ if ("p" in d) d.H = d.H % 12 + d.p * 12;
+ var localZ = d.Z != null && d3_date !== d3_date_utc, date = new (localZ ? d3_date_utc : d3_date)();
+ if ("j" in d) date.setFullYear(d.y, 0, d.j); else if ("w" in d && ("W" in d || "U" in d)) {
+ date.setFullYear(d.y, 0, 1);
+ date.setFullYear(d.y, 0, "W" in d ? (d.w + 6) % 7 + d.W * 7 - (date.getDay() + 5) % 7 : d.w + d.U * 7 - (date.getDay() + 6) % 7);
+ } else date.setFullYear(d.y, d.m, d.d);
+ date.setHours(d.H + Math.floor(d.Z / 100), d.M + d.Z % 100, d.S, d.L);
+ return localZ ? date._ : date;
+ };
+ format.toString = function() {
+ return template;
+ };
+ return format;
+ }
+ function d3_time_parse(date, template, string, j) {
+ var c, p, t, i = 0, n = template.length, m = string.length;
+ while (i < n) {
+ if (j >= m) return -1;
+ c = template.charCodeAt(i++);
+ if (c === 37) {
+ t = template.charAt(i++);
+ p = d3_time_parsers[t in d3_time_formatPads ? template.charAt(i++) : t];
+ if (!p || (j = p(date, string, j)) < 0) return -1;
+ } else if (c != string.charCodeAt(j++)) {
+ return -1;
+ }
+ }
+ return j;
+ }
+ d3_time_format.utc = function(template) {
+ var local = d3_time_format(template);
+ function format(date) {
+ try {
+ d3_date = d3_date_utc;
+ var utc = new d3_date();
+ utc._ = date;
+ return local(utc);
+ } finally {
+ d3_date = Date;
+ }
+ }
+ format.parse = function(string) {
+ try {
+ d3_date = d3_date_utc;
+ var date = local.parse(string);
+ return date && date._;
+ } finally {
+ d3_date = Date;
+ }
+ };
+ format.toString = local.toString;
+ return format;
+ };
+ d3_time_format.multi = d3_time_format.utc.multi = d3_time_formatMulti;
+ var d3_time_periodLookup = d3.map(), d3_time_dayRe = d3_time_formatRe(locale_days), d3_time_dayLookup = d3_time_formatLookup(locale_days), d3_time_dayAbbrevRe = d3_time_formatRe(locale_shortDays), d3_time_dayAbbrevLookup = d3_time_formatLookup(locale_shortDays), d3_time_monthRe = d3_time_formatRe(locale_months), d3_time_monthLookup = d3_time_formatLookup(locale_months), d3_time_monthAbbrevRe = d3_time_formatRe(locale_shortMonths), d3_time_monthAbbrevLookup = d3_time_formatLookup(locale_shortMonths);
+ locale_periods.forEach(function(p, i) {
+ d3_time_periodLookup.set(p.toLowerCase(), i);
+ });
+ var d3_time_formats = {
+ a: function(d) {
+ return locale_shortDays[d.getDay()];
+ },
+ A: function(d) {
+ return locale_days[d.getDay()];
+ },
+ b: function(d) {
+ return locale_shortMonths[d.getMonth()];
+ },
+ B: function(d) {
+ return locale_months[d.getMonth()];
+ },
+ c: d3_time_format(locale_dateTime),
+ d: function(d, p) {
+ return d3_time_formatPad(d.getDate(), p, 2);
+ },
+ e: function(d, p) {
+ return d3_time_formatPad(d.getDate(), p, 2);
+ },
+ H: function(d, p) {
+ return d3_time_formatPad(d.getHours(), p, 2);
+ },
+ I: function(d, p) {
+ return d3_time_formatPad(d.getHours() % 12 || 12, p, 2);
+ },
+ j: function(d, p) {
+ return d3_time_formatPad(1 + d3_time.dayOfYear(d), p, 3);
+ },
+ L: function(d, p) {
+ return d3_time_formatPad(d.getMilliseconds(), p, 3);
+ },
+ m: function(d, p) {
+ return d3_time_formatPad(d.getMonth() + 1, p, 2);
+ },
+ M: function(d, p) {
+ return d3_time_formatPad(d.getMinutes(), p, 2);
+ },
+ p: function(d) {
+ return locale_periods[+(d.getHours() >= 12)];
+ },
+ S: function(d, p) {
+ return d3_time_formatPad(d.getSeconds(), p, 2);
+ },
+ U: function(d, p) {
+ return d3_time_formatPad(d3_time.sundayOfYear(d), p, 2);
+ },
+ w: function(d) {
+ return d.getDay();
+ },
+ W: function(d, p) {
+ return d3_time_formatPad(d3_time.mondayOfYear(d), p, 2);
+ },
+ x: d3_time_format(locale_date),
+ X: d3_time_format(locale_time),
+ y: function(d, p) {
+ return d3_time_formatPad(d.getFullYear() % 100, p, 2);
+ },
+ Y: function(d, p) {
+ return d3_time_formatPad(d.getFullYear() % 1e4, p, 4);
+ },
+ Z: d3_time_zone,
+ "%": function() {
+ return "%";
+ }
+ };
+ var d3_time_parsers = {
+ a: d3_time_parseWeekdayAbbrev,
+ A: d3_time_parseWeekday,
+ b: d3_time_parseMonthAbbrev,
+ B: d3_time_parseMonth,
+ c: d3_time_parseLocaleFull,
+ d: d3_time_parseDay,
+ e: d3_time_parseDay,
+ H: d3_time_parseHour24,
+ I: d3_time_parseHour24,
+ j: d3_time_parseDayOfYear,
+ L: d3_time_parseMilliseconds,
+ m: d3_time_parseMonthNumber,
+ M: d3_time_parseMinutes,
+ p: d3_time_parseAmPm,
+ S: d3_time_parseSeconds,
+ U: d3_time_parseWeekNumberSunday,
+ w: d3_time_parseWeekdayNumber,
+ W: d3_time_parseWeekNumberMonday,
+ x: d3_time_parseLocaleDate,
+ X: d3_time_parseLocaleTime,
+ y: d3_time_parseYear,
+ Y: d3_time_parseFullYear,
+ Z: d3_time_parseZone,
+ "%": d3_time_parseLiteralPercent
+ };
+ function d3_time_parseWeekdayAbbrev(date, string, i) {
+ d3_time_dayAbbrevRe.lastIndex = 0;
+ var n = d3_time_dayAbbrevRe.exec(string.substring(i));
+ return n ? (date.w = d3_time_dayAbbrevLookup.get(n[0].toLowerCase()), i + n[0].length) : -1;
+ }
+ function d3_time_parseWeekday(date, string, i) {
+ d3_time_dayRe.lastIndex = 0;
+ var n = d3_time_dayRe.exec(string.substring(i));
+ return n ? (date.w = d3_time_dayLookup.get(n[0].toLowerCase()), i + n[0].length) : -1;
+ }
+ function d3_time_parseMonthAbbrev(date, string, i) {
+ d3_time_monthAbbrevRe.lastIndex = 0;
+ var n = d3_time_monthAbbrevRe.exec(string.substring(i));
+ return n ? (date.m = d3_time_monthAbbrevLookup.get(n[0].toLowerCase()), i + n[0].length) : -1;
+ }
+ function d3_time_parseMonth(date, string, i) {
+ d3_time_monthRe.lastIndex = 0;
+ var n = d3_time_monthRe.exec(string.substring(i));
+ return n ? (date.m = d3_time_monthLookup.get(n[0].toLowerCase()), i + n[0].length) : -1;
+ }
+ function d3_time_parseLocaleFull(date, string, i) {
+ return d3_time_parse(date, d3_time_formats.c.toString(), string, i);
+ }
+ function d3_time_parseLocaleDate(date, string, i) {
+ return d3_time_parse(date, d3_time_formats.x.toString(), string, i);
+ }
+ function d3_time_parseLocaleTime(date, string, i) {
+ return d3_time_parse(date, d3_time_formats.X.toString(), string, i);
+ }
+ function d3_time_parseAmPm(date, string, i) {
+ var n = d3_time_periodLookup.get(string.substring(i, i += 2).toLowerCase());
+ return n == null ? -1 : (date.p = n, i);
+ }
+ return d3_time_format;
+ }
+ var d3_time_formatPads = {
+ "-": "",
+ _: " ",
+ "0": "0"
+ }, d3_time_numberRe = /^\s*\d+/, d3_time_percentRe = /^%/;
+ function d3_time_formatPad(value, fill, width) {
+ var sign = value < 0 ? "-" : "", string = (sign ? -value : value) + "", length = string.length;
+ return sign + (length < width ? new Array(width - length + 1).join(fill) + string : string);
+ }
+ function d3_time_formatRe(names) {
+ return new RegExp("^(?:" + names.map(d3.requote).join("|") + ")", "i");
+ }
+ function d3_time_formatLookup(names) {
+ var map = new d3_Map(), i = -1, n = names.length;
+ while (++i < n) map.set(names[i].toLowerCase(), i);
+ return map;
+ }
+ function d3_time_parseWeekdayNumber(date, string, i) {
+ d3_time_numberRe.lastIndex = 0;
+ var n = d3_time_numberRe.exec(string.substring(i, i + 1));
+ return n ? (date.w = +n[0], i + n[0].length) : -1;
+ }
+ function d3_time_parseWeekNumberSunday(date, string, i) {
+ d3_time_numberRe.lastIndex = 0;
+ var n = d3_time_numberRe.exec(string.substring(i));
+ return n ? (date.U = +n[0], i + n[0].length) : -1;
+ }
+ function d3_time_parseWeekNumberMonday(date, string, i) {
+ d3_time_numberRe.lastIndex = 0;
+ var n = d3_time_numberRe.exec(string.substring(i));
+ return n ? (date.W = +n[0], i + n[0].length) : -1;
+ }
+ function d3_time_parseFullYear(date, string, i) {
+ d3_time_numberRe.lastIndex = 0;
+ var n = d3_time_numberRe.exec(string.substring(i, i + 4));
+ return n ? (date.y = +n[0], i + n[0].length) : -1;
+ }
+ function d3_time_parseYear(date, string, i) {
+ d3_time_numberRe.lastIndex = 0;
+ var n = d3_time_numberRe.exec(string.substring(i, i + 2));
+ return n ? (date.y = d3_time_expandYear(+n[0]), i + n[0].length) : -1;
+ }
+ function d3_time_parseZone(date, string, i) {
+ return /^[+-]\d{4}$/.test(string = string.substring(i, i + 5)) ? (date.Z = +string,
+ i + 5) : -1;
+ }
+ function d3_time_expandYear(d) {
+ return d + (d > 68 ? 1900 : 2e3);
+ }
+ function d3_time_parseMonthNumber(date, string, i) {
+ d3_time_numberRe.lastIndex = 0;
+ var n = d3_time_numberRe.exec(string.substring(i, i + 2));
+ return n ? (date.m = n[0] - 1, i + n[0].length) : -1;
+ }
+ function d3_time_parseDay(date, string, i) {
+ d3_time_numberRe.lastIndex = 0;
+ var n = d3_time_numberRe.exec(string.substring(i, i + 2));
+ return n ? (date.d = +n[0], i + n[0].length) : -1;
+ }
+ function d3_time_parseDayOfYear(date, string, i) {
+ d3_time_numberRe.lastIndex = 0;
+ var n = d3_time_numberRe.exec(string.substring(i, i + 3));
+ return n ? (date.j = +n[0], i + n[0].length) : -1;
+ }
+ function d3_time_parseHour24(date, string, i) {
+ d3_time_numberRe.lastIndex = 0;
+ var n = d3_time_numberRe.exec(string.substring(i, i + 2));
+ return n ? (date.H = +n[0], i + n[0].length) : -1;
+ }
+ function d3_time_parseMinutes(date, string, i) {
+ d3_time_numberRe.lastIndex = 0;
+ var n = d3_time_numberRe.exec(string.substring(i, i + 2));
+ return n ? (date.M = +n[0], i + n[0].length) : -1;
+ }
+ function d3_time_parseSeconds(date, string, i) {
+ d3_time_numberRe.lastIndex = 0;
+ var n = d3_time_numberRe.exec(string.substring(i, i + 2));
+ return n ? (date.S = +n[0], i + n[0].length) : -1;
+ }
+ function d3_time_parseMilliseconds(date, string, i) {
+ d3_time_numberRe.lastIndex = 0;
+ var n = d3_time_numberRe.exec(string.substring(i, i + 3));
+ return n ? (date.L = +n[0], i + n[0].length) : -1;
+ }
+ function d3_time_zone(d) {
+ var z = d.getTimezoneOffset(), zs = z > 0 ? "-" : "+", zh = ~~(abs(z) / 60), zm = abs(z) % 60;
+ return zs + d3_time_formatPad(zh, "0", 2) + d3_time_formatPad(zm, "0", 2);
+ }
+ function d3_time_parseLiteralPercent(date, string, i) {
+ d3_time_percentRe.lastIndex = 0;
+ var n = d3_time_percentRe.exec(string.substring(i, i + 1));
+ return n ? i + n[0].length : -1;
+ }
+ function d3_time_formatMulti(formats) {
+ var n = formats.length, i = -1;
+ while (++i < n) formats[i][0] = this(formats[i][0]);
+ return function(date) {
+ var i = 0, f = formats[i];
+ while (!f[1](date)) f = formats[++i];
+ return f[0](date);
+ };
+ }
+ d3.locale = function(locale) {
+ return {
+ numberFormat: d3_locale_numberFormat(locale),
+ timeFormat: d3_locale_timeFormat(locale)
+ };
+ };
+ var d3_locale_enUS = d3.locale({
+ decimal: ".",
+ thousands: ",",
+ grouping: [ 3 ],
+ currency: [ "$", "" ],
+ dateTime: "%a %b %e %X %Y",
+ date: "%m/%d/%Y",
+ time: "%H:%M:%S",
+ periods: [ "AM", "PM" ],
+ days: [ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ],
+ shortDays: [ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ],
+ months: [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ],
+ shortMonths: [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ]
+ });
+ d3.format = d3_locale_enUS.numberFormat;
+ d3.geo = {};
+ function d3_adder() {}
+ d3_adder.prototype = {
+ s: 0,
+ t: 0,
+ add: function(y) {
+ d3_adderSum(y, this.t, d3_adderTemp);
+ d3_adderSum(d3_adderTemp.s, this.s, this);
+ if (this.s) this.t += d3_adderTemp.t; else this.s = d3_adderTemp.t;
+ },
+ reset: function() {
+ this.s = this.t = 0;
+ },
+ valueOf: function() {
+ return this.s;
+ }
+ };
+ var d3_adderTemp = new d3_adder();
+ function d3_adderSum(a, b, o) {
+ var x = o.s = a + b, bv = x - a, av = x - bv;
+ o.t = a - av + (b - bv);
+ }
+ d3.geo.stream = function(object, listener) {
+ if (object && d3_geo_streamObjectType.hasOwnProperty(object.type)) {
+ d3_geo_streamObjectType[object.type](object, listener);
+ } else {
+ d3_geo_streamGeometry(object, listener);
+ }
+ };
+ function d3_geo_streamGeometry(geometry, listener) {
+ if (geometry && d3_geo_streamGeometryType.hasOwnProperty(geometry.type)) {
+ d3_geo_streamGeometryType[geometry.type](geometry, listener);
+ }
+ }
+ var d3_geo_streamObjectType = {
+ Feature: function(feature, listener) {
+ d3_geo_streamGeometry(feature.geometry, listener);
+ },
+ FeatureCollection: function(object, listener) {
+ var features = object.features, i = -1, n = features.length;
+ while (++i < n) d3_geo_streamGeometry(features[i].geometry, listener);
+ }
+ };
+ var d3_geo_streamGeometryType = {
+ Sphere: function(object, listener) {
+ listener.sphere();
+ },
+ Point: function(object, listener) {
+ object = object.coordinates;
+ listener.point(object[0], object[1], object[2]);
+ },
+ MultiPoint: function(object, listener) {
+ var coordinates = object.coordinates, i = -1, n = coordinates.length;
+ while (++i < n) object = coordinates[i], listener.point(object[0], object[1], object[2]);
+ },
+ LineString: function(object, listener) {
+ d3_geo_streamLine(object.coordinates, listener, 0);
+ },
+ MultiLineString: function(object, listener) {
+ var coordinates = object.coordinates, i = -1, n = coordinates.length;
+ while (++i < n) d3_geo_streamLine(coordinates[i], listener, 0);
+ },
+ Polygon: function(object, listener) {
+ d3_geo_streamPolygon(object.coordinates, listener);
+ },
+ MultiPolygon: function(object, listener) {
+ var coordinates = object.coordinates, i = -1, n = coordinates.length;
+ while (++i < n) d3_geo_streamPolygon(coordinates[i], listener);
+ },
+ GeometryCollection: function(object, listener) {
+ var geometries = object.geometries, i = -1, n = geometries.length;
+ while (++i < n) d3_geo_streamGeometry(geometries[i], listener);
+ }
+ };
+ function d3_geo_streamLine(coordinates, listener, closed) {
+ var i = -1, n = coordinates.length - closed, coordinate;
+ listener.lineStart();
+ while (++i < n) coordinate = coordinates[i], listener.point(coordinate[0], coordinate[1], coordinate[2]);
+ listener.lineEnd();
+ }
+ function d3_geo_streamPolygon(coordinates, listener) {
+ var i = -1, n = coordinates.length;
+ listener.polygonStart();
+ while (++i < n) d3_geo_streamLine(coordinates[i], listener, 1);
+ listener.polygonEnd();
+ }
+ d3.geo.area = function(object) {
+ d3_geo_areaSum = 0;
+ d3.geo.stream(object, d3_geo_area);
+ return d3_geo_areaSum;
+ };
+ var d3_geo_areaSum, d3_geo_areaRingSum = new d3_adder();
+ var d3_geo_area = {
+ sphere: function() {
+ d3_geo_areaSum += 4 * π;
+ },
+ point: d3_noop,
+ lineStart: d3_noop,
+ lineEnd: d3_noop,
+ polygonStart: function() {
+ d3_geo_areaRingSum.reset();
+ d3_geo_area.lineStart = d3_geo_areaRingStart;
+ },
+ polygonEnd: function() {
+ var area = 2 * d3_geo_areaRingSum;
+ d3_geo_areaSum += area < 0 ? 4 * π + area : area;
+ d3_geo_area.lineStart = d3_geo_area.lineEnd = d3_geo_area.point = d3_noop;
+ }
+ };
+ function d3_geo_areaRingStart() {
+ var λ00, φ00, λ0, cosφ0, sinφ0;
+ d3_geo_area.point = function(λ, φ) {
+ d3_geo_area.point = nextPoint;
+ λ0 = (λ00 = λ) * d3_radians, cosφ0 = Math.cos(φ = (φ00 = φ) * d3_radians / 2 + π / 4),
+ sinφ0 = Math.sin(φ);
+ };
+ function nextPoint(λ, φ) {
+ λ *= d3_radians;
+ φ = φ * d3_radians / 2 + π / 4;
+ var dλ = λ - λ0, sdλ = dλ >= 0 ? 1 : -1, adλ = sdλ * dλ, cosφ = Math.cos(φ), sinφ = Math.sin(φ), k = sinφ0 * sinφ, u = cosφ0 * cosφ + k * Math.cos(adλ), v = k * sdλ * Math.sin(adλ);
+ d3_geo_areaRingSum.add(Math.atan2(v, u));
+ λ0 = λ, cosφ0 = cosφ, sinφ0 = sinφ;
+ }
+ d3_geo_area.lineEnd = function() {
+ nextPoint(λ00, φ00);
+ };
+ }
+ function d3_geo_cartesian(spherical) {
+ var λ = spherical[0], φ = spherical[1], cosφ = Math.cos(φ);
+ return [ cosφ * Math.cos(λ), cosφ * Math.sin(λ), Math.sin(φ) ];
+ }
+ function d3_geo_cartesianDot(a, b) {
+ return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
+ }
+ function d3_geo_cartesianCross(a, b) {
+ return [ a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0] ];
+ }
+ function d3_geo_cartesianAdd(a, b) {
+ a[0] += b[0];
+ a[1] += b[1];
+ a[2] += b[2];
+ }
+ function d3_geo_cartesianScale(vector, k) {
+ return [ vector[0] * k, vector[1] * k, vector[2] * k ];
+ }
+ function d3_geo_cartesianNormalize(d) {
+ var l = Math.sqrt(d[0] * d[0] + d[1] * d[1] + d[2] * d[2]);
+ d[0] /= l;
+ d[1] /= l;
+ d[2] /= l;
+ }
+ function d3_geo_spherical(cartesian) {
+ return [ Math.atan2(cartesian[1], cartesian[0]), d3_asin(cartesian[2]) ];
+ }
+ function d3_geo_sphericalEqual(a, b) {
+ return abs(a[0] - b[0]) < ε && abs(a[1] - b[1]) < ε;
+ }
+ d3.geo.bounds = function() {
+ var λ0, φ0, λ1, φ1, λ_, λ__, φ__, p0, dλSum, ranges, range;
+ var bound = {
+ point: point,
+ lineStart: lineStart,
+ lineEnd: lineEnd,
+ polygonStart: function() {
+ bound.point = ringPoint;
+ bound.lineStart = ringStart;
+ bound.lineEnd = ringEnd;
+ dλSum = 0;
+ d3_geo_area.polygonStart();
+ },
+ polygonEnd: function() {
+ d3_geo_area.polygonEnd();
+ bound.point = point;
+ bound.lineStart = lineStart;
+ bound.lineEnd = lineEnd;
+ if (d3_geo_areaRingSum < 0) λ0 = -(λ1 = 180), φ0 = -(φ1 = 90); else if (dλSum > ε) φ1 = 90; else if (dλSum < -ε) φ0 = -90;
+ range[0] = λ0, range[1] = λ1;
+ }
+ };
+ function point(λ, φ) {
+ ranges.push(range = [ λ0 = λ, λ1 = λ ]);
+ if (φ < φ0) φ0 = φ;
+ if (φ > φ1) φ1 = φ;
+ }
+ function linePoint(λ, φ) {
+ var p = d3_geo_cartesian([ λ * d3_radians, φ * d3_radians ]);
+ if (p0) {
+ var normal = d3_geo_cartesianCross(p0, p), equatorial = [ normal[1], -normal[0], 0 ], inflection = d3_geo_cartesianCross(equatorial, normal);
+ d3_geo_cartesianNormalize(inflection);
+ inflection = d3_geo_spherical(inflection);
+ var dλ = λ - λ_, s = dλ > 0 ? 1 : -1, λi = inflection[0] * d3_degrees * s, antimeridian = abs(dλ) > 180;
+ if (antimeridian ^ (s * λ_ < λi && λi < s * λ)) {
+ var φi = inflection[1] * d3_degrees;
+ if (φi > φ1) φ1 = φi;
+ } else if (λi = (λi + 360) % 360 - 180, antimeridian ^ (s * λ_ < λi && λi < s * λ)) {
+ var φi = -inflection[1] * d3_degrees;
+ if (φi < φ0) φ0 = φi;
+ } else {
+ if (φ < φ0) φ0 = φ;
+ if (φ > φ1) φ1 = φ;
+ }
+ if (antimeridian) {
+ if (λ < λ_) {
+ if (angle(λ0, λ) > angle(λ0, λ1)) λ1 = λ;
+ } else {
+ if (angle(λ, λ1) > angle(λ0, λ1)) λ0 = λ;
+ }
+ } else {
+ if (λ1 >= λ0) {
+ if (λ < λ0) λ0 = λ;
+ if (λ > λ1) λ1 = λ;
+ } else {
+ if (λ > λ_) {
+ if (angle(λ0, λ) > angle(λ0, λ1)) λ1 = λ;
+ } else {
+ if (angle(λ, λ1) > angle(λ0, λ1)) λ0 = λ;
+ }
+ }
+ }
+ } else {
+ point(λ, φ);
+ }
+ p0 = p, λ_ = λ;
+ }
+ function lineStart() {
+ bound.point = linePoint;
+ }
+ function lineEnd() {
+ range[0] = λ0, range[1] = λ1;
+ bound.point = point;
+ p0 = null;
+ }
+ function ringPoint(λ, φ) {
+ if (p0) {
+ var dλ = λ - λ_;
+ dλSum += abs(dλ) > 180 ? dλ + (dλ > 0 ? 360 : -360) : dλ;
+ } else λ__ = λ, φ__ = φ;
+ d3_geo_area.point(λ, φ);
+ linePoint(λ, φ);
+ }
+ function ringStart() {
+ d3_geo_area.lineStart();
+ }
+ function ringEnd() {
+ ringPoint(λ__, φ__);
+ d3_geo_area.lineEnd();
+ if (abs(dλSum) > ε) λ0 = -(λ1 = 180);
+ range[0] = λ0, range[1] = λ1;
+ p0 = null;
+ }
+ function angle(λ0, λ1) {
+ return (λ1 -= λ0) < 0 ? λ1 + 360 : λ1;
+ }
+ function compareRanges(a, b) {
+ return a[0] - b[0];
+ }
+ function withinRange(x, range) {
+ return range[0] <= range[1] ? range[0] <= x && x <= range[1] : x < range[0] || range[1] < x;
+ }
+ return function(feature) {
+ φ1 = λ1 = -(λ0 = φ0 = Infinity);
+ ranges = [];
+ d3.geo.stream(feature, bound);
+ var n = ranges.length;
+ if (n) {
+ ranges.sort(compareRanges);
+ for (var i = 1, a = ranges[0], b, merged = [ a ]; i < n; ++i) {
+ b = ranges[i];
+ if (withinRange(b[0], a) || withinRange(b[1], a)) {
+ if (angle(a[0], b[1]) > angle(a[0], a[1])) a[1] = b[1];
+ if (angle(b[0], a[1]) > angle(a[0], a[1])) a[0] = b[0];
+ } else {
+ merged.push(a = b);
+ }
+ }
+ var best = -Infinity, dλ;
+ for (var n = merged.length - 1, i = 0, a = merged[n], b; i <= n; a = b, ++i) {
+ b = merged[i];
+ if ((dλ = angle(a[1], b[0])) > best) best = dλ, λ0 = b[0], λ1 = a[1];
+ }
+ }
+ ranges = range = null;
+ return λ0 === Infinity || φ0 === Infinity ? [ [ NaN, NaN ], [ NaN, NaN ] ] : [ [ λ0, φ0 ], [ λ1, φ1 ] ];
+ };
+ }();
+ d3.geo.centroid = function(object) {
+ d3_geo_centroidW0 = d3_geo_centroidW1 = d3_geo_centroidX0 = d3_geo_centroidY0 = d3_geo_centroidZ0 = d3_geo_centroidX1 = d3_geo_centroidY1 = d3_geo_centroidZ1 = d3_geo_centroidX2 = d3_geo_centroidY2 = d3_geo_centroidZ2 = 0;
+ d3.geo.stream(object, d3_geo_centroid);
+ var x = d3_geo_centroidX2, y = d3_geo_centroidY2, z = d3_geo_centroidZ2, m = x * x + y * y + z * z;
+ if (m < ε2) {
+ x = d3_geo_centroidX1, y = d3_geo_centroidY1, z = d3_geo_centroidZ1;
+ if (d3_geo_centroidW1 < ε) x = d3_geo_centroidX0, y = d3_geo_centroidY0, z = d3_geo_centroidZ0;
+ m = x * x + y * y + z * z;
+ if (m < ε2) return [ NaN, NaN ];
+ }
+ return [ Math.atan2(y, x) * d3_degrees, d3_asin(z / Math.sqrt(m)) * d3_degrees ];
+ };
+ var d3_geo_centroidW0, d3_geo_centroidW1, d3_geo_centroidX0, d3_geo_centroidY0, d3_geo_centroidZ0, d3_geo_centroidX1, d3_geo_centroidY1, d3_geo_centroidZ1, d3_geo_centroidX2, d3_geo_centroidY2, d3_geo_centroidZ2;
+ var d3_geo_centroid = {
+ sphere: d3_noop,
+ point: d3_geo_centroidPoint,
+ lineStart: d3_geo_centroidLineStart,
+ lineEnd: d3_geo_centroidLineEnd,
+ polygonStart: function() {
+ d3_geo_centroid.lineStart = d3_geo_centroidRingStart;
+ },
+ polygonEnd: function() {
+ d3_geo_centroid.lineStart = d3_geo_centroidLineStart;
+ }
+ };
+ function d3_geo_centroidPoint(λ, φ) {
+ λ *= d3_radians;
+ var cosφ = Math.cos(φ *= d3_radians);
+ d3_geo_centroidPointXYZ(cosφ * Math.cos(λ), cosφ * Math.sin(λ), Math.sin(φ));
+ }
+ function d3_geo_centroidPointXYZ(x, y, z) {
+ ++d3_geo_centroidW0;
+ d3_geo_centroidX0 += (x - d3_geo_centroidX0) / d3_geo_centroidW0;
+ d3_geo_centroidY0 += (y - d3_geo_centroidY0) / d3_geo_centroidW0;
+ d3_geo_centroidZ0 += (z - d3_geo_centroidZ0) / d3_geo_centroidW0;
+ }
+ function d3_geo_centroidLineStart() {
+ var x0, y0, z0;
+ d3_geo_centroid.point = function(λ, φ) {
+ λ *= d3_radians;
+ var cosφ = Math.cos(φ *= d3_radians);
+ x0 = cosφ * Math.cos(λ);
+ y0 = cosφ * Math.sin(λ);
+ z0 = Math.sin(φ);
+ d3_geo_centroid.point = nextPoint;
+ d3_geo_centroidPointXYZ(x0, y0, z0);
+ };
+ function nextPoint(λ, φ) {
+ λ *= d3_radians;
+ var cosφ = Math.cos(φ *= d3_radians), x = cosφ * Math.cos(λ), y = cosφ * Math.sin(λ), z = Math.sin(φ), w = Math.atan2(Math.sqrt((w = y0 * z - z0 * y) * w + (w = z0 * x - x0 * z) * w + (w = x0 * y - y0 * x) * w), x0 * x + y0 * y + z0 * z);
+ d3_geo_centroidW1 += w;
+ d3_geo_centroidX1 += w * (x0 + (x0 = x));
+ d3_geo_centroidY1 += w * (y0 + (y0 = y));
+ d3_geo_centroidZ1 += w * (z0 + (z0 = z));
+ d3_geo_centroidPointXYZ(x0, y0, z0);
+ }
+ }
+ function d3_geo_centroidLineEnd() {
+ d3_geo_centroid.point = d3_geo_centroidPoint;
+ }
+ function d3_geo_centroidRingStart() {
+ var λ00, φ00, x0, y0, z0;
+ d3_geo_centroid.point = function(λ, φ) {
+ λ00 = λ, φ00 = φ;
+ d3_geo_centroid.point = nextPoint;
+ λ *= d3_radians;
+ var cosφ = Math.cos(φ *= d3_radians);
+ x0 = cosφ * Math.cos(λ);
+ y0 = cosφ * Math.sin(λ);
+ z0 = Math.sin(φ);
+ d3_geo_centroidPointXYZ(x0, y0, z0);
+ };
+ d3_geo_centroid.lineEnd = function() {
+ nextPoint(λ00, φ00);
+ d3_geo_centroid.lineEnd = d3_geo_centroidLineEnd;
+ d3_geo_centroid.point = d3_geo_centroidPoint;
+ };
+ function nextPoint(λ, φ) {
+ λ *= d3_radians;
+ var cosφ = Math.cos(φ *= d3_radians), x = cosφ * Math.cos(λ), y = cosφ * Math.sin(λ), z = Math.sin(φ), cx = y0 * z - z0 * y, cy = z0 * x - x0 * z, cz = x0 * y - y0 * x, m = Math.sqrt(cx * cx + cy * cy + cz * cz), u = x0 * x + y0 * y + z0 * z, v = m && -d3_acos(u) / m, w = Math.atan2(m, u);
+ d3_geo_centroidX2 += v * cx;
+ d3_geo_centroidY2 += v * cy;
+ d3_geo_centroidZ2 += v * cz;
+ d3_geo_centroidW1 += w;
+ d3_geo_centroidX1 += w * (x0 + (x0 = x));
+ d3_geo_centroidY1 += w * (y0 + (y0 = y));
+ d3_geo_centroidZ1 += w * (z0 + (z0 = z));
+ d3_geo_centroidPointXYZ(x0, y0, z0);
+ }
+ }
+ function d3_true() {
+ return true;
+ }
+ function d3_geo_clipPolygon(segments, compare, clipStartInside, interpolate, listener) {
+ var subject = [], clip = [];
+ segments.forEach(function(segment) {
+ if ((n = segment.length - 1) <= 0) return;
+ var n, p0 = segment[0], p1 = segment[n];
+ if (d3_geo_sphericalEqual(p0, p1)) {
+ listener.lineStart();
+ for (var i = 0; i < n; ++i) listener.point((p0 = segment[i])[0], p0[1]);
+ listener.lineEnd();
+ return;
+ }
+ var a = new d3_geo_clipPolygonIntersection(p0, segment, null, true), b = new d3_geo_clipPolygonIntersection(p0, null, a, false);
+ a.o = b;
+ subject.push(a);
+ clip.push(b);
+ a = new d3_geo_clipPolygonIntersection(p1, segment, null, false);
+ b = new d3_geo_clipPolygonIntersection(p1, null, a, true);
+ a.o = b;
+ subject.push(a);
+ clip.push(b);
+ });
+ clip.sort(compare);
+ d3_geo_clipPolygonLinkCircular(subject);
+ d3_geo_clipPolygonLinkCircular(clip);
+ if (!subject.length) return;
+ for (var i = 0, entry = clipStartInside, n = clip.length; i < n; ++i) {
+ clip[i].e = entry = !entry;
+ }
+ var start = subject[0], points, point;
+ while (1) {
+ var current = start, isSubject = true;
+ while (current.v) if ((current = current.n) === start) return;
+ points = current.z;
+ listener.lineStart();
+ do {
+ current.v = current.o.v = true;
+ if (current.e) {
+ if (isSubject) {
+ for (var i = 0, n = points.length; i < n; ++i) listener.point((point = points[i])[0], point[1]);
+ } else {
+ interpolate(current.x, current.n.x, 1, listener);
+ }
+ current = current.n;
+ } else {
+ if (isSubject) {
+ points = current.p.z;
+ for (var i = points.length - 1; i >= 0; --i) listener.point((point = points[i])[0], point[1]);
+ } else {
+ interpolate(current.x, current.p.x, -1, listener);
+ }
+ current = current.p;
+ }
+ current = current.o;
+ points = current.z;
+ isSubject = !isSubject;
+ } while (!current.v);
+ listener.lineEnd();
+ }
+ }
+ function d3_geo_clipPolygonLinkCircular(array) {
+ if (!(n = array.length)) return;
+ var n, i = 0, a = array[0], b;
+ while (++i < n) {
+ a.n = b = array[i];
+ b.p = a;
+ a = b;
+ }
+ a.n = b = array[0];
+ b.p = a;
+ }
+ function d3_geo_clipPolygonIntersection(point, points, other, entry) {
+ this.x = point;
+ this.z = points;
+ this.o = other;
+ this.e = entry;
+ this.v = false;
+ this.n = this.p = null;
+ }
+ function d3_geo_clip(pointVisible, clipLine, interpolate, clipStart) {
+ return function(rotate, listener) {
+ var line = clipLine(listener), rotatedClipStart = rotate.invert(clipStart[0], clipStart[1]);
+ var clip = {
+ point: point,
+ lineStart: lineStart,
+ lineEnd: lineEnd,
+ polygonStart: function() {
+ clip.point = pointRing;
+ clip.lineStart = ringStart;
+ clip.lineEnd = ringEnd;
+ segments = [];
+ polygon = [];
+ listener.polygonStart();
+ },
+ polygonEnd: function() {
+ clip.point = point;
+ clip.lineStart = lineStart;
+ clip.lineEnd = lineEnd;
+ segments = d3.merge(segments);
+ var clipStartInside = d3_geo_pointInPolygon(rotatedClipStart, polygon);
+ if (segments.length) {
+ d3_geo_clipPolygon(segments, d3_geo_clipSort, clipStartInside, interpolate, listener);
+ } else if (clipStartInside) {
+ listener.lineStart();
+ interpolate(null, null, 1, listener);
+ listener.lineEnd();
+ }
+ listener.polygonEnd();
+ segments = polygon = null;
+ },
+ sphere: function() {
+ listener.polygonStart();
+ listener.lineStart();
+ interpolate(null, null, 1, listener);
+ listener.lineEnd();
+ listener.polygonEnd();
+ }
+ };
+ function point(λ, φ) {
+ var point = rotate(λ, φ);
+ if (pointVisible(λ = point[0], φ = point[1])) listener.point(λ, φ);
+ }
+ function pointLine(λ, φ) {
+ var point = rotate(λ, φ);
+ line.point(point[0], point[1]);
+ }
+ function lineStart() {
+ clip.point = pointLine;
+ line.lineStart();
+ }
+ function lineEnd() {
+ clip.point = point;
+ line.lineEnd();
+ }
+ var segments;
+ var buffer = d3_geo_clipBufferListener(), ringListener = clipLine(buffer), polygon, ring;
+ function pointRing(λ, φ) {
+ ring.push([ λ, φ ]);
+ var point = rotate(λ, φ);
+ ringListener.point(point[0], point[1]);
+ }
+ function ringStart() {
+ ringListener.lineStart();
+ ring = [];
+ }
+ function ringEnd() {
+ pointRing(ring[0][0], ring[0][1]);
+ ringListener.lineEnd();
+ var clean = ringListener.clean(), ringSegments = buffer.buffer(), segment, n = ringSegments.length;
+ ring.pop();
+ polygon.push(ring);
+ ring = null;
+ if (!n) return;
+ if (clean & 1) {
+ segment = ringSegments[0];
+ var n = segment.length - 1, i = -1, point;
+ listener.lineStart();
+ while (++i < n) listener.point((point = segment[i])[0], point[1]);
+ listener.lineEnd();
+ return;
+ }
+ if (n > 1 && clean & 2) ringSegments.push(ringSegments.pop().concat(ringSegments.shift()));
+ segments.push(ringSegments.filter(d3_geo_clipSegmentLength1));
+ }
+ return clip;
+ };
+ }
+ function d3_geo_clipSegmentLength1(segment) {
+ return segment.length > 1;
+ }
+ function d3_geo_clipBufferListener() {
+ var lines = [], line;
+ return {
+ lineStart: function() {
+ lines.push(line = []);
+ },
+ point: function(λ, φ) {
+ line.push([ λ, φ ]);
+ },
+ lineEnd: d3_noop,
+ buffer: function() {
+ var buffer = lines;
+ lines = [];
+ line = null;
+ return buffer;
+ },
+ rejoin: function() {
+ if (lines.length > 1) lines.push(lines.pop().concat(lines.shift()));
+ }
+ };
+ }
+ function d3_geo_clipSort(a, b) {
+ return ((a = a.x)[0] < 0 ? a[1] - halfπ - ε : halfπ - a[1]) - ((b = b.x)[0] < 0 ? b[1] - halfπ - ε : halfπ - b[1]);
+ }
+ function d3_geo_pointInPolygon(point, polygon) {
+ var meridian = point[0], parallel = point[1], meridianNormal = [ Math.sin(meridian), -Math.cos(meridian), 0 ], polarAngle = 0, winding = 0;
+ d3_geo_areaRingSum.reset();
+ for (var i = 0, n = polygon.length; i < n; ++i) {
+ var ring = polygon[i], m = ring.length;
+ if (!m) continue;
+ var point0 = ring[0], λ0 = point0[0], φ0 = point0[1] / 2 + π / 4, sinφ0 = Math.sin(φ0), cosφ0 = Math.cos(φ0), j = 1;
+ while (true) {
+ if (j === m) j = 0;
+ point = ring[j];
+ var λ = point[0], φ = point[1] / 2 + π / 4, sinφ = Math.sin(φ), cosφ = Math.cos(φ), dλ = λ - λ0, sdλ = dλ >= 0 ? 1 : -1, adλ = sdλ * dλ, antimeridian = adλ > π, k = sinφ0 * sinφ;
+ d3_geo_areaRingSum.add(Math.atan2(k * sdλ * Math.sin(adλ), cosφ0 * cosφ + k * Math.cos(adλ)));
+ polarAngle += antimeridian ? dλ + sdλ * τ : dλ;
+ if (antimeridian ^ λ0 >= meridian ^ λ >= meridian) {
+ var arc = d3_geo_cartesianCross(d3_geo_cartesian(point0), d3_geo_cartesian(point));
+ d3_geo_cartesianNormalize(arc);
+ var intersection = d3_geo_cartesianCross(meridianNormal, arc);
+ d3_geo_cartesianNormalize(intersection);
+ var φarc = (antimeridian ^ dλ >= 0 ? -1 : 1) * d3_asin(intersection[2]);
+ if (parallel > φarc || parallel === φarc && (arc[0] || arc[1])) {
+ winding += antimeridian ^ dλ >= 0 ? 1 : -1;
+ }
+ }
+ if (!j++) break;
+ λ0 = λ, sinφ0 = sinφ, cosφ0 = cosφ, point0 = point;
+ }
+ }
+ return (polarAngle < -ε || polarAngle < ε && d3_geo_areaRingSum < 0) ^ winding & 1;
+ }
+ var d3_geo_clipAntimeridian = d3_geo_clip(d3_true, d3_geo_clipAntimeridianLine, d3_geo_clipAntimeridianInterpolate, [ -π, -π / 2 ]);
+ function d3_geo_clipAntimeridianLine(listener) {
+ var λ0 = NaN, φ0 = NaN, sλ0 = NaN, clean;
+ return {
+ lineStart: function() {
+ listener.lineStart();
+ clean = 1;
+ },
+ point: function(λ1, φ1) {
+ var sλ1 = λ1 > 0 ? π : -π, dλ = abs(λ1 - λ0);
+ if (abs(dλ - π) < ε) {
+ listener.point(λ0, φ0 = (φ0 + φ1) / 2 > 0 ? halfπ : -halfπ);
+ listener.point(sλ0, φ0);
+ listener.lineEnd();
+ listener.lineStart();
+ listener.point(sλ1, φ0);
+ listener.point(λ1, φ0);
+ clean = 0;
+ } else if (sλ0 !== sλ1 && dλ >= π) {
+ if (abs(λ0 - sλ0) < ε) λ0 -= sλ0 * ε;
+ if (abs(λ1 - sλ1) < ε) λ1 -= sλ1 * ε;
+ φ0 = d3_geo_clipAntimeridianIntersect(λ0, φ0, λ1, φ1);
+ listener.point(sλ0, φ0);
+ listener.lineEnd();
+ listener.lineStart();
+ listener.point(sλ1, φ0);
+ clean = 0;
+ }
+ listener.point(λ0 = λ1, φ0 = φ1);
+ sλ0 = sλ1;
+ },
+ lineEnd: function() {
+ listener.lineEnd();
+ λ0 = φ0 = NaN;
+ },
+ clean: function() {
+ return 2 - clean;
+ }
+ };
+ }
+ function d3_geo_clipAntimeridianIntersect(λ0, φ0, λ1, φ1) {
+ var cosφ0, cosφ1, sinλ0_λ1 = Math.sin(λ0 - λ1);
+ return abs(sinλ0_λ1) > ε ? Math.atan((Math.sin(φ0) * (cosφ1 = Math.cos(φ1)) * Math.sin(λ1) - Math.sin(φ1) * (cosφ0 = Math.cos(φ0)) * Math.sin(λ0)) / (cosφ0 * cosφ1 * sinλ0_λ1)) : (φ0 + φ1) / 2;
+ }
+ function d3_geo_clipAntimeridianInterpolate(from, to, direction, listener) {
+ var φ;
+ if (from == null) {
+ φ = direction * halfπ;
+ listener.point(-π, φ);
+ listener.point(0, φ);
+ listener.point(π, φ);
+ listener.point(π, 0);
+ listener.point(π, -φ);
+ listener.point(0, -φ);
+ listener.point(-π, -φ);
+ listener.point(-π, 0);
+ listener.point(-π, φ);
+ } else if (abs(from[0] - to[0]) > ε) {
+ var s = from[0] < to[0] ? π : -π;
+ φ = direction * s / 2;
+ listener.point(-s, φ);
+ listener.point(0, φ);
+ listener.point(s, φ);
+ } else {
+ listener.point(to[0], to[1]);
+ }
+ }
+ function d3_geo_clipCircle(radius) {
+ var cr = Math.cos(radius), smallRadius = cr > 0, notHemisphere = abs(cr) > ε, interpolate = d3_geo_circleInterpolate(radius, 6 * d3_radians);
+ return d3_geo_clip(visible, clipLine, interpolate, smallRadius ? [ 0, -radius ] : [ -π, radius - π ]);
+ function visible(λ, φ) {
+ return Math.cos(λ) * Math.cos(φ) > cr;
+ }
+ function clipLine(listener) {
+ var point0, c0, v0, v00, clean;
+ return {
+ lineStart: function() {
+ v00 = v0 = false;
+ clean = 1;
+ },
+ point: function(λ, φ) {
+ var point1 = [ λ, φ ], point2, v = visible(λ, φ), c = smallRadius ? v ? 0 : code(λ, φ) : v ? code(λ + (λ < 0 ? π : -π), φ) : 0;
+ if (!point0 && (v00 = v0 = v)) listener.lineStart();
+ if (v !== v0) {
+ point2 = intersect(point0, point1);
+ if (d3_geo_sphericalEqual(point0, point2) || d3_geo_sphericalEqual(point1, point2)) {
+ point1[0] += ε;
+ point1[1] += ε;
+ v = visible(point1[0], point1[1]);
+ }
+ }
+ if (v !== v0) {
+ clean = 0;
+ if (v) {
+ listener.lineStart();
+ point2 = intersect(point1, point0);
+ listener.point(point2[0], point2[1]);
+ } else {
+ point2 = intersect(point0, point1);
+ listener.point(point2[0], point2[1]);
+ listener.lineEnd();
+ }
+ point0 = point2;
+ } else if (notHemisphere && point0 && smallRadius ^ v) {
+ var t;
+ if (!(c & c0) && (t = intersect(point1, point0, true))) {
+ clean = 0;
+ if (smallRadius) {
+ listener.lineStart();
+ listener.point(t[0][0], t[0][1]);
+ listener.point(t[1][0], t[1][1]);
+ listener.lineEnd();
+ } else {
+ listener.point(t[1][0], t[1][1]);
+ listener.lineEnd();
+ listener.lineStart();
+ listener.point(t[0][0], t[0][1]);
+ }
+ }
+ }
+ if (v && (!point0 || !d3_geo_sphericalEqual(point0, point1))) {
+ listener.point(point1[0], point1[1]);
+ }
+ point0 = point1, v0 = v, c0 = c;
+ },
+ lineEnd: function() {
+ if (v0) listener.lineEnd();
+ point0 = null;
+ },
+ clean: function() {
+ return clean | (v00 && v0) << 1;
+ }
+ };
+ }
+ function intersect(a, b, two) {
+ var pa = d3_geo_cartesian(a), pb = d3_geo_cartesian(b);
+ var n1 = [ 1, 0, 0 ], n2 = d3_geo_cartesianCross(pa, pb), n2n2 = d3_geo_cartesianDot(n2, n2), n1n2 = n2[0], determinant = n2n2 - n1n2 * n1n2;
+ if (!determinant) return !two && a;
+ var c1 = cr * n2n2 / determinant, c2 = -cr * n1n2 / determinant, n1xn2 = d3_geo_cartesianCross(n1, n2), A = d3_geo_cartesianScale(n1, c1), B = d3_geo_cartesianScale(n2, c2);
+ d3_geo_cartesianAdd(A, B);
+ var u = n1xn2, w = d3_geo_cartesianDot(A, u), uu = d3_geo_cartesianDot(u, u), t2 = w * w - uu * (d3_geo_cartesianDot(A, A) - 1);
+ if (t2 < 0) return;
+ var t = Math.sqrt(t2), q = d3_geo_cartesianScale(u, (-w - t) / uu);
+ d3_geo_cartesianAdd(q, A);
+ q = d3_geo_spherical(q);
+ if (!two) return q;
+ var λ0 = a[0], λ1 = b[0], φ0 = a[1], φ1 = b[1], z;
+ if (λ1 < λ0) z = λ0, λ0 = λ1, λ1 = z;
+ var δλ = λ1 - λ0, polar = abs(δλ - π) < ε, meridian = polar || δλ < ε;
+ if (!polar && φ1 < φ0) z = φ0, φ0 = φ1, φ1 = z;
+ if (meridian ? polar ? φ0 + φ1 > 0 ^ q[1] < (abs(q[0] - λ0) < ε ? φ0 : φ1) : φ0 <= q[1] && q[1] <= φ1 : δλ > π ^ (λ0 <= q[0] && q[0] <= λ1)) {
+ var q1 = d3_geo_cartesianScale(u, (-w + t) / uu);
+ d3_geo_cartesianAdd(q1, A);
+ return [ q, d3_geo_spherical(q1) ];
+ }
+ }
+ function code(λ, φ) {
+ var r = smallRadius ? radius : π - radius, code = 0;
+ if (λ < -r) code |= 1; else if (λ > r) code |= 2;
+ if (φ < -r) code |= 4; else if (φ > r) code |= 8;
+ return code;
+ }
+ }
+ function d3_geom_clipLine(x0, y0, x1, y1) {
+ return function(line) {
+ var a = line.a, b = line.b, ax = a.x, ay = a.y, bx = b.x, by = b.y, t0 = 0, t1 = 1, dx = bx - ax, dy = by - ay, r;
+ r = x0 - ax;
+ if (!dx && r > 0) return;
+ r /= dx;
+ if (dx < 0) {
+ if (r < t0) return;
+ if (r < t1) t1 = r;
+ } else if (dx > 0) {
+ if (r > t1) return;
+ if (r > t0) t0 = r;
+ }
+ r = x1 - ax;
+ if (!dx && r < 0) return;
+ r /= dx;
+ if (dx < 0) {
+ if (r > t1) return;
+ if (r > t0) t0 = r;
+ } else if (dx > 0) {
+ if (r < t0) return;
+ if (r < t1) t1 = r;
+ }
+ r = y0 - ay;
+ if (!dy && r > 0) return;
+ r /= dy;
+ if (dy < 0) {
+ if (r < t0) return;
+ if (r < t1) t1 = r;
+ } else if (dy > 0) {
+ if (r > t1) return;
+ if (r > t0) t0 = r;
+ }
+ r = y1 - ay;
+ if (!dy && r < 0) return;
+ r /= dy;
+ if (dy < 0) {
+ if (r > t1) return;
+ if (r > t0) t0 = r;
+ } else if (dy > 0) {
+ if (r < t0) return;
+ if (r < t1) t1 = r;
+ }
+ if (t0 > 0) line.a = {
+ x: ax + t0 * dx,
+ y: ay + t0 * dy
+ };
+ if (t1 < 1) line.b = {
+ x: ax + t1 * dx,
+ y: ay + t1 * dy
+ };
+ return line;
+ };
+ }
+ var d3_geo_clipExtentMAX = 1e9;
+ d3.geo.clipExtent = function() {
+ var x0, y0, x1, y1, stream, clip, clipExtent = {
+ stream: function(output) {
+ if (stream) stream.valid = false;
+ stream = clip(output);
+ stream.valid = true;
+ return stream;
+ },
+ extent: function(_) {
+ if (!arguments.length) return [ [ x0, y0 ], [ x1, y1 ] ];
+ clip = d3_geo_clipExtent(x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1]);
+ if (stream) stream.valid = false, stream = null;
+ return clipExtent;
+ }
+ };
+ return clipExtent.extent([ [ 0, 0 ], [ 960, 500 ] ]);
+ };
+ function d3_geo_clipExtent(x0, y0, x1, y1) {
+ return function(listener) {
+ var listener_ = listener, bufferListener = d3_geo_clipBufferListener(), clipLine = d3_geom_clipLine(x0, y0, x1, y1), segments, polygon, ring;
+ var clip = {
+ point: point,
+ lineStart: lineStart,
+ lineEnd: lineEnd,
+ polygonStart: function() {
+ listener = bufferListener;
+ segments = [];
+ polygon = [];
+ clean = true;
+ },
+ polygonEnd: function() {
+ listener = listener_;
+ segments = d3.merge(segments);
+ var clipStartInside = insidePolygon([ x0, y1 ]), inside = clean && clipStartInside, visible = segments.length;
+ if (inside || visible) {
+ listener.polygonStart();
+ if (inside) {
+ listener.lineStart();
+ interpolate(null, null, 1, listener);
+ listener.lineEnd();
+ }
+ if (visible) {
+ d3_geo_clipPolygon(segments, compare, clipStartInside, interpolate, listener);
+ }
+ listener.polygonEnd();
+ }
+ segments = polygon = ring = null;
+ }
+ };
+ function insidePolygon(p) {
+ var wn = 0, n = polygon.length, y = p[1];
+ for (var i = 0; i < n; ++i) {
+ for (var j = 1, v = polygon[i], m = v.length, a = v[0], b; j < m; ++j) {
+ b = v[j];
+ if (a[1] <= y) {
+ if (b[1] > y && d3_cross2d(a, b, p) > 0) ++wn;
+ } else {
+ if (b[1] <= y && d3_cross2d(a, b, p) < 0) --wn;
+ }
+ a = b;
+ }
+ }
+ return wn !== 0;
+ }
+ function interpolate(from, to, direction, listener) {
+ var a = 0, a1 = 0;
+ if (from == null || (a = corner(from, direction)) !== (a1 = corner(to, direction)) || comparePoints(from, to) < 0 ^ direction > 0) {
+ do {
+ listener.point(a === 0 || a === 3 ? x0 : x1, a > 1 ? y1 : y0);
+ } while ((a = (a + direction + 4) % 4) !== a1);
+ } else {
+ listener.point(to[0], to[1]);
+ }
+ }
+ function pointVisible(x, y) {
+ return x0 <= x && x <= x1 && y0 <= y && y <= y1;
+ }
+ function point(x, y) {
+ if (pointVisible(x, y)) listener.point(x, y);
+ }
+ var x__, y__, v__, x_, y_, v_, first, clean;
+ function lineStart() {
+ clip.point = linePoint;
+ if (polygon) polygon.push(ring = []);
+ first = true;
+ v_ = false;
+ x_ = y_ = NaN;
+ }
+ function lineEnd() {
+ if (segments) {
+ linePoint(x__, y__);
+ if (v__ && v_) bufferListener.rejoin();
+ segments.push(bufferListener.buffer());
+ }
+ clip.point = point;
+ if (v_) listener.lineEnd();
+ }
+ function linePoint(x, y) {
+ x = Math.max(-d3_geo_clipExtentMAX, Math.min(d3_geo_clipExtentMAX, x));
+ y = Math.max(-d3_geo_clipExtentMAX, Math.min(d3_geo_clipExtentMAX, y));
+ var v = pointVisible(x, y);
+ if (polygon) ring.push([ x, y ]);
+ if (first) {
+ x__ = x, y__ = y, v__ = v;
+ first = false;
+ if (v) {
+ listener.lineStart();
+ listener.point(x, y);
+ }
+ } else {
+ if (v && v_) listener.point(x, y); else {
+ var l = {
+ a: {
+ x: x_,
+ y: y_
+ },
+ b: {
+ x: x,
+ y: y
+ }
+ };
+ if (clipLine(l)) {
+ if (!v_) {
+ listener.lineStart();
+ listener.point(l.a.x, l.a.y);
+ }
+ listener.point(l.b.x, l.b.y);
+ if (!v) listener.lineEnd();
+ clean = false;
+ } else if (v) {
+ listener.lineStart();
+ listener.point(x, y);
+ clean = false;
+ }
+ }
+ }
+ x_ = x, y_ = y, v_ = v;
+ }
+ return clip;
+ };
+ function corner(p, direction) {
+ return abs(p[0] - x0) < ε ? direction > 0 ? 0 : 3 : abs(p[0] - x1) < ε ? direction > 0 ? 2 : 1 : abs(p[1] - y0) < ε ? direction > 0 ? 1 : 0 : direction > 0 ? 3 : 2;
+ }
+ function compare(a, b) {
+ return comparePoints(a.x, b.x);
+ }
+ function comparePoints(a, b) {
+ var ca = corner(a, 1), cb = corner(b, 1);
+ return ca !== cb ? ca - cb : ca === 0 ? b[1] - a[1] : ca === 1 ? a[0] - b[0] : ca === 2 ? a[1] - b[1] : b[0] - a[0];
+ }
+ }
+ function d3_geo_compose(a, b) {
+ function compose(x, y) {
+ return x = a(x, y), b(x[0], x[1]);
+ }
+ if (a.invert && b.invert) compose.invert = function(x, y) {
+ return x = b.invert(x, y), x && a.invert(x[0], x[1]);
+ };
+ return compose;
+ }
+ function d3_geo_conic(projectAt) {
+ var φ0 = 0, φ1 = π / 3, m = d3_geo_projectionMutator(projectAt), p = m(φ0, φ1);
+ p.parallels = function(_) {
+ if (!arguments.length) return [ φ0 / π * 180, φ1 / π * 180 ];
+ return m(φ0 = _[0] * π / 180, φ1 = _[1] * π / 180);
+ };
+ return p;
+ }
+ function d3_geo_conicEqualArea(φ0, φ1) {
+ var sinφ0 = Math.sin(φ0), n = (sinφ0 + Math.sin(φ1)) / 2, C = 1 + sinφ0 * (2 * n - sinφ0), ρ0 = Math.sqrt(C) / n;
+ function forward(λ, φ) {
+ var ρ = Math.sqrt(C - 2 * n * Math.sin(φ)) / n;
+ return [ ρ * Math.sin(λ *= n), ρ0 - ρ * Math.cos(λ) ];
+ }
+ forward.invert = function(x, y) {
+ var ρ0_y = ρ0 - y;
+ return [ Math.atan2(x, ρ0_y) / n, d3_asin((C - (x * x + ρ0_y * ρ0_y) * n * n) / (2 * n)) ];
+ };
+ return forward;
+ }
+ (d3.geo.conicEqualArea = function() {
+ return d3_geo_conic(d3_geo_conicEqualArea);
+ }).raw = d3_geo_conicEqualArea;
+ d3.geo.albers = function() {
+ return d3.geo.conicEqualArea().rotate([ 96, 0 ]).center([ -.6, 38.7 ]).parallels([ 29.5, 45.5 ]).scale(1070);
+ };
+ d3.geo.albersUsa = function() {
+ var lower48 = d3.geo.albers();
+ var alaska = d3.geo.conicEqualArea().rotate([ 154, 0 ]).center([ -2, 58.5 ]).parallels([ 55, 65 ]);
+ var hawaii = d3.geo.conicEqualArea().rotate([ 157, 0 ]).center([ -3, 19.9 ]).parallels([ 8, 18 ]);
+ var point, pointStream = {
+ point: function(x, y) {
+ point = [ x, y ];
+ }
+ }, lower48Point, alaskaPoint, hawaiiPoint;
+ function albersUsa(coordinates) {
+ var x = coordinates[0], y = coordinates[1];
+ point = null;
+ (lower48Point(x, y), point) || (alaskaPoint(x, y), point) || hawaiiPoint(x, y);
+ return point;
+ }
+ albersUsa.invert = function(coordinates) {
+ var k = lower48.scale(), t = lower48.translate(), x = (coordinates[0] - t[0]) / k, y = (coordinates[1] - t[1]) / k;
+ return (y >= .12 && y < .234 && x >= -.425 && x < -.214 ? alaska : y >= .166 && y < .234 && x >= -.214 && x < -.115 ? hawaii : lower48).invert(coordinates);
+ };
+ albersUsa.stream = function(stream) {
+ var lower48Stream = lower48.stream(stream), alaskaStream = alaska.stream(stream), hawaiiStream = hawaii.stream(stream);
+ return {
+ point: function(x, y) {
+ lower48Stream.point(x, y);
+ alaskaStream.point(x, y);
+ hawaiiStream.point(x, y);
+ },
+ sphere: function() {
+ lower48Stream.sphere();
+ alaskaStream.sphere();
+ hawaiiStream.sphere();
+ },
+ lineStart: function() {
+ lower48Stream.lineStart();
+ alaskaStream.lineStart();
+ hawaiiStream.lineStart();
+ },
+ lineEnd: function() {
+ lower48Stream.lineEnd();
+ alaskaStream.lineEnd();
+ hawaiiStream.lineEnd();
+ },
+ polygonStart: function() {
+ lower48Stream.polygonStart();
+ alaskaStream.polygonStart();
+ hawaiiStream.polygonStart();
+ },
+ polygonEnd: function() {
+ lower48Stream.polygonEnd();
+ alaskaStream.polygonEnd();
+ hawaiiStream.polygonEnd();
+ }
+ };
+ };
+ albersUsa.precision = function(_) {
+ if (!arguments.length) return lower48.precision();
+ lower48.precision(_);
+ alaska.precision(_);
+ hawaii.precision(_);
+ return albersUsa;
+ };
+ albersUsa.scale = function(_) {
+ if (!arguments.length) return lower48.scale();
+ lower48.scale(_);
+ alaska.scale(_ * .35);
+ hawaii.scale(_);
+ return albersUsa.translate(lower48.translate());
+ };
+ albersUsa.translate = function(_) {
+ if (!arguments.length) return lower48.translate();
+ var k = lower48.scale(), x = +_[0], y = +_[1];
+ lower48Point = lower48.translate(_).clipExtent([ [ x - .455 * k, y - .238 * k ], [ x + .455 * k, y + .238 * k ] ]).stream(pointStream).point;
+ alaskaPoint = alaska.translate([ x - .307 * k, y + .201 * k ]).clipExtent([ [ x - .425 * k + ε, y + .12 * k + ε ], [ x - .214 * k - ε, y + .234 * k - ε ] ]).stream(pointStream).point;
+ hawaiiPoint = hawaii.translate([ x - .205 * k, y + .212 * k ]).clipExtent([ [ x - .214 * k + ε, y + .166 * k + ε ], [ x - .115 * k - ε, y + .234 * k - ε ] ]).stream(pointStream).point;
+ return albersUsa;
+ };
+ return albersUsa.scale(1070);
+ };
+ var d3_geo_pathAreaSum, d3_geo_pathAreaPolygon, d3_geo_pathArea = {
+ point: d3_noop,
+ lineStart: d3_noop,
+ lineEnd: d3_noop,
+ polygonStart: function() {
+ d3_geo_pathAreaPolygon = 0;
+ d3_geo_pathArea.lineStart = d3_geo_pathAreaRingStart;
+ },
+ polygonEnd: function() {
+ d3_geo_pathArea.lineStart = d3_geo_pathArea.lineEnd = d3_geo_pathArea.point = d3_noop;
+ d3_geo_pathAreaSum += abs(d3_geo_pathAreaPolygon / 2);
+ }
+ };
+ function d3_geo_pathAreaRingStart() {
+ var x00, y00, x0, y0;
+ d3_geo_pathArea.point = function(x, y) {
+ d3_geo_pathArea.point = nextPoint;
+ x00 = x0 = x, y00 = y0 = y;
+ };
+ function nextPoint(x, y) {
+ d3_geo_pathAreaPolygon += y0 * x - x0 * y;
+ x0 = x, y0 = y;
+ }
+ d3_geo_pathArea.lineEnd = function() {
+ nextPoint(x00, y00);
+ };
+ }
+ var d3_geo_pathBoundsX0, d3_geo_pathBoundsY0, d3_geo_pathBoundsX1, d3_geo_pathBoundsY1;
+ var d3_geo_pathBounds = {
+ point: d3_geo_pathBoundsPoint,
+ lineStart: d3_noop,
+ lineEnd: d3_noop,
+ polygonStart: d3_noop,
+ polygonEnd: d3_noop
+ };
+ function d3_geo_pathBoundsPoint(x, y) {
+ if (x < d3_geo_pathBoundsX0) d3_geo_pathBoundsX0 = x;
+ if (x > d3_geo_pathBoundsX1) d3_geo_pathBoundsX1 = x;
+ if (y < d3_geo_pathBoundsY0) d3_geo_pathBoundsY0 = y;
+ if (y > d3_geo_pathBoundsY1) d3_geo_pathBoundsY1 = y;
+ }
+ function d3_geo_pathBuffer() {
+ var pointCircle = d3_geo_pathBufferCircle(4.5), buffer = [];
+ var stream = {
+ point: point,
+ lineStart: function() {
+ stream.point = pointLineStart;
+ },
+ lineEnd: lineEnd,
+ polygonStart: function() {
+ stream.lineEnd = lineEndPolygon;
+ },
+ polygonEnd: function() {
+ stream.lineEnd = lineEnd;
+ stream.point = point;
+ },
+ pointRadius: function(_) {
+ pointCircle = d3_geo_pathBufferCircle(_);
+ return stream;
+ },
+ result: function() {
+ if (buffer.length) {
+ var result = buffer.join("");
+ buffer = [];
+ return result;
+ }
+ }
+ };
+ function point(x, y) {
+ buffer.push("M", x, ",", y, pointCircle);
+ }
+ function pointLineStart(x, y) {
+ buffer.push("M", x, ",", y);
+ stream.point = pointLine;
+ }
+ function pointLine(x, y) {
+ buffer.push("L", x, ",", y);
+ }
+ function lineEnd() {
+ stream.point = point;
+ }
+ function lineEndPolygon() {
+ buffer.push("Z");
+ }
+ return stream;
+ }
+ function d3_geo_pathBufferCircle(radius) {
+ return "m0," + radius + "a" + radius + "," + radius + " 0 1,1 0," + -2 * radius + "a" + radius + "," + radius + " 0 1,1 0," + 2 * radius + "z";
+ }
+ var d3_geo_pathCentroid = {
+ point: d3_geo_pathCentroidPoint,
+ lineStart: d3_geo_pathCentroidLineStart,
+ lineEnd: d3_geo_pathCentroidLineEnd,
+ polygonStart: function() {
+ d3_geo_pathCentroid.lineStart = d3_geo_pathCentroidRingStart;
+ },
+ polygonEnd: function() {
+ d3_geo_pathCentroid.point = d3_geo_pathCentroidPoint;
+ d3_geo_pathCentroid.lineStart = d3_geo_pathCentroidLineStart;
+ d3_geo_pathCentroid.lineEnd = d3_geo_pathCentroidLineEnd;
+ }
+ };
+ function d3_geo_pathCentroidPoint(x, y) {
+ d3_geo_centroidX0 += x;
+ d3_geo_centroidY0 += y;
+ ++d3_geo_centroidZ0;
+ }
+ function d3_geo_pathCentroidLineStart() {
+ var x0, y0;
+ d3_geo_pathCentroid.point = function(x, y) {
+ d3_geo_pathCentroid.point = nextPoint;
+ d3_geo_pathCentroidPoint(x0 = x, y0 = y);
+ };
+ function nextPoint(x, y) {
+ var dx = x - x0, dy = y - y0, z = Math.sqrt(dx * dx + dy * dy);
+ d3_geo_centroidX1 += z * (x0 + x) / 2;
+ d3_geo_centroidY1 += z * (y0 + y) / 2;
+ d3_geo_centroidZ1 += z;
+ d3_geo_pathCentroidPoint(x0 = x, y0 = y);
+ }
+ }
+ function d3_geo_pathCentroidLineEnd() {
+ d3_geo_pathCentroid.point = d3_geo_pathCentroidPoint;
+ }
+ function d3_geo_pathCentroidRingStart() {
+ var x00, y00, x0, y0;
+ d3_geo_pathCentroid.point = function(x, y) {
+ d3_geo_pathCentroid.point = nextPoint;
+ d3_geo_pathCentroidPoint(x00 = x0 = x, y00 = y0 = y);
+ };
+ function nextPoint(x, y) {
+ var dx = x - x0, dy = y - y0, z = Math.sqrt(dx * dx + dy * dy);
+ d3_geo_centroidX1 += z * (x0 + x) / 2;
+ d3_geo_centroidY1 += z * (y0 + y) / 2;
+ d3_geo_centroidZ1 += z;
+ z = y0 * x - x0 * y;
+ d3_geo_centroidX2 += z * (x0 + x);
+ d3_geo_centroidY2 += z * (y0 + y);
+ d3_geo_centroidZ2 += z * 3;
+ d3_geo_pathCentroidPoint(x0 = x, y0 = y);
+ }
+ d3_geo_pathCentroid.lineEnd = function() {
+ nextPoint(x00, y00);
+ };
+ }
+ function d3_geo_pathContext(context) {
+ var pointRadius = 4.5;
+ var stream = {
+ point: point,
+ lineStart: function() {
+ stream.point = pointLineStart;
+ },
+ lineEnd: lineEnd,
+ polygonStart: function() {
+ stream.lineEnd = lineEndPolygon;
+ },
+ polygonEnd: function() {
+ stream.lineEnd = lineEnd;
+ stream.point = point;
+ },
+ pointRadius: function(_) {
+ pointRadius = _;
+ return stream;
+ },
+ result: d3_noop
+ };
+ function point(x, y) {
+ context.moveTo(x, y);
+ context.arc(x, y, pointRadius, 0, τ);
+ }
+ function pointLineStart(x, y) {
+ context.moveTo(x, y);
+ stream.point = pointLine;
+ }
+ function pointLine(x, y) {
+ context.lineTo(x, y);
+ }
+ function lineEnd() {
+ stream.point = point;
+ }
+ function lineEndPolygon() {
+ context.closePath();
+ }
+ return stream;
+ }
+ function d3_geo_resample(project) {
+ var δ2 = .5, cosMinDistance = Math.cos(30 * d3_radians), maxDepth = 16;
+ function resample(stream) {
+ return (maxDepth ? resampleRecursive : resampleNone)(stream);
+ }
+ function resampleNone(stream) {
+ return d3_geo_transformPoint(stream, function(x, y) {
+ x = project(x, y);
+ stream.point(x[0], x[1]);
+ });
+ }
+ function resampleRecursive(stream) {
+ var λ00, φ00, x00, y00, a00, b00, c00, λ0, x0, y0, a0, b0, c0;
+ var resample = {
+ point: point,
+ lineStart: lineStart,
+ lineEnd: lineEnd,
+ polygonStart: function() {
+ stream.polygonStart();
+ resample.lineStart = ringStart;
+ },
+ polygonEnd: function() {
+ stream.polygonEnd();
+ resample.lineStart = lineStart;
+ }
+ };
+ function point(x, y) {
+ x = project(x, y);
+ stream.point(x[0], x[1]);
+ }
+ function lineStart() {
+ x0 = NaN;
+ resample.point = linePoint;
+ stream.lineStart();
+ }
+ function linePoint(λ, φ) {
+ var c = d3_geo_cartesian([ λ, φ ]), p = project(λ, φ);
+ resampleLineTo(x0, y0, λ0, a0, b0, c0, x0 = p[0], y0 = p[1], λ0 = λ, a0 = c[0], b0 = c[1], c0 = c[2], maxDepth, stream);
+ stream.point(x0, y0);
+ }
+ function lineEnd() {
+ resample.point = point;
+ stream.lineEnd();
+ }
+ function ringStart() {
+ lineStart();
+ resample.point = ringPoint;
+ resample.lineEnd = ringEnd;
+ }
+ function ringPoint(λ, φ) {
+ linePoint(λ00 = λ, φ00 = φ), x00 = x0, y00 = y0, a00 = a0, b00 = b0, c00 = c0;
+ resample.point = linePoint;
+ }
+ function ringEnd() {
+ resampleLineTo(x0, y0, λ0, a0, b0, c0, x00, y00, λ00, a00, b00, c00, maxDepth, stream);
+ resample.lineEnd = lineEnd;
+ lineEnd();
+ }
+ return resample;
+ }
+ function resampleLineTo(x0, y0, λ0, a0, b0, c0, x1, y1, λ1, a1, b1, c1, depth, stream) {
+ var dx = x1 - x0, dy = y1 - y0, d2 = dx * dx + dy * dy;
+ if (d2 > 4 * δ2 && depth--) {
+ var a = a0 + a1, b = b0 + b1, c = c0 + c1, m = Math.sqrt(a * a + b * b + c * c), φ2 = Math.asin(c /= m), λ2 = abs(abs(c) - 1) < ε || abs(λ0 - λ1) < ε ? (λ0 + λ1) / 2 : Math.atan2(b, a), p = project(λ2, φ2), x2 = p[0], y2 = p[1], dx2 = x2 - x0, dy2 = y2 - y0, dz = dy * dx2 - dx * dy2;
+ if (dz * dz / d2 > δ2 || abs((dx * dx2 + dy * dy2) / d2 - .5) > .3 || a0 * a1 + b0 * b1 + c0 * c1 < cosMinDistance) {
+ resampleLineTo(x0, y0, λ0, a0, b0, c0, x2, y2, λ2, a /= m, b /= m, c, depth, stream);
+ stream.point(x2, y2);
+ resampleLineTo(x2, y2, λ2, a, b, c, x1, y1, λ1, a1, b1, c1, depth, stream);
+ }
+ }
+ }
+ resample.precision = function(_) {
+ if (!arguments.length) return Math.sqrt(δ2);
+ maxDepth = (δ2 = _ * _) > 0 && 16;
+ return resample;
+ };
+ return resample;
+ }
+ d3.geo.path = function() {
+ var pointRadius = 4.5, projection, context, projectStream, contextStream, cacheStream;
+ function path(object) {
+ if (object) {
+ if (typeof pointRadius === "function") contextStream.pointRadius(+pointRadius.apply(this, arguments));
+ if (!cacheStream || !cacheStream.valid) cacheStream = projectStream(contextStream);
+ d3.geo.stream(object, cacheStream);
+ }
+ return contextStream.result();
+ }
+ path.area = function(object) {
+ d3_geo_pathAreaSum = 0;
+ d3.geo.stream(object, projectStream(d3_geo_pathArea));
+ return d3_geo_pathAreaSum;
+ };
+ path.centroid = function(object) {
+ d3_geo_centroidX0 = d3_geo_centroidY0 = d3_geo_centroidZ0 = d3_geo_centroidX1 = d3_geo_centroidY1 = d3_geo_centroidZ1 = d3_geo_centroidX2 = d3_geo_centroidY2 = d3_geo_centroidZ2 = 0;
+ d3.geo.stream(object, projectStream(d3_geo_pathCentroid));
+ return d3_geo_centroidZ2 ? [ d3_geo_centroidX2 / d3_geo_centroidZ2, d3_geo_centroidY2 / d3_geo_centroidZ2 ] : d3_geo_centroidZ1 ? [ d3_geo_centroidX1 / d3_geo_centroidZ1, d3_geo_centroidY1 / d3_geo_centroidZ1 ] : d3_geo_centroidZ0 ? [ d3_geo_centroidX0 / d3_geo_centroidZ0, d3_geo_centroidY0 / d3_geo_centroidZ0 ] : [ NaN, NaN ];
+ };
+ path.bounds = function(object) {
+ d3_geo_pathBoundsX1 = d3_geo_pathBoundsY1 = -(d3_geo_pathBoundsX0 = d3_geo_pathBoundsY0 = Infinity);
+ d3.geo.stream(object, projectStream(d3_geo_pathBounds));
+ return [ [ d3_geo_pathBoundsX0, d3_geo_pathBoundsY0 ], [ d3_geo_pathBoundsX1, d3_geo_pathBoundsY1 ] ];
+ };
+ path.projection = function(_) {
+ if (!arguments.length) return projection;
+ projectStream = (projection = _) ? _.stream || d3_geo_pathProjectStream(_) : d3_identity;
+ return reset();
+ };
+ path.context = function(_) {
+ if (!arguments.length) return context;
+ contextStream = (context = _) == null ? new d3_geo_pathBuffer() : new d3_geo_pathContext(_);
+ if (typeof pointRadius !== "function") contextStream.pointRadius(pointRadius);
+ return reset();
+ };
+ path.pointRadius = function(_) {
+ if (!arguments.length) return pointRadius;
+ pointRadius = typeof _ === "function" ? _ : (contextStream.pointRadius(+_), +_);
+ return path;
+ };
+ function reset() {
+ cacheStream = null;
+ return path;
+ }
+ return path.projection(d3.geo.albersUsa()).context(null);
+ };
+ function d3_geo_pathProjectStream(project) {
+ var resample = d3_geo_resample(function(x, y) {
+ return project([ x * d3_degrees, y * d3_degrees ]);
+ });
+ return function(stream) {
+ return d3_geo_projectionRadians(resample(stream));
+ };
+ }
+ d3.geo.transform = function(methods) {
+ return {
+ stream: function(stream) {
+ var transform = new d3_geo_transform(stream);
+ for (var k in methods) transform[k] = methods[k];
+ return transform;
+ }
+ };
+ };
+ function d3_geo_transform(stream) {
+ this.stream = stream;
+ }
+ d3_geo_transform.prototype = {
+ point: function(x, y) {
+ this.stream.point(x, y);
+ },
+ sphere: function() {
+ this.stream.sphere();
+ },
+ lineStart: function() {
+ this.stream.lineStart();
+ },
+ lineEnd: function() {
+ this.stream.lineEnd();
+ },
+ polygonStart: function() {
+ this.stream.polygonStart();
+ },
+ polygonEnd: function() {
+ this.stream.polygonEnd();
+ }
+ };
+ function d3_geo_transformPoint(stream, point) {
+ return {
+ point: point,
+ sphere: function() {
+ stream.sphere();
+ },
+ lineStart: function() {
+ stream.lineStart();
+ },
+ lineEnd: function() {
+ stream.lineEnd();
+ },
+ polygonStart: function() {
+ stream.polygonStart();
+ },
+ polygonEnd: function() {
+ stream.polygonEnd();
+ }
+ };
+ }
+ d3.geo.projection = d3_geo_projection;
+ d3.geo.projectionMutator = d3_geo_projectionMutator;
+ function d3_geo_projection(project) {
+ return d3_geo_projectionMutator(function() {
+ return project;
+ })();
+ }
+ function d3_geo_projectionMutator(projectAt) {
+ var project, rotate, projectRotate, projectResample = d3_geo_resample(function(x, y) {
+ x = project(x, y);
+ return [ x[0] * k + δx, δy - x[1] * k ];
+ }), k = 150, x = 480, y = 250, λ = 0, φ = 0, δλ = 0, δφ = 0, δγ = 0, δx, δy, preclip = d3_geo_clipAntimeridian, postclip = d3_identity, clipAngle = null, clipExtent = null, stream;
+ function projection(point) {
+ point = projectRotate(point[0] * d3_radians, point[1] * d3_radians);
+ return [ point[0] * k + δx, δy - point[1] * k ];
+ }
+ function invert(point) {
+ point = projectRotate.invert((point[0] - δx) / k, (δy - point[1]) / k);
+ return point && [ point[0] * d3_degrees, point[1] * d3_degrees ];
+ }
+ projection.stream = function(output) {
+ if (stream) stream.valid = false;
+ stream = d3_geo_projectionRadians(preclip(rotate, projectResample(postclip(output))));
+ stream.valid = true;
+ return stream;
+ };
+ projection.clipAngle = function(_) {
+ if (!arguments.length) return clipAngle;
+ preclip = _ == null ? (clipAngle = _, d3_geo_clipAntimeridian) : d3_geo_clipCircle((clipAngle = +_) * d3_radians);
+ return invalidate();
+ };
+ projection.clipExtent = function(_) {
+ if (!arguments.length) return clipExtent;
+ clipExtent = _;
+ postclip = _ ? d3_geo_clipExtent(_[0][0], _[0][1], _[1][0], _[1][1]) : d3_identity;
+ return invalidate();
+ };
+ projection.scale = function(_) {
+ if (!arguments.length) return k;
+ k = +_;
+ return reset();
+ };
+ projection.translate = function(_) {
+ if (!arguments.length) return [ x, y ];
+ x = +_[0];
+ y = +_[1];
+ return reset();
+ };
+ projection.center = function(_) {
+ if (!arguments.length) return [ λ * d3_degrees, φ * d3_degrees ];
+ λ = _[0] % 360 * d3_radians;
+ φ = _[1] % 360 * d3_radians;
+ return reset();
+ };
+ projection.rotate = function(_) {
+ if (!arguments.length) return [ δλ * d3_degrees, δφ * d3_degrees, δγ * d3_degrees ];
+ δλ = _[0] % 360 * d3_radians;
+ δφ = _[1] % 360 * d3_radians;
+ δγ = _.length > 2 ? _[2] % 360 * d3_radians : 0;
+ return reset();
+ };
+ d3.rebind(projection, projectResample, "precision");
+ function reset() {
+ projectRotate = d3_geo_compose(rotate = d3_geo_rotation(δλ, δφ, δγ), project);
+ var center = project(λ, φ);
+ δx = x - center[0] * k;
+ δy = y + center[1] * k;
+ return invalidate();
+ }
+ function invalidate() {
+ if (stream) stream.valid = false, stream = null;
+ return projection;
+ }
+ return function() {
+ project = projectAt.apply(this, arguments);
+ projection.invert = project.invert && invert;
+ return reset();
+ };
+ }
+ function d3_geo_projectionRadians(stream) {
+ return d3_geo_transformPoint(stream, function(x, y) {
+ stream.point(x * d3_radians, y * d3_radians);
+ });
+ }
+ function d3_geo_equirectangular(λ, φ) {
+ return [ λ, φ ];
+ }
+ (d3.geo.equirectangular = function() {
+ return d3_geo_projection(d3_geo_equirectangular);
+ }).raw = d3_geo_equirectangular.invert = d3_geo_equirectangular;
+ d3.geo.rotation = function(rotate) {
+ rotate = d3_geo_rotation(rotate[0] % 360 * d3_radians, rotate[1] * d3_radians, rotate.length > 2 ? rotate[2] * d3_radians : 0);
+ function forward(coordinates) {
+ coordinates = rotate(coordinates[0] * d3_radians, coordinates[1] * d3_radians);
+ return coordinates[0] *= d3_degrees, coordinates[1] *= d3_degrees, coordinates;
+ }
+ forward.invert = function(coordinates) {
+ coordinates = rotate.invert(coordinates[0] * d3_radians, coordinates[1] * d3_radians);
+ return coordinates[0] *= d3_degrees, coordinates[1] *= d3_degrees, coordinates;
+ };
+ return forward;
+ };
+ function d3_geo_identityRotation(λ, φ) {
+ return [ λ > π ? λ - τ : λ < -π ? λ + τ : λ, φ ];
+ }
+ d3_geo_identityRotation.invert = d3_geo_equirectangular;
+ function d3_geo_rotation(δλ, δφ, δγ) {
+ return δλ ? δφ || δγ ? d3_geo_compose(d3_geo_rotationλ(δλ), d3_geo_rotationφγ(δφ, δγ)) : d3_geo_rotationλ(δλ) : δφ || δγ ? d3_geo_rotationφγ(δφ, δγ) : d3_geo_identityRotation;
+ }
+ function d3_geo_forwardRotationλ(δλ) {
+ return function(λ, φ) {
+ return λ += δλ, [ λ > π ? λ - τ : λ < -π ? λ + τ : λ, φ ];
+ };
+ }
+ function d3_geo_rotationλ(δλ) {
+ var rotation = d3_geo_forwardRotationλ(δλ);
+ rotation.invert = d3_geo_forwardRotationλ(-δλ);
+ return rotation;
+ }
+ function d3_geo_rotationφγ(δφ, δγ) {
+ var cosδφ = Math.cos(δφ), sinδφ = Math.sin(δφ), cosδγ = Math.cos(δγ), sinδγ = Math.sin(δγ);
+ function rotation(λ, φ) {
+ var cosφ = Math.cos(φ), x = Math.cos(λ) * cosφ, y = Math.sin(λ) * cosφ, z = Math.sin(φ), k = z * cosδφ + x * sinδφ;
+ return [ Math.atan2(y * cosδγ - k * sinδγ, x * cosδφ - z * sinδφ), d3_asin(k * cosδγ + y * sinδγ) ];
+ }
+ rotation.invert = function(λ, φ) {
+ var cosφ = Math.cos(φ), x = Math.cos(λ) * cosφ, y = Math.sin(λ) * cosφ, z = Math.sin(φ), k = z * cosδγ - y * sinδγ;
+ return [ Math.atan2(y * cosδγ + z * sinδγ, x * cosδφ + k * sinδφ), d3_asin(k * cosδφ - x * sinδφ) ];
+ };
+ return rotation;
+ }
+ d3.geo.circle = function() {
+ var origin = [ 0, 0 ], angle, precision = 6, interpolate;
+ function circle() {
+ var center = typeof origin === "function" ? origin.apply(this, arguments) : origin, rotate = d3_geo_rotation(-center[0] * d3_radians, -center[1] * d3_radians, 0).invert, ring = [];
+ interpolate(null, null, 1, {
+ point: function(x, y) {
+ ring.push(x = rotate(x, y));
+ x[0] *= d3_degrees, x[1] *= d3_degrees;
+ }
+ });
+ return {
+ type: "Polygon",
+ coordinates: [ ring ]
+ };
+ }
+ circle.origin = function(x) {
+ if (!arguments.length) return origin;
+ origin = x;
+ return circle;
+ };
+ circle.angle = function(x) {
+ if (!arguments.length) return angle;
+ interpolate = d3_geo_circleInterpolate((angle = +x) * d3_radians, precision * d3_radians);
+ return circle;
+ };
+ circle.precision = function(_) {
+ if (!arguments.length) return precision;
+ interpolate = d3_geo_circleInterpolate(angle * d3_radians, (precision = +_) * d3_radians);
+ return circle;
+ };
+ return circle.angle(90);
+ };
+ function d3_geo_circleInterpolate(radius, precision) {
+ var cr = Math.cos(radius), sr = Math.sin(radius);
+ return function(from, to, direction, listener) {
+ var step = direction * precision;
+ if (from != null) {
+ from = d3_geo_circleAngle(cr, from);
+ to = d3_geo_circleAngle(cr, to);
+ if (direction > 0 ? from < to : from > to) from += direction * τ;
+ } else {
+ from = radius + direction * τ;
+ to = radius - .5 * step;
+ }
+ for (var point, t = from; direction > 0 ? t > to : t < to; t -= step) {
+ listener.point((point = d3_geo_spherical([ cr, -sr * Math.cos(t), -sr * Math.sin(t) ]))[0], point[1]);
+ }
+ };
+ }
+ function d3_geo_circleAngle(cr, point) {
+ var a = d3_geo_cartesian(point);
+ a[0] -= cr;
+ d3_geo_cartesianNormalize(a);
+ var angle = d3_acos(-a[1]);
+ return ((-a[2] < 0 ? -angle : angle) + 2 * Math.PI - ε) % (2 * Math.PI);
+ }
+ d3.geo.distance = function(a, b) {
+ var Δλ = (b[0] - a[0]) * d3_radians, φ0 = a[1] * d3_radians, φ1 = b[1] * d3_radians, sinΔλ = Math.sin(Δλ), cosΔλ = Math.cos(Δλ), sinφ0 = Math.sin(φ0), cosφ0 = Math.cos(φ0), sinφ1 = Math.sin(φ1), cosφ1 = Math.cos(φ1), t;
+ return Math.atan2(Math.sqrt((t = cosφ1 * sinΔλ) * t + (t = cosφ0 * sinφ1 - sinφ0 * cosφ1 * cosΔλ) * t), sinφ0 * sinφ1 + cosφ0 * cosφ1 * cosΔλ);
+ };
+ d3.geo.graticule = function() {
+ var x1, x0, X1, X0, y1, y0, Y1, Y0, dx = 10, dy = dx, DX = 90, DY = 360, x, y, X, Y, precision = 2.5;
+ function graticule() {
+ return {
+ type: "MultiLineString",
+ coordinates: lines()
+ };
+ }
+ function lines() {
+ return d3.range(Math.ceil(X0 / DX) * DX, X1, DX).map(X).concat(d3.range(Math.ceil(Y0 / DY) * DY, Y1, DY).map(Y)).concat(d3.range(Math.ceil(x0 / dx) * dx, x1, dx).filter(function(x) {
+ return abs(x % DX) > ε;
+ }).map(x)).concat(d3.range(Math.ceil(y0 / dy) * dy, y1, dy).filter(function(y) {
+ return abs(y % DY) > ε;
+ }).map(y));
+ }
+ graticule.lines = function() {
+ return lines().map(function(coordinates) {
+ return {
+ type: "LineString",
+ coordinates: coordinates
+ };
+ });
+ };
+ graticule.outline = function() {
+ return {
+ type: "Polygon",
+ coordinates: [ X(X0).concat(Y(Y1).slice(1), X(X1).reverse().slice(1), Y(Y0).reverse().slice(1)) ]
+ };
+ };
+ graticule.extent = function(_) {
+ if (!arguments.length) return graticule.minorExtent();
+ return graticule.majorExtent(_).minorExtent(_);
+ };
+ graticule.majorExtent = function(_) {
+ if (!arguments.length) return [ [ X0, Y0 ], [ X1, Y1 ] ];
+ X0 = +_[0][0], X1 = +_[1][0];
+ Y0 = +_[0][1], Y1 = +_[1][1];
+ if (X0 > X1) _ = X0, X0 = X1, X1 = _;
+ if (Y0 > Y1) _ = Y0, Y0 = Y1, Y1 = _;
+ return graticule.precision(precision);
+ };
+ graticule.minorExtent = function(_) {
+ if (!arguments.length) return [ [ x0, y0 ], [ x1, y1 ] ];
+ x0 = +_[0][0], x1 = +_[1][0];
+ y0 = +_[0][1], y1 = +_[1][1];
+ if (x0 > x1) _ = x0, x0 = x1, x1 = _;
+ if (y0 > y1) _ = y0, y0 = y1, y1 = _;
+ return graticule.precision(precision);
+ };
+ graticule.step = function(_) {
+ if (!arguments.length) return graticule.minorStep();
+ return graticule.majorStep(_).minorStep(_);
+ };
+ graticule.majorStep = function(_) {
+ if (!arguments.length) return [ DX, DY ];
+ DX = +_[0], DY = +_[1];
+ return graticule;
+ };
+ graticule.minorStep = function(_) {
+ if (!arguments.length) return [ dx, dy ];
+ dx = +_[0], dy = +_[1];
+ return graticule;
+ };
+ graticule.precision = function(_) {
+ if (!arguments.length) return precision;
+ precision = +_;
+ x = d3_geo_graticuleX(y0, y1, 90);
+ y = d3_geo_graticuleY(x0, x1, precision);
+ X = d3_geo_graticuleX(Y0, Y1, 90);
+ Y = d3_geo_graticuleY(X0, X1, precision);
+ return graticule;
+ };
+ return graticule.majorExtent([ [ -180, -90 + ε ], [ 180, 90 - ε ] ]).minorExtent([ [ -180, -80 - ε ], [ 180, 80 + ε ] ]);
+ };
+ function d3_geo_graticuleX(y0, y1, dy) {
+ var y = d3.range(y0, y1 - ε, dy).concat(y1);
+ return function(x) {
+ return y.map(function(y) {
+ return [ x, y ];
+ });
+ };
+ }
+ function d3_geo_graticuleY(x0, x1, dx) {
+ var x = d3.range(x0, x1 - ε, dx).concat(x1);
+ return function(y) {
+ return x.map(function(x) {
+ return [ x, y ];
+ });
+ };
+ }
+ function d3_source(d) {
+ return d.source;
+ }
+ function d3_target(d) {
+ return d.target;
+ }
+ d3.geo.greatArc = function() {
+ var source = d3_source, source_, target = d3_target, target_;
+ function greatArc() {
+ return {
+ type: "LineString",
+ coordinates: [ source_ || source.apply(this, arguments), target_ || target.apply(this, arguments) ]
+ };
+ }
+ greatArc.distance = function() {
+ return d3.geo.distance(source_ || source.apply(this, arguments), target_ || target.apply(this, arguments));
+ };
+ greatArc.source = function(_) {
+ if (!arguments.length) return source;
+ source = _, source_ = typeof _ === "function" ? null : _;
+ return greatArc;
+ };
+ greatArc.target = function(_) {
+ if (!arguments.length) return target;
+ target = _, target_ = typeof _ === "function" ? null : _;
+ return greatArc;
+ };
+ greatArc.precision = function() {
+ return arguments.length ? greatArc : 0;
+ };
+ return greatArc;
+ };
+ d3.geo.interpolate = function(source, target) {
+ return d3_geo_interpolate(source[0] * d3_radians, source[1] * d3_radians, target[0] * d3_radians, target[1] * d3_radians);
+ };
+ function d3_geo_interpolate(x0, y0, x1, y1) {
+ var cy0 = Math.cos(y0), sy0 = Math.sin(y0), cy1 = Math.cos(y1), sy1 = Math.sin(y1), kx0 = cy0 * Math.cos(x0), ky0 = cy0 * Math.sin(x0), kx1 = cy1 * Math.cos(x1), ky1 = cy1 * Math.sin(x1), d = 2 * Math.asin(Math.sqrt(d3_haversin(y1 - y0) + cy0 * cy1 * d3_haversin(x1 - x0))), k = 1 / Math.sin(d);
+ var interpolate = d ? function(t) {
+ var B = Math.sin(t *= d) * k, A = Math.sin(d - t) * k, x = A * kx0 + B * kx1, y = A * ky0 + B * ky1, z = A * sy0 + B * sy1;
+ return [ Math.atan2(y, x) * d3_degrees, Math.atan2(z, Math.sqrt(x * x + y * y)) * d3_degrees ];
+ } : function() {
+ return [ x0 * d3_degrees, y0 * d3_degrees ];
+ };
+ interpolate.distance = d;
+ return interpolate;
+ }
+ d3.geo.length = function(object) {
+ d3_geo_lengthSum = 0;
+ d3.geo.stream(object, d3_geo_length);
+ return d3_geo_lengthSum;
+ };
+ var d3_geo_lengthSum;
+ var d3_geo_length = {
+ sphere: d3_noop,
+ point: d3_noop,
+ lineStart: d3_geo_lengthLineStart,
+ lineEnd: d3_noop,
+ polygonStart: d3_noop,
+ polygonEnd: d3_noop
+ };
+ function d3_geo_lengthLineStart() {
+ var λ0, sinφ0, cosφ0;
+ d3_geo_length.point = function(λ, φ) {
+ λ0 = λ * d3_radians, sinφ0 = Math.sin(φ *= d3_radians), cosφ0 = Math.cos(φ);
+ d3_geo_length.point = nextPoint;
+ };
+ d3_geo_length.lineEnd = function() {
+ d3_geo_length.point = d3_geo_length.lineEnd = d3_noop;
+ };
+ function nextPoint(λ, φ) {
+ var sinφ = Math.sin(φ *= d3_radians), cosφ = Math.cos(φ), t = abs((λ *= d3_radians) - λ0), cosΔλ = Math.cos(t);
+ d3_geo_lengthSum += Math.atan2(Math.sqrt((t = cosφ * Math.sin(t)) * t + (t = cosφ0 * sinφ - sinφ0 * cosφ * cosΔλ) * t), sinφ0 * sinφ + cosφ0 * cosφ * cosΔλ);
+ λ0 = λ, sinφ0 = sinφ, cosφ0 = cosφ;
+ }
+ }
+ function d3_geo_azimuthal(scale, angle) {
+ function azimuthal(λ, φ) {
+ var cosλ = Math.cos(λ), cosφ = Math.cos(φ), k = scale(cosλ * cosφ);
+ return [ k * cosφ * Math.sin(λ), k * Math.sin(φ) ];
+ }
+ azimuthal.invert = function(x, y) {
+ var ρ = Math.sqrt(x * x + y * y), c = angle(ρ), sinc = Math.sin(c), cosc = Math.cos(c);
+ return [ Math.atan2(x * sinc, ρ * cosc), Math.asin(ρ && y * sinc / ρ) ];
+ };
+ return azimuthal;
+ }
+ var d3_geo_azimuthalEqualArea = d3_geo_azimuthal(function(cosλcosφ) {
+ return Math.sqrt(2 / (1 + cosλcosφ));
+ }, function(ρ) {
+ return 2 * Math.asin(ρ / 2);
+ });
+ (d3.geo.azimuthalEqualArea = function() {
+ return d3_geo_projection(d3_geo_azimuthalEqualArea);
+ }).raw = d3_geo_azimuthalEqualArea;
+ var d3_geo_azimuthalEquidistant = d3_geo_azimuthal(function(cosλcosφ) {
+ var c = Math.acos(cosλcosφ);
+ return c && c / Math.sin(c);
+ }, d3_identity);
+ (d3.geo.azimuthalEquidistant = function() {
+ return d3_geo_projection(d3_geo_azimuthalEquidistant);
+ }).raw = d3_geo_azimuthalEquidistant;
+ function d3_geo_conicConformal(φ0, φ1) {
+ var cosφ0 = Math.cos(φ0), t = function(φ) {
+ return Math.tan(π / 4 + φ / 2);
+ }, n = φ0 === φ1 ? Math.sin(φ0) : Math.log(cosφ0 / Math.cos(φ1)) / Math.log(t(φ1) / t(φ0)), F = cosφ0 * Math.pow(t(φ0), n) / n;
+ if (!n) return d3_geo_mercator;
+ function forward(λ, φ) {
+ if (F > 0) {
+ if (φ < -halfπ + ε) φ = -halfπ + ε;
+ } else {
+ if (φ > halfπ - ε) φ = halfπ - ε;
+ }
+ var ρ = F / Math.pow(t(φ), n);
+ return [ ρ * Math.sin(n * λ), F - ρ * Math.cos(n * λ) ];
+ }
+ forward.invert = function(x, y) {
+ var ρ0_y = F - y, ρ = d3_sgn(n) * Math.sqrt(x * x + ρ0_y * ρ0_y);
+ return [ Math.atan2(x, ρ0_y) / n, 2 * Math.atan(Math.pow(F / ρ, 1 / n)) - halfπ ];
+ };
+ return forward;
+ }
+ (d3.geo.conicConformal = function() {
+ return d3_geo_conic(d3_geo_conicConformal);
+ }).raw = d3_geo_conicConformal;
+ function d3_geo_conicEquidistant(φ0, φ1) {
+ var cosφ0 = Math.cos(φ0), n = φ0 === φ1 ? Math.sin(φ0) : (cosφ0 - Math.cos(φ1)) / (φ1 - φ0), G = cosφ0 / n + φ0;
+ if (abs(n) < ε) return d3_geo_equirectangular;
+ function forward(λ, φ) {
+ var ρ = G - φ;
+ return [ ρ * Math.sin(n * λ), G - ρ * Math.cos(n * λ) ];
+ }
+ forward.invert = function(x, y) {
+ var ρ0_y = G - y;
+ return [ Math.atan2(x, ρ0_y) / n, G - d3_sgn(n) * Math.sqrt(x * x + ρ0_y * ρ0_y) ];
+ };
+ return forward;
+ }
+ (d3.geo.conicEquidistant = function() {
+ return d3_geo_conic(d3_geo_conicEquidistant);
+ }).raw = d3_geo_conicEquidistant;
+ var d3_geo_gnomonic = d3_geo_azimuthal(function(cosλcosφ) {
+ return 1 / cosλcosφ;
+ }, Math.atan);
+ (d3.geo.gnomonic = function() {
+ return d3_geo_projection(d3_geo_gnomonic);
+ }).raw = d3_geo_gnomonic;
+ function d3_geo_mercator(λ, φ) {
+ return [ λ, Math.log(Math.tan(π / 4 + φ / 2)) ];
+ }
+ d3_geo_mercator.invert = function(x, y) {
+ return [ x, 2 * Math.atan(Math.exp(y)) - halfπ ];
+ };
+ function d3_geo_mercatorProjection(project) {
+ var m = d3_geo_projection(project), scale = m.scale, translate = m.translate, clipExtent = m.clipExtent, clipAuto;
+ m.scale = function() {
+ var v = scale.apply(m, arguments);
+ return v === m ? clipAuto ? m.clipExtent(null) : m : v;
+ };
+ m.translate = function() {
+ var v = translate.apply(m, arguments);
+ return v === m ? clipAuto ? m.clipExtent(null) : m : v;
+ };
+ m.clipExtent = function(_) {
+ var v = clipExtent.apply(m, arguments);
+ if (v === m) {
+ if (clipAuto = _ == null) {
+ var k = π * scale(), t = translate();
+ clipExtent([ [ t[0] - k, t[1] - k ], [ t[0] + k, t[1] + k ] ]);
+ }
+ } else if (clipAuto) {
+ v = null;
+ }
+ return v;
+ };
+ return m.clipExtent(null);
+ }
+ (d3.geo.mercator = function() {
+ return d3_geo_mercatorProjection(d3_geo_mercator);
+ }).raw = d3_geo_mercator;
+ var d3_geo_orthographic = d3_geo_azimuthal(function() {
+ return 1;
+ }, Math.asin);
+ (d3.geo.orthographic = function() {
+ return d3_geo_projection(d3_geo_orthographic);
+ }).raw = d3_geo_orthographic;
+ var d3_geo_stereographic = d3_geo_azimuthal(function(cosλcosφ) {
+ return 1 / (1 + cosλcosφ);
+ }, function(ρ) {
+ return 2 * Math.atan(ρ);
+ });
+ (d3.geo.stereographic = function() {
+ return d3_geo_projection(d3_geo_stereographic);
+ }).raw = d3_geo_stereographic;
+ function d3_geo_transverseMercator(λ, φ) {
+ return [ Math.log(Math.tan(π / 4 + φ / 2)), -λ ];
+ }
+ d3_geo_transverseMercator.invert = function(x, y) {
+ return [ -y, 2 * Math.atan(Math.exp(x)) - halfπ ];
+ };
+ (d3.geo.transverseMercator = function() {
+ var projection = d3_geo_mercatorProjection(d3_geo_transverseMercator), center = projection.center, rotate = projection.rotate;
+ projection.center = function(_) {
+ return _ ? center([ -_[1], _[0] ]) : (_ = center(), [ -_[1], _[0] ]);
+ };
+ projection.rotate = function(_) {
+ return _ ? rotate([ _[0], _[1], _.length > 2 ? _[2] + 90 : 90 ]) : (_ = rotate(),
+ [ _[0], _[1], _[2] - 90 ]);
+ };
+ return projection.rotate([ 0, 0 ]);
+ }).raw = d3_geo_transverseMercator;
+ d3.geom = {};
+ function d3_geom_pointX(d) {
+ return d[0];
+ }
+ function d3_geom_pointY(d) {
+ return d[1];
+ }
+ d3.geom.hull = function(vertices) {
+ var x = d3_geom_pointX, y = d3_geom_pointY;
+ if (arguments.length) return hull(vertices);
+ function hull(data) {
+ if (data.length < 3) return [];
+ var fx = d3_functor(x), fy = d3_functor(y), i, n = data.length, points = [], flippedPoints = [];
+ for (i = 0; i < n; i++) {
+ points.push([ +fx.call(this, data[i], i), +fy.call(this, data[i], i), i ]);
+ }
+ points.sort(d3_geom_hullOrder);
+ for (i = 0; i < n; i++) flippedPoints.push([ points[i][0], -points[i][1] ]);
+ var upper = d3_geom_hullUpper(points), lower = d3_geom_hullUpper(flippedPoints);
+ var skipLeft = lower[0] === upper[0], skipRight = lower[lower.length - 1] === upper[upper.length - 1], polygon = [];
+ for (i = upper.length - 1; i >= 0; --i) polygon.push(data[points[upper[i]][2]]);
+ for (i = +skipLeft; i < lower.length - skipRight; ++i) polygon.push(data[points[lower[i]][2]]);
+ return polygon;
+ }
+ hull.x = function(_) {
+ return arguments.length ? (x = _, hull) : x;
+ };
+ hull.y = function(_) {
+ return arguments.length ? (y = _, hull) : y;
+ };
+ return hull;
+ };
+ function d3_geom_hullUpper(points) {
+ var n = points.length, hull = [ 0, 1 ], hs = 2;
+ for (var i = 2; i < n; i++) {
+ while (hs > 1 && d3_cross2d(points[hull[hs - 2]], points[hull[hs - 1]], points[i]) <= 0) --hs;
+ hull[hs++] = i;
+ }
+ return hull.slice(0, hs);
+ }
+ function d3_geom_hullOrder(a, b) {
+ return a[0] - b[0] || a[1] - b[1];
+ }
+ d3.geom.polygon = function(coordinates) {
+ d3_subclass(coordinates, d3_geom_polygonPrototype);
+ return coordinates;
+ };
+ var d3_geom_polygonPrototype = d3.geom.polygon.prototype = [];
+ d3_geom_polygonPrototype.area = function() {
+ var i = -1, n = this.length, a, b = this[n - 1], area = 0;
+ while (++i < n) {
+ a = b;
+ b = this[i];
+ area += a[1] * b[0] - a[0] * b[1];
+ }
+ return area * .5;
+ };
+ d3_geom_polygonPrototype.centroid = function(k) {
+ var i = -1, n = this.length, x = 0, y = 0, a, b = this[n - 1], c;
+ if (!arguments.length) k = -1 / (6 * this.area());
+ while (++i < n) {
+ a = b;
+ b = this[i];
+ c = a[0] * b[1] - b[0] * a[1];
+ x += (a[0] + b[0]) * c;
+ y += (a[1] + b[1]) * c;
+ }
+ return [ x * k, y * k ];
+ };
+ d3_geom_polygonPrototype.clip = function(subject) {
+ var input, closed = d3_geom_polygonClosed(subject), i = -1, n = this.length - d3_geom_polygonClosed(this), j, m, a = this[n - 1], b, c, d;
+ while (++i < n) {
+ input = subject.slice();
+ subject.length = 0;
+ b = this[i];
+ c = input[(m = input.length - closed) - 1];
+ j = -1;
+ while (++j < m) {
+ d = input[j];
+ if (d3_geom_polygonInside(d, a, b)) {
+ if (!d3_geom_polygonInside(c, a, b)) {
+ subject.push(d3_geom_polygonIntersect(c, d, a, b));
+ }
+ subject.push(d);
+ } else if (d3_geom_polygonInside(c, a, b)) {
+ subject.push(d3_geom_polygonIntersect(c, d, a, b));
+ }
+ c = d;
+ }
+ if (closed) subject.push(subject[0]);
+ a = b;
+ }
+ return subject;
+ };
+ function d3_geom_polygonInside(p, a, b) {
+ return (b[0] - a[0]) * (p[1] - a[1]) < (b[1] - a[1]) * (p[0] - a[0]);
+ }
+ function d3_geom_polygonIntersect(c, d, a, b) {
+ var x1 = c[0], x3 = a[0], x21 = d[0] - x1, x43 = b[0] - x3, y1 = c[1], y3 = a[1], y21 = d[1] - y1, y43 = b[1] - y3, ua = (x43 * (y1 - y3) - y43 * (x1 - x3)) / (y43 * x21 - x43 * y21);
+ return [ x1 + ua * x21, y1 + ua * y21 ];
+ }
+ function d3_geom_polygonClosed(coordinates) {
+ var a = coordinates[0], b = coordinates[coordinates.length - 1];
+ return !(a[0] - b[0] || a[1] - b[1]);
+ }
+ var d3_geom_voronoiEdges, d3_geom_voronoiCells, d3_geom_voronoiBeaches, d3_geom_voronoiBeachPool = [], d3_geom_voronoiFirstCircle, d3_geom_voronoiCircles, d3_geom_voronoiCirclePool = [];
+ function d3_geom_voronoiBeach() {
+ d3_geom_voronoiRedBlackNode(this);
+ this.edge = this.site = this.circle = null;
+ }
+ function d3_geom_voronoiCreateBeach(site) {
+ var beach = d3_geom_voronoiBeachPool.pop() || new d3_geom_voronoiBeach();
+ beach.site = site;
+ return beach;
+ }
+ function d3_geom_voronoiDetachBeach(beach) {
+ d3_geom_voronoiDetachCircle(beach);
+ d3_geom_voronoiBeaches.remove(beach);
+ d3_geom_voronoiBeachPool.push(beach);
+ d3_geom_voronoiRedBlackNode(beach);
+ }
+ function d3_geom_voronoiRemoveBeach(beach) {
+ var circle = beach.circle, x = circle.x, y = circle.cy, vertex = {
+ x: x,
+ y: y
+ }, previous = beach.P, next = beach.N, disappearing = [ beach ];
+ d3_geom_voronoiDetachBeach(beach);
+ var lArc = previous;
+ while (lArc.circle && abs(x - lArc.circle.x) < ε && abs(y - lArc.circle.cy) < ε) {
+ previous = lArc.P;
+ disappearing.unshift(lArc);
+ d3_geom_voronoiDetachBeach(lArc);
+ lArc = previous;
+ }
+ disappearing.unshift(lArc);
+ d3_geom_voronoiDetachCircle(lArc);
+ var rArc = next;
+ while (rArc.circle && abs(x - rArc.circle.x) < ε && abs(y - rArc.circle.cy) < ε) {
+ next = rArc.N;
+ disappearing.push(rArc);
+ d3_geom_voronoiDetachBeach(rArc);
+ rArc = next;
+ }
+ disappearing.push(rArc);
+ d3_geom_voronoiDetachCircle(rArc);
+ var nArcs = disappearing.length, iArc;
+ for (iArc = 1; iArc < nArcs; ++iArc) {
+ rArc = disappearing[iArc];
+ lArc = disappearing[iArc - 1];
+ d3_geom_voronoiSetEdgeEnd(rArc.edge, lArc.site, rArc.site, vertex);
+ }
+ lArc = disappearing[0];
+ rArc = disappearing[nArcs - 1];
+ rArc.edge = d3_geom_voronoiCreateEdge(lArc.site, rArc.site, null, vertex);
+ d3_geom_voronoiAttachCircle(lArc);
+ d3_geom_voronoiAttachCircle(rArc);
+ }
+ function d3_geom_voronoiAddBeach(site) {
+ var x = site.x, directrix = site.y, lArc, rArc, dxl, dxr, node = d3_geom_voronoiBeaches._;
+ while (node) {
+ dxl = d3_geom_voronoiLeftBreakPoint(node, directrix) - x;
+ if (dxl > ε) node = node.L; else {
+ dxr = x - d3_geom_voronoiRightBreakPoint(node, directrix);
+ if (dxr > ε) {
+ if (!node.R) {
+ lArc = node;
+ break;
+ }
+ node = node.R;
+ } else {
+ if (dxl > -ε) {
+ lArc = node.P;
+ rArc = node;
+ } else if (dxr > -ε) {
+ lArc = node;
+ rArc = node.N;
+ } else {
+ lArc = rArc = node;
+ }
+ break;
+ }
+ }
+ }
+ var newArc = d3_geom_voronoiCreateBeach(site);
+ d3_geom_voronoiBeaches.insert(lArc, newArc);
+ if (!lArc && !rArc) return;
+ if (lArc === rArc) {
+ d3_geom_voronoiDetachCircle(lArc);
+ rArc = d3_geom_voronoiCreateBeach(lArc.site);
+ d3_geom_voronoiBeaches.insert(newArc, rArc);
+ newArc.edge = rArc.edge = d3_geom_voronoiCreateEdge(lArc.site, newArc.site);
+ d3_geom_voronoiAttachCircle(lArc);
+ d3_geom_voronoiAttachCircle(rArc);
+ return;
+ }
+ if (!rArc) {
+ newArc.edge = d3_geom_voronoiCreateEdge(lArc.site, newArc.site);
+ return;
+ }
+ d3_geom_voronoiDetachCircle(lArc);
+ d3_geom_voronoiDetachCircle(rArc);
+ var lSite = lArc.site, ax = lSite.x, ay = lSite.y, bx = site.x - ax, by = site.y - ay, rSite = rArc.site, cx = rSite.x - ax, cy = rSite.y - ay, d = 2 * (bx * cy - by * cx), hb = bx * bx + by * by, hc = cx * cx + cy * cy, vertex = {
+ x: (cy * hb - by * hc) / d + ax,
+ y: (bx * hc - cx * hb) / d + ay
+ };
+ d3_geom_voronoiSetEdgeEnd(rArc.edge, lSite, rSite, vertex);
+ newArc.edge = d3_geom_voronoiCreateEdge(lSite, site, null, vertex);
+ rArc.edge = d3_geom_voronoiCreateEdge(site, rSite, null, vertex);
+ d3_geom_voronoiAttachCircle(lArc);
+ d3_geom_voronoiAttachCircle(rArc);
+ }
+ function d3_geom_voronoiLeftBreakPoint(arc, directrix) {
+ var site = arc.site, rfocx = site.x, rfocy = site.y, pby2 = rfocy - directrix;
+ if (!pby2) return rfocx;
+ var lArc = arc.P;
+ if (!lArc) return -Infinity;
+ site = lArc.site;
+ var lfocx = site.x, lfocy = site.y, plby2 = lfocy - directrix;
+ if (!plby2) return lfocx;
+ var hl = lfocx - rfocx, aby2 = 1 / pby2 - 1 / plby2, b = hl / plby2;
+ if (aby2) return (-b + Math.sqrt(b * b - 2 * aby2 * (hl * hl / (-2 * plby2) - lfocy + plby2 / 2 + rfocy - pby2 / 2))) / aby2 + rfocx;
+ return (rfocx + lfocx) / 2;
+ }
+ function d3_geom_voronoiRightBreakPoint(arc, directrix) {
+ var rArc = arc.N;
+ if (rArc) return d3_geom_voronoiLeftBreakPoint(rArc, directrix);
+ var site = arc.site;
+ return site.y === directrix ? site.x : Infinity;
+ }
+ function d3_geom_voronoiCell(site) {
+ this.site = site;
+ this.edges = [];
+ }
+ d3_geom_voronoiCell.prototype.prepare = function() {
+ var halfEdges = this.edges, iHalfEdge = halfEdges.length, edge;
+ while (iHalfEdge--) {
+ edge = halfEdges[iHalfEdge].edge;
+ if (!edge.b || !edge.a) halfEdges.splice(iHalfEdge, 1);
+ }
+ halfEdges.sort(d3_geom_voronoiHalfEdgeOrder);
+ return halfEdges.length;
+ };
+ function d3_geom_voronoiCloseCells(extent) {
+ var x0 = extent[0][0], x1 = extent[1][0], y0 = extent[0][1], y1 = extent[1][1], x2, y2, x3, y3, cells = d3_geom_voronoiCells, iCell = cells.length, cell, iHalfEdge, halfEdges, nHalfEdges, start, end;
+ while (iCell--) {
+ cell = cells[iCell];
+ if (!cell || !cell.prepare()) continue;
+ halfEdges = cell.edges;
+ nHalfEdges = halfEdges.length;
+ iHalfEdge = 0;
+ while (iHalfEdge < nHalfEdges) {
+ end = halfEdges[iHalfEdge].end(), x3 = end.x, y3 = end.y;
+ start = halfEdges[++iHalfEdge % nHalfEdges].start(), x2 = start.x, y2 = start.y;
+ if (abs(x3 - x2) > ε || abs(y3 - y2) > ε) {
+ halfEdges.splice(iHalfEdge, 0, new d3_geom_voronoiHalfEdge(d3_geom_voronoiCreateBorderEdge(cell.site, end, abs(x3 - x0) < ε && y1 - y3 > ε ? {
+ x: x0,
+ y: abs(x2 - x0) < ε ? y2 : y1
+ } : abs(y3 - y1) < ε && x1 - x3 > ε ? {
+ x: abs(y2 - y1) < ε ? x2 : x1,
+ y: y1
+ } : abs(x3 - x1) < ε && y3 - y0 > ε ? {
+ x: x1,
+ y: abs(x2 - x1) < ε ? y2 : y0
+ } : abs(y3 - y0) < ε && x3 - x0 > ε ? {
+ x: abs(y2 - y0) < ε ? x2 : x0,
+ y: y0
+ } : null), cell.site, null));
+ ++nHalfEdges;
+ }
+ }
+ }
+ }
+ function d3_geom_voronoiHalfEdgeOrder(a, b) {
+ return b.angle - a.angle;
+ }
+ function d3_geom_voronoiCircle() {
+ d3_geom_voronoiRedBlackNode(this);
+ this.x = this.y = this.arc = this.site = this.cy = null;
+ }
+ function d3_geom_voronoiAttachCircle(arc) {
+ var lArc = arc.P, rArc = arc.N;
+ if (!lArc || !rArc) return;
+ var lSite = lArc.site, cSite = arc.site, rSite = rArc.site;
+ if (lSite === rSite) return;
+ var bx = cSite.x, by = cSite.y, ax = lSite.x - bx, ay = lSite.y - by, cx = rSite.x - bx, cy = rSite.y - by;
+ var d = 2 * (ax * cy - ay * cx);
+ if (d >= -ε2) return;
+ var ha = ax * ax + ay * ay, hc = cx * cx + cy * cy, x = (cy * ha - ay * hc) / d, y = (ax * hc - cx * ha) / d, cy = y + by;
+ var circle = d3_geom_voronoiCirclePool.pop() || new d3_geom_voronoiCircle();
+ circle.arc = arc;
+ circle.site = cSite;
+ circle.x = x + bx;
+ circle.y = cy + Math.sqrt(x * x + y * y);
+ circle.cy = cy;
+ arc.circle = circle;
+ var before = null, node = d3_geom_voronoiCircles._;
+ while (node) {
+ if (circle.y < node.y || circle.y === node.y && circle.x <= node.x) {
+ if (node.L) node = node.L; else {
+ before = node.P;
+ break;
+ }
+ } else {
+ if (node.R) node = node.R; else {
+ before = node;
+ break;
+ }
+ }
+ }
+ d3_geom_voronoiCircles.insert(before, circle);
+ if (!before) d3_geom_voronoiFirstCircle = circle;
+ }
+ function d3_geom_voronoiDetachCircle(arc) {
+ var circle = arc.circle;
+ if (circle) {
+ if (!circle.P) d3_geom_voronoiFirstCircle = circle.N;
+ d3_geom_voronoiCircles.remove(circle);
+ d3_geom_voronoiCirclePool.push(circle);
+ d3_geom_voronoiRedBlackNode(circle);
+ arc.circle = null;
+ }
+ }
+ function d3_geom_voronoiClipEdges(extent) {
+ var edges = d3_geom_voronoiEdges, clip = d3_geom_clipLine(extent[0][0], extent[0][1], extent[1][0], extent[1][1]), i = edges.length, e;
+ while (i--) {
+ e = edges[i];
+ if (!d3_geom_voronoiConnectEdge(e, extent) || !clip(e) || abs(e.a.x - e.b.x) < ε && abs(e.a.y - e.b.y) < ε) {
+ e.a = e.b = null;
+ edges.splice(i, 1);
+ }
+ }
+ }
+ function d3_geom_voronoiConnectEdge(edge, extent) {
+ var vb = edge.b;
+ if (vb) return true;
+ var va = edge.a, x0 = extent[0][0], x1 = extent[1][0], y0 = extent[0][1], y1 = extent[1][1], lSite = edge.l, rSite = edge.r, lx = lSite.x, ly = lSite.y, rx = rSite.x, ry = rSite.y, fx = (lx + rx) / 2, fy = (ly + ry) / 2, fm, fb;
+ if (ry === ly) {
+ if (fx < x0 || fx >= x1) return;
+ if (lx > rx) {
+ if (!va) va = {
+ x: fx,
+ y: y0
+ }; else if (va.y >= y1) return;
+ vb = {
+ x: fx,
+ y: y1
+ };
+ } else {
+ if (!va) va = {
+ x: fx,
+ y: y1
+ }; else if (va.y < y0) return;
+ vb = {
+ x: fx,
+ y: y0
+ };
+ }
+ } else {
+ fm = (lx - rx) / (ry - ly);
+ fb = fy - fm * fx;
+ if (fm < -1 || fm > 1) {
+ if (lx > rx) {
+ if (!va) va = {
+ x: (y0 - fb) / fm,
+ y: y0
+ }; else if (va.y >= y1) return;
+ vb = {
+ x: (y1 - fb) / fm,
+ y: y1
+ };
+ } else {
+ if (!va) va = {
+ x: (y1 - fb) / fm,
+ y: y1
+ }; else if (va.y < y0) return;
+ vb = {
+ x: (y0 - fb) / fm,
+ y: y0
+ };
+ }
+ } else {
+ if (ly < ry) {
+ if (!va) va = {
+ x: x0,
+ y: fm * x0 + fb
+ }; else if (va.x >= x1) return;
+ vb = {
+ x: x1,
+ y: fm * x1 + fb
+ };
+ } else {
+ if (!va) va = {
+ x: x1,
+ y: fm * x1 + fb
+ }; else if (va.x < x0) return;
+ vb = {
+ x: x0,
+ y: fm * x0 + fb
+ };
+ }
+ }
+ }
+ edge.a = va;
+ edge.b = vb;
+ return true;
+ }
+ function d3_geom_voronoiEdge(lSite, rSite) {
+ this.l = lSite;
+ this.r = rSite;
+ this.a = this.b = null;
+ }
+ function d3_geom_voronoiCreateEdge(lSite, rSite, va, vb) {
+ var edge = new d3_geom_voronoiEdge(lSite, rSite);
+ d3_geom_voronoiEdges.push(edge);
+ if (va) d3_geom_voronoiSetEdgeEnd(edge, lSite, rSite, va);
+ if (vb) d3_geom_voronoiSetEdgeEnd(edge, rSite, lSite, vb);
+ d3_geom_voronoiCells[lSite.i].edges.push(new d3_geom_voronoiHalfEdge(edge, lSite, rSite));
+ d3_geom_voronoiCells[rSite.i].edges.push(new d3_geom_voronoiHalfEdge(edge, rSite, lSite));
+ return edge;
+ }
+ function d3_geom_voronoiCreateBorderEdge(lSite, va, vb) {
+ var edge = new d3_geom_voronoiEdge(lSite, null);
+ edge.a = va;
+ edge.b = vb;
+ d3_geom_voronoiEdges.push(edge);
+ return edge;
+ }
+ function d3_geom_voronoiSetEdgeEnd(edge, lSite, rSite, vertex) {
+ if (!edge.a && !edge.b) {
+ edge.a = vertex;
+ edge.l = lSite;
+ edge.r = rSite;
+ } else if (edge.l === rSite) {
+ edge.b = vertex;
+ } else {
+ edge.a = vertex;
+ }
+ }
+ function d3_geom_voronoiHalfEdge(edge, lSite, rSite) {
+ var va = edge.a, vb = edge.b;
+ this.edge = edge;
+ this.site = lSite;
+ this.angle = rSite ? Math.atan2(rSite.y - lSite.y, rSite.x - lSite.x) : edge.l === lSite ? Math.atan2(vb.x - va.x, va.y - vb.y) : Math.atan2(va.x - vb.x, vb.y - va.y);
+ }
+ d3_geom_voronoiHalfEdge.prototype = {
+ start: function() {
+ return this.edge.l === this.site ? this.edge.a : this.edge.b;
+ },
+ end: function() {
+ return this.edge.l === this.site ? this.edge.b : this.edge.a;
+ }
+ };
+ function d3_geom_voronoiRedBlackTree() {
+ this._ = null;
+ }
+ function d3_geom_voronoiRedBlackNode(node) {
+ node.U = node.C = node.L = node.R = node.P = node.N = null;
+ }
+ d3_geom_voronoiRedBlackTree.prototype = {
+ insert: function(after, node) {
+ var parent, grandpa, uncle;
+ if (after) {
+ node.P = after;
+ node.N = after.N;
+ if (after.N) after.N.P = node;
+ after.N = node;
+ if (after.R) {
+ after = after.R;
+ while (after.L) after = after.L;
+ after.L = node;
+ } else {
+ after.R = node;
+ }
+ parent = after;
+ } else if (this._) {
+ after = d3_geom_voronoiRedBlackFirst(this._);
+ node.P = null;
+ node.N = after;
+ after.P = after.L = node;
+ parent = after;
+ } else {
+ node.P = node.N = null;
+ this._ = node;
+ parent = null;
+ }
+ node.L = node.R = null;
+ node.U = parent;
+ node.C = true;
+ after = node;
+ while (parent && parent.C) {
+ grandpa = parent.U;
+ if (parent === grandpa.L) {
+ uncle = grandpa.R;
+ if (uncle && uncle.C) {
+ parent.C = uncle.C = false;
+ grandpa.C = true;
+ after = grandpa;
+ } else {
+ if (after === parent.R) {
+ d3_geom_voronoiRedBlackRotateLeft(this, parent);
+ after = parent;
+ parent = after.U;
+ }
+ parent.C = false;
+ grandpa.C = true;
+ d3_geom_voronoiRedBlackRotateRight(this, grandpa);
+ }
+ } else {
+ uncle = grandpa.L;
+ if (uncle && uncle.C) {
+ parent.C = uncle.C = false;
+ grandpa.C = true;
+ after = grandpa;
+ } else {
+ if (after === parent.L) {
+ d3_geom_voronoiRedBlackRotateRight(this, parent);
+ after = parent;
+ parent = after.U;
+ }
+ parent.C = false;
+ grandpa.C = true;
+ d3_geom_voronoiRedBlackRotateLeft(this, grandpa);
+ }
+ }
+ parent = after.U;
+ }
+ this._.C = false;
+ },
+ remove: function(node) {
+ if (node.N) node.N.P = node.P;
+ if (node.P) node.P.N = node.N;
+ node.N = node.P = null;
+ var parent = node.U, sibling, left = node.L, right = node.R, next, red;
+ if (!left) next = right; else if (!right) next = left; else next = d3_geom_voronoiRedBlackFirst(right);
+ if (parent) {
+ if (parent.L === node) parent.L = next; else parent.R = next;
+ } else {
+ this._ = next;
+ }
+ if (left && right) {
+ red = next.C;
+ next.C = node.C;
+ next.L = left;
+ left.U = next;
+ if (next !== right) {
+ parent = next.U;
+ next.U = node.U;
+ node = next.R;
+ parent.L = node;
+ next.R = right;
+ right.U = next;
+ } else {
+ next.U = parent;
+ parent = next;
+ node = next.R;
+ }
+ } else {
+ red = node.C;
+ node = next;
+ }
+ if (node) node.U = parent;
+ if (red) return;
+ if (node && node.C) {
+ node.C = false;
+ return;
+ }
+ do {
+ if (node === this._) break;
+ if (node === parent.L) {
+ sibling = parent.R;
+ if (sibling.C) {
+ sibling.C = false;
+ parent.C = true;
+ d3_geom_voronoiRedBlackRotateLeft(this, parent);
+ sibling = parent.R;
+ }
+ if (sibling.L && sibling.L.C || sibling.R && sibling.R.C) {
+ if (!sibling.R || !sibling.R.C) {
+ sibling.L.C = false;
+ sibling.C = true;
+ d3_geom_voronoiRedBlackRotateRight(this, sibling);
+ sibling = parent.R;
+ }
+ sibling.C = parent.C;
+ parent.C = sibling.R.C = false;
+ d3_geom_voronoiRedBlackRotateLeft(this, parent);
+ node = this._;
+ break;
+ }
+ } else {
+ sibling = parent.L;
+ if (sibling.C) {
+ sibling.C = false;
+ parent.C = true;
+ d3_geom_voronoiRedBlackRotateRight(this, parent);
+ sibling = parent.L;
+ }
+ if (sibling.L && sibling.L.C || sibling.R && sibling.R.C) {
+ if (!sibling.L || !sibling.L.C) {
+ sibling.R.C = false;
+ sibling.C = true;
+ d3_geom_voronoiRedBlackRotateLeft(this, sibling);
+ sibling = parent.L;
+ }
+ sibling.C = parent.C;
+ parent.C = sibling.L.C = false;
+ d3_geom_voronoiRedBlackRotateRight(this, parent);
+ node = this._;
+ break;
+ }
+ }
+ sibling.C = true;
+ node = parent;
+ parent = parent.U;
+ } while (!node.C);
+ if (node) node.C = false;
+ }
+ };
+ function d3_geom_voronoiRedBlackRotateLeft(tree, node) {
+ var p = node, q = node.R, parent = p.U;
+ if (parent) {
+ if (parent.L === p) parent.L = q; else parent.R = q;
+ } else {
+ tree._ = q;
+ }
+ q.U = parent;
+ p.U = q;
+ p.R = q.L;
+ if (p.R) p.R.U = p;
+ q.L = p;
+ }
+ function d3_geom_voronoiRedBlackRotateRight(tree, node) {
+ var p = node, q = node.L, parent = p.U;
+ if (parent) {
+ if (parent.L === p) parent.L = q; else parent.R = q;
+ } else {
+ tree._ = q;
+ }
+ q.U = parent;
+ p.U = q;
+ p.L = q.R;
+ if (p.L) p.L.U = p;
+ q.R = p;
+ }
+ function d3_geom_voronoiRedBlackFirst(node) {
+ while (node.L) node = node.L;
+ return node;
+ }
+ function d3_geom_voronoi(sites, bbox) {
+ var site = sites.sort(d3_geom_voronoiVertexOrder).pop(), x0, y0, circle;
+ d3_geom_voronoiEdges = [];
+ d3_geom_voronoiCells = new Array(sites.length);
+ d3_geom_voronoiBeaches = new d3_geom_voronoiRedBlackTree();
+ d3_geom_voronoiCircles = new d3_geom_voronoiRedBlackTree();
+ while (true) {
+ circle = d3_geom_voronoiFirstCircle;
+ if (site && (!circle || site.y < circle.y || site.y === circle.y && site.x < circle.x)) {
+ if (site.x !== x0 || site.y !== y0) {
+ d3_geom_voronoiCells[site.i] = new d3_geom_voronoiCell(site);
+ d3_geom_voronoiAddBeach(site);
+ x0 = site.x, y0 = site.y;
+ }
+ site = sites.pop();
+ } else if (circle) {
+ d3_geom_voronoiRemoveBeach(circle.arc);
+ } else {
+ break;
+ }
+ }
+ if (bbox) d3_geom_voronoiClipEdges(bbox), d3_geom_voronoiCloseCells(bbox);
+ var diagram = {
+ cells: d3_geom_voronoiCells,
+ edges: d3_geom_voronoiEdges
+ };
+ d3_geom_voronoiBeaches = d3_geom_voronoiCircles = d3_geom_voronoiEdges = d3_geom_voronoiCells = null;
+ return diagram;
+ }
+ function d3_geom_voronoiVertexOrder(a, b) {
+ return b.y - a.y || b.x - a.x;
+ }
+ d3.geom.voronoi = function(points) {
+ var x = d3_geom_pointX, y = d3_geom_pointY, fx = x, fy = y, clipExtent = d3_geom_voronoiClipExtent;
+ if (points) return voronoi(points);
+ function voronoi(data) {
+ var polygons = new Array(data.length), x0 = clipExtent[0][0], y0 = clipExtent[0][1], x1 = clipExtent[1][0], y1 = clipExtent[1][1];
+ d3_geom_voronoi(sites(data), clipExtent).cells.forEach(function(cell, i) {
+ var edges = cell.edges, site = cell.site, polygon = polygons[i] = edges.length ? edges.map(function(e) {
+ var s = e.start();
+ return [ s.x, s.y ];
+ }) : site.x >= x0 && site.x <= x1 && site.y >= y0 && site.y <= y1 ? [ [ x0, y1 ], [ x1, y1 ], [ x1, y0 ], [ x0, y0 ] ] : [];
+ polygon.point = data[i];
+ });
+ return polygons;
+ }
+ function sites(data) {
+ return data.map(function(d, i) {
+ return {
+ x: Math.round(fx(d, i) / ε) * ε,
+ y: Math.round(fy(d, i) / ε) * ε,
+ i: i
+ };
+ });
+ }
+ voronoi.links = function(data) {
+ return d3_geom_voronoi(sites(data)).edges.filter(function(edge) {
+ return edge.l && edge.r;
+ }).map(function(edge) {
+ return {
+ source: data[edge.l.i],
+ target: data[edge.r.i]
+ };
+ });
+ };
+ voronoi.triangles = function(data) {
+ var triangles = [];
+ d3_geom_voronoi(sites(data)).cells.forEach(function(cell, i) {
+ var site = cell.site, edges = cell.edges.sort(d3_geom_voronoiHalfEdgeOrder), j = -1, m = edges.length, e0, s0, e1 = edges[m - 1].edge, s1 = e1.l === site ? e1.r : e1.l;
+ while (++j < m) {
+ e0 = e1;
+ s0 = s1;
+ e1 = edges[j].edge;
+ s1 = e1.l === site ? e1.r : e1.l;
+ if (i < s0.i && i < s1.i && d3_geom_voronoiTriangleArea(site, s0, s1) < 0) {
+ triangles.push([ data[i], data[s0.i], data[s1.i] ]);
+ }
+ }
+ });
+ return triangles;
+ };
+ voronoi.x = function(_) {
+ return arguments.length ? (fx = d3_functor(x = _), voronoi) : x;
+ };
+ voronoi.y = function(_) {
+ return arguments.length ? (fy = d3_functor(y = _), voronoi) : y;
+ };
+ voronoi.clipExtent = function(_) {
+ if (!arguments.length) return clipExtent === d3_geom_voronoiClipExtent ? null : clipExtent;
+ clipExtent = _ == null ? d3_geom_voronoiClipExtent : _;
+ return voronoi;
+ };
+ voronoi.size = function(_) {
+ if (!arguments.length) return clipExtent === d3_geom_voronoiClipExtent ? null : clipExtent && clipExtent[1];
+ return voronoi.clipExtent(_ && [ [ 0, 0 ], _ ]);
+ };
+ return voronoi;
+ };
+ var d3_geom_voronoiClipExtent = [ [ -1e6, -1e6 ], [ 1e6, 1e6 ] ];
+ function d3_geom_voronoiTriangleArea(a, b, c) {
+ return (a.x - c.x) * (b.y - a.y) - (a.x - b.x) * (c.y - a.y);
+ }
+ d3.geom.delaunay = function(vertices) {
+ return d3.geom.voronoi().triangles(vertices);
+ };
+ d3.geom.quadtree = function(points, x1, y1, x2, y2) {
+ var x = d3_geom_pointX, y = d3_geom_pointY, compat;
+ if (compat = arguments.length) {
+ x = d3_geom_quadtreeCompatX;
+ y = d3_geom_quadtreeCompatY;
+ if (compat === 3) {
+ y2 = y1;
+ x2 = x1;
+ y1 = x1 = 0;
+ }
+ return quadtree(points);
+ }
+ function quadtree(data) {
+ var d, fx = d3_functor(x), fy = d3_functor(y), xs, ys, i, n, x1_, y1_, x2_, y2_;
+ if (x1 != null) {
+ x1_ = x1, y1_ = y1, x2_ = x2, y2_ = y2;
+ } else {
+ x2_ = y2_ = -(x1_ = y1_ = Infinity);
+ xs = [], ys = [];
+ n = data.length;
+ if (compat) for (i = 0; i < n; ++i) {
+ d = data[i];
+ if (d.x < x1_) x1_ = d.x;
+ if (d.y < y1_) y1_ = d.y;
+ if (d.x > x2_) x2_ = d.x;
+ if (d.y > y2_) y2_ = d.y;
+ xs.push(d.x);
+ ys.push(d.y);
+ } else for (i = 0; i < n; ++i) {
+ var x_ = +fx(d = data[i], i), y_ = +fy(d, i);
+ if (x_ < x1_) x1_ = x_;
+ if (y_ < y1_) y1_ = y_;
+ if (x_ > x2_) x2_ = x_;
+ if (y_ > y2_) y2_ = y_;
+ xs.push(x_);
+ ys.push(y_);
+ }
+ }
+ var dx = x2_ - x1_, dy = y2_ - y1_;
+ if (dx > dy) y2_ = y1_ + dx; else x2_ = x1_ + dy;
+ function insert(n, d, x, y, x1, y1, x2, y2) {
+ if (isNaN(x) || isNaN(y)) return;
+ if (n.leaf) {
+ var nx = n.x, ny = n.y;
+ if (nx != null) {
+ if (abs(nx - x) + abs(ny - y) < .01) {
+ insertChild(n, d, x, y, x1, y1, x2, y2);
+ } else {
+ var nPoint = n.point;
+ n.x = n.y = n.point = null;
+ insertChild(n, nPoint, nx, ny, x1, y1, x2, y2);
+ insertChild(n, d, x, y, x1, y1, x2, y2);
+ }
+ } else {
+ n.x = x, n.y = y, n.point = d;
+ }
+ } else {
+ insertChild(n, d, x, y, x1, y1, x2, y2);
+ }
+ }
+ function insertChild(n, d, x, y, x1, y1, x2, y2) {
+ var sx = (x1 + x2) * .5, sy = (y1 + y2) * .5, right = x >= sx, bottom = y >= sy, i = (bottom << 1) + right;
+ n.leaf = false;
+ n = n.nodes[i] || (n.nodes[i] = d3_geom_quadtreeNode());
+ if (right) x1 = sx; else x2 = sx;
+ if (bottom) y1 = sy; else y2 = sy;
+ insert(n, d, x, y, x1, y1, x2, y2);
+ }
+ var root = d3_geom_quadtreeNode();
+ root.add = function(d) {
+ insert(root, d, +fx(d, ++i), +fy(d, i), x1_, y1_, x2_, y2_);
+ };
+ root.visit = function(f) {
+ d3_geom_quadtreeVisit(f, root, x1_, y1_, x2_, y2_);
+ };
+ i = -1;
+ if (x1 == null) {
+ while (++i < n) {
+ insert(root, data[i], xs[i], ys[i], x1_, y1_, x2_, y2_);
+ }
+ --i;
+ } else data.forEach(root.add);
+ xs = ys = data = d = null;
+ return root;
+ }
+ quadtree.x = function(_) {
+ return arguments.length ? (x = _, quadtree) : x;
+ };
+ quadtree.y = function(_) {
+ return arguments.length ? (y = _, quadtree) : y;
+ };
+ quadtree.extent = function(_) {
+ if (!arguments.length) return x1 == null ? null : [ [ x1, y1 ], [ x2, y2 ] ];
+ if (_ == null) x1 = y1 = x2 = y2 = null; else x1 = +_[0][0], y1 = +_[0][1], x2 = +_[1][0],
+ y2 = +_[1][1];
+ return quadtree;
+ };
+ quadtree.size = function(_) {
+ if (!arguments.length) return x1 == null ? null : [ x2 - x1, y2 - y1 ];
+ if (_ == null) x1 = y1 = x2 = y2 = null; else x1 = y1 = 0, x2 = +_[0], y2 = +_[1];
+ return quadtree;
+ };
+ return quadtree;
+ };
+ function d3_geom_quadtreeCompatX(d) {
+ return d.x;
+ }
+ function d3_geom_quadtreeCompatY(d) {
+ return d.y;
+ }
+ function d3_geom_quadtreeNode() {
+ return {
+ leaf: true,
+ nodes: [],
+ point: null,
+ x: null,
+ y: null
+ };
+ }
+ function d3_geom_quadtreeVisit(f, node, x1, y1, x2, y2) {
+ if (!f(node, x1, y1, x2, y2)) {
+ var sx = (x1 + x2) * .5, sy = (y1 + y2) * .5, children = node.nodes;
+ if (children[0]) d3_geom_quadtreeVisit(f, children[0], x1, y1, sx, sy);
+ if (children[1]) d3_geom_quadtreeVisit(f, children[1], sx, y1, x2, sy);
+ if (children[2]) d3_geom_quadtreeVisit(f, children[2], x1, sy, sx, y2);
+ if (children[3]) d3_geom_quadtreeVisit(f, children[3], sx, sy, x2, y2);
+ }
+ }
+ d3.interpolateRgb = d3_interpolateRgb;
+ function d3_interpolateRgb(a, b) {
+ a = d3.rgb(a);
+ b = d3.rgb(b);
+ var ar = a.r, ag = a.g, ab = a.b, br = b.r - ar, bg = b.g - ag, bb = b.b - ab;
+ return function(t) {
+ return "#" + d3_rgb_hex(Math.round(ar + br * t)) + d3_rgb_hex(Math.round(ag + bg * t)) + d3_rgb_hex(Math.round(ab + bb * t));
+ };
+ }
+ d3.interpolateObject = d3_interpolateObject;
+ function d3_interpolateObject(a, b) {
+ var i = {}, c = {}, k;
+ for (k in a) {
+ if (k in b) {
+ i[k] = d3_interpolate(a[k], b[k]);
+ } else {
+ c[k] = a[k];
+ }
+ }
+ for (k in b) {
+ if (!(k in a)) {
+ c[k] = b[k];
+ }
+ }
+ return function(t) {
+ for (k in i) c[k] = i[k](t);
+ return c;
+ };
+ }
+ d3.interpolateNumber = d3_interpolateNumber;
+ function d3_interpolateNumber(a, b) {
+ b -= a = +a;
+ return function(t) {
+ return a + b * t;
+ };
+ }
+ d3.interpolateString = d3_interpolateString;
+ function d3_interpolateString(a, b) {
+ var m, i, j, s0 = 0, s1 = 0, s = [], q = [], n, o;
+ a = a + "", b = b + "";
+ d3_interpolate_number.lastIndex = 0;
+ for (i = 0; m = d3_interpolate_number.exec(b); ++i) {
+ if (m.index) s.push(b.substring(s0, s1 = m.index));
+ q.push({
+ i: s.length,
+ x: m[0]
+ });
+ s.push(null);
+ s0 = d3_interpolate_number.lastIndex;
+ }
+ if (s0 < b.length) s.push(b.substring(s0));
+ for (i = 0, n = q.length; (m = d3_interpolate_number.exec(a)) && i < n; ++i) {
+ o = q[i];
+ if (o.x == m[0]) {
+ if (o.i) {
+ if (s[o.i + 1] == null) {
+ s[o.i - 1] += o.x;
+ s.splice(o.i, 1);
+ for (j = i + 1; j < n; ++j) q[j].i--;
+ } else {
+ s[o.i - 1] += o.x + s[o.i + 1];
+ s.splice(o.i, 2);
+ for (j = i + 1; j < n; ++j) q[j].i -= 2;
+ }
+ } else {
+ if (s[o.i + 1] == null) {
+ s[o.i] = o.x;
+ } else {
+ s[o.i] = o.x + s[o.i + 1];
+ s.splice(o.i + 1, 1);
+ for (j = i + 1; j < n; ++j) q[j].i--;
+ }
+ }
+ q.splice(i, 1);
+ n--;
+ i--;
+ } else {
+ o.x = d3_interpolateNumber(parseFloat(m[0]), parseFloat(o.x));
+ }
+ }
+ while (i < n) {
+ o = q.pop();
+ if (s[o.i + 1] == null) {
+ s[o.i] = o.x;
+ } else {
+ s[o.i] = o.x + s[o.i + 1];
+ s.splice(o.i + 1, 1);
+ }
+ n--;
+ }
+ if (s.length === 1) {
+ return s[0] == null ? (o = q[0].x, function(t) {
+ return o(t) + "";
+ }) : function() {
+ return b;
+ };
+ }
+ return function(t) {
+ for (i = 0; i < n; ++i) s[(o = q[i]).i] = o.x(t);
+ return s.join("");
+ };
+ }
+ var d3_interpolate_number = /[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g;
+ d3.interpolate = d3_interpolate;
+ function d3_interpolate(a, b) {
+ var i = d3.interpolators.length, f;
+ while (--i >= 0 && !(f = d3.interpolators[i](a, b))) ;
+ return f;
+ }
+ d3.interpolators = [ function(a, b) {
+ var t = typeof b;
+ return (t === "string" ? d3_rgb_names.has(b) || /^(#|rgb\(|hsl\()/.test(b) ? d3_interpolateRgb : d3_interpolateString : b instanceof d3_Color ? d3_interpolateRgb : Array.isArray(b) ? d3_interpolateArray : t === "object" && isNaN(b) ? d3_interpolateObject : d3_interpolateNumber)(a, b);
+ } ];
+ d3.interpolateArray = d3_interpolateArray;
+ function d3_interpolateArray(a, b) {
+ var x = [], c = [], na = a.length, nb = b.length, n0 = Math.min(a.length, b.length), i;
+ for (i = 0; i < n0; ++i) x.push(d3_interpolate(a[i], b[i]));
+ for (;i < na; ++i) c[i] = a[i];
+ for (;i < nb; ++i) c[i] = b[i];
+ return function(t) {
+ for (i = 0; i < n0; ++i) c[i] = x[i](t);
+ return c;
+ };
+ }
+ var d3_ease_default = function() {
+ return d3_identity;
+ };
+ var d3_ease = d3.map({
+ linear: d3_ease_default,
+ poly: d3_ease_poly,
+ quad: function() {
+ return d3_ease_quad;
+ },
+ cubic: function() {
+ return d3_ease_cubic;
+ },
+ sin: function() {
+ return d3_ease_sin;
+ },
+ exp: function() {
+ return d3_ease_exp;
+ },
+ circle: function() {
+ return d3_ease_circle;
+ },
+ elastic: d3_ease_elastic,
+ back: d3_ease_back,
+ bounce: function() {
+ return d3_ease_bounce;
+ }
+ });
+ var d3_ease_mode = d3.map({
+ "in": d3_identity,
+ out: d3_ease_reverse,
+ "in-out": d3_ease_reflect,
+ "out-in": function(f) {
+ return d3_ease_reflect(d3_ease_reverse(f));
+ }
+ });
+ d3.ease = function(name) {
+ var i = name.indexOf("-"), t = i >= 0 ? name.substring(0, i) : name, m = i >= 0 ? name.substring(i + 1) : "in";
+ t = d3_ease.get(t) || d3_ease_default;
+ m = d3_ease_mode.get(m) || d3_identity;
+ return d3_ease_clamp(m(t.apply(null, d3_arraySlice.call(arguments, 1))));
+ };
+ function d3_ease_clamp(f) {
+ return function(t) {
+ return t <= 0 ? 0 : t >= 1 ? 1 : f(t);
+ };
+ }
+ function d3_ease_reverse(f) {
+ return function(t) {
+ return 1 - f(1 - t);
+ };
+ }
+ function d3_ease_reflect(f) {
+ return function(t) {
+ return .5 * (t < .5 ? f(2 * t) : 2 - f(2 - 2 * t));
+ };
+ }
+ function d3_ease_quad(t) {
+ return t * t;
+ }
+ function d3_ease_cubic(t) {
+ return t * t * t;
+ }
+ function d3_ease_cubicInOut(t) {
+ if (t <= 0) return 0;
+ if (t >= 1) return 1;
+ var t2 = t * t, t3 = t2 * t;
+ return 4 * (t < .5 ? t3 : 3 * (t - t2) + t3 - .75);
+ }
+ function d3_ease_poly(e) {
+ return function(t) {
+ return Math.pow(t, e);
+ };
+ }
+ function d3_ease_sin(t) {
+ return 1 - Math.cos(t * halfπ);
+ }
+ function d3_ease_exp(t) {
+ return Math.pow(2, 10 * (t - 1));
+ }
+ function d3_ease_circle(t) {
+ return 1 - Math.sqrt(1 - t * t);
+ }
+ function d3_ease_elastic(a, p) {
+ var s;
+ if (arguments.length < 2) p = .45;
+ if (arguments.length) s = p / τ * Math.asin(1 / a); else a = 1, s = p / 4;
+ return function(t) {
+ return 1 + a * Math.pow(2, -10 * t) * Math.sin((t - s) * τ / p);
+ };
+ }
+ function d3_ease_back(s) {
+ if (!s) s = 1.70158;
+ return function(t) {
+ return t * t * ((s + 1) * t - s);
+ };
+ }
+ function d3_ease_bounce(t) {
+ return t < 1 / 2.75 ? 7.5625 * t * t : t < 2 / 2.75 ? 7.5625 * (t -= 1.5 / 2.75) * t + .75 : t < 2.5 / 2.75 ? 7.5625 * (t -= 2.25 / 2.75) * t + .9375 : 7.5625 * (t -= 2.625 / 2.75) * t + .984375;
+ }
+ d3.interpolateHcl = d3_interpolateHcl;
+ function d3_interpolateHcl(a, b) {
+ a = d3.hcl(a);
+ b = d3.hcl(b);
+ var ah = a.h, ac = a.c, al = a.l, bh = b.h - ah, bc = b.c - ac, bl = b.l - al;
+ if (isNaN(bc)) bc = 0, ac = isNaN(ac) ? b.c : ac;
+ if (isNaN(bh)) bh = 0, ah = isNaN(ah) ? b.h : ah; else if (bh > 180) bh -= 360; else if (bh < -180) bh += 360;
+ return function(t) {
+ return d3_hcl_lab(ah + bh * t, ac + bc * t, al + bl * t) + "";
+ };
+ }
+ d3.interpolateHsl = d3_interpolateHsl;
+ function d3_interpolateHsl(a, b) {
+ a = d3.hsl(a);
+ b = d3.hsl(b);
+ var ah = a.h, as = a.s, al = a.l, bh = b.h - ah, bs = b.s - as, bl = b.l - al;
+ if (isNaN(bs)) bs = 0, as = isNaN(as) ? b.s : as;
+ if (isNaN(bh)) bh = 0, ah = isNaN(ah) ? b.h : ah; else if (bh > 180) bh -= 360; else if (bh < -180) bh += 360;
+ return function(t) {
+ return d3_hsl_rgb(ah + bh * t, as + bs * t, al + bl * t) + "";
+ };
+ }
+ d3.interpolateLab = d3_interpolateLab;
+ function d3_interpolateLab(a, b) {
+ a = d3.lab(a);
+ b = d3.lab(b);
+ var al = a.l, aa = a.a, ab = a.b, bl = b.l - al, ba = b.a - aa, bb = b.b - ab;
+ return function(t) {
+ return d3_lab_rgb(al + bl * t, aa + ba * t, ab + bb * t) + "";
+ };
+ }
+ d3.interpolateRound = d3_interpolateRound;
+ function d3_interpolateRound(a, b) {
+ b -= a;
+ return function(t) {
+ return Math.round(a + b * t);
+ };
+ }
+ d3.transform = function(string) {
+ var g = d3_document.createElementNS(d3.ns.prefix.svg, "g");
+ return (d3.transform = function(string) {
+ if (string != null) {
+ g.setAttribute("transform", string);
+ var t = g.transform.baseVal.consolidate();
+ }
+ return new d3_transform(t ? t.matrix : d3_transformIdentity);
+ })(string);
+ };
+ function d3_transform(m) {
+ var r0 = [ m.a, m.b ], r1 = [ m.c, m.d ], kx = d3_transformNormalize(r0), kz = d3_transformDot(r0, r1), ky = d3_transformNormalize(d3_transformCombine(r1, r0, -kz)) || 0;
+ if (r0[0] * r1[1] < r1[0] * r0[1]) {
+ r0[0] *= -1;
+ r0[1] *= -1;
+ kx *= -1;
+ kz *= -1;
+ }
+ this.rotate = (kx ? Math.atan2(r0[1], r0[0]) : Math.atan2(-r1[0], r1[1])) * d3_degrees;
+ this.translate = [ m.e, m.f ];
+ this.scale = [ kx, ky ];
+ this.skew = ky ? Math.atan2(kz, ky) * d3_degrees : 0;
+ }
+ d3_transform.prototype.toString = function() {
+ return "translate(" + this.translate + ")rotate(" + this.rotate + ")skewX(" + this.skew + ")scale(" + this.scale + ")";
+ };
+ function d3_transformDot(a, b) {
+ return a[0] * b[0] + a[1] * b[1];
+ }
+ function d3_transformNormalize(a) {
+ var k = Math.sqrt(d3_transformDot(a, a));
+ if (k) {
+ a[0] /= k;
+ a[1] /= k;
+ }
+ return k;
+ }
+ function d3_transformCombine(a, b, k) {
+ a[0] += k * b[0];
+ a[1] += k * b[1];
+ return a;
+ }
+ var d3_transformIdentity = {
+ a: 1,
+ b: 0,
+ c: 0,
+ d: 1,
+ e: 0,
+ f: 0
+ };
+ d3.interpolateTransform = d3_interpolateTransform;
+ function d3_interpolateTransform(a, b) {
+ var s = [], q = [], n, A = d3.transform(a), B = d3.transform(b), ta = A.translate, tb = B.translate, ra = A.rotate, rb = B.rotate, wa = A.skew, wb = B.skew, ka = A.scale, kb = B.scale;
+ if (ta[0] != tb[0] || ta[1] != tb[1]) {
+ s.push("translate(", null, ",", null, ")");
+ q.push({
+ i: 1,
+ x: d3_interpolateNumber(ta[0], tb[0])
+ }, {
+ i: 3,
+ x: d3_interpolateNumber(ta[1], tb[1])
+ });
+ } else if (tb[0] || tb[1]) {
+ s.push("translate(" + tb + ")");
+ } else {
+ s.push("");
+ }
+ if (ra != rb) {
+ if (ra - rb > 180) rb += 360; else if (rb - ra > 180) ra += 360;
+ q.push({
+ i: s.push(s.pop() + "rotate(", null, ")") - 2,
+ x: d3_interpolateNumber(ra, rb)
+ });
+ } else if (rb) {
+ s.push(s.pop() + "rotate(" + rb + ")");
+ }
+ if (wa != wb) {
+ q.push({
+ i: s.push(s.pop() + "skewX(", null, ")") - 2,
+ x: d3_interpolateNumber(wa, wb)
+ });
+ } else if (wb) {
+ s.push(s.pop() + "skewX(" + wb + ")");
+ }
+ if (ka[0] != kb[0] || ka[1] != kb[1]) {
+ n = s.push(s.pop() + "scale(", null, ",", null, ")");
+ q.push({
+ i: n - 4,
+ x: d3_interpolateNumber(ka[0], kb[0])
+ }, {
+ i: n - 2,
+ x: d3_interpolateNumber(ka[1], kb[1])
+ });
+ } else if (kb[0] != 1 || kb[1] != 1) {
+ s.push(s.pop() + "scale(" + kb + ")");
+ }
+ n = q.length;
+ return function(t) {
+ var i = -1, o;
+ while (++i < n) s[(o = q[i]).i] = o.x(t);
+ return s.join("");
+ };
+ }
+ function d3_uninterpolateNumber(a, b) {
+ b = b - (a = +a) ? 1 / (b - a) : 0;
+ return function(x) {
+ return (x - a) * b;
+ };
+ }
+ function d3_uninterpolateClamp(a, b) {
+ b = b - (a = +a) ? 1 / (b - a) : 0;
+ return function(x) {
+ return Math.max(0, Math.min(1, (x - a) * b));
+ };
+ }
+ d3.layout = {};
+ d3.layout.bundle = function() {
+ return function(links) {
+ var paths = [], i = -1, n = links.length;
+ while (++i < n) paths.push(d3_layout_bundlePath(links[i]));
+ return paths;
+ };
+ };
+ function d3_layout_bundlePath(link) {
+ var start = link.source, end = link.target, lca = d3_layout_bundleLeastCommonAncestor(start, end), points = [ start ];
+ while (start !== lca) {
+ start = start.parent;
+ points.push(start);
+ }
+ var k = points.length;
+ while (end !== lca) {
+ points.splice(k, 0, end);
+ end = end.parent;
+ }
+ return points;
+ }
+ function d3_layout_bundleAncestors(node) {
+ var ancestors = [], parent = node.parent;
+ while (parent != null) {
+ ancestors.push(node);
+ node = parent;
+ parent = parent.parent;
+ }
+ ancestors.push(node);
+ return ancestors;
+ }
+ function d3_layout_bundleLeastCommonAncestor(a, b) {
+ if (a === b) return a;
+ var aNodes = d3_layout_bundleAncestors(a), bNodes = d3_layout_bundleAncestors(b), aNode = aNodes.pop(), bNode = bNodes.pop(), sharedNode = null;
+ while (aNode === bNode) {
+ sharedNode = aNode;
+ aNode = aNodes.pop();
+ bNode = bNodes.pop();
+ }
+ return sharedNode;
+ }
+ d3.layout.chord = function() {
+ var chord = {}, chords, groups, matrix, n, padding = 0, sortGroups, sortSubgroups, sortChords;
+ function relayout() {
+ var subgroups = {}, groupSums = [], groupIndex = d3.range(n), subgroupIndex = [], k, x, x0, i, j;
+ chords = [];
+ groups = [];
+ k = 0, i = -1;
+ while (++i < n) {
+ x = 0, j = -1;
+ while (++j < n) {
+ x += matrix[i][j];
+ }
+ groupSums.push(x);
+ subgroupIndex.push(d3.range(n));
+ k += x;
+ }
+ if (sortGroups) {
+ groupIndex.sort(function(a, b) {
+ return sortGroups(groupSums[a], groupSums[b]);
+ });
+ }
+ if (sortSubgroups) {
+ subgroupIndex.forEach(function(d, i) {
+ d.sort(function(a, b) {
+ return sortSubgroups(matrix[i][a], matrix[i][b]);
+ });
+ });
+ }
+ k = (τ - padding * n) / k;
+ x = 0, i = -1;
+ while (++i < n) {
+ x0 = x, j = -1;
+ while (++j < n) {
+ var di = groupIndex[i], dj = subgroupIndex[di][j], v = matrix[di][dj], a0 = x, a1 = x += v * k;
+ subgroups[di + "-" + dj] = {
+ index: di,
+ subindex: dj,
+ startAngle: a0,
+ endAngle: a1,
+ value: v
+ };
+ }
+ groups[di] = {
+ index: di,
+ startAngle: x0,
+ endAngle: x,
+ value: (x - x0) / k
+ };
+ x += padding;
+ }
+ i = -1;
+ while (++i < n) {
+ j = i - 1;
+ while (++j < n) {
+ var source = subgroups[i + "-" + j], target = subgroups[j + "-" + i];
+ if (source.value || target.value) {
+ chords.push(source.value < target.value ? {
+ source: target,
+ target: source
+ } : {
+ source: source,
+ target: target
+ });
+ }
+ }
+ }
+ if (sortChords) resort();
+ }
+ function resort() {
+ chords.sort(function(a, b) {
+ return sortChords((a.source.value + a.target.value) / 2, (b.source.value + b.target.value) / 2);
+ });
+ }
+ chord.matrix = function(x) {
+ if (!arguments.length) return matrix;
+ n = (matrix = x) && matrix.length;
+ chords = groups = null;
+ return chord;
+ };
+ chord.padding = function(x) {
+ if (!arguments.length) return padding;
+ padding = x;
+ chords = groups = null;
+ return chord;
+ };
+ chord.sortGroups = function(x) {
+ if (!arguments.length) return sortGroups;
+ sortGroups = x;
+ chords = groups = null;
+ return chord;
+ };
+ chord.sortSubgroups = function(x) {
+ if (!arguments.length) return sortSubgroups;
+ sortSubgroups = x;
+ chords = null;
+ return chord;
+ };
+ chord.sortChords = function(x) {
+ if (!arguments.length) return sortChords;
+ sortChords = x;
+ if (chords) resort();
+ return chord;
+ };
+ chord.chords = function() {
+ if (!chords) relayout();
+ return chords;
+ };
+ chord.groups = function() {
+ if (!groups) relayout();
+ return groups;
+ };
+ return chord;
+ };
+ d3.layout.force = function() {
+ var force = {}, event = d3.dispatch("start", "tick", "end"), size = [ 1, 1 ], drag, alpha, friction = .9, linkDistance = d3_layout_forceLinkDistance, linkStrength = d3_layout_forceLinkStrength, charge = -30, chargeDistance2 = d3_layout_forceChargeDistance2, gravity = .1, theta2 = .64, nodes = [], links = [], distances, strengths, charges;
+ function repulse(node) {
+ return function(quad, x1, _, x2) {
+ if (quad.point !== node) {
+ var dx = quad.cx - node.x, dy = quad.cy - node.y, dw = x2 - x1, dn = dx * dx + dy * dy;
+ if (dw * dw / theta2 < dn) {
+ if (dn < chargeDistance2) {
+ var k = quad.charge / dn;
+ node.px -= dx * k;
+ node.py -= dy * k;
+ }
+ return true;
+ }
+ if (quad.point && dn && dn < chargeDistance2) {
+ var k = quad.pointCharge / dn;
+ node.px -= dx * k;
+ node.py -= dy * k;
+ }
+ }
+ return !quad.charge;
+ };
+ }
+ force.tick = function() {
+ if ((alpha *= .99) < .005) {
+ event.end({
+ type: "end",
+ alpha: alpha = 0
+ });
+ return true;
+ }
+ var n = nodes.length, m = links.length, q, i, o, s, t, l, k, x, y;
+ for (i = 0; i < m; ++i) {
+ o = links[i];
+ s = o.source;
+ t = o.target;
+ x = t.x - s.x;
+ y = t.y - s.y;
+ if (l = x * x + y * y) {
+ l = alpha * strengths[i] * ((l = Math.sqrt(l)) - distances[i]) / l;
+ x *= l;
+ y *= l;
+ t.x -= x * (k = s.weight / (t.weight + s.weight));
+ t.y -= y * k;
+ s.x += x * (k = 1 - k);
+ s.y += y * k;
+ }
+ }
+ if (k = alpha * gravity) {
+ x = size[0] / 2;
+ y = size[1] / 2;
+ i = -1;
+ if (k) while (++i < n) {
+ o = nodes[i];
+ o.x += (x - o.x) * k;
+ o.y += (y - o.y) * k;
+ }
+ }
+ if (charge) {
+ d3_layout_forceAccumulate(q = d3.geom.quadtree(nodes), alpha, charges);
+ i = -1;
+ while (++i < n) {
+ if (!(o = nodes[i]).fixed) {
+ q.visit(repulse(o));
+ }
+ }
+ }
+ i = -1;
+ while (++i < n) {
+ o = nodes[i];
+ if (o.fixed) {
+ o.x = o.px;
+ o.y = o.py;
+ } else {
+ o.x -= (o.px - (o.px = o.x)) * friction;
+ o.y -= (o.py - (o.py = o.y)) * friction;
+ }
+ }
+ event.tick({
+ type: "tick",
+ alpha: alpha
+ });
+ };
+ force.nodes = function(x) {
+ if (!arguments.length) return nodes;
+ nodes = x;
+ return force;
+ };
+ force.links = function(x) {
+ if (!arguments.length) return links;
+ links = x;
+ return force;
+ };
+ force.size = function(x) {
+ if (!arguments.length) return size;
+ size = x;
+ return force;
+ };
+ force.linkDistance = function(x) {
+ if (!arguments.length) return linkDistance;
+ linkDistance = typeof x === "function" ? x : +x;
+ return force;
+ };
+ force.distance = force.linkDistance;
+ force.linkStrength = function(x) {
+ if (!arguments.length) return linkStrength;
+ linkStrength = typeof x === "function" ? x : +x;
+ return force;
+ };
+ force.friction = function(x) {
+ if (!arguments.length) return friction;
+ friction = +x;
+ return force;
+ };
+ force.charge = function(x) {
+ if (!arguments.length) return charge;
+ charge = typeof x === "function" ? x : +x;
+ return force;
+ };
+ force.chargeDistance = function(x) {
+ if (!arguments.length) return Math.sqrt(chargeDistance2);
+ chargeDistance2 = x * x;
+ return force;
+ };
+ force.gravity = function(x) {
+ if (!arguments.length) return gravity;
+ gravity = +x;
+ return force;
+ };
+ force.theta = function(x) {
+ if (!arguments.length) return Math.sqrt(theta2);
+ theta2 = x * x;
+ return force;
+ };
+ force.alpha = function(x) {
+ if (!arguments.length) return alpha;
+ x = +x;
+ if (alpha) {
+ if (x > 0) alpha = x; else alpha = 0;
+ } else if (x > 0) {
+ event.start({
+ type: "start",
+ alpha: alpha = x
+ });
+ d3.timer(force.tick);
+ }
+ return force;
+ };
+ force.start = function() {
+ var i, n = nodes.length, m = links.length, w = size[0], h = size[1], neighbors, o;
+ for (i = 0; i < n; ++i) {
+ (o = nodes[i]).index = i;
+ o.weight = 0;
+ }
+ for (i = 0; i < m; ++i) {
+ o = links[i];
+ if (typeof o.source == "number") o.source = nodes[o.source];
+ if (typeof o.target == "number") o.target = nodes[o.target];
+ ++o.source.weight;
+ ++o.target.weight;
+ }
+ for (i = 0; i < n; ++i) {
+ o = nodes[i];
+ if (isNaN(o.x)) o.x = position("x", w);
+ if (isNaN(o.y)) o.y = position("y", h);
+ if (isNaN(o.px)) o.px = o.x;
+ if (isNaN(o.py)) o.py = o.y;
+ }
+ distances = [];
+ if (typeof linkDistance === "function") for (i = 0; i < m; ++i) distances[i] = +linkDistance.call(this, links[i], i); else for (i = 0; i < m; ++i) distances[i] = linkDistance;
+ strengths = [];
+ if (typeof linkStrength === "function") for (i = 0; i < m; ++i) strengths[i] = +linkStrength.call(this, links[i], i); else for (i = 0; i < m; ++i) strengths[i] = linkStrength;
+ charges = [];
+ if (typeof charge === "function") for (i = 0; i < n; ++i) charges[i] = +charge.call(this, nodes[i], i); else for (i = 0; i < n; ++i) charges[i] = charge;
+ function position(dimension, size) {
+ if (!neighbors) {
+ neighbors = new Array(n);
+ for (j = 0; j < n; ++j) {
+ neighbors[j] = [];
+ }
+ for (j = 0; j < m; ++j) {
+ var o = links[j];
+ neighbors[o.source.index].push(o.target);
+ neighbors[o.target.index].push(o.source);
+ }
+ }
+ var candidates = neighbors[i], j = -1, m = candidates.length, x;
+ while (++j < m) if (!isNaN(x = candidates[j][dimension])) return x;
+ return Math.random() * size;
+ }
+ return force.resume();
+ };
+ force.resume = function() {
+ return force.alpha(.1);
+ };
+ force.stop = function() {
+ return force.alpha(0);
+ };
+ force.drag = function() {
+ if (!drag) drag = d3.behavior.drag().origin(d3_identity).on("dragstart.force", d3_layout_forceDragstart).on("drag.force", dragmove).on("dragend.force", d3_layout_forceDragend);
+ if (!arguments.length) return drag;
+ this.on("mouseover.force", d3_layout_forceMouseover).on("mouseout.force", d3_layout_forceMouseout).call(drag);
+ };
+ function dragmove(d) {
+ d.px = d3.event.x, d.py = d3.event.y;
+ force.resume();
+ }
+ return d3.rebind(force, event, "on");
+ };
+ function d3_layout_forceDragstart(d) {
+ d.fixed |= 2;
+ }
+ function d3_layout_forceDragend(d) {
+ d.fixed &= ~6;
+ }
+ function d3_layout_forceMouseover(d) {
+ d.fixed |= 4;
+ d.px = d.x, d.py = d.y;
+ }
+ function d3_layout_forceMouseout(d) {
+ d.fixed &= ~4;
+ }
+ function d3_layout_forceAccumulate(quad, alpha, charges) {
+ var cx = 0, cy = 0;
+ quad.charge = 0;
+ if (!quad.leaf) {
+ var nodes = quad.nodes, n = nodes.length, i = -1, c;
+ while (++i < n) {
+ c = nodes[i];
+ if (c == null) continue;
+ d3_layout_forceAccumulate(c, alpha, charges);
+ quad.charge += c.charge;
+ cx += c.charge * c.cx;
+ cy += c.charge * c.cy;
+ }
+ }
+ if (quad.point) {
+ if (!quad.leaf) {
+ quad.point.x += Math.random() - .5;
+ quad.point.y += Math.random() - .5;
+ }
+ var k = alpha * charges[quad.point.index];
+ quad.charge += quad.pointCharge = k;
+ cx += k * quad.point.x;
+ cy += k * quad.point.y;
+ }
+ quad.cx = cx / quad.charge;
+ quad.cy = cy / quad.charge;
+ }
+ var d3_layout_forceLinkDistance = 20, d3_layout_forceLinkStrength = 1, d3_layout_forceChargeDistance2 = Infinity;
+ d3.layout.hierarchy = function() {
+ var sort = d3_layout_hierarchySort, children = d3_layout_hierarchyChildren, value = d3_layout_hierarchyValue;
+ function recurse(node, depth, nodes) {
+ var childs = children.call(hierarchy, node, depth);
+ node.depth = depth;
+ nodes.push(node);
+ if (childs && (n = childs.length)) {
+ var i = -1, n, c = node.children = new Array(n), v = 0, j = depth + 1, d;
+ while (++i < n) {
+ d = c[i] = recurse(childs[i], j, nodes);
+ d.parent = node;
+ v += d.value;
+ }
+ if (sort) c.sort(sort);
+ if (value) node.value = v;
+ } else {
+ delete node.children;
+ if (value) {
+ node.value = +value.call(hierarchy, node, depth) || 0;
+ }
+ }
+ return node;
+ }
+ function revalue(node, depth) {
+ var children = node.children, v = 0;
+ if (children && (n = children.length)) {
+ var i = -1, n, j = depth + 1;
+ while (++i < n) v += revalue(children[i], j);
+ } else if (value) {
+ v = +value.call(hierarchy, node, depth) || 0;
+ }
+ if (value) node.value = v;
+ return v;
+ }
+ function hierarchy(d) {
+ var nodes = [];
+ recurse(d, 0, nodes);
+ return nodes;
+ }
+ hierarchy.sort = function(x) {
+ if (!arguments.length) return sort;
+ sort = x;
+ return hierarchy;
+ };
+ hierarchy.children = function(x) {
+ if (!arguments.length) return children;
+ children = x;
+ return hierarchy;
+ };
+ hierarchy.value = function(x) {
+ if (!arguments.length) return value;
+ value = x;
+ return hierarchy;
+ };
+ hierarchy.revalue = function(root) {
+ revalue(root, 0);
+ return root;
+ };
+ return hierarchy;
+ };
+ function d3_layout_hierarchyRebind(object, hierarchy) {
+ d3.rebind(object, hierarchy, "sort", "children", "value");
+ object.nodes = object;
+ object.links = d3_layout_hierarchyLinks;
+ return object;
+ }
+ function d3_layout_hierarchyChildren(d) {
+ return d.children;
+ }
+ function d3_layout_hierarchyValue(d) {
+ return d.value;
+ }
+ function d3_layout_hierarchySort(a, b) {
+ return b.value - a.value;
+ }
+ function d3_layout_hierarchyLinks(nodes) {
+ return d3.merge(nodes.map(function(parent) {
+ return (parent.children || []).map(function(child) {
+ return {
+ source: parent,
+ target: child
+ };
+ });
+ }));
+ }
+ d3.layout.partition = function() {
+ var hierarchy = d3.layout.hierarchy(), size = [ 1, 1 ];
+ function position(node, x, dx, dy) {
+ var children = node.children;
+ node.x = x;
+ node.y = node.depth * dy;
+ node.dx = dx;
+ node.dy = dy;
+ if (children && (n = children.length)) {
+ var i = -1, n, c, d;
+ dx = node.value ? dx / node.value : 0;
+ while (++i < n) {
+ position(c = children[i], x, d = c.value * dx, dy);
+ x += d;
+ }
+ }
+ }
+ function depth(node) {
+ var children = node.children, d = 0;
+ if (children && (n = children.length)) {
+ var i = -1, n;
+ while (++i < n) d = Math.max(d, depth(children[i]));
+ }
+ return 1 + d;
+ }
+ function partition(d, i) {
+ var nodes = hierarchy.call(this, d, i);
+ position(nodes[0], 0, size[0], size[1] / depth(nodes[0]));
+ return nodes;
+ }
+ partition.size = function(x) {
+ if (!arguments.length) return size;
+ size = x;
+ return partition;
+ };
+ return d3_layout_hierarchyRebind(partition, hierarchy);
+ };
+ d3.layout.pie = function() {
+ var value = Number, sort = d3_layout_pieSortByValue, startAngle = 0, endAngle = τ;
+ function pie(data) {
+ var values = data.map(function(d, i) {
+ return +value.call(pie, d, i);
+ });
+ var a = +(typeof startAngle === "function" ? startAngle.apply(this, arguments) : startAngle);
+ var k = ((typeof endAngle === "function" ? endAngle.apply(this, arguments) : endAngle) - a) / d3.sum(values);
+ var index = d3.range(data.length);
+ if (sort != null) index.sort(sort === d3_layout_pieSortByValue ? function(i, j) {
+ return values[j] - values[i];
+ } : function(i, j) {
+ return sort(data[i], data[j]);
+ });
+ var arcs = [];
+ index.forEach(function(i) {
+ var d;
+ arcs[i] = {
+ data: data[i],
+ value: d = values[i],
+ startAngle: a,
+ endAngle: a += d * k
+ };
+ });
+ return arcs;
+ }
+ pie.value = function(x) {
+ if (!arguments.length) return value;
+ value = x;
+ return pie;
+ };
+ pie.sort = function(x) {
+ if (!arguments.length) return sort;
+ sort = x;
+ return pie;
+ };
+ pie.startAngle = function(x) {
+ if (!arguments.length) return startAngle;
+ startAngle = x;
+ return pie;
+ };
+ pie.endAngle = function(x) {
+ if (!arguments.length) return endAngle;
+ endAngle = x;
+ return pie;
+ };
+ return pie;
+ };
+ var d3_layout_pieSortByValue = {};
+ d3.layout.stack = function() {
+ var values = d3_identity, order = d3_layout_stackOrderDefault, offset = d3_layout_stackOffsetZero, out = d3_layout_stackOut, x = d3_layout_stackX, y = d3_layout_stackY;
+ function stack(data, index) {
+ var series = data.map(function(d, i) {
+ return values.call(stack, d, i);
+ });
+ var points = series.map(function(d) {
+ return d.map(function(v, i) {
+ return [ x.call(stack, v, i), y.call(stack, v, i) ];
+ });
+ });
+ var orders = order.call(stack, points, index);
+ series = d3.permute(series, orders);
+ points = d3.permute(points, orders);
+ var offsets = offset.call(stack, points, index);
+ var n = series.length, m = series[0].length, i, j, o;
+ for (j = 0; j < m; ++j) {
+ out.call(stack, series[0][j], o = offsets[j], points[0][j][1]);
+ for (i = 1; i < n; ++i) {
+ out.call(stack, series[i][j], o += points[i - 1][j][1], points[i][j][1]);
+ }
+ }
+ return data;
+ }
+ stack.values = function(x) {
+ if (!arguments.length) return values;
+ values = x;
+ return stack;
+ };
+ stack.order = function(x) {
+ if (!arguments.length) return order;
+ order = typeof x === "function" ? x : d3_layout_stackOrders.get(x) || d3_layout_stackOrderDefault;
+ return stack;
+ };
+ stack.offset = function(x) {
+ if (!arguments.length) return offset;
+ offset = typeof x === "function" ? x : d3_layout_stackOffsets.get(x) || d3_layout_stackOffsetZero;
+ return stack;
+ };
+ stack.x = function(z) {
+ if (!arguments.length) return x;
+ x = z;
+ return stack;
+ };
+ stack.y = function(z) {
+ if (!arguments.length) return y;
+ y = z;
+ return stack;
+ };
+ stack.out = function(z) {
+ if (!arguments.length) return out;
+ out = z;
+ return stack;
+ };
+ return stack;
+ };
+ function d3_layout_stackX(d) {
+ return d.x;
+ }
+ function d3_layout_stackY(d) {
+ return d.y;
+ }
+ function d3_layout_stackOut(d, y0, y) {
+ d.y0 = y0;
+ d.y = y;
+ }
+ var d3_layout_stackOrders = d3.map({
+ "inside-out": function(data) {
+ var n = data.length, i, j, max = data.map(d3_layout_stackMaxIndex), sums = data.map(d3_layout_stackReduceSum), index = d3.range(n).sort(function(a, b) {
+ return max[a] - max[b];
+ }), top = 0, bottom = 0, tops = [], bottoms = [];
+ for (i = 0; i < n; ++i) {
+ j = index[i];
+ if (top < bottom) {
+ top += sums[j];
+ tops.push(j);
+ } else {
+ bottom += sums[j];
+ bottoms.push(j);
+ }
+ }
+ return bottoms.reverse().concat(tops);
+ },
+ reverse: function(data) {
+ return d3.range(data.length).reverse();
+ },
+ "default": d3_layout_stackOrderDefault
+ });
+ var d3_layout_stackOffsets = d3.map({
+ silhouette: function(data) {
+ var n = data.length, m = data[0].length, sums = [], max = 0, i, j, o, y0 = [];
+ for (j = 0; j < m; ++j) {
+ for (i = 0, o = 0; i < n; i++) o += data[i][j][1];
+ if (o > max) max = o;
+ sums.push(o);
+ }
+ for (j = 0; j < m; ++j) {
+ y0[j] = (max - sums[j]) / 2;
+ }
+ return y0;
+ },
+ wiggle: function(data) {
+ var n = data.length, x = data[0], m = x.length, i, j, k, s1, s2, s3, dx, o, o0, y0 = [];
+ y0[0] = o = o0 = 0;
+ for (j = 1; j < m; ++j) {
+ for (i = 0, s1 = 0; i < n; ++i) s1 += data[i][j][1];
+ for (i = 0, s2 = 0, dx = x[j][0] - x[j - 1][0]; i < n; ++i) {
+ for (k = 0, s3 = (data[i][j][1] - data[i][j - 1][1]) / (2 * dx); k < i; ++k) {
+ s3 += (data[k][j][1] - data[k][j - 1][1]) / dx;
+ }
+ s2 += s3 * data[i][j][1];
+ }
+ y0[j] = o -= s1 ? s2 / s1 * dx : 0;
+ if (o < o0) o0 = o;
+ }
+ for (j = 0; j < m; ++j) y0[j] -= o0;
+ return y0;
+ },
+ expand: function(data) {
+ var n = data.length, m = data[0].length, k = 1 / n, i, j, o, y0 = [];
+ for (j = 0; j < m; ++j) {
+ for (i = 0, o = 0; i < n; i++) o += data[i][j][1];
+ if (o) for (i = 0; i < n; i++) data[i][j][1] /= o; else for (i = 0; i < n; i++) data[i][j][1] = k;
+ }
+ for (j = 0; j < m; ++j) y0[j] = 0;
+ return y0;
+ },
+ zero: d3_layout_stackOffsetZero
+ });
+ function d3_layout_stackOrderDefault(data) {
+ return d3.range(data.length);
+ }
+ function d3_layout_stackOffsetZero(data) {
+ var j = -1, m = data[0].length, y0 = [];
+ while (++j < m) y0[j] = 0;
+ return y0;
+ }
+ function d3_layout_stackMaxIndex(array) {
+ var i = 1, j = 0, v = array[0][1], k, n = array.length;
+ for (;i < n; ++i) {
+ if ((k = array[i][1]) > v) {
+ j = i;
+ v = k;
+ }
+ }
+ return j;
+ }
+ function d3_layout_stackReduceSum(d) {
+ return d.reduce(d3_layout_stackSum, 0);
+ }
+ function d3_layout_stackSum(p, d) {
+ return p + d[1];
+ }
+ d3.layout.histogram = function() {
+ var frequency = true, valuer = Number, ranger = d3_layout_histogramRange, binner = d3_layout_histogramBinSturges;
+ function histogram(data, i) {
+ var bins = [], values = data.map(valuer, this), range = ranger.call(this, values, i), thresholds = binner.call(this, range, values, i), bin, i = -1, n = values.length, m = thresholds.length - 1, k = frequency ? 1 : 1 / n, x;
+ while (++i < m) {
+ bin = bins[i] = [];
+ bin.dx = thresholds[i + 1] - (bin.x = thresholds[i]);
+ bin.y = 0;
+ }
+ if (m > 0) {
+ i = -1;
+ while (++i < n) {
+ x = values[i];
+ if (x >= range[0] && x <= range[1]) {
+ bin = bins[d3.bisect(thresholds, x, 1, m) - 1];
+ bin.y += k;
+ bin.push(data[i]);
+ }
+ }
+ }
+ return bins;
+ }
+ histogram.value = function(x) {
+ if (!arguments.length) return valuer;
+ valuer = x;
+ return histogram;
+ };
+ histogram.range = function(x) {
+ if (!arguments.length) return ranger;
+ ranger = d3_functor(x);
+ return histogram;
+ };
+ histogram.bins = function(x) {
+ if (!arguments.length) return binner;
+ binner = typeof x === "number" ? function(range) {
+ return d3_layout_histogramBinFixed(range, x);
+ } : d3_functor(x);
+ return histogram;
+ };
+ histogram.frequency = function(x) {
+ if (!arguments.length) return frequency;
+ frequency = !!x;
+ return histogram;
+ };
+ return histogram;
+ };
+ function d3_layout_histogramBinSturges(range, values) {
+ return d3_layout_histogramBinFixed(range, Math.ceil(Math.log(values.length) / Math.LN2 + 1));
+ }
+ function d3_layout_histogramBinFixed(range, n) {
+ var x = -1, b = +range[0], m = (range[1] - b) / n, f = [];
+ while (++x <= n) f[x] = m * x + b;
+ return f;
+ }
+ function d3_layout_histogramRange(values) {
+ return [ d3.min(values), d3.max(values) ];
+ }
+ d3.layout.tree = function() {
+ var hierarchy = d3.layout.hierarchy().sort(null).value(null), separation = d3_layout_treeSeparation, size = [ 1, 1 ], nodeSize = false;
+ function tree(d, i) {
+ var nodes = hierarchy.call(this, d, i), root = nodes[0];
+ function firstWalk(node, previousSibling) {
+ var children = node.children, layout = node._tree;
+ if (children && (n = children.length)) {
+ var n, firstChild = children[0], previousChild, ancestor = firstChild, child, i = -1;
+ while (++i < n) {
+ child = children[i];
+ firstWalk(child, previousChild);
+ ancestor = apportion(child, previousChild, ancestor);
+ previousChild = child;
+ }
+ d3_layout_treeShift(node);
+ var midpoint = .5 * (firstChild._tree.prelim + child._tree.prelim);
+ if (previousSibling) {
+ layout.prelim = previousSibling._tree.prelim + separation(node, previousSibling);
+ layout.mod = layout.prelim - midpoint;
+ } else {
+ layout.prelim = midpoint;
+ }
+ } else {
+ if (previousSibling) {
+ layout.prelim = previousSibling._tree.prelim + separation(node, previousSibling);
+ }
+ }
+ }
+ function secondWalk(node, x) {
+ node.x = node._tree.prelim + x;
+ var children = node.children;
+ if (children && (n = children.length)) {
+ var i = -1, n;
+ x += node._tree.mod;
+ while (++i < n) {
+ secondWalk(children[i], x);
+ }
+ }
+ }
+ function apportion(node, previousSibling, ancestor) {
+ if (previousSibling) {
+ var vip = node, vop = node, vim = previousSibling, vom = node.parent.children[0], sip = vip._tree.mod, sop = vop._tree.mod, sim = vim._tree.mod, som = vom._tree.mod, shift;
+ while (vim = d3_layout_treeRight(vim), vip = d3_layout_treeLeft(vip), vim && vip) {
+ vom = d3_layout_treeLeft(vom);
+ vop = d3_layout_treeRight(vop);
+ vop._tree.ancestor = node;
+ shift = vim._tree.prelim + sim - vip._tree.prelim - sip + separation(vim, vip);
+ if (shift > 0) {
+ d3_layout_treeMove(d3_layout_treeAncestor(vim, node, ancestor), node, shift);
+ sip += shift;
+ sop += shift;
+ }
+ sim += vim._tree.mod;
+ sip += vip._tree.mod;
+ som += vom._tree.mod;
+ sop += vop._tree.mod;
+ }
+ if (vim && !d3_layout_treeRight(vop)) {
+ vop._tree.thread = vim;
+ vop._tree.mod += sim - sop;
+ }
+ if (vip && !d3_layout_treeLeft(vom)) {
+ vom._tree.thread = vip;
+ vom._tree.mod += sip - som;
+ ancestor = node;
+ }
+ }
+ return ancestor;
+ }
+ d3_layout_treeVisitAfter(root, function(node, previousSibling) {
+ node._tree = {
+ ancestor: node,
+ prelim: 0,
+ mod: 0,
+ change: 0,
+ shift: 0,
+ number: previousSibling ? previousSibling._tree.number + 1 : 0
+ };
+ });
+ firstWalk(root);
+ secondWalk(root, -root._tree.prelim);
+ var left = d3_layout_treeSearch(root, d3_layout_treeLeftmost), right = d3_layout_treeSearch(root, d3_layout_treeRightmost), deep = d3_layout_treeSearch(root, d3_layout_treeDeepest), x0 = left.x - separation(left, right) / 2, x1 = right.x + separation(right, left) / 2, y1 = deep.depth || 1;
+ d3_layout_treeVisitAfter(root, nodeSize ? function(node) {
+ node.x *= size[0];
+ node.y = node.depth * size[1];
+ delete node._tree;
+ } : function(node) {
+ node.x = (node.x - x0) / (x1 - x0) * size[0];
+ node.y = node.depth / y1 * size[1];
+ delete node._tree;
+ });
+ return nodes;
+ }
+ tree.separation = function(x) {
+ if (!arguments.length) return separation;
+ separation = x;
+ return tree;
+ };
+ tree.size = function(x) {
+ if (!arguments.length) return nodeSize ? null : size;
+ nodeSize = (size = x) == null;
+ return tree;
+ };
+ tree.nodeSize = function(x) {
+ if (!arguments.length) return nodeSize ? size : null;
+ nodeSize = (size = x) != null;
+ return tree;
+ };
+ return d3_layout_hierarchyRebind(tree, hierarchy);
+ };
+ function d3_layout_treeSeparation(a, b) {
+ return a.parent == b.parent ? 1 : 2;
+ }
+ function d3_layout_treeLeft(node) {
+ var children = node.children;
+ return children && children.length ? children[0] : node._tree.thread;
+ }
+ function d3_layout_treeRight(node) {
+ var children = node.children, n;
+ return children && (n = children.length) ? children[n - 1] : node._tree.thread;
+ }
+ function d3_layout_treeSearch(node, compare) {
+ var children = node.children;
+ if (children && (n = children.length)) {
+ var child, n, i = -1;
+ while (++i < n) {
+ if (compare(child = d3_layout_treeSearch(children[i], compare), node) > 0) {
+ node = child;
+ }
+ }
+ }
+ return node;
+ }
+ function d3_layout_treeRightmost(a, b) {
+ return a.x - b.x;
+ }
+ function d3_layout_treeLeftmost(a, b) {
+ return b.x - a.x;
+ }
+ function d3_layout_treeDeepest(a, b) {
+ return a.depth - b.depth;
+ }
+ function d3_layout_treeVisitAfter(node, callback) {
+ function visit(node, previousSibling) {
+ var children = node.children;
+ if (children && (n = children.length)) {
+ var child, previousChild = null, i = -1, n;
+ while (++i < n) {
+ child = children[i];
+ visit(child, previousChild);
+ previousChild = child;
+ }
+ }
+ callback(node, previousSibling);
+ }
+ visit(node, null);
+ }
+ function d3_layout_treeShift(node) {
+ var shift = 0, change = 0, children = node.children, i = children.length, child;
+ while (--i >= 0) {
+ child = children[i]._tree;
+ child.prelim += shift;
+ child.mod += shift;
+ shift += child.shift + (change += child.change);
+ }
+ }
+ function d3_layout_treeMove(ancestor, node, shift) {
+ ancestor = ancestor._tree;
+ node = node._tree;
+ var change = shift / (node.number - ancestor.number);
+ ancestor.change += change;
+ node.change -= change;
+ node.shift += shift;
+ node.prelim += shift;
+ node.mod += shift;
+ }
+ function d3_layout_treeAncestor(vim, node, ancestor) {
+ return vim._tree.ancestor.parent == node.parent ? vim._tree.ancestor : ancestor;
+ }
+ d3.layout.pack = function() {
+ var hierarchy = d3.layout.hierarchy().sort(d3_layout_packSort), padding = 0, size = [ 1, 1 ], radius;
+ function pack(d, i) {
+ var nodes = hierarchy.call(this, d, i), root = nodes[0], w = size[0], h = size[1], r = radius == null ? Math.sqrt : typeof radius === "function" ? radius : function() {
+ return radius;
+ };
+ root.x = root.y = 0;
+ d3_layout_treeVisitAfter(root, function(d) {
+ d.r = +r(d.value);
+ });
+ d3_layout_treeVisitAfter(root, d3_layout_packSiblings);
+ if (padding) {
+ var dr = padding * (radius ? 1 : Math.max(2 * root.r / w, 2 * root.r / h)) / 2;
+ d3_layout_treeVisitAfter(root, function(d) {
+ d.r += dr;
+ });
+ d3_layout_treeVisitAfter(root, d3_layout_packSiblings);
+ d3_layout_treeVisitAfter(root, function(d) {
+ d.r -= dr;
+ });
+ }
+ d3_layout_packTransform(root, w / 2, h / 2, radius ? 1 : 1 / Math.max(2 * root.r / w, 2 * root.r / h));
+ return nodes;
+ }
+ pack.size = function(_) {
+ if (!arguments.length) return size;
+ size = _;
+ return pack;
+ };
+ pack.radius = function(_) {
+ if (!arguments.length) return radius;
+ radius = _ == null || typeof _ === "function" ? _ : +_;
+ return pack;
+ };
+ pack.padding = function(_) {
+ if (!arguments.length) return padding;
+ padding = +_;
+ return pack;
+ };
+ return d3_layout_hierarchyRebind(pack, hierarchy);
+ };
+ function d3_layout_packSort(a, b) {
+ return a.value - b.value;
+ }
+ function d3_layout_packInsert(a, b) {
+ var c = a._pack_next;
+ a._pack_next = b;
+ b._pack_prev = a;
+ b._pack_next = c;
+ c._pack_prev = b;
+ }
+ function d3_layout_packSplice(a, b) {
+ a._pack_next = b;
+ b._pack_prev = a;
+ }
+ function d3_layout_packIntersects(a, b) {
+ var dx = b.x - a.x, dy = b.y - a.y, dr = a.r + b.r;
+ return .999 * dr * dr > dx * dx + dy * dy;
+ }
+ function d3_layout_packSiblings(node) {
+ if (!(nodes = node.children) || !(n = nodes.length)) return;
+ var nodes, xMin = Infinity, xMax = -Infinity, yMin = Infinity, yMax = -Infinity, a, b, c, i, j, k, n;
+ function bound(node) {
+ xMin = Math.min(node.x - node.r, xMin);
+ xMax = Math.max(node.x + node.r, xMax);
+ yMin = Math.min(node.y - node.r, yMin);
+ yMax = Math.max(node.y + node.r, yMax);
+ }
+ nodes.forEach(d3_layout_packLink);
+ a = nodes[0];
+ a.x = -a.r;
+ a.y = 0;
+ bound(a);
+ if (n > 1) {
+ b = nodes[1];
+ b.x = b.r;
+ b.y = 0;
+ bound(b);
+ if (n > 2) {
+ c = nodes[2];
+ d3_layout_packPlace(a, b, c);
+ bound(c);
+ d3_layout_packInsert(a, c);
+ a._pack_prev = c;
+ d3_layout_packInsert(c, b);
+ b = a._pack_next;
+ for (i = 3; i < n; i++) {
+ d3_layout_packPlace(a, b, c = nodes[i]);
+ var isect = 0, s1 = 1, s2 = 1;
+ for (j = b._pack_next; j !== b; j = j._pack_next, s1++) {
+ if (d3_layout_packIntersects(j, c)) {
+ isect = 1;
+ break;
+ }
+ }
+ if (isect == 1) {
+ for (k = a._pack_prev; k !== j._pack_prev; k = k._pack_prev, s2++) {
+ if (d3_layout_packIntersects(k, c)) {
+ break;
+ }
+ }
+ }
+ if (isect) {
+ if (s1 < s2 || s1 == s2 && b.r < a.r) d3_layout_packSplice(a, b = j); else d3_layout_packSplice(a = k, b);
+ i--;
+ } else {
+ d3_layout_packInsert(a, c);
+ b = c;
+ bound(c);
+ }
+ }
+ }
+ }
+ var cx = (xMin + xMax) / 2, cy = (yMin + yMax) / 2, cr = 0;
+ for (i = 0; i < n; i++) {
+ c = nodes[i];
+ c.x -= cx;
+ c.y -= cy;
+ cr = Math.max(cr, c.r + Math.sqrt(c.x * c.x + c.y * c.y));
+ }
+ node.r = cr;
+ nodes.forEach(d3_layout_packUnlink);
+ }
+ function d3_layout_packLink(node) {
+ node._pack_next = node._pack_prev = node;
+ }
+ function d3_layout_packUnlink(node) {
+ delete node._pack_next;
+ delete node._pack_prev;
+ }
+ function d3_layout_packTransform(node, x, y, k) {
+ var children = node.children;
+ node.x = x += k * node.x;
+ node.y = y += k * node.y;
+ node.r *= k;
+ if (children) {
+ var i = -1, n = children.length;
+ while (++i < n) d3_layout_packTransform(children[i], x, y, k);
+ }
+ }
+ function d3_layout_packPlace(a, b, c) {
+ var db = a.r + c.r, dx = b.x - a.x, dy = b.y - a.y;
+ if (db && (dx || dy)) {
+ var da = b.r + c.r, dc = dx * dx + dy * dy;
+ da *= da;
+ db *= db;
+ var x = .5 + (db - da) / (2 * dc), y = Math.sqrt(Math.max(0, 2 * da * (db + dc) - (db -= dc) * db - da * da)) / (2 * dc);
+ c.x = a.x + x * dx + y * dy;
+ c.y = a.y + x * dy - y * dx;
+ } else {
+ c.x = a.x + db;
+ c.y = a.y;
+ }
+ }
+ d3.layout.cluster = function() {
+ var hierarchy = d3.layout.hierarchy().sort(null).value(null), separation = d3_layout_treeSeparation, size = [ 1, 1 ], nodeSize = false;
+ function cluster(d, i) {
+ var nodes = hierarchy.call(this, d, i), root = nodes[0], previousNode, x = 0;
+ d3_layout_treeVisitAfter(root, function(node) {
+ var children = node.children;
+ if (children && children.length) {
+ node.x = d3_layout_clusterX(children);
+ node.y = d3_layout_clusterY(children);
+ } else {
+ node.x = previousNode ? x += separation(node, previousNode) : 0;
+ node.y = 0;
+ previousNode = node;
+ }
+ });
+ var left = d3_layout_clusterLeft(root), right = d3_layout_clusterRight(root), x0 = left.x - separation(left, right) / 2, x1 = right.x + separation(right, left) / 2;
+ d3_layout_treeVisitAfter(root, nodeSize ? function(node) {
+ node.x = (node.x - root.x) * size[0];
+ node.y = (root.y - node.y) * size[1];
+ } : function(node) {
+ node.x = (node.x - x0) / (x1 - x0) * size[0];
+ node.y = (1 - (root.y ? node.y / root.y : 1)) * size[1];
+ });
+ return nodes;
+ }
+ cluster.separation = function(x) {
+ if (!arguments.length) return separation;
+ separation = x;
+ return cluster;
+ };
+ cluster.size = function(x) {
+ if (!arguments.length) return nodeSize ? null : size;
+ nodeSize = (size = x) == null;
+ return cluster;
+ };
+ cluster.nodeSize = function(x) {
+ if (!arguments.length) return nodeSize ? size : null;
+ nodeSize = (size = x) != null;
+ return cluster;
+ };
+ return d3_layout_hierarchyRebind(cluster, hierarchy);
+ };
+ function d3_layout_clusterY(children) {
+ return 1 + d3.max(children, function(child) {
+ return child.y;
+ });
+ }
+ function d3_layout_clusterX(children) {
+ return children.reduce(function(x, child) {
+ return x + child.x;
+ }, 0) / children.length;
+ }
+ function d3_layout_clusterLeft(node) {
+ var children = node.children;
+ return children && children.length ? d3_layout_clusterLeft(children[0]) : node;
+ }
+ function d3_layout_clusterRight(node) {
+ var children = node.children, n;
+ return children && (n = children.length) ? d3_layout_clusterRight(children[n - 1]) : node;
+ }
+ d3.layout.treemap = function() {
+ var hierarchy = d3.layout.hierarchy(), round = Math.round, size = [ 1, 1 ], padding = null, pad = d3_layout_treemapPadNull, sticky = false, stickies, mode = "squarify", ratio = .5 * (1 + Math.sqrt(5));
+ function scale(children, k) {
+ var i = -1, n = children.length, child, area;
+ while (++i < n) {
+ area = (child = children[i]).value * (k < 0 ? 0 : k);
+ child.area = isNaN(area) || area <= 0 ? 0 : area;
+ }
+ }
+ function squarify(node) {
+ var children = node.children;
+ if (children && children.length) {
+ var rect = pad(node), row = [], remaining = children.slice(), child, best = Infinity, score, u = mode === "slice" ? rect.dx : mode === "dice" ? rect.dy : mode === "slice-dice" ? node.depth & 1 ? rect.dy : rect.dx : Math.min(rect.dx, rect.dy), n;
+ scale(remaining, rect.dx * rect.dy / node.value);
+ row.area = 0;
+ while ((n = remaining.length) > 0) {
+ row.push(child = remaining[n - 1]);
+ row.area += child.area;
+ if (mode !== "squarify" || (score = worst(row, u)) <= best) {
+ remaining.pop();
+ best = score;
+ } else {
+ row.area -= row.pop().area;
+ position(row, u, rect, false);
+ u = Math.min(rect.dx, rect.dy);
+ row.length = row.area = 0;
+ best = Infinity;
+ }
+ }
+ if (row.length) {
+ position(row, u, rect, true);
+ row.length = row.area = 0;
+ }
+ children.forEach(squarify);
+ }
+ }
+ function stickify(node) {
+ var children = node.children;
+ if (children && children.length) {
+ var rect = pad(node), remaining = children.slice(), child, row = [];
+ scale(remaining, rect.dx * rect.dy / node.value);
+ row.area = 0;
+ while (child = remaining.pop()) {
+ row.push(child);
+ row.area += child.area;
+ if (child.z != null) {
+ position(row, child.z ? rect.dx : rect.dy, rect, !remaining.length);
+ row.length = row.area = 0;
+ }
+ }
+ children.forEach(stickify);
+ }
+ }
+ function worst(row, u) {
+ var s = row.area, r, rmax = 0, rmin = Infinity, i = -1, n = row.length;
+ while (++i < n) {
+ if (!(r = row[i].area)) continue;
+ if (r < rmin) rmin = r;
+ if (r > rmax) rmax = r;
+ }
+ s *= s;
+ u *= u;
+ return s ? Math.max(u * rmax * ratio / s, s / (u * rmin * ratio)) : Infinity;
+ }
+ function position(row, u, rect, flush) {
+ var i = -1, n = row.length, x = rect.x, y = rect.y, v = u ? round(row.area / u) : 0, o;
+ if (u == rect.dx) {
+ if (flush || v > rect.dy) v = rect.dy;
+ while (++i < n) {
+ o = row[i];
+ o.x = x;
+ o.y = y;
+ o.dy = v;
+ x += o.dx = Math.min(rect.x + rect.dx - x, v ? round(o.area / v) : 0);
+ }
+ o.z = true;
+ o.dx += rect.x + rect.dx - x;
+ rect.y += v;
+ rect.dy -= v;
+ } else {
+ if (flush || v > rect.dx) v = rect.dx;
+ while (++i < n) {
+ o = row[i];
+ o.x = x;
+ o.y = y;
+ o.dx = v;
+ y += o.dy = Math.min(rect.y + rect.dy - y, v ? round(o.area / v) : 0);
+ }
+ o.z = false;
+ o.dy += rect.y + rect.dy - y;
+ rect.x += v;
+ rect.dx -= v;
+ }
+ }
+ function treemap(d) {
+ var nodes = stickies || hierarchy(d), root = nodes[0];
+ root.x = 0;
+ root.y = 0;
+ root.dx = size[0];
+ root.dy = size[1];
+ if (stickies) hierarchy.revalue(root);
+ scale([ root ], root.dx * root.dy / root.value);
+ (stickies ? stickify : squarify)(root);
+ if (sticky) stickies = nodes;
+ return nodes;
+ }
+ treemap.size = function(x) {
+ if (!arguments.length) return size;
+ size = x;
+ return treemap;
+ };
+ treemap.padding = function(x) {
+ if (!arguments.length) return padding;
+ function padFunction(node) {
+ var p = x.call(treemap, node, node.depth);
+ return p == null ? d3_layout_treemapPadNull(node) : d3_layout_treemapPad(node, typeof p === "number" ? [ p, p, p, p ] : p);
+ }
+ function padConstant(node) {
+ return d3_layout_treemapPad(node, x);
+ }
+ var type;
+ pad = (padding = x) == null ? d3_layout_treemapPadNull : (type = typeof x) === "function" ? padFunction : type === "number" ? (x = [ x, x, x, x ],
+ padConstant) : padConstant;
+ return treemap;
+ };
+ treemap.round = function(x) {
+ if (!arguments.length) return round != Number;
+ round = x ? Math.round : Number;
+ return treemap;
+ };
+ treemap.sticky = function(x) {
+ if (!arguments.length) return sticky;
+ sticky = x;
+ stickies = null;
+ return treemap;
+ };
+ treemap.ratio = function(x) {
+ if (!arguments.length) return ratio;
+ ratio = x;
+ return treemap;
+ };
+ treemap.mode = function(x) {
+ if (!arguments.length) return mode;
+ mode = x + "";
+ return treemap;
+ };
+ return d3_layout_hierarchyRebind(treemap, hierarchy);
+ };
+ function d3_layout_treemapPadNull(node) {
+ return {
+ x: node.x,
+ y: node.y,
+ dx: node.dx,
+ dy: node.dy
+ };
+ }
+ function d3_layout_treemapPad(node, padding) {
+ var x = node.x + padding[3], y = node.y + padding[0], dx = node.dx - padding[1] - padding[3], dy = node.dy - padding[0] - padding[2];
+ if (dx < 0) {
+ x += dx / 2;
+ dx = 0;
+ }
+ if (dy < 0) {
+ y += dy / 2;
+ dy = 0;
+ }
+ return {
+ x: x,
+ y: y,
+ dx: dx,
+ dy: dy
+ };
+ }
+ d3.random = {
+ normal: function(µ, σ) {
+ var n = arguments.length;
+ if (n < 2) σ = 1;
+ if (n < 1) µ = 0;
+ return function() {
+ var x, y, r;
+ do {
+ x = Math.random() * 2 - 1;
+ y = Math.random() * 2 - 1;
+ r = x * x + y * y;
+ } while (!r || r > 1);
+ return µ + σ * x * Math.sqrt(-2 * Math.log(r) / r);
+ };
+ },
+ logNormal: function() {
+ var random = d3.random.normal.apply(d3, arguments);
+ return function() {
+ return Math.exp(random());
+ };
+ },
+ bates: function(m) {
+ var random = d3.random.irwinHall(m);
+ return function() {
+ return random() / m;
+ };
+ },
+ irwinHall: function(m) {
+ return function() {
+ for (var s = 0, j = 0; j < m; j++) s += Math.random();
+ return s;
+ };
+ }
+ };
+ d3.scale = {};
+ function d3_scaleExtent(domain) {
+ var start = domain[0], stop = domain[domain.length - 1];
+ return start < stop ? [ start, stop ] : [ stop, start ];
+ }
+ function d3_scaleRange(scale) {
+ return scale.rangeExtent ? scale.rangeExtent() : d3_scaleExtent(scale.range());
+ }
+ function d3_scale_bilinear(domain, range, uninterpolate, interpolate) {
+ var u = uninterpolate(domain[0], domain[1]), i = interpolate(range[0], range[1]);
+ return function(x) {
+ return i(u(x));
+ };
+ }
+ function d3_scale_nice(domain, nice) {
+ var i0 = 0, i1 = domain.length - 1, x0 = domain[i0], x1 = domain[i1], dx;
+ if (x1 < x0) {
+ dx = i0, i0 = i1, i1 = dx;
+ dx = x0, x0 = x1, x1 = dx;
+ }
+ domain[i0] = nice.floor(x0);
+ domain[i1] = nice.ceil(x1);
+ return domain;
+ }
+ function d3_scale_niceStep(step) {
+ return step ? {
+ floor: function(x) {
+ return Math.floor(x / step) * step;
+ },
+ ceil: function(x) {
+ return Math.ceil(x / step) * step;
+ }
+ } : d3_scale_niceIdentity;
+ }
+ var d3_scale_niceIdentity = {
+ floor: d3_identity,
+ ceil: d3_identity
+ };
+ function d3_scale_polylinear(domain, range, uninterpolate, interpolate) {
+ var u = [], i = [], j = 0, k = Math.min(domain.length, range.length) - 1;
+ if (domain[k] < domain[0]) {
+ domain = domain.slice().reverse();
+ range = range.slice().reverse();
+ }
+ while (++j <= k) {
+ u.push(uninterpolate(domain[j - 1], domain[j]));
+ i.push(interpolate(range[j - 1], range[j]));
+ }
+ return function(x) {
+ var j = d3.bisect(domain, x, 1, k) - 1;
+ return i[j](u[j](x));
+ };
+ }
+ d3.scale.linear = function() {
+ return d3_scale_linear([ 0, 1 ], [ 0, 1 ], d3_interpolate, false);
+ };
+ function d3_scale_linear(domain, range, interpolate, clamp) {
+ var output, input;
+ function rescale() {
+ var linear = Math.min(domain.length, range.length) > 2 ? d3_scale_polylinear : d3_scale_bilinear, uninterpolate = clamp ? d3_uninterpolateClamp : d3_uninterpolateNumber;
+ output = linear(domain, range, uninterpolate, interpolate);
+ input = linear(range, domain, uninterpolate, d3_interpolate);
+ return scale;
+ }
+ function scale(x) {
+ return output(x);
+ }
+ scale.invert = function(y) {
+ return input(y);
+ };
+ scale.domain = function(x) {
+ if (!arguments.length) return domain;
+ domain = x.map(Number);
+ return rescale();
+ };
+ scale.range = function(x) {
+ if (!arguments.length) return range;
+ range = x;
+ return rescale();
+ };
+ scale.rangeRound = function(x) {
+ return scale.range(x).interpolate(d3_interpolateRound);
+ };
+ scale.clamp = function(x) {
+ if (!arguments.length) return clamp;
+ clamp = x;
+ return rescale();
+ };
+ scale.interpolate = function(x) {
+ if (!arguments.length) return interpolate;
+ interpolate = x;
+ return rescale();
+ };
+ scale.ticks = function(m) {
+ return d3_scale_linearTicks(domain, m);
+ };
+ scale.tickFormat = function(m, format) {
+ return d3_scale_linearTickFormat(domain, m, format);
+ };
+ scale.nice = function(m) {
+ d3_scale_linearNice(domain, m);
+ return rescale();
+ };
+ scale.copy = function() {
+ return d3_scale_linear(domain, range, interpolate, clamp);
+ };
+ return rescale();
+ }
+ function d3_scale_linearRebind(scale, linear) {
+ return d3.rebind(scale, linear, "range", "rangeRound", "interpolate", "clamp");
+ }
+ function d3_scale_linearNice(domain, m) {
+ return d3_scale_nice(domain, d3_scale_niceStep(d3_scale_linearTickRange(domain, m)[2]));
+ }
+ function d3_scale_linearTickRange(domain, m) {
+ if (m == null) m = 10;
+ var extent = d3_scaleExtent(domain), span = extent[1] - extent[0], step = Math.pow(10, Math.floor(Math.log(span / m) / Math.LN10)), err = m / span * step;
+ if (err <= .15) step *= 10; else if (err <= .35) step *= 5; else if (err <= .75) step *= 2;
+ extent[0] = Math.ceil(extent[0] / step) * step;
+ extent[1] = Math.floor(extent[1] / step) * step + step * .5;
+ extent[2] = step;
+ return extent;
+ }
+ function d3_scale_linearTicks(domain, m) {
+ return d3.range.apply(d3, d3_scale_linearTickRange(domain, m));
+ }
+ function d3_scale_linearTickFormat(domain, m, format) {
+ var range = d3_scale_linearTickRange(domain, m);
+ if (format) {
+ var match = d3_format_re.exec(format);
+ match.shift();
+ if (match[8] === "s") {
+ var prefix = d3.formatPrefix(Math.max(abs(range[0]), abs(range[1])));
+ if (!match[7]) match[7] = "." + d3_scale_linearPrecision(prefix.scale(range[2]));
+ match[8] = "f";
+ format = d3.format(match.join(""));
+ return function(d) {
+ return format(prefix.scale(d)) + prefix.symbol;
+ };
+ }
+ if (!match[7]) match[7] = "." + d3_scale_linearFormatPrecision(match[8], range);
+ format = match.join("");
+ } else {
+ format = ",." + d3_scale_linearPrecision(range[2]) + "f";
+ }
+ return d3.format(format);
+ }
+ var d3_scale_linearFormatSignificant = {
+ s: 1,
+ g: 1,
+ p: 1,
+ r: 1,
+ e: 1
+ };
+ function d3_scale_linearPrecision(value) {
+ return -Math.floor(Math.log(value) / Math.LN10 + .01);
+ }
+ function d3_scale_linearFormatPrecision(type, range) {
+ var p = d3_scale_linearPrecision(range[2]);
+ return type in d3_scale_linearFormatSignificant ? Math.abs(p - d3_scale_linearPrecision(Math.max(abs(range[0]), abs(range[1])))) + +(type !== "e") : p - (type === "%") * 2;
+ }
+ d3.scale.log = function() {
+ return d3_scale_log(d3.scale.linear().domain([ 0, 1 ]), 10, true, [ 1, 10 ]);
+ };
+ function d3_scale_log(linear, base, positive, domain) {
+ function log(x) {
+ return (positive ? Math.log(x < 0 ? 0 : x) : -Math.log(x > 0 ? 0 : -x)) / Math.log(base);
+ }
+ function pow(x) {
+ return positive ? Math.pow(base, x) : -Math.pow(base, -x);
+ }
+ function scale(x) {
+ return linear(log(x));
+ }
+ scale.invert = function(x) {
+ return pow(linear.invert(x));
+ };
+ scale.domain = function(x) {
+ if (!arguments.length) return domain;
+ positive = x[0] >= 0;
+ linear.domain((domain = x.map(Number)).map(log));
+ return scale;
+ };
+ scale.base = function(_) {
+ if (!arguments.length) return base;
+ base = +_;
+ linear.domain(domain.map(log));
+ return scale;
+ };
+ scale.nice = function() {
+ var niced = d3_scale_nice(domain.map(log), positive ? Math : d3_scale_logNiceNegative);
+ linear.domain(niced);
+ domain = niced.map(pow);
+ return scale;
+ };
+ scale.ticks = function() {
+ var extent = d3_scaleExtent(domain), ticks = [], u = extent[0], v = extent[1], i = Math.floor(log(u)), j = Math.ceil(log(v)), n = base % 1 ? 2 : base;
+ if (isFinite(j - i)) {
+ if (positive) {
+ for (;i < j; i++) for (var k = 1; k < n; k++) ticks.push(pow(i) * k);
+ ticks.push(pow(i));
+ } else {
+ ticks.push(pow(i));
+ for (;i++ < j; ) for (var k = n - 1; k > 0; k--) ticks.push(pow(i) * k);
+ }
+ for (i = 0; ticks[i] < u; i++) {}
+ for (j = ticks.length; ticks[j - 1] > v; j--) {}
+ ticks = ticks.slice(i, j);
+ }
+ return ticks;
+ };
+ scale.tickFormat = function(n, format) {
+ if (!arguments.length) return d3_scale_logFormat;
+ if (arguments.length < 2) format = d3_scale_logFormat; else if (typeof format !== "function") format = d3.format(format);
+ var k = Math.max(.1, n / scale.ticks().length), f = positive ? (e = 1e-12, Math.ceil) : (e = -1e-12,
+ Math.floor), e;
+ return function(d) {
+ return d / pow(f(log(d) + e)) <= k ? format(d) : "";
+ };
+ };
+ scale.copy = function() {
+ return d3_scale_log(linear.copy(), base, positive, domain);
+ };
+ return d3_scale_linearRebind(scale, linear);
+ }
+ var d3_scale_logFormat = d3.format(".0e"), d3_scale_logNiceNegative = {
+ floor: function(x) {
+ return -Math.ceil(-x);
+ },
+ ceil: function(x) {
+ return -Math.floor(-x);
+ }
+ };
+ d3.scale.pow = function() {
+ return d3_scale_pow(d3.scale.linear(), 1, [ 0, 1 ]);
+ };
+ function d3_scale_pow(linear, exponent, domain) {
+ var powp = d3_scale_powPow(exponent), powb = d3_scale_powPow(1 / exponent);
+ function scale(x) {
+ return linear(powp(x));
+ }
+ scale.invert = function(x) {
+ return powb(linear.invert(x));
+ };
+ scale.domain = function(x) {
+ if (!arguments.length) return domain;
+ linear.domain((domain = x.map(Number)).map(powp));
+ return scale;
+ };
+ scale.ticks = function(m) {
+ return d3_scale_linearTicks(domain, m);
+ };
+ scale.tickFormat = function(m, format) {
+ return d3_scale_linearTickFormat(domain, m, format);
+ };
+ scale.nice = function(m) {
+ return scale.domain(d3_scale_linearNice(domain, m));
+ };
+ scale.exponent = function(x) {
+ if (!arguments.length) return exponent;
+ powp = d3_scale_powPow(exponent = x);
+ powb = d3_scale_powPow(1 / exponent);
+ linear.domain(domain.map(powp));
+ return scale;
+ };
+ scale.copy = function() {
+ return d3_scale_pow(linear.copy(), exponent, domain);
+ };
+ return d3_scale_linearRebind(scale, linear);
+ }
+ function d3_scale_powPow(e) {
+ return function(x) {
+ return x < 0 ? -Math.pow(-x, e) : Math.pow(x, e);
+ };
+ }
+ d3.scale.sqrt = function() {
+ return d3.scale.pow().exponent(.5);
+ };
+ d3.scale.ordinal = function() {
+ return d3_scale_ordinal([], {
+ t: "range",
+ a: [ [] ]
+ });
+ };
+ function d3_scale_ordinal(domain, ranger) {
+ var index, range, rangeBand;
+ function scale(x) {
+ return range[((index.get(x) || (ranger.t === "range" ? index.set(x, domain.push(x)) : NaN)) - 1) % range.length];
+ }
+ function steps(start, step) {
+ return d3.range(domain.length).map(function(i) {
+ return start + step * i;
+ });
+ }
+ scale.domain = function(x) {
+ if (!arguments.length) return domain;
+ domain = [];
+ index = new d3_Map();
+ var i = -1, n = x.length, xi;
+ while (++i < n) if (!index.has(xi = x[i])) index.set(xi, domain.push(xi));
+ return scale[ranger.t].apply(scale, ranger.a);
+ };
+ scale.range = function(x) {
+ if (!arguments.length) return range;
+ range = x;
+ rangeBand = 0;
+ ranger = {
+ t: "range",
+ a: arguments
+ };
+ return scale;
+ };
+ scale.rangePoints = function(x, padding) {
+ if (arguments.length < 2) padding = 0;
+ var start = x[0], stop = x[1], step = (stop - start) / (Math.max(1, domain.length - 1) + padding);
+ range = steps(domain.length < 2 ? (start + stop) / 2 : start + step * padding / 2, step);
+ rangeBand = 0;
+ ranger = {
+ t: "rangePoints",
+ a: arguments
+ };
+ return scale;
+ };
+ scale.rangeBands = function(x, padding, outerPadding) {
+ if (arguments.length < 2) padding = 0;
+ if (arguments.length < 3) outerPadding = padding;
+ var reverse = x[1] < x[0], start = x[reverse - 0], stop = x[1 - reverse], step = (stop - start) / (domain.length - padding + 2 * outerPadding);
+ range = steps(start + step * outerPadding, step);
+ if (reverse) range.reverse();
+ rangeBand = step * (1 - padding);
+ ranger = {
+ t: "rangeBands",
+ a: arguments
+ };
+ return scale;
+ };
+ scale.rangeRoundBands = function(x, padding, outerPadding) {
+ if (arguments.length < 2) padding = 0;
+ if (arguments.length < 3) outerPadding = padding;
+ var reverse = x[1] < x[0], start = x[reverse - 0], stop = x[1 - reverse], step = Math.floor((stop - start) / (domain.length - padding + 2 * outerPadding)), error = stop - start - (domain.length - padding) * step;
+ range = steps(start + Math.round(error / 2), step);
+ if (reverse) range.reverse();
+ rangeBand = Math.round(step * (1 - padding));
+ ranger = {
+ t: "rangeRoundBands",
+ a: arguments
+ };
+ return scale;
+ };
+ scale.rangeBand = function() {
+ return rangeBand;
+ };
+ scale.rangeExtent = function() {
+ return d3_scaleExtent(ranger.a[0]);
+ };
+ scale.copy = function() {
+ return d3_scale_ordinal(domain, ranger);
+ };
+ return scale.domain(domain);
+ }
+ d3.scale.category10 = function() {
+ return d3.scale.ordinal().range(d3_category10);
+ };
+ d3.scale.category20 = function() {
+ return d3.scale.ordinal().range(d3_category20);
+ };
+ d3.scale.category20b = function() {
+ return d3.scale.ordinal().range(d3_category20b);
+ };
+ d3.scale.category20c = function() {
+ return d3.scale.ordinal().range(d3_category20c);
+ };
+ var d3_category10 = [ 2062260, 16744206, 2924588, 14034728, 9725885, 9197131, 14907330, 8355711, 12369186, 1556175 ].map(d3_rgbString);
+ var d3_category20 = [ 2062260, 11454440, 16744206, 16759672, 2924588, 10018698, 14034728, 16750742, 9725885, 12955861, 9197131, 12885140, 14907330, 16234194, 8355711, 13092807, 12369186, 14408589, 1556175, 10410725 ].map(d3_rgbString);
+ var d3_category20b = [ 3750777, 5395619, 7040719, 10264286, 6519097, 9216594, 11915115, 13556636, 9202993, 12426809, 15186514, 15190932, 8666169, 11356490, 14049643, 15177372, 8077683, 10834324, 13528509, 14589654 ].map(d3_rgbString);
+ var d3_category20c = [ 3244733, 7057110, 10406625, 13032431, 15095053, 16616764, 16625259, 16634018, 3253076, 7652470, 10607003, 13101504, 7695281, 10394312, 12369372, 14342891, 6513507, 9868950, 12434877, 14277081 ].map(d3_rgbString);
+ d3.scale.quantile = function() {
+ return d3_scale_quantile([], []);
+ };
+ function d3_scale_quantile(domain, range) {
+ var thresholds;
+ function rescale() {
+ var k = 0, q = range.length;
+ thresholds = [];
+ while (++k < q) thresholds[k - 1] = d3.quantile(domain, k / q);
+ return scale;
+ }
+ function scale(x) {
+ if (!isNaN(x = +x)) return range[d3.bisect(thresholds, x)];
+ }
+ scale.domain = function(x) {
+ if (!arguments.length) return domain;
+ domain = x.filter(function(d) {
+ return !isNaN(d);
+ }).sort(d3_ascending);
+ return rescale();
+ };
+ scale.range = function(x) {
+ if (!arguments.length) return range;
+ range = x;
+ return rescale();
+ };
+ scale.quantiles = function() {
+ return thresholds;
+ };
+ scale.invertExtent = function(y) {
+ y = range.indexOf(y);
+ return y < 0 ? [ NaN, NaN ] : [ y > 0 ? thresholds[y - 1] : domain[0], y < thresholds.length ? thresholds[y] : domain[domain.length - 1] ];
+ };
+ scale.copy = function() {
+ return d3_scale_quantile(domain, range);
+ };
+ return rescale();
+ }
+ d3.scale.quantize = function() {
+ return d3_scale_quantize(0, 1, [ 0, 1 ]);
+ };
+ function d3_scale_quantize(x0, x1, range) {
+ var kx, i;
+ function scale(x) {
+ return range[Math.max(0, Math.min(i, Math.floor(kx * (x - x0))))];
+ }
+ function rescale() {
+ kx = range.length / (x1 - x0);
+ i = range.length - 1;
+ return scale;
+ }
+ scale.domain = function(x) {
+ if (!arguments.length) return [ x0, x1 ];
+ x0 = +x[0];
+ x1 = +x[x.length - 1];
+ return rescale();
+ };
+ scale.range = function(x) {
+ if (!arguments.length) return range;
+ range = x;
+ return rescale();
+ };
+ scale.invertExtent = function(y) {
+ y = range.indexOf(y);
+ y = y < 0 ? NaN : y / kx + x0;
+ return [ y, y + 1 / kx ];
+ };
+ scale.copy = function() {
+ return d3_scale_quantize(x0, x1, range);
+ };
+ return rescale();
+ }
+ d3.scale.threshold = function() {
+ return d3_scale_threshold([ .5 ], [ 0, 1 ]);
+ };
+ function d3_scale_threshold(domain, range) {
+ function scale(x) {
+ if (x <= x) return range[d3.bisect(domain, x)];
+ }
+ scale.domain = function(_) {
+ if (!arguments.length) return domain;
+ domain = _;
+ return scale;
+ };
+ scale.range = function(_) {
+ if (!arguments.length) return range;
+ range = _;
+ return scale;
+ };
+ scale.invertExtent = function(y) {
+ y = range.indexOf(y);
+ return [ domain[y - 1], domain[y] ];
+ };
+ scale.copy = function() {
+ return d3_scale_threshold(domain, range);
+ };
+ return scale;
+ }
+ d3.scale.identity = function() {
+ return d3_scale_identity([ 0, 1 ]);
+ };
+ function d3_scale_identity(domain) {
+ function identity(x) {
+ return +x;
+ }
+ identity.invert = identity;
+ identity.domain = identity.range = function(x) {
+ if (!arguments.length) return domain;
+ domain = x.map(identity);
+ return identity;
+ };
+ identity.ticks = function(m) {
+ return d3_scale_linearTicks(domain, m);
+ };
+ identity.tickFormat = function(m, format) {
+ return d3_scale_linearTickFormat(domain, m, format);
+ };
+ identity.copy = function() {
+ return d3_scale_identity(domain);
+ };
+ return identity;
+ }
+ d3.svg = {};
+ d3.svg.arc = function() {
+ var innerRadius = d3_svg_arcInnerRadius, outerRadius = d3_svg_arcOuterRadius, startAngle = d3_svg_arcStartAngle, endAngle = d3_svg_arcEndAngle;
+ function arc() {
+ var r0 = innerRadius.apply(this, arguments), r1 = outerRadius.apply(this, arguments), a0 = startAngle.apply(this, arguments) + d3_svg_arcOffset, a1 = endAngle.apply(this, arguments) + d3_svg_arcOffset, da = (a1 < a0 && (da = a0,
+ a0 = a1, a1 = da), a1 - a0), df = da < π ? "0" : "1", c0 = Math.cos(a0), s0 = Math.sin(a0), c1 = Math.cos(a1), s1 = Math.sin(a1);
+ return da >= d3_svg_arcMax ? r0 ? "M0," + r1 + "A" + r1 + "," + r1 + " 0 1,1 0," + -r1 + "A" + r1 + "," + r1 + " 0 1,1 0," + r1 + "M0," + r0 + "A" + r0 + "," + r0 + " 0 1,0 0," + -r0 + "A" + r0 + "," + r0 + " 0 1,0 0," + r0 + "Z" : "M0," + r1 + "A" + r1 + "," + r1 + " 0 1,1 0," + -r1 + "A" + r1 + "," + r1 + " 0 1,1 0," + r1 + "Z" : r0 ? "M" + r1 * c0 + "," + r1 * s0 + "A" + r1 + "," + r1 + " 0 " + df + ",1 " + r1 * c1 + "," + r1 * s1 + "L" + r0 * c1 + "," + r0 * s1 + "A" + r0 + "," + r0 + " 0 " + df + ",0 " + r0 * c0 + "," + r0 * s0 + "Z" : "M" + r1 * c0 + "," + r1 * s0 + "A" + r1 + "," + r1 + " 0 " + df + ",1 " + r1 * c1 + "," + r1 * s1 + "L0,0" + "Z";
+ }
+ arc.innerRadius = function(v) {
+ if (!arguments.length) return innerRadius;
+ innerRadius = d3_functor(v);
+ return arc;
+ };
+ arc.outerRadius = function(v) {
+ if (!arguments.length) return outerRadius;
+ outerRadius = d3_functor(v);
+ return arc;
+ };
+ arc.startAngle = function(v) {
+ if (!arguments.length) return startAngle;
+ startAngle = d3_functor(v);
+ return arc;
+ };
+ arc.endAngle = function(v) {
+ if (!arguments.length) return endAngle;
+ endAngle = d3_functor(v);
+ return arc;
+ };
+ arc.centroid = function() {
+ var r = (innerRadius.apply(this, arguments) + outerRadius.apply(this, arguments)) / 2, a = (startAngle.apply(this, arguments) + endAngle.apply(this, arguments)) / 2 + d3_svg_arcOffset;
+ return [ Math.cos(a) * r, Math.sin(a) * r ];
+ };
+ return arc;
+ };
+ var d3_svg_arcOffset = -halfπ, d3_svg_arcMax = τ - ε;
+ function d3_svg_arcInnerRadius(d) {
+ return d.innerRadius;
+ }
+ function d3_svg_arcOuterRadius(d) {
+ return d.outerRadius;
+ }
+ function d3_svg_arcStartAngle(d) {
+ return d.startAngle;
+ }
+ function d3_svg_arcEndAngle(d) {
+ return d.endAngle;
+ }
+ function d3_svg_line(projection) {
+ var x = d3_geom_pointX, y = d3_geom_pointY, defined = d3_true, interpolate = d3_svg_lineLinear, interpolateKey = interpolate.key, tension = .7;
+ function line(data) {
+ var segments = [], points = [], i = -1, n = data.length, d, fx = d3_functor(x), fy = d3_functor(y);
+ function segment() {
+ segments.push("M", interpolate(projection(points), tension));
+ }
+ while (++i < n) {
+ if (defined.call(this, d = data[i], i)) {
+ points.push([ +fx.call(this, d, i), +fy.call(this, d, i) ]);
+ } else if (points.length) {
+ segment();
+ points = [];
+ }
+ }
+ if (points.length) segment();
+ return segments.length ? segments.join("") : null;
+ }
+ line.x = function(_) {
+ if (!arguments.length) return x;
+ x = _;
+ return line;
+ };
+ line.y = function(_) {
+ if (!arguments.length) return y;
+ y = _;
+ return line;
+ };
+ line.defined = function(_) {
+ if (!arguments.length) return defined;
+ defined = _;
+ return line;
+ };
+ line.interpolate = function(_) {
+ if (!arguments.length) return interpolateKey;
+ if (typeof _ === "function") interpolateKey = interpolate = _; else interpolateKey = (interpolate = d3_svg_lineInterpolators.get(_) || d3_svg_lineLinear).key;
+ return line;
+ };
+ line.tension = function(_) {
+ if (!arguments.length) return tension;
+ tension = _;
+ return line;
+ };
+ return line;
+ }
+ d3.svg.line = function() {
+ return d3_svg_line(d3_identity);
+ };
+ var d3_svg_lineInterpolators = d3.map({
+ linear: d3_svg_lineLinear,
+ "linear-closed": d3_svg_lineLinearClosed,
+ step: d3_svg_lineStep,
+ "step-before": d3_svg_lineStepBefore,
+ "step-after": d3_svg_lineStepAfter,
+ basis: d3_svg_lineBasis,
+ "basis-open": d3_svg_lineBasisOpen,
+ "basis-closed": d3_svg_lineBasisClosed,
+ bundle: d3_svg_lineBundle,
+ cardinal: d3_svg_lineCardinal,
+ "cardinal-open": d3_svg_lineCardinalOpen,
+ "cardinal-closed": d3_svg_lineCardinalClosed,
+ monotone: d3_svg_lineMonotone
+ });
+ d3_svg_lineInterpolators.forEach(function(key, value) {
+ value.key = key;
+ value.closed = /-closed$/.test(key);
+ });
+ function d3_svg_lineLinear(points) {
+ return points.join("L");
+ }
+ function d3_svg_lineLinearClosed(points) {
+ return d3_svg_lineLinear(points) + "Z";
+ }
+ function d3_svg_lineStep(points) {
+ var i = 0, n = points.length, p = points[0], path = [ p[0], ",", p[1] ];
+ while (++i < n) path.push("H", (p[0] + (p = points[i])[0]) / 2, "V", p[1]);
+ if (n > 1) path.push("H", p[0]);
+ return path.join("");
+ }
+ function d3_svg_lineStepBefore(points) {
+ var i = 0, n = points.length, p = points[0], path = [ p[0], ",", p[1] ];
+ while (++i < n) path.push("V", (p = points[i])[1], "H", p[0]);
+ return path.join("");
+ }
+ function d3_svg_lineStepAfter(points) {
+ var i = 0, n = points.length, p = points[0], path = [ p[0], ",", p[1] ];
+ while (++i < n) path.push("H", (p = points[i])[0], "V", p[1]);
+ return path.join("");
+ }
+ function d3_svg_lineCardinalOpen(points, tension) {
+ return points.length < 4 ? d3_svg_lineLinear(points) : points[1] + d3_svg_lineHermite(points.slice(1, points.length - 1), d3_svg_lineCardinalTangents(points, tension));
+ }
+ function d3_svg_lineCardinalClosed(points, tension) {
+ return points.length < 3 ? d3_svg_lineLinear(points) : points[0] + d3_svg_lineHermite((points.push(points[0]),
+ points), d3_svg_lineCardinalTangents([ points[points.length - 2] ].concat(points, [ points[1] ]), tension));
+ }
+ function d3_svg_lineCardinal(points, tension) {
+ return points.length < 3 ? d3_svg_lineLinear(points) : points[0] + d3_svg_lineHermite(points, d3_svg_lineCardinalTangents(points, tension));
+ }
+ function d3_svg_lineHermite(points, tangents) {
+ if (tangents.length < 1 || points.length != tangents.length && points.length != tangents.length + 2) {
+ return d3_svg_lineLinear(points);
+ }
+ var quad = points.length != tangents.length, path = "", p0 = points[0], p = points[1], t0 = tangents[0], t = t0, pi = 1;
+ if (quad) {
+ path += "Q" + (p[0] - t0[0] * 2 / 3) + "," + (p[1] - t0[1] * 2 / 3) + "," + p[0] + "," + p[1];
+ p0 = points[1];
+ pi = 2;
+ }
+ if (tangents.length > 1) {
+ t = tangents[1];
+ p = points[pi];
+ pi++;
+ path += "C" + (p0[0] + t0[0]) + "," + (p0[1] + t0[1]) + "," + (p[0] - t[0]) + "," + (p[1] - t[1]) + "," + p[0] + "," + p[1];
+ for (var i = 2; i < tangents.length; i++, pi++) {
+ p = points[pi];
+ t = tangents[i];
+ path += "S" + (p[0] - t[0]) + "," + (p[1] - t[1]) + "," + p[0] + "," + p[1];
+ }
+ }
+ if (quad) {
+ var lp = points[pi];
+ path += "Q" + (p[0] + t[0] * 2 / 3) + "," + (p[1] + t[1] * 2 / 3) + "," + lp[0] + "," + lp[1];
+ }
+ return path;
+ }
+ function d3_svg_lineCardinalTangents(points, tension) {
+ var tangents = [], a = (1 - tension) / 2, p0, p1 = points[0], p2 = points[1], i = 1, n = points.length;
+ while (++i < n) {
+ p0 = p1;
+ p1 = p2;
+ p2 = points[i];
+ tangents.push([ a * (p2[0] - p0[0]), a * (p2[1] - p0[1]) ]);
+ }
+ return tangents;
+ }
+ function d3_svg_lineBasis(points) {
+ if (points.length < 3) return d3_svg_lineLinear(points);
+ var i = 1, n = points.length, pi = points[0], x0 = pi[0], y0 = pi[1], px = [ x0, x0, x0, (pi = points[1])[0] ], py = [ y0, y0, y0, pi[1] ], path = [ x0, ",", y0, "L", d3_svg_lineDot4(d3_svg_lineBasisBezier3, px), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, py) ];
+ points.push(points[n - 1]);
+ while (++i <= n) {
+ pi = points[i];
+ px.shift();
+ px.push(pi[0]);
+ py.shift();
+ py.push(pi[1]);
+ d3_svg_lineBasisBezier(path, px, py);
+ }
+ points.pop();
+ path.push("L", pi);
+ return path.join("");
+ }
+ function d3_svg_lineBasisOpen(points) {
+ if (points.length < 4) return d3_svg_lineLinear(points);
+ var path = [], i = -1, n = points.length, pi, px = [ 0 ], py = [ 0 ];
+ while (++i < 3) {
+ pi = points[i];
+ px.push(pi[0]);
+ py.push(pi[1]);
+ }
+ path.push(d3_svg_lineDot4(d3_svg_lineBasisBezier3, px) + "," + d3_svg_lineDot4(d3_svg_lineBasisBezier3, py));
+ --i;
+ while (++i < n) {
+ pi = points[i];
+ px.shift();
+ px.push(pi[0]);
+ py.shift();
+ py.push(pi[1]);
+ d3_svg_lineBasisBezier(path, px, py);
+ }
+ return path.join("");
+ }
+ function d3_svg_lineBasisClosed(points) {
+ var path, i = -1, n = points.length, m = n + 4, pi, px = [], py = [];
+ while (++i < 4) {
+ pi = points[i % n];
+ px.push(pi[0]);
+ py.push(pi[1]);
+ }
+ path = [ d3_svg_lineDot4(d3_svg_lineBasisBezier3, px), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, py) ];
+ --i;
+ while (++i < m) {
+ pi = points[i % n];
+ px.shift();
+ px.push(pi[0]);
+ py.shift();
+ py.push(pi[1]);
+ d3_svg_lineBasisBezier(path, px, py);
+ }
+ return path.join("");
+ }
+ function d3_svg_lineBundle(points, tension) {
+ var n = points.length - 1;
+ if (n) {
+ var x0 = points[0][0], y0 = points[0][1], dx = points[n][0] - x0, dy = points[n][1] - y0, i = -1, p, t;
+ while (++i <= n) {
+ p = points[i];
+ t = i / n;
+ p[0] = tension * p[0] + (1 - tension) * (x0 + t * dx);
+ p[1] = tension * p[1] + (1 - tension) * (y0 + t * dy);
+ }
+ }
+ return d3_svg_lineBasis(points);
+ }
+ function d3_svg_lineDot4(a, b) {
+ return a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3];
+ }
+ var d3_svg_lineBasisBezier1 = [ 0, 2 / 3, 1 / 3, 0 ], d3_svg_lineBasisBezier2 = [ 0, 1 / 3, 2 / 3, 0 ], d3_svg_lineBasisBezier3 = [ 0, 1 / 6, 2 / 3, 1 / 6 ];
+ function d3_svg_lineBasisBezier(path, x, y) {
+ path.push("C", d3_svg_lineDot4(d3_svg_lineBasisBezier1, x), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier1, y), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier2, x), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier2, y), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, x), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, y));
+ }
+ function d3_svg_lineSlope(p0, p1) {
+ return (p1[1] - p0[1]) / (p1[0] - p0[0]);
+ }
+ function d3_svg_lineFiniteDifferences(points) {
+ var i = 0, j = points.length - 1, m = [], p0 = points[0], p1 = points[1], d = m[0] = d3_svg_lineSlope(p0, p1);
+ while (++i < j) {
+ m[i] = (d + (d = d3_svg_lineSlope(p0 = p1, p1 = points[i + 1]))) / 2;
+ }
+ m[i] = d;
+ return m;
+ }
+ function d3_svg_lineMonotoneTangents(points) {
+ var tangents = [], d, a, b, s, m = d3_svg_lineFiniteDifferences(points), i = -1, j = points.length - 1;
+ while (++i < j) {
+ d = d3_svg_lineSlope(points[i], points[i + 1]);
+ if (abs(d) < ε) {
+ m[i] = m[i + 1] = 0;
+ } else {
+ a = m[i] / d;
+ b = m[i + 1] / d;
+ s = a * a + b * b;
+ if (s > 9) {
+ s = d * 3 / Math.sqrt(s);
+ m[i] = s * a;
+ m[i + 1] = s * b;
+ }
+ }
+ }
+ i = -1;
+ while (++i <= j) {
+ s = (points[Math.min(j, i + 1)][0] - points[Math.max(0, i - 1)][0]) / (6 * (1 + m[i] * m[i]));
+ tangents.push([ s || 0, m[i] * s || 0 ]);
+ }
+ return tangents;
+ }
+ function d3_svg_lineMonotone(points) {
+ return points.length < 3 ? d3_svg_lineLinear(points) : points[0] + d3_svg_lineHermite(points, d3_svg_lineMonotoneTangents(points));
+ }
+ d3.svg.line.radial = function() {
+ var line = d3_svg_line(d3_svg_lineRadial);
+ line.radius = line.x, delete line.x;
+ line.angle = line.y, delete line.y;
+ return line;
+ };
+ function d3_svg_lineRadial(points) {
+ var point, i = -1, n = points.length, r, a;
+ while (++i < n) {
+ point = points[i];
+ r = point[0];
+ a = point[1] + d3_svg_arcOffset;
+ point[0] = r * Math.cos(a);
+ point[1] = r * Math.sin(a);
+ }
+ return points;
+ }
+ function d3_svg_area(projection) {
+ var x0 = d3_geom_pointX, x1 = d3_geom_pointX, y0 = 0, y1 = d3_geom_pointY, defined = d3_true, interpolate = d3_svg_lineLinear, interpolateKey = interpolate.key, interpolateReverse = interpolate, L = "L", tension = .7;
+ function area(data) {
+ var segments = [], points0 = [], points1 = [], i = -1, n = data.length, d, fx0 = d3_functor(x0), fy0 = d3_functor(y0), fx1 = x0 === x1 ? function() {
+ return x;
+ } : d3_functor(x1), fy1 = y0 === y1 ? function() {
+ return y;
+ } : d3_functor(y1), x, y;
+ function segment() {
+ segments.push("M", interpolate(projection(points1), tension), L, interpolateReverse(projection(points0.reverse()), tension), "Z");
+ }
+ while (++i < n) {
+ if (defined.call(this, d = data[i], i)) {
+ points0.push([ x = +fx0.call(this, d, i), y = +fy0.call(this, d, i) ]);
+ points1.push([ +fx1.call(this, d, i), +fy1.call(this, d, i) ]);
+ } else if (points0.length) {
+ segment();
+ points0 = [];
+ points1 = [];
+ }
+ }
+ if (points0.length) segment();
+ return segments.length ? segments.join("") : null;
+ }
+ area.x = function(_) {
+ if (!arguments.length) return x1;
+ x0 = x1 = _;
+ return area;
+ };
+ area.x0 = function(_) {
+ if (!arguments.length) return x0;
+ x0 = _;
+ return area;
+ };
+ area.x1 = function(_) {
+ if (!arguments.length) return x1;
+ x1 = _;
+ return area;
+ };
+ area.y = function(_) {
+ if (!arguments.length) return y1;
+ y0 = y1 = _;
+ return area;
+ };
+ area.y0 = function(_) {
+ if (!arguments.length) return y0;
+ y0 = _;
+ return area;
+ };
+ area.y1 = function(_) {
+ if (!arguments.length) return y1;
+ y1 = _;
+ return area;
+ };
+ area.defined = function(_) {
+ if (!arguments.length) return defined;
+ defined = _;
+ return area;
+ };
+ area.interpolate = function(_) {
+ if (!arguments.length) return interpolateKey;
+ if (typeof _ === "function") interpolateKey = interpolate = _; else interpolateKey = (interpolate = d3_svg_lineInterpolators.get(_) || d3_svg_lineLinear).key;
+ interpolateReverse = interpolate.reverse || interpolate;
+ L = interpolate.closed ? "M" : "L";
+ return area;
+ };
+ area.tension = function(_) {
+ if (!arguments.length) return tension;
+ tension = _;
+ return area;
+ };
+ return area;
+ }
+ d3_svg_lineStepBefore.reverse = d3_svg_lineStepAfter;
+ d3_svg_lineStepAfter.reverse = d3_svg_lineStepBefore;
+ d3.svg.area = function() {
+ return d3_svg_area(d3_identity);
+ };
+ d3.svg.area.radial = function() {
+ var area = d3_svg_area(d3_svg_lineRadial);
+ area.radius = area.x, delete area.x;
+ area.innerRadius = area.x0, delete area.x0;
+ area.outerRadius = area.x1, delete area.x1;
+ area.angle = area.y, delete area.y;
+ area.startAngle = area.y0, delete area.y0;
+ area.endAngle = area.y1, delete area.y1;
+ return area;
+ };
+ d3.svg.chord = function() {
+ var source = d3_source, target = d3_target, radius = d3_svg_chordRadius, startAngle = d3_svg_arcStartAngle, endAngle = d3_svg_arcEndAngle;
+ function chord(d, i) {
+ var s = subgroup(this, source, d, i), t = subgroup(this, target, d, i);
+ return "M" + s.p0 + arc(s.r, s.p1, s.a1 - s.a0) + (equals(s, t) ? curve(s.r, s.p1, s.r, s.p0) : curve(s.r, s.p1, t.r, t.p0) + arc(t.r, t.p1, t.a1 - t.a0) + curve(t.r, t.p1, s.r, s.p0)) + "Z";
+ }
+ function subgroup(self, f, d, i) {
+ var subgroup = f.call(self, d, i), r = radius.call(self, subgroup, i), a0 = startAngle.call(self, subgroup, i) + d3_svg_arcOffset, a1 = endAngle.call(self, subgroup, i) + d3_svg_arcOffset;
+ return {
+ r: r,
+ a0: a0,
+ a1: a1,
+ p0: [ r * Math.cos(a0), r * Math.sin(a0) ],
+ p1: [ r * Math.cos(a1), r * Math.sin(a1) ]
+ };
+ }
+ function equals(a, b) {
+ return a.a0 == b.a0 && a.a1 == b.a1;
+ }
+ function arc(r, p, a) {
+ return "A" + r + "," + r + " 0 " + +(a > π) + ",1 " + p;
+ }
+ function curve(r0, p0, r1, p1) {
+ return "Q 0,0 " + p1;
+ }
+ chord.radius = function(v) {
+ if (!arguments.length) return radius;
+ radius = d3_functor(v);
+ return chord;
+ };
+ chord.source = function(v) {
+ if (!arguments.length) return source;
+ source = d3_functor(v);
+ return chord;
+ };
+ chord.target = function(v) {
+ if (!arguments.length) return target;
+ target = d3_functor(v);
+ return chord;
+ };
+ chord.startAngle = function(v) {
+ if (!arguments.length) return startAngle;
+ startAngle = d3_functor(v);
+ return chord;
+ };
+ chord.endAngle = function(v) {
+ if (!arguments.length) return endAngle;
+ endAngle = d3_functor(v);
+ return chord;
+ };
+ return chord;
+ };
+ function d3_svg_chordRadius(d) {
+ return d.radius;
+ }
+ d3.svg.diagonal = function() {
+ var source = d3_source, target = d3_target, projection = d3_svg_diagonalProjection;
+ function diagonal(d, i) {
+ var p0 = source.call(this, d, i), p3 = target.call(this, d, i), m = (p0.y + p3.y) / 2, p = [ p0, {
+ x: p0.x,
+ y: m
+ }, {
+ x: p3.x,
+ y: m
+ }, p3 ];
+ p = p.map(projection);
+ return "M" + p[0] + "C" + p[1] + " " + p[2] + " " + p[3];
+ }
+ diagonal.source = function(x) {
+ if (!arguments.length) return source;
+ source = d3_functor(x);
+ return diagonal;
+ };
+ diagonal.target = function(x) {
+ if (!arguments.length) return target;
+ target = d3_functor(x);
+ return diagonal;
+ };
+ diagonal.projection = function(x) {
+ if (!arguments.length) return projection;
+ projection = x;
+ return diagonal;
+ };
+ return diagonal;
+ };
+ function d3_svg_diagonalProjection(d) {
+ return [ d.x, d.y ];
+ }
+ d3.svg.diagonal.radial = function() {
+ var diagonal = d3.svg.diagonal(), projection = d3_svg_diagonalProjection, projection_ = diagonal.projection;
+ diagonal.projection = function(x) {
+ return arguments.length ? projection_(d3_svg_diagonalRadialProjection(projection = x)) : projection;
+ };
+ return diagonal;
+ };
+ function d3_svg_diagonalRadialProjection(projection) {
+ return function() {
+ var d = projection.apply(this, arguments), r = d[0], a = d[1] + d3_svg_arcOffset;
+ return [ r * Math.cos(a), r * Math.sin(a) ];
+ };
+ }
+ d3.svg.symbol = function() {
+ var type = d3_svg_symbolType, size = d3_svg_symbolSize;
+ function symbol(d, i) {
+ return (d3_svg_symbols.get(type.call(this, d, i)) || d3_svg_symbolCircle)(size.call(this, d, i));
+ }
+ symbol.type = function(x) {
+ if (!arguments.length) return type;
+ type = d3_functor(x);
+ return symbol;
+ };
+ symbol.size = function(x) {
+ if (!arguments.length) return size;
+ size = d3_functor(x);
+ return symbol;
+ };
+ return symbol;
+ };
+ function d3_svg_symbolSize() {
+ return 64;
+ }
+ function d3_svg_symbolType() {
+ return "circle";
+ }
+ function d3_svg_symbolCircle(size) {
+ var r = Math.sqrt(size / π);
+ return "M0," + r + "A" + r + "," + r + " 0 1,1 0," + -r + "A" + r + "," + r + " 0 1,1 0," + r + "Z";
+ }
+ var d3_svg_symbols = d3.map({
+ circle: d3_svg_symbolCircle,
+ cross: function(size) {
+ var r = Math.sqrt(size / 5) / 2;
+ return "M" + -3 * r + "," + -r + "H" + -r + "V" + -3 * r + "H" + r + "V" + -r + "H" + 3 * r + "V" + r + "H" + r + "V" + 3 * r + "H" + -r + "V" + r + "H" + -3 * r + "Z";
+ },
+ diamond: function(size) {
+ var ry = Math.sqrt(size / (2 * d3_svg_symbolTan30)), rx = ry * d3_svg_symbolTan30;
+ return "M0," + -ry + "L" + rx + ",0" + " 0," + ry + " " + -rx + ",0" + "Z";
+ },
+ square: function(size) {
+ var r = Math.sqrt(size) / 2;
+ return "M" + -r + "," + -r + "L" + r + "," + -r + " " + r + "," + r + " " + -r + "," + r + "Z";
+ },
+ "triangle-down": function(size) {
+ var rx = Math.sqrt(size / d3_svg_symbolSqrt3), ry = rx * d3_svg_symbolSqrt3 / 2;
+ return "M0," + ry + "L" + rx + "," + -ry + " " + -rx + "," + -ry + "Z";
+ },
+ "triangle-up": function(size) {
+ var rx = Math.sqrt(size / d3_svg_symbolSqrt3), ry = rx * d3_svg_symbolSqrt3 / 2;
+ return "M0," + -ry + "L" + rx + "," + ry + " " + -rx + "," + ry + "Z";
+ }
+ });
+ d3.svg.symbolTypes = d3_svg_symbols.keys();
+ var d3_svg_symbolSqrt3 = Math.sqrt(3), d3_svg_symbolTan30 = Math.tan(30 * d3_radians);
+ function d3_transition(groups, id) {
+ d3_subclass(groups, d3_transitionPrototype);
+ groups.id = id;
+ return groups;
+ }
+ var d3_transitionPrototype = [], d3_transitionId = 0, d3_transitionInheritId, d3_transitionInherit;
+ d3_transitionPrototype.call = d3_selectionPrototype.call;
+ d3_transitionPrototype.empty = d3_selectionPrototype.empty;
+ d3_transitionPrototype.node = d3_selectionPrototype.node;
+ d3_transitionPrototype.size = d3_selectionPrototype.size;
+ d3.transition = function(selection) {
+ return arguments.length ? d3_transitionInheritId ? selection.transition() : selection : d3_selectionRoot.transition();
+ };
+ d3.transition.prototype = d3_transitionPrototype;
+ d3_transitionPrototype.select = function(selector) {
+ var id = this.id, subgroups = [], subgroup, subnode, node;
+ selector = d3_selection_selector(selector);
+ for (var j = -1, m = this.length; ++j < m; ) {
+ subgroups.push(subgroup = []);
+ for (var group = this[j], i = -1, n = group.length; ++i < n; ) {
+ if ((node = group[i]) && (subnode = selector.call(node, node.__data__, i, j))) {
+ if ("__data__" in node) subnode.__data__ = node.__data__;
+ d3_transitionNode(subnode, i, id, node.__transition__[id]);
+ subgroup.push(subnode);
+ } else {
+ subgroup.push(null);
+ }
+ }
+ }
+ return d3_transition(subgroups, id);
+ };
+ d3_transitionPrototype.selectAll = function(selector) {
+ var id = this.id, subgroups = [], subgroup, subnodes, node, subnode, transition;
+ selector = d3_selection_selectorAll(selector);
+ for (var j = -1, m = this.length; ++j < m; ) {
+ for (var group = this[j], i = -1, n = group.length; ++i < n; ) {
+ if (node = group[i]) {
+ transition = node.__transition__[id];
+ subnodes = selector.call(node, node.__data__, i, j);
+ subgroups.push(subgroup = []);
+ for (var k = -1, o = subnodes.length; ++k < o; ) {
+ if (subnode = subnodes[k]) d3_transitionNode(subnode, k, id, transition);
+ subgroup.push(subnode);
+ }
+ }
+ }
+ }
+ return d3_transition(subgroups, id);
+ };
+ d3_transitionPrototype.filter = function(filter) {
+ var subgroups = [], subgroup, group, node;
+ if (typeof filter !== "function") filter = d3_selection_filter(filter);
+ for (var j = 0, m = this.length; j < m; j++) {
+ subgroups.push(subgroup = []);
+ for (var group = this[j], i = 0, n = group.length; i < n; i++) {
+ if ((node = group[i]) && filter.call(node, node.__data__, i, j)) {
+ subgroup.push(node);
+ }
+ }
+ }
+ return d3_transition(subgroups, this.id);
+ };
+ d3_transitionPrototype.tween = function(name, tween) {
+ var id = this.id;
+ if (arguments.length < 2) return this.node().__transition__[id].tween.get(name);
+ return d3_selection_each(this, tween == null ? function(node) {
+ node.__transition__[id].tween.remove(name);
+ } : function(node) {
+ node.__transition__[id].tween.set(name, tween);
+ });
+ };
+ function d3_transition_tween(groups, name, value, tween) {
+ var id = groups.id;
+ return d3_selection_each(groups, typeof value === "function" ? function(node, i, j) {
+ node.__transition__[id].tween.set(name, tween(value.call(node, node.__data__, i, j)));
+ } : (value = tween(value), function(node) {
+ node.__transition__[id].tween.set(name, value);
+ }));
+ }
+ d3_transitionPrototype.attr = function(nameNS, value) {
+ if (arguments.length < 2) {
+ for (value in nameNS) this.attr(value, nameNS[value]);
+ return this;
+ }
+ var interpolate = nameNS == "transform" ? d3_interpolateTransform : d3_interpolate, name = d3.ns.qualify(nameNS);
+ function attrNull() {
+ this.removeAttribute(name);
+ }
+ function attrNullNS() {
+ this.removeAttributeNS(name.space, name.local);
+ }
+ function attrTween(b) {
+ return b == null ? attrNull : (b += "", function() {
+ var a = this.getAttribute(name), i;
+ return a !== b && (i = interpolate(a, b), function(t) {
+ this.setAttribute(name, i(t));
+ });
+ });
+ }
+ function attrTweenNS(b) {
+ return b == null ? attrNullNS : (b += "", function() {
+ var a = this.getAttributeNS(name.space, name.local), i;
+ return a !== b && (i = interpolate(a, b), function(t) {
+ this.setAttributeNS(name.space, name.local, i(t));
+ });
+ });
+ }
+ return d3_transition_tween(this, "attr." + nameNS, value, name.local ? attrTweenNS : attrTween);
+ };
+ d3_transitionPrototype.attrTween = function(nameNS, tween) {
+ var name = d3.ns.qualify(nameNS);
+ function attrTween(d, i) {
+ var f = tween.call(this, d, i, this.getAttribute(name));
+ return f && function(t) {
+ this.setAttribute(name, f(t));
+ };
+ }
+ function attrTweenNS(d, i) {
+ var f = tween.call(this, d, i, this.getAttributeNS(name.space, name.local));
+ return f && function(t) {
+ this.setAttributeNS(name.space, name.local, f(t));
+ };
+ }
+ return this.tween("attr." + nameNS, name.local ? attrTweenNS : attrTween);
+ };
+ d3_transitionPrototype.style = function(name, value, priority) {
+ var n = arguments.length;
+ if (n < 3) {
+ if (typeof name !== "string") {
+ if (n < 2) value = "";
+ for (priority in name) this.style(priority, name[priority], value);
+ return this;
+ }
+ priority = "";
+ }
+ function styleNull() {
+ this.style.removeProperty(name);
+ }
+ function styleString(b) {
+ return b == null ? styleNull : (b += "", function() {
+ var a = d3_window.getComputedStyle(this, null).getPropertyValue(name), i;
+ return a !== b && (i = d3_interpolate(a, b), function(t) {
+ this.style.setProperty(name, i(t), priority);
+ });
+ });
+ }
+ return d3_transition_tween(this, "style." + name, value, styleString);
+ };
+ d3_transitionPrototype.styleTween = function(name, tween, priority) {
+ if (arguments.length < 3) priority = "";
+ function styleTween(d, i) {
+ var f = tween.call(this, d, i, d3_window.getComputedStyle(this, null).getPropertyValue(name));
+ return f && function(t) {
+ this.style.setProperty(name, f(t), priority);
+ };
+ }
+ return this.tween("style." + name, styleTween);
+ };
+ d3_transitionPrototype.text = function(value) {
+ return d3_transition_tween(this, "text", value, d3_transition_text);
+ };
+ function d3_transition_text(b) {
+ if (b == null) b = "";
+ return function() {
+ this.textContent = b;
+ };
+ }
+ d3_transitionPrototype.remove = function() {
+ return this.each("end.transition", function() {
+ var p;
+ if (this.__transition__.count < 2 && (p = this.parentNode)) p.removeChild(this);
+ });
+ };
+ d3_transitionPrototype.ease = function(value) {
+ var id = this.id;
+ if (arguments.length < 1) return this.node().__transition__[id].ease;
+ if (typeof value !== "function") value = d3.ease.apply(d3, arguments);
+ return d3_selection_each(this, function(node) {
+ node.__transition__[id].ease = value;
+ });
+ };
+ d3_transitionPrototype.delay = function(value) {
+ var id = this.id;
+ if (arguments.length < 1) return this.node().__transition__[id].delay;
+ return d3_selection_each(this, typeof value === "function" ? function(node, i, j) {
+ node.__transition__[id].delay = +value.call(node, node.__data__, i, j);
+ } : (value = +value, function(node) {
+ node.__transition__[id].delay = value;
+ }));
+ };
+ d3_transitionPrototype.duration = function(value) {
+ var id = this.id;
+ if (arguments.length < 1) return this.node().__transition__[id].duration;
+ return d3_selection_each(this, typeof value === "function" ? function(node, i, j) {
+ node.__transition__[id].duration = Math.max(1, value.call(node, node.__data__, i, j));
+ } : (value = Math.max(1, value), function(node) {
+ node.__transition__[id].duration = value;
+ }));
+ };
+ d3_transitionPrototype.each = function(type, listener) {
+ var id = this.id;
+ if (arguments.length < 2) {
+ var inherit = d3_transitionInherit, inheritId = d3_transitionInheritId;
+ d3_transitionInheritId = id;
+ d3_selection_each(this, function(node, i, j) {
+ d3_transitionInherit = node.__transition__[id];
+ type.call(node, node.__data__, i, j);
+ });
+ d3_transitionInherit = inherit;
+ d3_transitionInheritId = inheritId;
+ } else {
+ d3_selection_each(this, function(node) {
+ var transition = node.__transition__[id];
+ (transition.event || (transition.event = d3.dispatch("start", "end"))).on(type, listener);
+ });
+ }
+ return this;
+ };
+ d3_transitionPrototype.transition = function() {
+ var id0 = this.id, id1 = ++d3_transitionId, subgroups = [], subgroup, group, node, transition;
+ for (var j = 0, m = this.length; j < m; j++) {
+ subgroups.push(subgroup = []);
+ for (var group = this[j], i = 0, n = group.length; i < n; i++) {
+ if (node = group[i]) {
+ transition = Object.create(node.__transition__[id0]);
+ transition.delay += transition.duration;
+ d3_transitionNode(node, i, id1, transition);
+ }
+ subgroup.push(node);
+ }
+ }
+ return d3_transition(subgroups, id1);
+ };
+ function d3_transitionNode(node, i, id, inherit) {
+ var lock = node.__transition__ || (node.__transition__ = {
+ active: 0,
+ count: 0
+ }), transition = lock[id];
+ if (!transition) {
+ var time = inherit.time;
+ transition = lock[id] = {
+ tween: new d3_Map(),
+ time: time,
+ ease: inherit.ease,
+ delay: inherit.delay,
+ duration: inherit.duration
+ };
+ ++lock.count;
+ d3.timer(function(elapsed) {
+ var d = node.__data__, ease = transition.ease, delay = transition.delay, duration = transition.duration, timer = d3_timer_active, tweened = [];
+ timer.t = delay + time;
+ if (delay <= elapsed) return start(elapsed - delay);
+ timer.c = start;
+ function start(elapsed) {
+ if (lock.active > id) return stop();
+ lock.active = id;
+ transition.event && transition.event.start.call(node, d, i);
+ transition.tween.forEach(function(key, value) {
+ if (value = value.call(node, d, i)) {
+ tweened.push(value);
+ }
+ });
+ d3.timer(function() {
+ timer.c = tick(elapsed || 1) ? d3_true : tick;
+ return 1;
+ }, 0, time);
+ }
+ function tick(elapsed) {
+ if (lock.active !== id) return stop();
+ var t = elapsed / duration, e = ease(t), n = tweened.length;
+ while (n > 0) {
+ tweened[--n].call(node, e);
+ }
+ if (t >= 1) {
+ transition.event && transition.event.end.call(node, d, i);
+ return stop();
+ }
+ }
+ function stop() {
+ if (--lock.count) delete lock[id]; else delete node.__transition__;
+ return 1;
+ }
+ }, 0, time);
+ }
+ }
+ d3.svg.axis = function() {
+ var scale = d3.scale.linear(), orient = d3_svg_axisDefaultOrient, innerTickSize = 6, outerTickSize = 6, tickPadding = 3, tickArguments_ = [ 10 ], tickValues = null, tickFormat_;
+ function axis(g) {
+ g.each(function() {
+ var g = d3.select(this);
+ var scale0 = this.__chart__ || scale, scale1 = this.__chart__ = scale.copy();
+ var ticks = tickValues == null ? scale1.ticks ? scale1.ticks.apply(scale1, tickArguments_) : scale1.domain() : tickValues, tickFormat = tickFormat_ == null ? scale1.tickFormat ? scale1.tickFormat.apply(scale1, tickArguments_) : d3_identity : tickFormat_, tick = g.selectAll(".tick").data(ticks, scale1), tickEnter = tick.enter().insert("g", ".domain").attr("class", "tick").style("opacity", ε), tickExit = d3.transition(tick.exit()).style("opacity", ε).remove(), tickUpdate = d3.transition(tick.order()).style("opacity", 1), tickTransform;
+ var range = d3_scaleRange(scale1), path = g.selectAll(".domain").data([ 0 ]), pathUpdate = (path.enter().append("path").attr("class", "domain"),
+ d3.transition(path));
+ tickEnter.append("line");
+ tickEnter.append("text");
+ var lineEnter = tickEnter.select("line"), lineUpdate = tickUpdate.select("line"), text = tick.select("text").text(tickFormat), textEnter = tickEnter.select("text"), textUpdate = tickUpdate.select("text");
+ switch (orient) {
+ case "bottom":
+ {
+ tickTransform = d3_svg_axisX;
+ lineEnter.attr("y2", innerTickSize);
+ textEnter.attr("y", Math.max(innerTickSize, 0) + tickPadding);
+ lineUpdate.attr("x2", 0).attr("y2", innerTickSize);
+ textUpdate.attr("x", 0).attr("y", Math.max(innerTickSize, 0) + tickPadding);
+ text.attr("dy", ".71em").style("text-anchor", "middle");
+ pathUpdate.attr("d", "M" + range[0] + "," + outerTickSize + "V0H" + range[1] + "V" + outerTickSize);
+ break;
+ }
+
+ case "top":
+ {
+ tickTransform = d3_svg_axisX;
+ lineEnter.attr("y2", -innerTickSize);
+ textEnter.attr("y", -(Math.max(innerTickSize, 0) + tickPadding));
+ lineUpdate.attr("x2", 0).attr("y2", -innerTickSize);
+ textUpdate.attr("x", 0).attr("y", -(Math.max(innerTickSize, 0) + tickPadding));
+ text.attr("dy", "0em").style("text-anchor", "middle");
+ pathUpdate.attr("d", "M" + range[0] + "," + -outerTickSize + "V0H" + range[1] + "V" + -outerTickSize);
+ break;
+ }
+
+ case "left":
+ {
+ tickTransform = d3_svg_axisY;
+ lineEnter.attr("x2", -innerTickSize);
+ textEnter.attr("x", -(Math.max(innerTickSize, 0) + tickPadding));
+ lineUpdate.attr("x2", -innerTickSize).attr("y2", 0);
+ textUpdate.attr("x", -(Math.max(innerTickSize, 0) + tickPadding)).attr("y", 0);
+ text.attr("dy", ".32em").style("text-anchor", "end");
+ pathUpdate.attr("d", "M" + -outerTickSize + "," + range[0] + "H0V" + range[1] + "H" + -outerTickSize);
+ break;
+ }
+
+ case "right":
+ {
+ tickTransform = d3_svg_axisY;
+ lineEnter.attr("x2", innerTickSize);
+ textEnter.attr("x", Math.max(innerTickSize, 0) + tickPadding);
+ lineUpdate.attr("x2", innerTickSize).attr("y2", 0);
+ textUpdate.attr("x", Math.max(innerTickSize, 0) + tickPadding).attr("y", 0);
+ text.attr("dy", ".32em").style("text-anchor", "start");
+ pathUpdate.attr("d", "M" + outerTickSize + "," + range[0] + "H0V" + range[1] + "H" + outerTickSize);
+ break;
+ }
+ }
+ if (scale1.rangeBand) {
+ var x = scale1, dx = x.rangeBand() / 2;
+ scale0 = scale1 = function(d) {
+ return x(d) + dx;
+ };
+ } else if (scale0.rangeBand) {
+ scale0 = scale1;
+ } else {
+ tickExit.call(tickTransform, scale1);
+ }
+ tickEnter.call(tickTransform, scale0);
+ tickUpdate.call(tickTransform, scale1);
+ });
+ }
+ axis.scale = function(x) {
+ if (!arguments.length) return scale;
+ scale = x;
+ return axis;
+ };
+ axis.orient = function(x) {
+ if (!arguments.length) return orient;
+ orient = x in d3_svg_axisOrients ? x + "" : d3_svg_axisDefaultOrient;
+ return axis;
+ };
+ axis.ticks = function() {
+ if (!arguments.length) return tickArguments_;
+ tickArguments_ = arguments;
+ return axis;
+ };
+ axis.tickValues = function(x) {
+ if (!arguments.length) return tickValues;
+ tickValues = x;
+ return axis;
+ };
+ axis.tickFormat = function(x) {
+ if (!arguments.length) return tickFormat_;
+ tickFormat_ = x;
+ return axis;
+ };
+ axis.tickSize = function(x) {
+ var n = arguments.length;
+ if (!n) return innerTickSize;
+ innerTickSize = +x;
+ outerTickSize = +arguments[n - 1];
+ return axis;
+ };
+ axis.innerTickSize = function(x) {
+ if (!arguments.length) return innerTickSize;
+ innerTickSize = +x;
+ return axis;
+ };
+ axis.outerTickSize = function(x) {
+ if (!arguments.length) return outerTickSize;
+ outerTickSize = +x;
+ return axis;
+ };
+ axis.tickPadding = function(x) {
+ if (!arguments.length) return tickPadding;
+ tickPadding = +x;
+ return axis;
+ };
+ axis.tickSubdivide = function() {
+ return arguments.length && axis;
+ };
+ return axis;
+ };
+ var d3_svg_axisDefaultOrient = "bottom", d3_svg_axisOrients = {
+ top: 1,
+ right: 1,
+ bottom: 1,
+ left: 1
+ };
+ function d3_svg_axisX(selection, x) {
+ selection.attr("transform", function(d) {
+ return "translate(" + x(d) + ",0)";
+ });
+ }
+ function d3_svg_axisY(selection, y) {
+ selection.attr("transform", function(d) {
+ return "translate(0," + y(d) + ")";
+ });
+ }
+ d3.svg.brush = function() {
+ var event = d3_eventDispatch(brush, "brushstart", "brush", "brushend"), x = null, y = null, xExtent = [ 0, 0 ], yExtent = [ 0, 0 ], xExtentDomain, yExtentDomain, xClamp = true, yClamp = true, resizes = d3_svg_brushResizes[0];
+ function brush(g) {
+ g.each(function() {
+ var g = d3.select(this).style("pointer-events", "all").style("-webkit-tap-highlight-color", "rgba(0,0,0,0)").on("mousedown.brush", brushstart).on("touchstart.brush", brushstart);
+ var background = g.selectAll(".background").data([ 0 ]);
+ background.enter().append("rect").attr("class", "background").style("visibility", "hidden").style("cursor", "crosshair");
+ g.selectAll(".extent").data([ 0 ]).enter().append("rect").attr("class", "extent").style("cursor", "move");
+ var resize = g.selectAll(".resize").data(resizes, d3_identity);
+ resize.exit().remove();
+ resize.enter().append("g").attr("class", function(d) {
+ return "resize " + d;
+ }).style("cursor", function(d) {
+ return d3_svg_brushCursor[d];
+ }).append("rect").attr("x", function(d) {
+ return /[ew]$/.test(d) ? -3 : null;
+ }).attr("y", function(d) {
+ return /^[ns]/.test(d) ? -3 : null;
+ }).attr("width", 6).attr("height", 6).style("visibility", "hidden");
+ resize.style("display", brush.empty() ? "none" : null);
+ var gUpdate = d3.transition(g), backgroundUpdate = d3.transition(background), range;
+ if (x) {
+ range = d3_scaleRange(x);
+ backgroundUpdate.attr("x", range[0]).attr("width", range[1] - range[0]);
+ redrawX(gUpdate);
+ }
+ if (y) {
+ range = d3_scaleRange(y);
+ backgroundUpdate.attr("y", range[0]).attr("height", range[1] - range[0]);
+ redrawY(gUpdate);
+ }
+ redraw(gUpdate);
+ });
+ }
+ brush.event = function(g) {
+ g.each(function() {
+ var event_ = event.of(this, arguments), extent1 = {
+ x: xExtent,
+ y: yExtent,
+ i: xExtentDomain,
+ j: yExtentDomain
+ }, extent0 = this.__chart__ || extent1;
+ this.__chart__ = extent1;
+ if (d3_transitionInheritId) {
+ d3.select(this).transition().each("start.brush", function() {
+ xExtentDomain = extent0.i;
+ yExtentDomain = extent0.j;
+ xExtent = extent0.x;
+ yExtent = extent0.y;
+ event_({
+ type: "brushstart"
+ });
+ }).tween("brush:brush", function() {
+ var xi = d3_interpolateArray(xExtent, extent1.x), yi = d3_interpolateArray(yExtent, extent1.y);
+ xExtentDomain = yExtentDomain = null;
+ return function(t) {
+ xExtent = extent1.x = xi(t);
+ yExtent = extent1.y = yi(t);
+ event_({
+ type: "brush",
+ mode: "resize"
+ });
+ };
+ }).each("end.brush", function() {
+ xExtentDomain = extent1.i;
+ yExtentDomain = extent1.j;
+ event_({
+ type: "brush",
+ mode: "resize"
+ });
+ event_({
+ type: "brushend"
+ });
+ });
+ } else {
+ event_({
+ type: "brushstart"
+ });
+ event_({
+ type: "brush",
+ mode: "resize"
+ });
+ event_({
+ type: "brushend"
+ });
+ }
+ });
+ };
+ function redraw(g) {
+ g.selectAll(".resize").attr("transform", function(d) {
+ return "translate(" + xExtent[+/e$/.test(d)] + "," + yExtent[+/^s/.test(d)] + ")";
+ });
+ }
+ function redrawX(g) {
+ g.select(".extent").attr("x", xExtent[0]);
+ g.selectAll(".extent,.n>rect,.s>rect").attr("width", xExtent[1] - xExtent[0]);
+ }
+ function redrawY(g) {
+ g.select(".extent").attr("y", yExtent[0]);
+ g.selectAll(".extent,.e>rect,.w>rect").attr("height", yExtent[1] - yExtent[0]);
+ }
+ function brushstart() {
+ var target = this, eventTarget = d3.select(d3.event.target), event_ = event.of(target, arguments), g = d3.select(target), resizing = eventTarget.datum(), resizingX = !/^(n|s)$/.test(resizing) && x, resizingY = !/^(e|w)$/.test(resizing) && y, dragging = eventTarget.classed("extent"), dragRestore = d3_event_dragSuppress(), center, origin = d3.mouse(target), offset;
+ var w = d3.select(d3_window).on("keydown.brush", keydown).on("keyup.brush", keyup);
+ if (d3.event.changedTouches) {
+ w.on("touchmove.brush", brushmove).on("touchend.brush", brushend);
+ } else {
+ w.on("mousemove.brush", brushmove).on("mouseup.brush", brushend);
+ }
+ g.interrupt().selectAll("*").interrupt();
+ if (dragging) {
+ origin[0] = xExtent[0] - origin[0];
+ origin[1] = yExtent[0] - origin[1];
+ } else if (resizing) {
+ var ex = +/w$/.test(resizing), ey = +/^n/.test(resizing);
+ offset = [ xExtent[1 - ex] - origin[0], yExtent[1 - ey] - origin[1] ];
+ origin[0] = xExtent[ex];
+ origin[1] = yExtent[ey];
+ } else if (d3.event.altKey) center = origin.slice();
+ g.style("pointer-events", "none").selectAll(".resize").style("display", null);
+ d3.select("body").style("cursor", eventTarget.style("cursor"));
+ event_({
+ type: "brushstart"
+ });
+ brushmove();
+ function keydown() {
+ if (d3.event.keyCode == 32) {
+ if (!dragging) {
+ center = null;
+ origin[0] -= xExtent[1];
+ origin[1] -= yExtent[1];
+ dragging = 2;
+ }
+ d3_eventPreventDefault();
+ }
+ }
+ function keyup() {
+ if (d3.event.keyCode == 32 && dragging == 2) {
+ origin[0] += xExtent[1];
+ origin[1] += yExtent[1];
+ dragging = 0;
+ d3_eventPreventDefault();
+ }
+ }
+ function brushmove() {
+ var point = d3.mouse(target), moved = false;
+ if (offset) {
+ point[0] += offset[0];
+ point[1] += offset[1];
+ }
+ if (!dragging) {
+ if (d3.event.altKey) {
+ if (!center) center = [ (xExtent[0] + xExtent[1]) / 2, (yExtent[0] + yExtent[1]) / 2 ];
+ origin[0] = xExtent[+(point[0] < center[0])];
+ origin[1] = yExtent[+(point[1] < center[1])];
+ } else center = null;
+ }
+ if (resizingX && move1(point, x, 0)) {
+ redrawX(g);
+ moved = true;
+ }
+ if (resizingY && move1(point, y, 1)) {
+ redrawY(g);
+ moved = true;
+ }
+ if (moved) {
+ redraw(g);
+ event_({
+ type: "brush",
+ mode: dragging ? "move" : "resize"
+ });
+ }
+ }
+ function move1(point, scale, i) {
+ var range = d3_scaleRange(scale), r0 = range[0], r1 = range[1], position = origin[i], extent = i ? yExtent : xExtent, size = extent[1] - extent[0], min, max;
+ if (dragging) {
+ r0 -= position;
+ r1 -= size + position;
+ }
+ min = (i ? yClamp : xClamp) ? Math.max(r0, Math.min(r1, point[i])) : point[i];
+ if (dragging) {
+ max = (min += position) + size;
+ } else {
+ if (center) position = Math.max(r0, Math.min(r1, 2 * center[i] - min));
+ if (position < min) {
+ max = min;
+ min = position;
+ } else {
+ max = position;
+ }
+ }
+ if (extent[0] != min || extent[1] != max) {
+ if (i) yExtentDomain = null; else xExtentDomain = null;
+ extent[0] = min;
+ extent[1] = max;
+ return true;
+ }
+ }
+ function brushend() {
+ brushmove();
+ g.style("pointer-events", "all").selectAll(".resize").style("display", brush.empty() ? "none" : null);
+ d3.select("body").style("cursor", null);
+ w.on("mousemove.brush", null).on("mouseup.brush", null).on("touchmove.brush", null).on("touchend.brush", null).on("keydown.brush", null).on("keyup.brush", null);
+ dragRestore();
+ event_({
+ type: "brushend"
+ });
+ }
+ }
+ brush.x = function(z) {
+ if (!arguments.length) return x;
+ x = z;
+ resizes = d3_svg_brushResizes[!x << 1 | !y];
+ return brush;
+ };
+ brush.y = function(z) {
+ if (!arguments.length) return y;
+ y = z;
+ resizes = d3_svg_brushResizes[!x << 1 | !y];
+ return brush;
+ };
+ brush.clamp = function(z) {
+ if (!arguments.length) return x && y ? [ xClamp, yClamp ] : x ? xClamp : y ? yClamp : null;
+ if (x && y) xClamp = !!z[0], yClamp = !!z[1]; else if (x) xClamp = !!z; else if (y) yClamp = !!z;
+ return brush;
+ };
+ brush.extent = function(z) {
+ var x0, x1, y0, y1, t;
+ if (!arguments.length) {
+ if (x) {
+ if (xExtentDomain) {
+ x0 = xExtentDomain[0], x1 = xExtentDomain[1];
+ } else {
+ x0 = xExtent[0], x1 = xExtent[1];
+ if (x.invert) x0 = x.invert(x0), x1 = x.invert(x1);
+ if (x1 < x0) t = x0, x0 = x1, x1 = t;
+ }
+ }
+ if (y) {
+ if (yExtentDomain) {
+ y0 = yExtentDomain[0], y1 = yExtentDomain[1];
+ } else {
+ y0 = yExtent[0], y1 = yExtent[1];
+ if (y.invert) y0 = y.invert(y0), y1 = y.invert(y1);
+ if (y1 < y0) t = y0, y0 = y1, y1 = t;
+ }
+ }
+ return x && y ? [ [ x0, y0 ], [ x1, y1 ] ] : x ? [ x0, x1 ] : y && [ y0, y1 ];
+ }
+ if (x) {
+ x0 = z[0], x1 = z[1];
+ if (y) x0 = x0[0], x1 = x1[0];
+ xExtentDomain = [ x0, x1 ];
+ if (x.invert) x0 = x(x0), x1 = x(x1);
+ if (x1 < x0) t = x0, x0 = x1, x1 = t;
+ if (x0 != xExtent[0] || x1 != xExtent[1]) xExtent = [ x0, x1 ];
+ }
+ if (y) {
+ y0 = z[0], y1 = z[1];
+ if (x) y0 = y0[1], y1 = y1[1];
+ yExtentDomain = [ y0, y1 ];
+ if (y.invert) y0 = y(y0), y1 = y(y1);
+ if (y1 < y0) t = y0, y0 = y1, y1 = t;
+ if (y0 != yExtent[0] || y1 != yExtent[1]) yExtent = [ y0, y1 ];
+ }
+ return brush;
+ };
+ brush.clear = function() {
+ if (!brush.empty()) {
+ xExtent = [ 0, 0 ], yExtent = [ 0, 0 ];
+ xExtentDomain = yExtentDomain = null;
+ }
+ return brush;
+ };
+ brush.empty = function() {
+ return !!x && xExtent[0] == xExtent[1] || !!y && yExtent[0] == yExtent[1];
+ };
+ return d3.rebind(brush, event, "on");
+ };
+ var d3_svg_brushCursor = {
+ n: "ns-resize",
+ e: "ew-resize",
+ s: "ns-resize",
+ w: "ew-resize",
+ nw: "nwse-resize",
+ ne: "nesw-resize",
+ se: "nwse-resize",
+ sw: "nesw-resize"
+ };
+ var d3_svg_brushResizes = [ [ "n", "e", "s", "w", "nw", "ne", "se", "sw" ], [ "e", "w" ], [ "n", "s" ], [] ];
+ var d3_time_format = d3_time.format = d3_locale_enUS.timeFormat;
+ var d3_time_formatUtc = d3_time_format.utc;
+ var d3_time_formatIso = d3_time_formatUtc("%Y-%m-%dT%H:%M:%S.%LZ");
+ d3_time_format.iso = Date.prototype.toISOString && +new Date("2000-01-01T00:00:00.000Z") ? d3_time_formatIsoNative : d3_time_formatIso;
+ function d3_time_formatIsoNative(date) {
+ return date.toISOString();
+ }
+ d3_time_formatIsoNative.parse = function(string) {
+ var date = new Date(string);
+ return isNaN(date) ? null : date;
+ };
+ d3_time_formatIsoNative.toString = d3_time_formatIso.toString;
+ d3_time.second = d3_time_interval(function(date) {
+ return new d3_date(Math.floor(date / 1e3) * 1e3);
+ }, function(date, offset) {
+ date.setTime(date.getTime() + Math.floor(offset) * 1e3);
+ }, function(date) {
+ return date.getSeconds();
+ });
+ d3_time.seconds = d3_time.second.range;
+ d3_time.seconds.utc = d3_time.second.utc.range;
+ d3_time.minute = d3_time_interval(function(date) {
+ return new d3_date(Math.floor(date / 6e4) * 6e4);
+ }, function(date, offset) {
+ date.setTime(date.getTime() + Math.floor(offset) * 6e4);
+ }, function(date) {
+ return date.getMinutes();
+ });
+ d3_time.minutes = d3_time.minute.range;
+ d3_time.minutes.utc = d3_time.minute.utc.range;
+ d3_time.hour = d3_time_interval(function(date) {
+ var timezone = date.getTimezoneOffset() / 60;
+ return new d3_date((Math.floor(date / 36e5 - timezone) + timezone) * 36e5);
+ }, function(date, offset) {
+ date.setTime(date.getTime() + Math.floor(offset) * 36e5);
+ }, function(date) {
+ return date.getHours();
+ });
+ d3_time.hours = d3_time.hour.range;
+ d3_time.hours.utc = d3_time.hour.utc.range;
+ d3_time.month = d3_time_interval(function(date) {
+ date = d3_time.day(date);
+ date.setDate(1);
+ return date;
+ }, function(date, offset) {
+ date.setMonth(date.getMonth() + offset);
+ }, function(date) {
+ return date.getMonth();
+ });
+ d3_time.months = d3_time.month.range;
+ d3_time.months.utc = d3_time.month.utc.range;
+ function d3_time_scale(linear, methods, format) {
+ function scale(x) {
+ return linear(x);
+ }
+ scale.invert = function(x) {
+ return d3_time_scaleDate(linear.invert(x));
+ };
+ scale.domain = function(x) {
+ if (!arguments.length) return linear.domain().map(d3_time_scaleDate);
+ linear.domain(x);
+ return scale;
+ };
+ function tickMethod(extent, count) {
+ var span = extent[1] - extent[0], target = span / count, i = d3.bisect(d3_time_scaleSteps, target);
+ return i == d3_time_scaleSteps.length ? [ methods.year, d3_scale_linearTickRange(extent.map(function(d) {
+ return d / 31536e6;
+ }), count)[2] ] : !i ? [ d3_time_scaleMilliseconds, d3_scale_linearTickRange(extent, count)[2] ] : methods[target / d3_time_scaleSteps[i - 1] < d3_time_scaleSteps[i] / target ? i - 1 : i];
+ }
+ scale.nice = function(interval, skip) {
+ var domain = scale.domain(), extent = d3_scaleExtent(domain), method = interval == null ? tickMethod(extent, 10) : typeof interval === "number" && tickMethod(extent, interval);
+ if (method) interval = method[0], skip = method[1];
+ function skipped(date) {
+ return !isNaN(date) && !interval.range(date, d3_time_scaleDate(+date + 1), skip).length;
+ }
+ return scale.domain(d3_scale_nice(domain, skip > 1 ? {
+ floor: function(date) {
+ while (skipped(date = interval.floor(date))) date = d3_time_scaleDate(date - 1);
+ return date;
+ },
+ ceil: function(date) {
+ while (skipped(date = interval.ceil(date))) date = d3_time_scaleDate(+date + 1);
+ return date;
+ }
+ } : interval));
+ };
+ scale.ticks = function(interval, skip) {
+ var extent = d3_scaleExtent(scale.domain()), method = interval == null ? tickMethod(extent, 10) : typeof interval === "number" ? tickMethod(extent, interval) : !interval.range && [ {
+ range: interval
+ }, skip ];
+ if (method) interval = method[0], skip = method[1];
+ return interval.range(extent[0], d3_time_scaleDate(+extent[1] + 1), skip < 1 ? 1 : skip);
+ };
+ scale.tickFormat = function() {
+ return format;
+ };
+ scale.copy = function() {
+ return d3_time_scale(linear.copy(), methods, format);
+ };
+ return d3_scale_linearRebind(scale, linear);
+ }
+ function d3_time_scaleDate(t) {
+ return new Date(t);
+ }
+ var d3_time_scaleSteps = [ 1e3, 5e3, 15e3, 3e4, 6e4, 3e5, 9e5, 18e5, 36e5, 108e5, 216e5, 432e5, 864e5, 1728e5, 6048e5, 2592e6, 7776e6, 31536e6 ];
+ var d3_time_scaleLocalMethods = [ [ d3_time.second, 1 ], [ d3_time.second, 5 ], [ d3_time.second, 15 ], [ d3_time.second, 30 ], [ d3_time.minute, 1 ], [ d3_time.minute, 5 ], [ d3_time.minute, 15 ], [ d3_time.minute, 30 ], [ d3_time.hour, 1 ], [ d3_time.hour, 3 ], [ d3_time.hour, 6 ], [ d3_time.hour, 12 ], [ d3_time.day, 1 ], [ d3_time.day, 2 ], [ d3_time.week, 1 ], [ d3_time.month, 1 ], [ d3_time.month, 3 ], [ d3_time.year, 1 ] ];
+ var d3_time_scaleLocalFormat = d3_time_format.multi([ [ ".%L", function(d) {
+ return d.getMilliseconds();
+ } ], [ ":%S", function(d) {
+ return d.getSeconds();
+ } ], [ "%I:%M", function(d) {
+ return d.getMinutes();
+ } ], [ "%I %p", function(d) {
+ return d.getHours();
+ } ], [ "%a %d", function(d) {
+ return d.getDay() && d.getDate() != 1;
+ } ], [ "%b %d", function(d) {
+ return d.getDate() != 1;
+ } ], [ "%B", function(d) {
+ return d.getMonth();
+ } ], [ "%Y", d3_true ] ]);
+ var d3_time_scaleMilliseconds = {
+ range: function(start, stop, step) {
+ return d3.range(Math.ceil(start / step) * step, +stop, step).map(d3_time_scaleDate);
+ },
+ floor: d3_identity,
+ ceil: d3_identity
+ };
+ d3_time_scaleLocalMethods.year = d3_time.year;
+ d3_time.scale = function() {
+ return d3_time_scale(d3.scale.linear(), d3_time_scaleLocalMethods, d3_time_scaleLocalFormat);
+ };
+ var d3_time_scaleUtcMethods = d3_time_scaleLocalMethods.map(function(m) {
+ return [ m[0].utc, m[1] ];
+ });
+ var d3_time_scaleUtcFormat = d3_time_formatUtc.multi([ [ ".%L", function(d) {
+ return d.getUTCMilliseconds();
+ } ], [ ":%S", function(d) {
+ return d.getUTCSeconds();
+ } ], [ "%I:%M", function(d) {
+ return d.getUTCMinutes();
+ } ], [ "%I %p", function(d) {
+ return d.getUTCHours();
+ } ], [ "%a %d", function(d) {
+ return d.getUTCDay() && d.getUTCDate() != 1;
+ } ], [ "%b %d", function(d) {
+ return d.getUTCDate() != 1;
+ } ], [ "%B", function(d) {
+ return d.getUTCMonth();
+ } ], [ "%Y", d3_true ] ]);
+ d3_time_scaleUtcMethods.year = d3_time.year.utc;
+ d3_time.scale.utc = function() {
+ return d3_time_scale(d3.scale.linear(), d3_time_scaleUtcMethods, d3_time_scaleUtcFormat);
+ };
+ d3.text = d3_xhrType(function(request) {
+ return request.responseText;
+ });
+ d3.json = function(url, callback) {
+ return d3_xhr(url, "application/json", d3_json, callback);
+ };
+ function d3_json(request) {
+ return JSON.parse(request.responseText);
+ }
+ d3.html = function(url, callback) {
+ throw "disallowed by chromium security";
+ return d3_xhr(url, "text/html", d3_html, callback);
+ };
+ function d3_html(request) {
+ throw "disallowed by chromium security";
+ var range = d3_document.createRange();
+ range.selectNode(d3_document.body);
+ return range.createContextualFragment(request.responseText);
+ }
+ d3.xml = d3_xhrType(function(request) {
+ return request.responseXML;
+ });
+ if (typeof define === "function" && define.amd) {
+ define(d3);
+ } else if (typeof module === "object" && module.exports) {
+ module.exports = d3;
+ } else {
+ this.d3 = d3;
+ }
+}();
\ No newline at end of file
diff --git a/pkg/vm_snapshot_analysis/lib/src/assets/index.html b/pkg/vm_snapshot_analysis/lib/src/assets/index.html
new file mode 100644
index 0000000..7e1a1fc
--- /dev/null
+++ b/pkg/vm_snapshot_analysis/lib/src/assets/index.html
@@ -0,0 +1,525 @@
+<!--
+ Copyright 2014 The Chromium Authors. All rights reserved.
+ Use of this source code is governed by a BSD-style license that can be
+ found in the LICENSE file.
+-->
+<html>
+<head>
+<title>Binary Size Analysis</title>
+<script src="d3/d3.js" charset="utf-8"></script>
+<script src="D3SymbolTreeMap.js" charset="utf-8"></script>
+<script src="data.js" charset="utf-8"></script>
+<style>
+body {
+ margin: 0px;
+ padding: 5px;
+}
+.swatch {
+ border: 1px solid rgb(100,100,100);
+ -webkit-user-select: none;
+ cursor: default;
+}
+</style>
+<script>
+var treemap;
+var filterChanging = false;
+var savedSettings = {};
+
+function init() {
+ if (window.metadata !== undefined && window.metadata.subtitle) {
+ document.getElementById('subtitle').innerHTML = ': ' + escape(metadata.subtitle);
+ }
+ initFilterOptions();
+ treemap = new D3SymbolTreeMap(
+ savedSettings.width,
+ savedSettings.height,
+ savedSettings.maxLevels);
+ treemap.init();
+}
+
+function getIdealSizes() {
+ var width = window.innerWidth - 20;
+ var height = window.innerHeight - 70;
+ return {'width': width, 'height': height};
+}
+
+function showReport(title, data, headers, dataFunction, styleFunction) {
+ var div = d3.select('body').append('div')
+ .style('margin', '0')
+ .style('padding', '5px')
+ .style('position', 'absolute')
+ .style('top', '10%')
+ .style('left', '10%')
+ .style('background-color', 'rgba(255,255,255,0.9)')
+ .style('width', '80%')
+ .style('height', '80%')
+ .style('z-index', '2147483647')
+ .style('border', '3px ridge grey')
+ .style('box-shadow', '10px 10px 5px rgba(80,80,80,0.7)')
+ .style('text-align', 'center')
+ .style('border-radius', '10px');
+ var titlebar = div.append('div')
+ .style('margin', '0')
+ .style('padding', '5px')
+ .style('position', 'absolute')
+ .style('top', '0%')
+ .style('left', '0%')
+ .style('width', '100%')
+ .style('height', '10%')
+ .style('font-size', 'x-large');
+ titlebar.text(title);
+ var controls = div.append('div')
+ .style('margin', '0')
+ .style('padding', '5px')
+ .style('position', 'absolute')
+ .style('top', '90%')
+ .style('left', '0%')
+ .style('width', '100%')
+ .style('height', '10%');
+ controls.append('input').attr('type', 'button')
+ .attr('value', 'Dismiss')
+ .on('click', function(){div.remove();});
+
+ var tableDiv = div.append('div')
+ .style('overflow', 'auto')
+ .style('position', 'absolute')
+ .style('top', '10%')
+ .style('left', '0%')
+ .style('width', '100%')
+ .style('height', '80%')
+ .style('border-top', '1px solid rgb(230,230,230)')
+ .style('border-bottom', '1px solid rgb(230,230,230)');
+ var table = tableDiv.append('table')
+ .attr('border', '1')
+ .attr('cellspacing', '0')
+ .attr('cellpadding', '2')
+ .style('margin-left', 'auto')
+ .style('margin-right', 'auto');
+ var header = table.append('tr');
+ for (var i = 0; i < headers.length; i++) {
+ header.append('th').text(headers[i]);
+ }
+
+ for (var i = 0; i < data.length; i++) {
+ var row = table.append('tr');
+ for (j = 0; j < headers.length; j++) {
+ var td = row.append('td');
+ if (styleFunction) {
+ styleFunction.call(this, td, j);
+ }
+ dataFunction.call(this, data[i], j, td);
+ }
+ }
+}
+
+function bigSymbolsReport() {
+ var list = treemap.biggestSymbols(100);
+ var headers = ['Rank', 'Size (Bytes)', 'Type', 'Location'];
+ var styleFunction = function(selection, index) {
+ if (index === 3) {
+ selection.style('font-family', 'monospace');
+ }
+ };
+ var recordIndex = 1;
+ var dataFunction = function(record, index, cell) {
+ if (index === 0) {
+ cell.text(recordIndex++);
+ } else if (index === 1) {
+ cell.text(D3SymbolTreeMap._pretty(record.value));
+ } else if (index === 2) {
+ cell.text(record.t);
+ } else {
+ if (treemap.pathFor(record).indexOf('/out') == 0) {
+ cell.append('span').text(treemap.pathFor(record));
+ cell.append('br');
+ cell.append('span').text('Symbol: ');
+ cell.append('span').text(record.n);
+ } else {
+ var href = 'https://code.google.com/p/chromium/codesearch#chromium/src'
+ + treemap.pathFor(record)
+ + '&q='
+ + record.n;
+ cell.append('a')
+ .attr('href', href)
+ .attr('target', '_blank')
+ .text(treemap.pathFor(record));
+ cell.append('br');
+ cell.append('span').text('Symbol: ');
+ cell.append('span').text(record.n);
+ }
+ }
+ };
+ showReport('100 Largest Symbols', list, headers, dataFunction, styleFunction);
+}
+
+function bigPathsReport() {
+ var list = treemap.biggestPaths(100);
+ var headers = ['Rank', 'Size (Bytes)', 'Location'];
+ var styleFunction = function(selection, index) {
+ if (index === 2) {
+ selection.style('font-family', 'monospace');
+ }
+ };
+ var recordIndex = 1;
+ var dataFunction = function(record, index, cell) {
+ if (index === 0) {
+ cell.text(recordIndex++);
+ } else if (index === 1) {
+ cell.text(D3SymbolTreeMap._pretty(record.value));
+ } else if (index === 2) {
+ if (treemap.pathFor(record).indexOf('/out') == 0) {
+ cell.text(treemap.pathFor(record));
+ } else {
+ var href = 'https://code.google.com/p/chromium/codesearch#chromium/src' + treemap.pathFor(record);
+ cell.append('a')
+ .attr('href', href)
+ .attr('target', '_blank')
+ .text(treemap.pathFor(record));
+ }
+
+ }
+ };
+ showReport('100 Largest Paths', list, headers, dataFunction, styleFunction);
+}
+
+function symbolFilterTextChanged() {
+ if (filterChanging) return true;
+ filterChanging = true;
+ var enabled = document.getElementById('symbol_types_filter').value;
+ for (var x=0; x<=25; x++) {
+ var checkBox = document.getElementById('check_' + x);
+ checkBox.checked = (enabled.indexOf(checkBox.value) != -1);
+ }
+ filterChanging = false;
+}
+
+function updateFilterText() {
+ if (filterChanging) return true;
+ filterChanging = true;
+ var text = '';
+ for (var x=0; x<=25; x++) {
+ var checkBox = document.getElementById('check_' + x);
+ if (checkBox.checked) {
+ text += checkBox.value;
+ }
+ }
+ document.getElementById('symbol_types_filter').value=text;
+ filterChanging = false;
+}
+
+function initFilterOptions() {
+ updateFilterText();
+ for (var x=0; x<=25; x++) {
+ var checkBox = document.getElementById('check_' + x);
+ checkBox.onchange=updateFilterText;
+ var swatch = document.getElementById('swatch_' + x);
+ swatch.style.backgroundColor = D3SymbolTreeMap.getColorForType(checkBox.value).toString();
+ }
+ var gteCheckbox = document.getElementById('check_gte');
+ gteCheckbox.onchange = function() {
+ document.getElementById('symbol_filter_gte').disabled = !gteCheckbox.checked;
+ }
+ var regexCheckbox = document.getElementById('check_regex');
+ regexCheckbox.onchange = function() {
+ document.getElementById('symbol_filter_regex').disabled = !regexCheckbox.checked;
+ }
+ var excludeRegexCheckbox = document.getElementById('check_exclude_regex');
+ excludeRegexCheckbox.onchange = function() {
+ document.getElementById('symbol_filter_exclude_regex').disabled = !excludeRegexCheckbox.checked;
+ }
+ var idealSizes = getIdealSizes();
+ document.getElementById('width').value = idealSizes.width;
+ document.getElementById('height').value = idealSizes.height;
+ saveFilterSettings();
+}
+
+function filterSetAll(enabled) {
+ for (var x=0; x<=25; x++) {
+ var checkBox = document.getElementById('check_' + x);
+ checkBox.checked = enabled;
+ }
+ updateFilterText();
+}
+
+function showOptions() {
+ loadFilterSettings();
+ var container = document.getElementById('options_container');
+ var w = container.offsetWidth;
+ var h = container.offsetHeight;
+ container.style.margin = '-' + (h/2) + 'px 0 0 -' + (w/2) + 'px';
+ container.style.visibility = 'visible';
+}
+
+function hideOptions() {
+ var container = document.getElementById('options_container');
+ container.style.visibility = 'hidden';
+}
+
+function applySettings() {
+ hideOptions();
+ var oldWidth = savedSettings.width;
+ var oldHeight = savedSettings.height;
+ var oldSymbols = savedSettings.symbolTypes;
+ var oldRegex = savedSettings.regex;
+ var oldExcludeRegex = savedSettings.excludeRegex;
+ var oldGte = savedSettings.gte;
+ var oldMaxLevels = savedSettings.maxLevels;
+ saveFilterSettings();
+ var resizeNeeded = oldWidth !== savedSettings.width || oldHeight !== savedSettings.height;
+ var regexChanged = oldRegex !== savedSettings.regex;
+ var excludeRegexChanged = oldExcludeRegex !== savedSettings.excludeRegex;
+ var symbolsChanged = oldSymbols !== savedSettings.symbolTypes;
+ var gteChanged = oldGte !== savedSettings.gte;
+ var filterChanged = regexChanged || excludeRegexChanged || symbolsChanged || gteChanged;
+ var maxLevelsChanged = oldMaxLevels !== savedSettings.maxLevels;
+
+ if (filterChanged) {
+ // Type filters
+ typeFilter = function(datum) {
+ if (datum.depth === 0) return true; // root node
+ if (datum.t === undefined) return true;
+ return savedSettings.symbolTypes !== undefined &&
+ savedSettings.symbolTypes.indexOf(datum.t) !== -1;
+ }
+
+ // Regex filter
+ var regexFilter = undefined;
+ if (savedSettings.regex !== undefined && savedSettings.regex.length > 0) {
+ console.log('filter: regex is "' + savedSettings.regex + '"');
+ var regex = new RegExp(savedSettings.regex);
+ regexFilter = function(datum) {
+ if (datum.depth === 0) return true; // root node
+ var fullName = this.pathFor(datum);
+ if (datum.children === undefined) { // it is a leaf node (symbol)
+ fullName += ':' + datum.n;
+ }
+ return regex.test(fullName);
+ }
+ }
+
+ // Exclude regex filter
+ var excludeRegexFilter = undefined;
+ if (savedSettings.excludeRegex !== undefined && savedSettings.excludeRegex.length > 0) {
+ console.log('filter: exclude-regex is "' + savedSettings.excludeRegex + '"');
+ var excludeRegex = new RegExp(savedSettings.excludeRegex);
+ excludeRegexFilter = function(datum) {
+ if (datum.depth === 0) return true; // root node
+ var fullName = this.pathFor(datum);
+ if (datum.children === undefined) { // it is a leaf node (symbol)
+ fullName += ':' + datum.n;
+ }
+ return !excludeRegex.test(fullName);
+ }
+ }
+
+ // Size filter
+ var sizeFilter = undefined;
+ if (savedSettings.gte !== undefined) {
+ console.log('filter: minimum size is ' + savedSettings.gte + ' bytes');
+ sizeFilter = function(datum) {
+ if (datum.children !== undefined) return true; // non-leaf
+ if (datum.value === undefined) console.log('whoops');
+ return datum.value >= savedSettings.gte;
+ }
+ }
+
+ // Make a filter to apply to the tree
+ var filter = function(datum) {
+ if (typeFilter && !typeFilter.call(this, datum)) return false;
+ if (regexFilter && !regexFilter.call(this, datum)) return false;
+ if (excludeRegexFilter && !excludeRegexFilter.call(this, datum)) return false;
+ if (sizeFilter && !sizeFilter.call(this, datum)) return false;
+ return true;
+ };
+ treemap.filter(filter);
+ }
+
+ // Adjust levels if needed.
+ if (maxLevelsChanged) {
+ treemap.setMaxLevels(savedSettings.maxLevels);
+ }
+
+ // Resize map if necessary.
+ if (resizeNeeded) {
+ console.log('desired treemap dimensions have changed, requesting resize');
+ treemap.resize(savedSettings.width, savedSettings.height);
+ }
+}
+
+function cancelSettings() {
+ hideOptions();
+ loadFilterSettings();
+}
+
+function saveFilterSettings() {
+ savedSettings.symbolTypes = document.getElementById('symbol_types_filter').value;
+ if (document.getElementById('check_regex').checked) {
+ savedSettings.regex = document.getElementById('symbol_filter_regex').value;
+ } else {
+ savedSettings.regex = undefined;
+ }
+ if (document.getElementById('check_exclude_regex').checked) {
+ savedSettings.excludeRegex = document.getElementById('symbol_filter_exclude_regex').value;
+ } else {
+ savedSettings.excludeRegex = undefined;
+ }
+ if (document.getElementById('check_gte').checked) {
+ savedSettings.gte = parseInt(document.getElementById('symbol_filter_gte').value);
+ } else {
+ savedSettings.gte = undefined;
+ }
+ savedSettings.width = parseInt(document.getElementById('width').value);
+ savedSettings.height = parseInt(document.getElementById('height').value);
+ savedSettings.maxLevels = parseInt(document.getElementById('max_levels').value);
+}
+
+function loadFilterSettings() {
+ document.getElementById('symbol_types_filter').value = savedSettings.symbolTypes;
+ symbolFilterTextChanged();
+ if (savedSettings.regex !== undefined) {
+ document.getElementById('check_regex').checked = true;
+ document.getElementById('symbol_filter_regex').value = savedSettings.regex;
+ } else {
+ document.getElementById('check_regex').checked = false;
+ }
+ if (savedSettings.excludeRegex !== undefined) {
+ document.getElementById('check_exclude_regex').checked = true;
+ document.getElementById('symbol_filter_exclude_regex').value = savedSettings.excludeRegex;
+ } else {
+ document.getElementById('check_exclude_regex').checked = false;
+ }
+ if (savedSettings.gte !== undefined) {
+ document.getElementById('check_gte').checked = true;
+ document.getElementById('symbol_filter_gte').value = savedSettings.gte;
+ } else {
+ document.getElementById('check_gte').checked = false;
+ }
+ document.getElementById('width').value = savedSettings.width;
+ document.getElementById('height').value = savedSettings.height;
+ document.getElementById('max_levels').value = savedSettings.maxLevels;
+}
+
+function escape(str) {
+ return str.replace(/&/g, '&')
+ .replace(/"/g, '"')
+ .replace(/</g, '<')
+ .replace(/>/g, '>');
+}
+</script>
+</head>
+<body onload='init()'>
+<div style='position: absolute; top: 5px; left: 5px;'>
+ <input type='button' onclick='showOptions()' value='Options & Legend...'>
+ <span style='-webkit-user-select: none; cursor: help;' title='Click to view the symbol legend or to configure filters and options for the treemap'>[?]</span>
+</div>
+<div style='position: absolute; right: 5px; top: 5px; white-space: nowrap;'>
+ Reports:
+ <input type='button' onclick='bigSymbolsReport()' value='Large Symbols' title='Click to view a report of the largest 100 symbols that are with the bounds of the treemap that is currently displayed.'>
+ <input type='button' onclick='bigPathsReport()' value='Large Files' title='Click to view a report of the largest 100 source files that are with the bounds of the treemap that is currently displayed.'>
+</div>
+<div style='text-align: center; margin-bottom: 5px;'>
+ <span style='font-size: x-large; font-weight: bold; font-variant: small-caps'>Binary Size Analysis<span id='subtitle'></span></span>
+ <br><span style='font-size: small; font-style: italic;'>Double-click a box to zoom in, double-click outermost title to zoom out.</span>
+</div>
+<table id='options_container' style='visibility: hidden; border: 3px ridge grey; padding: 0px; top: 50%; left: 50%; position: fixed; z-index: 2147483646; overflow: auto; background-color: rgba(255,255,255,0.9); border-radius: 10px; box-shadow: 10px 10px 5px rgba(80,80,80,0.7);'><tr><td style='vertical-align: top'>
+ <table cellspacing=0 cellborder=0 style='width:100%'>
+ <tr><th colspan=3 style='padding-bottom: .25em; text-decoration: underline;'>Symbol Types To Show</th></tr>
+ <tr>
+ <td style='width: 33%; white-space: nowrap; vertical-align: top;'>
+ <span class='swatch' id='swatch_0'> </span><input checked type='checkbox' id='check_0' value='A'>Global absolute (A)
+ <br><span class='swatch' id='swatch_1'> </span><input checked type='checkbox' id='check_1' value='B'>Global uninitialized data (B)
+ <br><span class='swatch' id='swatch_2'> </span><input checked type='checkbox' id='check_2' value='b'>Local uninitialized data (b)
+ <br><span class='swatch' id='swatch_3'> </span><input checked type='checkbox' id='check_3' value='C'>Global uninitialized common (C)
+ <br><span class='swatch' id='swatch_4'> </span><input checked type='checkbox' id='check_4' value='D'>Global initialized data (D)
+ <br><span class='swatch' id='swatch_5'> </span><input checked type='checkbox' id='check_5' value='d'>Local initialized data (d)
+ <br><span class='swatch' id='swatch_6'> </span><input checked type='checkbox' id='check_6' value='G'>Global small initialized data (G)
+ <br><span class='swatch' id='swatch_7'> </span><input checked type='checkbox' id='check_7' value='g'>Local small initialized data (g)
+ <br><span class='swatch' id='swatch_8'> </span><input checked type='checkbox' id='check_8' value='i'>Indirect function (i)
+ </td>
+ <td style='width: 33%; white-space: nowrap; vertical-align: top;'>
+ <span class='swatch' id='swatch_9'> </span><input checked type='checkbox' id='check_9' value='N'>Debugging (N)
+ <br><span class='swatch' id='swatch_10'> </span><input checked type='checkbox' id='check_10' value='p'>Stack unwind (p)
+ <br><span class='swatch' id='swatch_11'> </span><input checked type='checkbox' id='check_11' value='R'>Global read-only data (R)
+ <br><span class='swatch' id='swatch_12'> </span><input checked type='checkbox' id='check_12' value='r'>Local read-only data (r)
+ <br><span class='swatch' id='swatch_13'> </span><input checked type='checkbox' id='check_13' value='S'>Global small uninitialized data (S)
+ <br><span class='swatch' id='swatch_14'> </span><input checked type='checkbox' id='check_14' value='s'>Local small uninitialized data (s)
+ <br><span class='swatch' id='swatch_15'> </span><input checked type='checkbox' id='check_15' value='T'>Global code (T)
+ <br><span class='swatch' id='swatch_16'> </span><input checked type='checkbox' id='check_16' value='t'>Local code (t)
+ <br><span class='swatch' id='swatch_17'> </span><input checked type='checkbox' id='check_17' value='U'>Undefined (U)
+ </td>
+ <td style='width: 33%; white-space: nowrap; vertical-align: top;'>
+ <span class='swatch' id='swatch_18'> </span><input checked type='checkbox' id='check_18' value='u'>Unique (u)
+ <br><span class='swatch' id='swatch_19'> </span><input checked type='checkbox' id='check_19' value='V'>Global weak object (V)
+ <br><span class='swatch' id='swatch_20'> </span><input checked type='checkbox' id='check_20' value='v'>Local weak object (v)
+ <br><span class='swatch' id='swatch_21'> </span><input checked type='checkbox' id='check_21' value='W'>Global weak symbol (W)
+ <br><span class='swatch' id='swatch_22'> </span><input checked type='checkbox' id='check_22' value='w'>Local weak symbol (w)
+ <br><span class='swatch' id='swatch_23'> </span><input checked type='checkbox' id='check_23' value='@'>Vtable entry (@)
+ <br><span class='swatch' id='swatch_24'> </span><input checked type='checkbox' id='check_24' value='-'>STABS debugging (-)
+ <br><span class='swatch' id='swatch_25'> </span><input checked type='checkbox' id='check_25' value='?'>Unrecognized (?)
+ </td>
+ </tr>
+ <tr><td colspan=3 style='text-align: center; white-space: nowrap; padding-top: 1em;'>
+ Select <input type='button' onclick='filterSetAll(true)' value='All'>,
+ <input type='button' onclick='filterSetAll(false)' value='None'>,
+ or type a string: <input id='symbol_types_filter' size=30 value='' onkeyup='symbolFilterTextChanged()' onblur='updateFilterText()'>
+ <span style='-webkit-user-select: none; cursor: help;' title='Enter codes from the list above for the symbols you want to see. The checkboxes will update automatically to match the string that you enter.'>[?]</span>
+ </td></tr>
+ </table>
+</td></tr><tr><td style='vertical-align: top; padding-top: 10px; border-top: 1px solid grey;'>
+ <table cellspacing=0 cellborder=0 style='width: 100%'>
+ <tr><th colspan=2 style='padding-bottom: .25em; text-decoration: underline;'>Advanced Options</th></tr>
+ <tr>
+ <td style='white-space: nowrap; vertical-align: top;'>
+ <input type='checkbox' id='check_regex'>
+ Only include symbols matching this regex:
+ </td>
+ <td style='text-align: right; vertical-align: top;'>
+ <input disabled id='symbol_filter_regex' size=30 value='' style='text-align: right;'>
+ <span style='-webkit-user-select: none; cursor: help;' title='Enter a javascript regex. Only symbols that match this regex will be shown. This filter applies before any exclusion regex specified below. The format of each symbol is [path]:[symbol_name]'>[?]</span>
+ </td>
+ </tr>
+ <tr>
+ <td style='white-space: nowrap; vertical-align: top;'>
+ <input type='checkbox' id='check_exclude_regex'>
+ Exclude all symbols matching this regex:
+ </td>
+ <td style='text-align: right; vertical-align: top;'>
+ <input disabled id='symbol_filter_exclude_regex' size=30 value='' style='text-align: right;'>
+ <span style='-webkit-user-select: none; cursor: help;' title='Enter a javascript regex. Symbols that match this tegex will not be shown. This filter applies after any inclusion filter specified above. The format of each symbol is [path]:[symbol_name]'>[?]</span>
+ </td>
+ </tr>
+ <tr>
+ <td style='white-space: nowrap; vertical-align: top;'>
+ <input type='checkbox' id='check_gte'>
+ Only include symbols that are at least <span style='font-style: italic;'>n</span> bytes:
+ </td>
+ <td style='text-align: right; vertical-align: top;'>
+ <input disabled id='symbol_filter_gte' size=8 value='' style='text-align: right;'>
+ <span style='-webkit-user-select: none; cursor: help;' title='Symbols whose size is less than this value will be hidden.'>[?]</span>
+ </td>
+ </tr>
+ <tr>
+ <td style='white-space: nowrap vertical-align: top;;'>
+ Show at most <span style='font-style: italic;'>n</span> levels of detail at a time:
+ </td>
+ <td style='text-align: right; vertical-align: top;'>
+ <input id='max_levels' size=4 value='2' style='text-align: right;'><span style='-webkit-user-select: none; cursor: help;' title='Increasing this value shows more detail without the need to zoom, but uses more computing power.'>[?]</span>
+ </td>
+ </tr>
+ <tr>
+ <td style='white-space: nowrap vertical-align: top;;'>
+ Set the size of the treemap to <span style='font-style: italic;'>W x H</span> pixels:
+ </td>
+ <td style='text-align: right; vertical-align: top;'>
+ <input id='width' size=4 value='' style='text-align: right;'>
+ x <input id='height' size=4 value='' style='text-align: right;'>
+ </td>
+ </tr>
+ </table>
+</td></tr>
+<tr><td style='padding-top: 10px; text-align: right; border-top: 1px solid grey'>
+ <input type='button' value='Apply' onclick='applySettings()'>
+ <input type='button' value='Cancel' onclick='cancelSettings()'>
+</td></tr></table>
+</body>
+</html>
diff --git a/pkg/vm_snapshot_analysis/lib/src/commands/compare.dart b/pkg/vm_snapshot_analysis/lib/src/commands/compare.dart
new file mode 100644
index 0000000..52f8b43
--- /dev/null
+++ b/pkg/vm_snapshot_analysis/lib/src/commands/compare.dart
@@ -0,0 +1,153 @@
+// Copyright (c) 2018, 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 tool compares two JSON size reports produced by
+/// --print-instructions-sizes-to and reports which symbols increased in size
+/// and which symbols decreased in size.
+library vm_snapshot_analysis.compare;
+
+import 'dart:io';
+
+import 'package:args/command_runner.dart';
+
+import 'package:vm_snapshot_analysis/ascii_table.dart';
+import 'package:vm_snapshot_analysis/program_info.dart';
+import 'package:vm_snapshot_analysis/utils.dart';
+import 'package:vm_snapshot_analysis/v8_profile.dart';
+
+class CompareCommand extends Command<void> {
+ @override
+ final String name = 'compare';
+
+ @override
+ final String description = '''
+Compare two instruction size outputs and report which symbols changed in size.
+
+This tool compares two JSON size reports produced by
+--print-instructions-sizes-to or --write-v8-snapshot-profile-to
+and reports which symbols changed in size.
+
+Both reports should be produced by the same flag!
+
+Use --narrow flag to limit column widths.''';
+
+ @override
+ String get invocation =>
+ super.invocation.replaceAll('[arguments]', '<old.json> <new.json>');
+
+ CompareCommand() {
+ argParser
+ ..addOption('column-width',
+ help: 'Truncate column content to the given width'
+ ' (${AsciiTable.unlimitedWidth} means do not truncate).',
+ defaultsTo: AsciiTable.unlimitedWidth.toString())
+ ..addOption('by',
+ abbr: 'b',
+ help: 'Choose the breakdown rule for the output.',
+ allowed: ['method', 'class', 'library', 'package'],
+ defaultsTo: 'method')
+ ..addFlag('collapse-anonymous-closures', help: '''
+Collapse all anonymous closures from the same scope into a single entry.
+When comparing size of AOT snapshots for two different versions of a
+program there is no reliable way to precisely establish which two anonymous
+closures are the same and should be compared in size - so
+comparison might produce a noisy output. This option reduces confusion
+by collapsing different anonymous closures within the same scope into a
+single entry. Note that when comparing the same application compiled
+with two different versions of an AOT compiler closures can be distinguished
+precisely based on their source position (which is included in their name).
+''');
+ }
+
+ @override
+ Future<void> run() async {
+ if (argResults.rest.length != 2) {
+ usageException('Need to provide path to old.json and new.json reports.');
+ }
+
+ final columnWidth = argResults['column-width'];
+ final maxWidth = int.tryParse(columnWidth);
+ if (maxWidth == null) {
+ usageException(
+ 'Specified column width (${columnWidth}) is not an integer');
+ }
+
+ final oldJsonPath = _checkExists(argResults.rest[0]);
+ final newJsonPath = _checkExists(argResults.rest[1]);
+ printComparison(oldJsonPath, newJsonPath,
+ maxWidth: maxWidth,
+ granularity: _parseHistogramType(argResults['by']),
+ collapseAnonymousClosures: argResults['collapse-anonymous-closures']);
+ }
+
+ HistogramType _parseHistogramType(String value) {
+ switch (value) {
+ case 'method':
+ return HistogramType.bySymbol;
+ case 'class':
+ return HistogramType.byClass;
+ case 'library':
+ return HistogramType.byLibrary;
+ case 'package':
+ return HistogramType.byPackage;
+ }
+ return null;
+ }
+
+ File _checkExists(String path) {
+ final file = File(path);
+ if (!file.existsSync()) {
+ usageException('File $path does not exist!');
+ }
+ return file;
+ }
+
+ void printComparison(File oldJson, File newJson,
+ {int maxWidth = 0,
+ bool collapseAnonymousClosures = false,
+ HistogramType granularity = HistogramType.bySymbol}) async {
+ final oldSizes = await loadProgramInfo(oldJson,
+ collapseAnonymousClosures: collapseAnonymousClosures);
+ final newSizes = await loadProgramInfo(newJson,
+ collapseAnonymousClosures: collapseAnonymousClosures);
+
+ if ((oldSizes.snapshotInfo == null) != (newSizes.snapshotInfo == null)) {
+ usageException('Input files must be produced by the same flag.');
+ }
+
+ final diff = computeDiff(oldSizes, newSizes);
+
+ // Compute total sizes.
+ final totalOld = oldSizes.totalSize;
+ final totalNew = newSizes.totalSize;
+ final totalDiff = diff.totalSize;
+
+ // Compute histogram.
+ final histogram = computeHistogram(diff, granularity);
+
+ // Now produce the report table.
+ const numLargerSymbolsToReport = 30;
+ const numSmallerSymbolsToReport = 10;
+ printHistogram(diff, histogram,
+ sizeHeader: 'Diff (Bytes)',
+ prefix: histogram.bySize
+ .where((k) => histogram.buckets[k] > 0)
+ .take(numLargerSymbolsToReport),
+ suffix: histogram.bySize.reversed
+ .where((k) => histogram.buckets[k] < 0)
+ .take(numSmallerSymbolsToReport)
+ .toList()
+ .reversed,
+ maxWidth: maxWidth);
+
+ print('Comparing ${oldJson.path} (old) to ${newJson.path} (new)');
+ print('Old : ${totalOld} bytes.');
+ print('New : ${totalNew} bytes.');
+ print('Change: ${totalDiff > 0 ? '+' : ''}${totalDiff} bytes.');
+
+ if (oldSizes.snapshotInfo != null) {
+ print(bucketLegend);
+ }
+ }
+}
diff --git a/pkg/vm/lib/snapshot/commands/summary.dart b/pkg/vm_snapshot_analysis/lib/src/commands/summary.dart
similarity index 75%
rename from pkg/vm/lib/snapshot/commands/summary.dart
rename to pkg/vm_snapshot_analysis/lib/src/commands/summary.dart
index f3860a7..af159f1 100644
--- a/pkg/vm/lib/snapshot/commands/summary.dart
+++ b/pkg/vm_snapshot_analysis/lib/src/commands/summary.dart
@@ -5,16 +5,17 @@
/// This tool generates a summary report from a binary size reports produced by
/// the AOT compiler's --print-instructions-sizes-to and
/// --write-v8-snapshot-profile-to flags.
-library vm.snapshot.summary;
+library vm_snapshot_analysis.summary;
import 'dart:async';
import 'dart:io';
import 'package:args/command_runner.dart';
-import 'package:vm/snapshot/ascii_table.dart';
-import 'package:vm/snapshot/program_info.dart';
-import 'package:vm/snapshot/utils.dart';
+import 'package:vm_snapshot_analysis/ascii_table.dart';
+import 'package:vm_snapshot_analysis/program_info.dart';
+import 'package:vm_snapshot_analysis/utils.dart';
+import 'package:vm_snapshot_analysis/v8_profile.dart';
class SummaryCommand extends Command<void> {
@override
@@ -35,10 +36,16 @@
help: 'Truncate column content to the given width'
' (${AsciiTable.unlimitedWidth} means do not truncate).',
defaultsTo: AsciiTable.unlimitedWidth.toString())
- ..addOption('granularity',
- help: 'Choose the granularity of the output.',
+ ..addOption('by',
+ abbr: 'b',
+ help: 'Choose breakdown rule of the output.',
allowed: ['method', 'class', 'library', 'package'],
defaultsTo: 'method')
+ ..addOption(
+ 'where',
+ abbr: 'w',
+ help: 'Filter output using the given glob.',
+ )
..addFlag('collapse-anonymous-closures', help: '''
Collapse all anonymous closures from the same scope into a single entry.
When comparing size of AOT snapshots for two different versions of a
@@ -76,8 +83,9 @@
await outputSummary(input,
maxWidth: maxWidth,
- granularity: _parseHistogramType(argResults['granularity']),
- collapseAnonymousClosures: argResults['collapse-anonymous-closures']);
+ granularity: _parseHistogramType(argResults['by']),
+ collapseAnonymousClosures: argResults['collapse-anonymous-closures'],
+ filter: argResults['where']);
}
static HistogramType _parseHistogramType(String value) {
@@ -98,14 +106,25 @@
void outputSummary(File input,
{int maxWidth = 0,
bool collapseAnonymousClosures = false,
- HistogramType granularity = HistogramType.bySymbol}) async {
+ HistogramType granularity = HistogramType.bySymbol,
+ String filter}) async {
final info = await loadProgramInfo(input);
// Compute histogram.
- final histogram = SizesHistogram.from(info, granularity);
+ final histogram = computeHistogram(info, granularity, filter: filter);
// Now produce the report table.
const topToReport = 30;
- printHistogram(histogram,
+ printHistogram(info, histogram,
prefix: histogram.bySize.take(topToReport), maxWidth: maxWidth);
+
+ if (info.snapshotInfo != null) {
+ print('\nBreakdown by object type:');
+ final typeHistogram =
+ computeHistogram(info, HistogramType.byNodeType, filter: filter);
+ printHistogram(info, typeHistogram,
+ prefix: typeHistogram.bySize, maxWidth: maxWidth);
+
+ print(bucketLegend);
+ }
}
diff --git a/pkg/vm/lib/snapshot/commands/treemap.dart b/pkg/vm_snapshot_analysis/lib/src/commands/treemap.dart
similarity index 64%
rename from pkg/vm/lib/snapshot/commands/treemap.dart
rename to pkg/vm_snapshot_analysis/lib/src/commands/treemap.dart
index 1aa05d3..c5b1b55 100644
--- a/pkg/vm/lib/snapshot/commands/treemap.dart
+++ b/pkg/vm_snapshot_analysis/lib/src/commands/treemap.dart
@@ -8,18 +8,22 @@
///
/// It used the same visualization framework as Chromium's binary_size tool
/// located in runtime/third_party/binary_size.
-library vm.snapshot.commands.treemap;
+library vm_snapshot_analysis.commands.treemap;
import 'dart:async';
import 'dart:convert';
import 'dart:io';
+import 'dart:isolate';
import 'dart:math' show max;
import 'package:path/path.dart' as p;
import 'package:args/command_runner.dart';
-import 'package:vm/snapshot/instruction_sizes.dart' as instruction_sizes;
-import 'package:vm/snapshot/utils.dart';
+import 'package:vm_snapshot_analysis/instruction_sizes.dart'
+ as instruction_sizes;
+import 'package:vm_snapshot_analysis/program_info.dart';
+import 'package:vm_snapshot_analysis/v8_profile.dart' as v8_profile;
+import 'package:vm_snapshot_analysis/utils.dart';
class TreemapCommand extends Command<void> {
@override
@@ -30,7 +34,7 @@
Create interactive treemap from snapshot profiling information.
This tool is used to process snapshot size reports produced by
---print-instructions-sizes-to=symbol-sizes.json.
+--print-instructions-sizes-to and --write-v8-snapshot-profile-to flags.
It will create an interactive web-page in the output-directory which can be
viewed in a browser:
@@ -55,8 +59,8 @@
usageException('Need to specify input JSON and output directory.');
}
- final input = new File(argResults.rest[0]);
- final outputDir = new Directory(argResults.rest[1]);
+ final input = File(argResults.rest[0]);
+ final outputDir = Directory(argResults.rest[1]);
if (!input.existsSync()) {
usageException('Input file ${input.path} does not exist!');
@@ -74,38 +78,43 @@
Future<void> generateTreeMap(File input, Directory outputDir) async {
// Load symbols data produced by the AOT compiler and convert it to
// a tree.
- final symbols = instruction_sizes.fromJson(await loadJson(input));
+ final inputJson = await loadJson(input);
final root = {'n': '', 'children': {}, 'k': kindPath, 'maxDepth': 0};
- for (var symbol in symbols) {
- addSymbol(root, treePath(symbol), symbol.name.scrubbed, symbol.size);
+
+ if (v8_profile.Snapshot.isV8HeapSnapshot(inputJson)) {
+ treemapFromSnapshot(root, v8_profile.Snapshot.fromJson(inputJson));
+ } else {
+ final symbols = instruction_sizes.fromJson(inputJson);
+ for (var symbol in symbols) {
+ addSymbol(root, treePath(symbol), symbol.name.scrubbed, symbol.size);
+ }
}
final tree = flatten(root);
// Create output directory and copy all auxiliary files from binary_size tool.
await outputDir.create(recursive: true);
- final scriptLocation = p.dirname(Platform.script.toFilePath());
- final sdkRoot = p.join(scriptLocation, '..', '..', '..');
- final d3SrcDir = p.join(sdkRoot, 'runtime', 'third_party', 'd3', 'src');
- final templateDir = p.join(
- sdkRoot, 'runtime', 'third_party', 'binary_size', 'src', 'template');
+ final assetsUri = await Isolate.resolvePackageUri(
+ Uri.parse('package:vm_snapshot_analysis/src/assets'));
+ final assetsDir = assetsUri.toFilePath();
+ final d3SrcDir = p.join(assetsDir, 'd3', 'src');
final d3OutDir = p.join(outputDir.path, 'd3');
- await new Directory(d3OutDir).create(recursive: true);
+ await Directory(d3OutDir).create(recursive: true);
for (var file in ['LICENSE', 'd3.js']) {
await copyFile(d3SrcDir, file, d3OutDir);
}
for (var file in ['index.html', 'D3SymbolTreeMap.js']) {
- await copyFile(templateDir, file, outputDir.path);
+ await copyFile(assetsDir, file, outputDir.path);
}
// Serialize symbol size tree as JSON.
final dataJsPath = p.join(outputDir.path, 'data.js');
- final sink = new File(dataJsPath).openWrite();
+ final sink = File(dataJsPath).openWrite();
sink.write('var tree_data=');
- await sink.addStream(new Stream<Object>.fromIterable([tree])
+ await sink.addStream(Stream<Object>.fromIterable([tree])
.transform(json.encoder.fuse(utf8.encoder)));
await sink.close();
@@ -113,6 +122,42 @@
print('Generated ${p.toUri(p.absolute(outputDir.path, 'index.html'))}');
}
+void treemapFromSnapshot(Map<String, dynamic> root, v8_profile.Snapshot snap) {
+ final info = v8_profile.toProgramInfo(snap);
+
+ final ownerPathCache = List<String>(info.snapshotInfo.infoNodes.length);
+ ownerPathCache[info.root.id] = info.root.name;
+
+ String nameOf(v8_profile.Node node) {
+ switch (node.type) {
+ case 'Library':
+ case 'Class':
+ case 'Function':
+ return node.name;
+
+ default:
+ return '${node.type}';
+ }
+ }
+
+ String ownerPath(ProgramInfoNode n) {
+ return ownerPathCache[n.id] ??=
+ ((n.parent != info.root) ? '${ownerPath(n.parent)}/${n.name}' : n.name);
+ }
+
+ for (var node in snap.nodes) {
+ if (node.selfSize > 0) {
+ final owner = info.snapshotInfo.ownerOf(node);
+ final path = ownerPath(owner);
+ final name = nameOf(node);
+ addSymbol(root, path, name, node.selfSize,
+ symbolType: node.type == '(RO) Instructions'
+ ? symbolTypeGlobalText
+ : symbolTypeGlobalInitializedData);
+ }
+ }
+}
+
/// Returns a /-separated path to the given symbol within the treemap.
String treePath(instruction_sizes.SymbolInfo symbol) {
if (symbol.name.isStub) {
@@ -130,14 +175,12 @@
const kindPath = 'p';
const kindBucket = 'b';
const symbolTypeGlobalText = 'T';
+const symbolTypeGlobalInitializedData = 'D';
/// Create a child with the given name within the given node or return
/// an existing child.
Map<String, dynamic> addChild(
Map<String, dynamic> node, String kind, String name) {
- if (kind == kindSymbol && node['children'].containsKey(name)) {
- print('Duplicate symbol ${name}');
- }
return node['children'].putIfAbsent(name, () {
final n = <String, dynamic>{'n': name, 'k': kind};
if (kind != kindSymbol) {
@@ -148,7 +191,8 @@
}
/// Add the given symbol to the tree.
-void addSymbol(Map<String, dynamic> root, String path, String name, int size) {
+void addSymbol(Map<String, dynamic> root, String path, String name, int size,
+ {String symbolType = symbolTypeGlobalText}) {
var node = root;
var depth = 0;
for (var part in path.split('/')) {
@@ -156,11 +200,11 @@
depth++;
}
node['lastPathElement'] = true;
- node = addChild(node, kindBucket, symbolTypeGlobalText);
- node['t'] = symbolTypeGlobalText;
+ node = addChild(node, kindBucket, symbolType);
+ node['t'] = symbolType;
node = addChild(node, kindSymbol, name);
- node['t'] = symbolTypeGlobalText;
- node['value'] = size;
+ node['t'] = symbolType;
+ node['value'] = (node['value'] ?? 0) + size;
depth += 2;
root['maxDepth'] = max<int>(root['maxDepth'], depth);
}
@@ -182,5 +226,5 @@
/// Copy file with the given name from [fromDir] to [toDir].
Future<void> copyFile(String fromDir, String name, String toDir) async {
- await new File(p.join(fromDir, name)).copy(p.join(toDir, name));
+ await File(p.join(fromDir, name)).copy(p.join(toDir, name));
}
diff --git a/pkg/vm_snapshot_analysis/lib/utils.dart b/pkg/vm_snapshot_analysis/lib/utils.dart
new file mode 100644
index 0000000..2f6746a
--- /dev/null
+++ b/pkg/vm_snapshot_analysis/lib/utils.dart
@@ -0,0 +1,102 @@
+// 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.
+library vm_snapshot_analysis.utils;
+
+import 'dart:io';
+import 'dart:convert';
+
+import 'package:vm_snapshot_analysis/ascii_table.dart';
+import 'package:vm_snapshot_analysis/program_info.dart';
+import 'package:vm_snapshot_analysis/instruction_sizes.dart'
+ as instruction_sizes;
+import 'package:vm_snapshot_analysis/v8_profile.dart' as v8_profile;
+
+Future<Object> loadJson(File input) async {
+ return await input
+ .openRead()
+ .transform(utf8.decoder)
+ .transform(json.decoder)
+ .first;
+}
+
+Future<ProgramInfo> loadProgramInfo(File input,
+ {bool collapseAnonymousClosures = false}) async {
+ final json = await loadJson(input);
+ if (v8_profile.Snapshot.isV8HeapSnapshot(json)) {
+ return v8_profile.toProgramInfo(v8_profile.Snapshot.fromJson(json),
+ collapseAnonymousClosures: collapseAnonymousClosures);
+ } else {
+ return instruction_sizes.loadProgramInfo(json,
+ collapseAnonymousClosures: collapseAnonymousClosures);
+ }
+}
+
+void printHistogram(ProgramInfo info, Histogram histogram,
+ {Iterable<String> prefix = const [],
+ Iterable<String> suffix = const [],
+ String sizeHeader = 'Size (Bytes)',
+ int maxWidth = 0}) {
+ final totalSize = info.totalSize;
+ final wasFiltered = totalSize != histogram.totalSize;
+ final table = AsciiTable(header: [
+ for (var col in histogram.bucketInfo.nameComponents) Text.left(col),
+ Text.right(sizeHeader),
+ Text.right('Percent'),
+ if (wasFiltered) Text.right('Of total'),
+ ], maxWidth: maxWidth);
+
+ String formatPercent(int value, int total) {
+ final p = value / total * 100.0;
+ return p.toStringAsFixed(2) + "%";
+ }
+
+ if (prefix.isNotEmpty) {
+ for (var key in prefix) {
+ final size = histogram.buckets[key];
+ table.addRow([
+ ...histogram.bucketInfo.namesFromBucket(key),
+ size.toString(),
+ formatPercent(size, histogram.totalSize),
+ if (wasFiltered) formatPercent(size, totalSize),
+ ]);
+ }
+ table.addSeparator(
+ prefix.length < histogram.length ? Separator.Wave : Separator.Line);
+ }
+
+ final visibleSize = [prefix, suffix]
+ .expand((l) => l)
+ .fold(0, (sum, key) => sum + histogram.buckets[key]);
+ final numRestRows = histogram.length - (suffix.length + prefix.length);
+ if (numRestRows > 0) {
+ final totalRestBytes = histogram.totalSize - visibleSize;
+ table.addTextSeparator(
+ '$numRestRows more rows accounting for ${totalRestBytes}'
+ ' (${formatPercent(totalRestBytes, totalSize)} of total) bytes');
+ final avg = (totalRestBytes / numRestRows).round();
+ table.addTextSeparator(
+ 'on average that is ${avg} (${formatPercent(avg, histogram.totalSize)})'
+ ' bytes per row');
+ table.addSeparator(suffix.isNotEmpty ? Separator.Wave : Separator.Line);
+ }
+
+ if (suffix.isNotEmpty) {
+ for (var key in suffix) {
+ table.addRow([
+ ...histogram.bucketInfo.namesFromBucket(key),
+ histogram.buckets[key].toString(),
+ formatPercent(histogram.buckets[key], histogram.totalSize),
+ ]);
+ }
+ table.addSeparator(Separator.Line);
+ }
+
+ table.render();
+
+ if (wasFiltered || visibleSize != histogram.totalSize) {
+ print('In visible rows: ${visibleSize}'
+ ' (${formatPercent(visibleSize, totalSize)} of total)');
+ }
+ print('Total: ${totalSize} bytes');
+}
diff --git a/pkg/vm_snapshot_analysis/lib/v8_profile.dart b/pkg/vm_snapshot_analysis/lib/v8_profile.dart
new file mode 100644
index 0000000..c002c6f
--- /dev/null
+++ b/pkg/vm_snapshot_analysis/lib/v8_profile.dart
@@ -0,0 +1,564 @@
+// 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 library contains utilities for reading and analyzing snapshot profiles
+/// produced by `--write-v8-snapshot-profile-to` VM flag.
+library vm_snapshot_analysis.v8_profile;
+
+import 'package:meta/meta.dart';
+import 'package:vm_snapshot_analysis/name.dart';
+
+import 'package:vm_snapshot_analysis/program_info.dart';
+
+/// This class represents snapshot graph.
+///
+/// Note that we do not eagerly deserialize the graph, instead we provide helper
+/// methods and wrapper objects to work with serialized representation.
+class Snapshot {
+ final Meta meta;
+ final int nodeCount;
+ final int edgeCount;
+
+ /// Serialized flat representation of nodes in the graph. Each node occupies
+ /// [meta.nodeFieldCount] consecutive elements of the list.
+ final List _nodes;
+
+ /// Serialized flat representation of edges between nodes. Each edge occupies
+ /// [meta.edgeFieldCount] consecutive elements of the list. All outgoing edges
+ /// for a node are serialized consecutively, number of outgoing edges is given
+ /// by the value at index [meta.nodeEdgeCountIndex] inside the node.
+ final List _edges;
+
+ /// Auxiliary array which gives starting index of edges (in the [_edges] list)
+ /// for the given node index.
+ final List<int> _edgesStartIndexForNode;
+
+ final List strings;
+
+ Snapshot._(this.meta, this.nodeCount, this.edgeCount, this._nodes,
+ this._edges, this.strings, this._edgesStartIndexForNode);
+
+ /// Return node with the given index.
+ Node nodeAt(int index) {
+ assert(index >= 0, 'Node index should be positive: $index');
+ return Node._(snapshot: this, index: index);
+ }
+
+ /// Return all nodes in the snapshot.
+ Iterable<Node> get nodes => Iterable.generate(nodeCount, nodeAt);
+
+ /// Returns true if the given JSON object is likely to be a serialized
+ /// snapshot using V8 heap snapshot format.
+ static bool isV8HeapSnapshot(Object m) =>
+ m is Map<String, dynamic> && m.containsKey('snapshot');
+
+ /// Construct [Snapshot] object from the given JSON object.
+ factory Snapshot.fromJson(Map<String, dynamic> m) {
+ // Extract meta information first.
+ final meta = Meta._fromJson(m['snapshot']['meta']);
+
+ final nodes = m['nodes'];
+
+ // Build an array of starting indexes of edges for each node.
+ final edgesStartIndexForNode = <int>[0];
+ int nextStartIndex = 0;
+ for (var i = meta.nodeEdgeCountIndex;
+ i < nodes.length;
+ i += meta.nodeFieldCount) {
+ nextStartIndex += nodes[i];
+ edgesStartIndexForNode.add(nextStartIndex);
+ }
+
+ return Snapshot._(
+ meta,
+ m['snapshot']['node_count'],
+ m['snapshot']['edge_count'],
+ m['nodes'],
+ m['edges'],
+ m['strings'],
+ edgesStartIndexForNode);
+ }
+}
+
+/// Meta-information about the serialized snapshot.
+///
+/// Describes the structure of serialized nodes and edges by giving indexes of
+/// the various fields.
+class Meta {
+ final int nodeTypeIndex;
+ final int nodeNameIndex;
+ final int nodeIdIndex;
+ final int nodeSelfSizeIndex;
+ final int nodeEdgeCountIndex;
+ final int nodeFieldCount;
+
+ final int edgeTypeIndex;
+ final int edgeNameOrIndexIndex;
+ final int edgeToNodeIndex;
+ final int edgeFieldCount;
+
+ final List<String> nodeTypes;
+ final List<String> edgeTypes;
+
+ Meta._(
+ {this.nodeTypeIndex,
+ this.nodeNameIndex,
+ this.nodeIdIndex,
+ this.nodeSelfSizeIndex,
+ this.nodeEdgeCountIndex,
+ this.nodeFieldCount,
+ this.edgeTypeIndex,
+ this.edgeNameOrIndexIndex,
+ this.edgeToNodeIndex,
+ this.edgeFieldCount,
+ this.nodeTypes,
+ this.edgeTypes});
+
+ factory Meta._fromJson(Map<String, dynamic> m) {
+ final nodeFields = m['node_fields'];
+ final nodeTypes = m['node_types'].first.cast<String>();
+ final edgeFields = m['edge_fields'];
+ final edgeTypes = m['edge_types'].first.cast<String>();
+ return Meta._(
+ nodeTypeIndex: nodeFields.indexOf('type'),
+ nodeNameIndex: nodeFields.indexOf('name'),
+ nodeIdIndex: nodeFields.indexOf('id'),
+ nodeSelfSizeIndex: nodeFields.indexOf('self_size'),
+ nodeEdgeCountIndex: nodeFields.indexOf('edge_count'),
+ nodeFieldCount: nodeFields.length,
+ edgeTypeIndex: edgeFields.indexOf('type'),
+ edgeNameOrIndexIndex: edgeFields.indexOf('name_or_index'),
+ edgeToNodeIndex: edgeFields.indexOf('to_node'),
+ edgeFieldCount: edgeFields.length,
+ nodeTypes: nodeTypes,
+ edgeTypes: edgeTypes);
+ }
+}
+
+/// Edge from [Node] to [Node] in the [Snapshot] graph.
+class Edge {
+ final Snapshot snapshot;
+
+ /// Index of this [Edge] within the [snapshot].
+ final int index;
+
+ Edge._({this.snapshot, this.index});
+
+ String get type => snapshot
+ .meta.edgeTypes[snapshot._edges[_offset + snapshot.meta.edgeTypeIndex]];
+
+ Node get target {
+ return Node._(
+ snapshot: snapshot,
+ index: snapshot._edges[_offset + snapshot.meta.edgeToNodeIndex] ~/
+ snapshot.meta.nodeFieldCount);
+ }
+
+ String get name {
+ final nameOrIndex =
+ snapshot._edges[_offset + snapshot.meta.edgeNameOrIndexIndex];
+ return type == 'property' ? snapshot.strings[nameOrIndex] : '@$nameOrIndex';
+ }
+
+ @override
+ String toString() {
+ final nameOrIndex =
+ snapshot._edges[_offset + snapshot.meta.edgeNameOrIndexIndex];
+ return {
+ 'type': type,
+ 'nameOrIndex':
+ type == 'property' ? snapshot.strings[nameOrIndex] : nameOrIndex,
+ 'toNode': target.index,
+ }.toString();
+ }
+
+ /// Offset into [Snapshot._edges] list at which this edge begins.
+ int get _offset => index * snapshot.meta.edgeFieldCount;
+}
+
+/// Node in the [Snapshot] graph.
+class Node {
+ final Snapshot snapshot;
+
+ /// Index of this [Node] within the [snapshot].
+ final int index;
+
+ Node._({this.snapshot, this.index});
+
+ int get edgeCount =>
+ snapshot._nodes[_offset + snapshot.meta.nodeEdgeCountIndex];
+
+ String get type => snapshot
+ .meta.nodeTypes[snapshot._nodes[_offset + snapshot.meta.nodeTypeIndex]];
+
+ String get name =>
+ snapshot.strings[snapshot._nodes[_offset + snapshot.meta.nodeNameIndex]];
+
+ int get selfSize =>
+ snapshot._nodes[_offset + snapshot.meta.nodeSelfSizeIndex];
+
+ int get id => snapshot._nodes[_offset + snapshot.meta.nodeIdIndex];
+
+ /// Returns all outgoing edges for this node.
+ Iterable<Edge> get edges sync* {
+ var firstEdgeIndex = snapshot._edgesStartIndexForNode[index];
+ for (var i = 0, n = edgeCount; i < n; i++) {
+ yield Edge._(snapshot: snapshot, index: firstEdgeIndex + i);
+ }
+ }
+
+ @override
+ String toString() {
+ return {
+ 'type': type,
+ 'name': name,
+ 'id': id,
+ 'selfSize': selfSize,
+ 'edges': edges.toList(),
+ }.toString();
+ }
+
+ /// Returns the target of an outgoing edge with the given name (if any).
+ Node operator [](String edgeName) => this
+ .edges
+ .firstWhere((e) => e.name == edgeName, orElse: () => null)
+ ?.target;
+
+ @override
+ bool operator ==(Object other) {
+ return other is Node && other.index == index;
+ }
+
+ @override
+ int get hashCode => this.index.hashCode;
+
+ /// Offset into [Snapshot._nodes] list at which this node begins.
+ int get _offset => index * snapshot.meta.nodeFieldCount;
+}
+
+/// Class representing information about V8 snapshot profile in relation
+/// to a [ProgramInfo] structure that was derived from it.
+class SnapshotInfo {
+ final Snapshot snapshot;
+
+ final List<ProgramInfoNode> infoNodes;
+ final Map<int, int> _ownerOf;
+
+ SnapshotInfo._(this.snapshot, this.infoNodes, this._ownerOf);
+
+ ProgramInfoNode ownerOf(Node node) =>
+ infoNodes[_ownerOf[node.index] ?? ProgramInfo.unknownId];
+}
+
+ProgramInfo toProgramInfo(Snapshot snap,
+ {bool collapseAnonymousClosures = false}) {
+ return _ProgramInfoBuilder(
+ collapseAnonymousClosures: collapseAnonymousClosures)
+ .build(snap);
+}
+
+class _ProgramInfoBuilder {
+ final bool collapseAnonymousClosures;
+
+ final program = ProgramInfo();
+
+ final List<ProgramInfoNode> infoNodes = [];
+
+ /// Mapping between snapshot [Node] index and id of [ProgramInfoNode] which
+ /// own this node.
+ final Map<int, int> ownerOf = {};
+
+ /// Mapping between snapshot [Node] indices and corresponding
+ /// [ProgramInfoNode] objects. Note that multiple snapshot nodes might be
+ /// mapped to a single [ProgramInfoNode] (e.g. when anonymous closures are
+ /// collapsed).
+ final Map<int, ProgramInfoNode> infoNodeByIndex = {};
+
+ // Mapping between package names and corresponding [ProgramInfoNode] objects
+ // representing those packages.
+ final Map<String, ProgramInfoNode> infoNodeForPackage = {};
+
+ /// Owners of some [Node] are determined by the program structure and not
+ /// by their reachability through the graph. For example, an owner of a
+ /// function is a class that contains it, even though the function can
+ /// also be reachable from another function through object pool.
+ final Set<int> nodesWithFrozenOwner = {};
+
+ /// Cache used to optimize common ancestor operation on [ProgramInfoNode] ids.
+ /// See [findCommonAncestor] method.
+ final Map<int, int> commonAncestorCache = {};
+
+ _ProgramInfoBuilder({this.collapseAnonymousClosures});
+
+ /// Recover [ProgramInfo] structure from the snapshot profile.
+ ///
+ /// This is done via a simple graph traversal: first all nodes representing
+ /// objects with clear ownership (like libraries, classes, functions) are
+ /// discovered and corresponding [ProgramInfoNode] objects are created for
+ /// them. Then the rest of the snapshot is attributed to one of these nodes
+ /// based on reachability (ignoring reachability from normal snapshot roots):
+ /// let `R(n)` be a set of [ProgramInfoNode] objects from which a given
+ /// snapshot node `n` is reachable. Then we define an owner of `n` to be
+ /// a lowest common ancestor of all nodes in `R(n)`.
+ ///
+ /// Nodes which are not reachable from any normal [ProgramInfoNode] are
+ /// attributed to special `@unknown` [ProgramInfoNode].
+ ProgramInfo build(Snapshot snap) {
+ infoNodes.add(program.root);
+ infoNodes.add(program.stubs);
+ infoNodes.add(program.unknown);
+
+ // Create ProgramInfoNode for every snapshot node representing an element
+ // of the program structure (e.g. a library, a class, a function).
+ snap.nodes.forEach(getInfoNodeFor);
+
+ // Propagate the ownership information across the edges.
+ final worklist = ownerOf.keys.toList();
+ while (worklist.isNotEmpty) {
+ final node = snap.nodeAt(worklist.removeLast());
+ final sourceOwner = ownerOf[node.index];
+ for (var e in node.edges) {
+ final target = e.target;
+ if (!nodesWithFrozenOwner.contains(target.index)) {
+ final targetOwner = ownerOf[target.index];
+ final updatedOwner = findCommonAncestor(sourceOwner, targetOwner);
+ if (updatedOwner != targetOwner) {
+ ownerOf[target.index] = updatedOwner;
+ worklist.add(target.index);
+ }
+ }
+ }
+ }
+
+ // Now attribute sizes from the snapshot to nodes that own them.
+ for (var node in snap.nodes) {
+ if (node.selfSize > 0) {
+ final owner = infoNodes[ownerOf[node.index] ?? ProgramInfo.unknownId];
+ owner.size = (owner.size ?? 0) + node.selfSize;
+ }
+ }
+
+ program.snapshotInfo = SnapshotInfo._(snap, infoNodes, ownerOf);
+
+ return program;
+ }
+
+ ProgramInfoNode getInfoNodeFor(Node node) {
+ var info = infoNodeByIndex[node.index];
+ if (info == null) {
+ info = createInfoNodeFor(node);
+ if (info != null) {
+ // Snapshot nodes which represent the program structure can't change
+ // their owner during iteration - their owner is frozen and is given
+ // by the program structure.
+ nodesWithFrozenOwner.add(node.index);
+ ownerOf[node.index] = info.parent?.id ?? info.id;
+
+ // Handle some nodes specially.
+ switch (node.type) {
+ case 'Code':
+ // Freeze ownership of the Instructions object.
+ final instructions = node['<instructions>'];
+ nodesWithFrozenOwner.add(instructions.index);
+ ownerOf[instructions.index] =
+ findCommonAncestor(ownerOf[instructions.index], info.id);
+ break;
+ case 'Library':
+ // Freeze ownership of the Script objects owned by this library.
+ final scripts = node['owned_scripts_'];
+ if (scripts != null) {
+ for (var e in scripts.edges) {
+ if (e.target.type == 'Script') {
+ nodesWithFrozenOwner.add(e.target.index);
+ ownerOf[e.target.index] =
+ findCommonAncestor(ownerOf[e.target.index], info.id);
+ }
+ }
+ }
+ break;
+ }
+ }
+ }
+ return info;
+ }
+
+ ProgramInfoNode createInfoNodeFor(Node node) {
+ switch (node.type) {
+ case 'Code':
+ var owner = node['owner_'];
+ if (owner.type != 'Type') {
+ if (owner.type == 'WeakSerializationReference') {
+ owner = node[':owner_'];
+ }
+ final ownerNode =
+ owner.type == 'Null' ? program.stubs : getInfoNodeFor(owner);
+ return makeInfoNode(node.index,
+ name: node.name, parent: ownerNode, type: NodeType.other);
+ }
+ break;
+
+ case 'Function':
+ if (node.name != '<anonymous signature>') {
+ var owner = node['owner_'];
+ if (node['data_'].type == 'ClosureData') {
+ owner = node['data_']['parent_function_'];
+ }
+ return makeInfoNode(node.index,
+ name: node.name,
+ parent: getInfoNodeFor(owner),
+ type: NodeType.functionNode);
+ }
+ break;
+
+ case 'PatchClass':
+ return getInfoNodeFor(node['patched_class_']);
+
+ case 'Class':
+ if (node['library_'] != null) {
+ return makeInfoNode(node.index,
+ name: node.name,
+ parent: getInfoNodeFor(node['library_']) ?? program.root,
+ type: NodeType.classNode);
+ }
+ break;
+
+ case 'Library':
+ // Create fake owner node for the package which contains this library.
+ final packageName = packageOf(node.name);
+ return makeInfoNode(node.index,
+ name: node.name,
+ parent: packageName != node.name
+ ? packageOwner(packageName)
+ : program.root,
+ type: NodeType.libraryNode);
+
+ case 'Field':
+ return makeInfoNode(node.index,
+ name: node.name,
+ parent: getInfoNodeFor(node['owner_']),
+ type: NodeType.other);
+ }
+ return null;
+ }
+
+ ProgramInfoNode makeInfoNode(int index,
+ {@required ProgramInfoNode parent,
+ @required String name,
+ @required NodeType type}) {
+ assert(parent != null,
+ 'Trying to create node of type ${type} with ${name} and no parent.');
+ assert(name != null);
+
+ name = Name(name).scrubbed;
+ if (collapseAnonymousClosures) {
+ name = Name.collapse(name);
+ }
+
+ final node = program.makeNode(name: name, parent: parent, type: type);
+ if (node.id == infoNodes.length) {
+ infoNodes.add(node);
+ }
+ if (index != null) {
+ assert(!infoNodeByIndex.containsKey(index));
+ infoNodeByIndex[index] = node;
+ }
+ return node;
+ }
+
+ ProgramInfoNode packageOwner(String packageName) =>
+ infoNodeForPackage.putIfAbsent(
+ packageName,
+ () => makeInfoNode(null,
+ name: packageName,
+ type: NodeType.packageNode,
+ parent: program.root));
+
+ /// Create a single key from two node ids.
+ /// Note that this operation is commutative, because common ancestor of A and
+ /// B is the same as common ancestor of B and A.
+ static int ancestorCacheKey(int a, int b) {
+ if (a > b) {
+ return b << 32 | a;
+ } else {
+ return a << 32 | b;
+ }
+ }
+
+ /// Returns id of a common ancestor between [ProgramInfoNode] with [idA] and
+ /// [idB].
+ int findCommonAncestor(int idA, int idB) {
+ if (idA == null) {
+ return idB;
+ }
+ if (idB == null) {
+ return idA;
+ }
+ if (idA == idB) {
+ return idA;
+ }
+
+ // If either are shared - then result is shared.
+ if (idA == ProgramInfo.rootId || idB == ProgramInfo.rootId) {
+ return ProgramInfo.rootId;
+ }
+
+ final infoA = infoNodes[idA];
+ final infoB = infoNodes[idB];
+
+ final key = ancestorCacheKey(idA, idB);
+ var ancestor = commonAncestorCache[key];
+ if (ancestor == null) {
+ commonAncestorCache[key] =
+ ancestor = findCommonAncestorImpl(infoA, infoB).id;
+ }
+ return ancestor;
+ }
+
+ static List<ProgramInfoNode> pathToRoot(ProgramInfoNode node) {
+ final path = <ProgramInfoNode>[];
+ while (node != null) {
+ path.add(node);
+ node = node.parent;
+ }
+ return path;
+ }
+
+ static ProgramInfoNode findCommonAncestorImpl(
+ ProgramInfoNode a, ProgramInfoNode b) {
+ final pathA = pathToRoot(a);
+ final pathB = pathToRoot(b);
+ var i = pathA.length - 1, j = pathB.length - 1;
+ while (i > 0 && j > 0 && (pathA[i - 1] == pathB[j - 1])) {
+ i--;
+ j--;
+ }
+ assert(pathA[i] == pathB[j]);
+ return pathA[i];
+ }
+}
+
+final bucketLegend = '''
+
+--------------------------------------------------------------------------------
+IMPORTANT: Dart AOT snapshot is a serialized representation of Dart VM heap.
+Outside of few specific cases (e.g. an object representing a library clearly
+originates from the library it represents) there is no well defined relationship
+between snapshot bytes and a specific method/class/library to which these
+bytes can be attributed with certainty. This snapshot analysis tool tries
+to attribute bytes to specific program structure elements based on their
+reachability from objects with well defined origin - meaning that this analysis
+has some margin of error and imprecision.
+
+- @other bucket denotes bytes attributed to entities outside of the current
+granularity. For example, when breaking down the size by method name there
+might be bytes which exist outside of any specific symbol - in which case
+they will be attributed to @other.
+- @stubs bucket accumulates bytes attributed to stubs (pieces of machine code
+produced by the VM for internal purposes).
+- @shared bucket accumulates bytes shared between otherwise unrelated program
+entities
+- @unknown bucket accumulates bytes which are not reachable from any program
+structure nodes (usually VM internal objects).
+--------------------------------------------------------------------------------
+''';
diff --git a/pkg/vm_snapshot_analysis/pubspec.yaml b/pkg/vm_snapshot_analysis/pubspec.yaml
new file mode 100644
index 0000000..ab0a015
--- /dev/null
+++ b/pkg/vm_snapshot_analysis/pubspec.yaml
@@ -0,0 +1,20 @@
+name: vm_snapshot_analysis
+description: Utilities for working with non-symbolic stack traces.
+version: 0.1.0
+
+homepage: https://github.com/dart-lang/sdk/tree/master/pkg/vm_snapshot_analysis
+
+environment:
+ sdk: '>=2.8.0 <3.0.0'
+
+executables:
+ snapshot_analysis: analyse
+
+dependencies:
+ args: ^1.6.0
+ path: ^1.7.0
+ meta: ^1.1.8
+
+dev_dependencies:
+ pedantic: ^1.9.0
+ test: ^1.15.1
diff --git a/pkg/vm_snapshot_analysis/test/instruction_sizes_test.dart b/pkg/vm_snapshot_analysis/test/instruction_sizes_test.dart
new file mode 100644
index 0000000..55c71f0
--- /dev/null
+++ b/pkg/vm_snapshot_analysis/test/instruction_sizes_test.dart
@@ -0,0 +1,720 @@
+// 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:path/path.dart' as path;
+import 'package:test/test.dart';
+
+import 'package:vm_snapshot_analysis/instruction_sizes.dart'
+ as instruction_sizes;
+import 'package:vm_snapshot_analysis/program_info.dart';
+import 'package:vm_snapshot_analysis/utils.dart';
+
+final dart2native = () {
+ final sdkBin = path.dirname(Platform.executable);
+ final dart2native =
+ path.join(sdkBin, Platform.isWindows ? 'dart2native.bat' : 'dart2native');
+
+ if (!File(dart2native).existsSync()) {
+ throw 'Failed to locate dart2native in the SDK';
+ }
+
+ return path.canonicalize(dart2native);
+}();
+
+final testSource = {
+ 'input.dart': """
+class K {
+ final value;
+ const K(this.value);
+}
+
+@pragma('vm:never-inline')
+dynamic makeSomeClosures() {
+ return [
+ () => const K(0),
+ () => const K(1),
+ () => 11,
+ ];
+}
+
+class A {
+ @pragma('vm:never-inline')
+ dynamic tornOff() {
+ return const K(2);
+ }
+}
+
+class B {
+ @pragma('vm:never-inline')
+ dynamic tornOff() {
+ return const K(3);
+ }
+}
+
+class C {
+ static dynamic tornOff() async {
+ return const K(4);
+ }
+}
+
+@pragma('vm:never-inline')
+Function tearOff(dynamic o) {
+ return o.tornOff;
+}
+
+void main(List<String> args) {
+ for (var cl in makeSomeClosures()) {
+ print(cl());
+ }
+ print(tearOff(args.isEmpty ? A() : B()));
+ print(C.tornOff);
+}
+"""
+};
+
+// Almost exactly the same source as above, but with few modifications
+// marked with a 'modified' comment.
+final testSourceModified = {
+ 'input.dart': """
+class K {
+ final value;
+ const K(this.value);
+}
+
+@pragma('vm:never-inline')
+dynamic makeSomeClosures() {
+ return [
+ () => const K(0),
+ () => const K(1),
+ () => 11,
+ () => {}, // modified
+ ];
+}
+
+class A {
+ @pragma('vm:never-inline')
+ dynamic tornOff() {
+ for (var cl in makeSomeClosures()) { // modified
+ print(cl()); // modified
+ } // modified
+ return const K(2);
+ }
+}
+
+class B {
+ @pragma('vm:never-inline')
+ dynamic tornOff() {
+ return const K(3);
+ }
+}
+
+class C {
+ static dynamic tornOff() async {
+ return const K(4);
+ }
+}
+
+@pragma('vm:never-inline')
+Function tearOff(dynamic o) {
+ return o.tornOff;
+}
+
+void main(List<String> args) {
+ // modified
+ print(tearOff(args.isEmpty ? A() : B()));
+ print(C.tornOff);
+}
+"""
+};
+
+final testSourceModified2 = {
+ 'input.dart': """
+class K {
+ final value;
+ const K(this.value);
+}
+
+@pragma('vm:never-inline')
+dynamic makeSomeClosures() {
+ return [
+ () => const K(0),
+ ];
+}
+
+class A {
+ @pragma('vm:never-inline')
+ dynamic tornOff() {
+ return const K(2);
+ }
+}
+
+class B {
+ @pragma('vm:never-inline')
+ dynamic tornOff() {
+ return const K(3);
+ }
+}
+
+class C {
+ static dynamic tornOff() async {
+ return const K(4);
+ }
+}
+
+@pragma('vm:never-inline')
+Function tearOff(dynamic o) {
+ return o.tornOff;
+}
+
+void main(List<String> args) {
+ for (var cl in makeSomeClosures()) {
+ print(cl());
+ }
+ print(tearOff(args.isEmpty ? A() : B()));
+ print(C.tornOff);
+}
+"""
+};
+
+extension on Histogram {
+ String bucketFor(String pkg, String lib, String cls, String fun) =>
+ (this.bucketInfo as Bucketing).bucketFor(pkg, lib, cls, fun);
+}
+
+void main() async {
+ if (!Platform.executable.contains('dart-sdk')) {
+ // If we are not running from the prebuilt SDK then this test does nothing.
+ return;
+ }
+
+ group('instruction-sizes', () {
+ test('basic-parsing', () async {
+ await withSymbolSizes('basic-parsing', testSource, (sizesJson) async {
+ final symbols =
+ instruction_sizes.fromJson(await loadJson(File(sizesJson)));
+ expect(symbols, isNotNull,
+ reason: 'Sizes file was successfully parsed');
+ expect(symbols.length, greaterThan(0),
+ reason: 'Sizes file is non-empty');
+
+ // Check for duplicated symbols (using both raw and scrubbed names).
+ // Maps below contain mappings library-uri -> class-name -> names.
+ final symbolRawNames = <String, Map<String, Set<String>>>{};
+ final symbolScrubbedNames = <String, Map<String, Set<String>>>{};
+
+ Set<String> getSetOfNames(Map<String, Map<String, Set<String>>> map,
+ String libraryUri, String className) {
+ return map
+ .putIfAbsent(libraryUri ?? '', () => {})
+ .putIfAbsent(className ?? '', () => {});
+ }
+
+ for (var sym in symbols) {
+ expect(
+ getSetOfNames(symbolRawNames, sym.libraryUri, sym.className)
+ .add(sym.name.raw),
+ isTrue,
+ reason:
+ 'All symbols should have unique names (within libraries): ${sym.name.raw}');
+ expect(
+ getSetOfNames(symbolScrubbedNames, sym.libraryUri, sym.className)
+ .add(sym.name.scrubbed),
+ isTrue,
+ reason: 'Scrubbing the name should not make it non-unique');
+ }
+
+ // Check for expected names which should appear in the output.
+ final inputDartSymbolNames =
+ symbolScrubbedNames['package:input/input.dart'];
+ expect(inputDartSymbolNames, isNotNull,
+ reason: 'Symbols from input.dart are included into sizes output');
+
+ expect(inputDartSymbolNames[''], isNotNull,
+ reason: 'Should include top-level members from input.dart');
+ expect(inputDartSymbolNames[''], contains('makeSomeClosures'));
+ final closures = inputDartSymbolNames[''].where(
+ (name) => name.startsWith('makeSomeClosures.<anonymous closure'));
+ expect(closures.length, 3,
+ reason: 'There are three closures inside makeSomeClosure');
+
+ expect(inputDartSymbolNames['A'], isNotNull,
+ reason: 'Should include class A members from input.dart');
+ expect(inputDartSymbolNames['A'], contains('tornOff'));
+ expect(inputDartSymbolNames['A'], contains('[tear-off] tornOff'));
+ expect(inputDartSymbolNames['A'],
+ contains('[tear-off-extractor] get:tornOff'));
+
+ expect(inputDartSymbolNames['B'], isNotNull,
+ reason: 'Should include class B members from input.dart');
+ expect(inputDartSymbolNames['B'], contains('tornOff'));
+ expect(inputDartSymbolNames['B'], contains('[tear-off] tornOff'));
+ expect(inputDartSymbolNames['B'],
+ contains('[tear-off-extractor] get:tornOff'));
+
+ // Presence of async modifier should not cause tear-off name to end
+ // with {body}.
+ expect(inputDartSymbolNames['C'], contains('[tear-off] tornOff'));
+
+ // Check that output does not contain '[unknown stub]'
+ expect(symbolRawNames[''][''], isNot(contains('[unknown stub]')),
+ reason: 'All stubs must be named');
+ });
+ });
+
+ test('program-info-from-sizes', () async {
+ await withSymbolSizes('program-info-from-sizes', testSource,
+ (sizesJson) async {
+ final info = await loadProgramInfo(File(sizesJson));
+ expect(info.root.children, contains('dart:core'));
+ expect(info.root.children, contains('dart:typed_data'));
+ expect(info.root.children, contains('package:input'));
+
+ final inputLib = info.root.children['package:input']
+ .children['package:input/input.dart'];
+ expect(inputLib, isNotNull);
+ expect(inputLib.children, contains('')); // Top-level class.
+ expect(inputLib.children, contains('A'));
+ expect(inputLib.children, contains('B'));
+ expect(inputLib.children, contains('C'));
+
+ final topLevel = inputLib.children[''];
+ expect(topLevel.children, contains('makeSomeClosures'));
+ expect(
+ topLevel.children['makeSomeClosures'].children.length, equals(3));
+
+ for (var name in [
+ '[tear-off] tornOff',
+ 'tornOff',
+ 'Allocate A',
+ '[tear-off-extractor] get:tornOff'
+ ]) {
+ expect(inputLib.children['A'].children, contains(name));
+ expect(inputLib.children['A'].children[name].children, isEmpty);
+ }
+
+ for (var name in [
+ '[tear-off] tornOff',
+ 'tornOff',
+ 'Allocate B',
+ '[tear-off-extractor] get:tornOff'
+ ]) {
+ expect(inputLib.children['B'].children, contains(name));
+ expect(inputLib.children['B'].children[name].children, isEmpty);
+ }
+
+ for (var name in ['tornOff{body}', 'tornOff', '[tear-off] tornOff']) {
+ expect(inputLib.children['C'].children, contains(name));
+ expect(inputLib.children['C'].children[name].children, isEmpty);
+ }
+ });
+ });
+
+ test('histograms', () async {
+ await withSymbolSizes('histograms', testSource, (sizesJson) async {
+ final info = await loadProgramInfo(File(sizesJson));
+ final bySymbol = computeHistogram(info, HistogramType.bySymbol);
+ expect(
+ bySymbol.buckets,
+ contains(bySymbol.bucketFor(
+ 'package:input', 'package:input/input.dart', 'A', 'tornOff')));
+ expect(
+ bySymbol.buckets,
+ contains(bySymbol.bucketFor(
+ 'package:input', 'package:input/input.dart', 'B', 'tornOff')));
+ expect(
+ bySymbol.buckets,
+ contains(bySymbol.bucketFor(
+ 'package:input', 'package:input/input.dart', 'C', 'tornOff')));
+
+ final byClass = computeHistogram(info, HistogramType.byClass);
+ expect(
+ byClass.buckets,
+ contains(byClass.bucketFor('package:input',
+ 'package:input/input.dart', 'A', 'does-not-matter')));
+ expect(
+ byClass.buckets,
+ contains(byClass.bucketFor('package:input',
+ 'package:input/input.dart', 'B', 'does-not-matter')));
+ expect(
+ byClass.buckets,
+ contains(byClass.bucketFor('package:input',
+ 'package:input/input.dart', 'C', 'does-not-matter')));
+
+ final byLibrary = computeHistogram(info, HistogramType.byLibrary);
+ expect(
+ byLibrary.buckets,
+ contains(byLibrary.bucketFor(
+ 'package:input',
+ 'package:input/input.dart',
+ 'does-not-matter',
+ 'does-not-matter')));
+
+ final byPackage = computeHistogram(info, HistogramType.byPackage);
+ expect(
+ byPackage.buckets,
+ contains(byPackage.bucketFor(
+ 'package:input',
+ 'package:input/does-not-matter.dart',
+ 'does-not-matter',
+ 'does-not-matter')));
+ });
+ });
+
+ test('diff', () async {
+ await withSymbolSizes('diff-1', testSource, (sizesJson) async {
+ await withSymbolSizes('diff-2', testSourceModified,
+ (modifiedSizesJson) async {
+ final info = await loadProgramInfo(File(sizesJson));
+ final modifiedInfo = await loadProgramInfo(File(modifiedSizesJson));
+ final diff = computeDiff(info, modifiedInfo);
+
+ expect(
+ diffToJson(diff),
+ equals({
+ '#type': 'library',
+ '@stubs': {'#type': 'library'},
+ '@unknown': {'#type': 'library'},
+ 'package:input': {
+ '#type': 'package',
+ 'package:input/input.dart': {
+ '#type': 'library',
+ '': {
+ '#type': 'class',
+ 'makeSomeClosures': {
+ '#type': 'function',
+ '#size': greaterThan(0), // We added code here.
+ '<anonymous closure @180>': {
+ '#type': 'function',
+ '#size': greaterThan(0),
+ },
+ },
+ 'main': {
+ '#type': 'function',
+ '#size': lessThan(0), // We removed code from main.
+ },
+ },
+ 'A': {
+ '#type': 'class',
+ 'tornOff': {
+ '#type': 'function',
+ '#size': greaterThan(0),
+ },
+ }
+ }
+ }
+ }));
+ });
+ });
+ });
+
+ test('diff-collapsed', () async {
+ await withSymbolSizes('diff-collapsed-1', testSource, (sizesJson) async {
+ await withSymbolSizes('diff-collapsed-2', testSourceModified2,
+ (modifiedSizesJson) async {
+ final info = await loadProgramInfo(File(sizesJson),
+ collapseAnonymousClosures: true);
+ final modifiedInfo = await loadProgramInfo(File(modifiedSizesJson),
+ collapseAnonymousClosures: true);
+ final diff = computeDiff(info, modifiedInfo);
+
+ expect(
+ diffToJson(diff),
+ equals({
+ '#type': 'library',
+ '@stubs': {'#type': 'library'},
+ '@unknown': {'#type': 'library'},
+ 'package:input': {
+ '#type': 'package',
+ 'package:input/input.dart': {
+ '#type': 'library',
+ '': {
+ '#type': 'class',
+ 'makeSomeClosures': {
+ '#size': lessThan(0),
+ '#type': 'function',
+ '<anonymous closure>': {
+ '#size': lessThan(0),
+ '#type': 'function'
+ }
+ }
+ }
+ }
+ }
+ }));
+ });
+ });
+ });
+ });
+
+ group('v8-profile', () {
+ test('program-info-from-profile', () async {
+ await withV8Profile('program-info-from-profile', testSource,
+ (profileJson) async {
+ final info = await loadProgramInfo(File(profileJson));
+ expect(info.root.children, contains('dart:core'));
+ expect(info.root.children, contains('dart:typed_data'));
+ expect(info.root.children, contains('package:input'));
+
+ final inputLib = info.root.children['package:input']
+ .children['package:input/input.dart'];
+ expect(inputLib, isNotNull);
+ expect(inputLib.children, contains('::')); // Top-level class.
+ expect(inputLib.children, contains('A'));
+ expect(inputLib.children, contains('B'));
+ expect(inputLib.children, contains('C'));
+
+ final topLevel = inputLib.children['::'];
+ expect(topLevel.children, contains('makeSomeClosures'));
+ expect(
+ topLevel.children['makeSomeClosures'].children.values
+ .where((child) => child.type == NodeType.functionNode)
+ .length,
+ equals(3));
+
+ for (var name in [
+ 'tornOff',
+ 'Allocate A',
+ '[tear-off-extractor] get:tornOff'
+ ]) {
+ expect(inputLib.children['A'].children, contains(name));
+ }
+ expect(inputLib.children['A'].children['tornOff'].children,
+ contains('[tear-off] tornOff'));
+
+ for (var name in [
+ 'tornOff',
+ 'Allocate B',
+ '[tear-off-extractor] get:tornOff'
+ ]) {
+ expect(inputLib.children['B'].children, contains(name));
+ }
+ expect(inputLib.children['B'].children['tornOff'].children,
+ contains('[tear-off] tornOff'));
+
+ expect(inputLib.children['C'].children, contains('tornOff'));
+ for (var name in ['tornOff{body}', '[tear-off] tornOff']) {
+ expect(inputLib.children['C'].children['tornOff'].children,
+ contains(name));
+ }
+ });
+ });
+
+ test('histograms', () async {
+ await withV8Profile('histograms', testSource, (sizesJson) async {
+ final info = await loadProgramInfo(File(sizesJson));
+ final bySymbol = computeHistogram(info, HistogramType.bySymbol);
+ expect(
+ bySymbol.buckets,
+ contains(bySymbol.bucketFor(
+ 'package:input', 'package:input/input.dart', 'A', 'tornOff')));
+ expect(
+ bySymbol.buckets,
+ contains(bySymbol.bucketFor(
+ 'package:input', 'package:input/input.dart', 'B', 'tornOff')));
+ expect(
+ bySymbol.buckets,
+ contains(bySymbol.bucketFor(
+ 'package:input', 'package:input/input.dart', 'C', 'tornOff')));
+
+ final byClass = computeHistogram(info, HistogramType.byClass);
+ expect(
+ byClass.buckets,
+ contains(byClass.bucketFor('package:input',
+ 'package:input/input.dart', 'A', 'does-not-matter')));
+ expect(
+ byClass.buckets,
+ contains(byClass.bucketFor('package:input',
+ 'package:input/input.dart', 'B', 'does-not-matter')));
+ expect(
+ byClass.buckets,
+ contains(byClass.bucketFor('package:input',
+ 'package:input/input.dart', 'C', 'does-not-matter')));
+
+ final byLibrary = computeHistogram(info, HistogramType.byLibrary);
+ expect(
+ byLibrary.buckets,
+ contains(byLibrary.bucketFor(
+ 'package:input',
+ 'package:input/input.dart',
+ 'does-not-matter',
+ 'does-not-matter')));
+
+ final byPackage = computeHistogram(info, HistogramType.byPackage);
+ expect(
+ byPackage.buckets,
+ contains(byPackage.bucketFor(
+ 'package:input',
+ 'package:input/does-not-matter.dart',
+ 'does-not-matter',
+ 'does-not-matter')));
+ });
+ });
+
+ test('diff', () async {
+ await withV8Profile('diff-1', testSource, (profileJson) async {
+ await withV8Profile('diff-2', testSourceModified,
+ (modifiedProfileJson) async {
+ final info = await loadProgramInfo(File(profileJson));
+ final modifiedInfo = await loadProgramInfo(File(modifiedProfileJson));
+ final diff = computeDiff(info, modifiedInfo);
+
+ expect(
+ diffToJson(diff, keepOnlyInputPackage: true),
+ equals({
+ 'package:input': {
+ '#type': 'package',
+ 'package:input/input.dart': {
+ '#type': 'library',
+ '::': {
+ '#type': 'class',
+ 'makeSomeClosures': {
+ '#type': 'function',
+ '#size': greaterThan(0), // We added code here.
+ '<anonymous closure @180>': {
+ '#type': 'function',
+ '#size': greaterThan(0),
+ 'makeSomeClosures.<anonymous closure @180>': {
+ '#size': greaterThan(0)
+ },
+ },
+ 'makeSomeClosures': {'#size': greaterThan(0)},
+ },
+ 'main': {
+ '#type': 'function',
+ '#size': lessThan(0), // We removed code from main.
+ 'main': {'#size': lessThan(0)},
+ },
+ },
+ 'A': {
+ '#type': 'class',
+ 'tornOff': {
+ '#type': 'function',
+ '#size': greaterThan(0),
+ 'tornOff': {'#size': greaterThan(0)},
+ },
+ }
+ }
+ }
+ }));
+ });
+ });
+ });
+
+ test('diff-collapsed', () async {
+ await withV8Profile('diff-collapsed-1', testSource, (profileJson) async {
+ await withV8Profile('diff-collapsed-2', testSourceModified2,
+ (modifiedProfileJson) async {
+ final info = await loadProgramInfo(File(profileJson),
+ collapseAnonymousClosures: true);
+ final modifiedInfo = await loadProgramInfo(File(modifiedProfileJson),
+ collapseAnonymousClosures: true);
+ final diff = computeDiff(info, modifiedInfo);
+
+ expect(
+ diffToJson(diff, keepOnlyInputPackage: true),
+ equals({
+ 'package:input': {
+ '#type': 'package',
+ 'package:input/input.dart': {
+ '#type': 'library',
+ '#size': lessThan(0),
+ '::': {
+ '#type': 'class',
+ 'makeSomeClosures': {
+ '#type': 'function',
+ '#size': lessThan(0),
+ '<anonymous closure>': {
+ '#type': 'function',
+ '#size': lessThan(0),
+ 'makeSomeClosures.<anonymous closure>': {
+ '#size': lessThan(0)
+ },
+ },
+ 'makeSomeClosures': {'#size': lessThan(0)},
+ },
+ },
+ }
+ }
+ }));
+ });
+ });
+ });
+ });
+}
+
+Future withSymbolSizes(String prefix, Map<String, String> source,
+ Future Function(String sizesJson) f) =>
+ withFlag(prefix, source, '--print_instructions_sizes_to', f);
+
+Future withV8Profile(String prefix, Map<String, String> source,
+ Future Function(String sizesJson) f) =>
+ withFlag(prefix, source, '--write_v8_snapshot_profile_to', f);
+
+Future withFlag(String prefix, Map<String, String> source, String flag,
+ Future Function(String sizesJson) f) {
+ return withTempDir(prefix, (dir) async {
+ final outputBinary = path.join(dir, 'output.exe');
+ final sizesJson = path.join(dir, 'sizes.json');
+ final packages = path.join(dir, '.packages');
+ final mainDart = path.join(dir, 'main.dart');
+
+ // Create test input.
+ for (var file in source.entries) {
+ await File(path.join(dir, file.key)).writeAsString(file.value);
+ }
+ await File(packages).writeAsString('''
+input:./
+''');
+ await File(mainDart).writeAsString('''
+import 'package:input/input.dart' as input;
+
+void main(List<String> args) => input.main(args);
+''');
+
+ // Compile input.dart to native and output instruction sizes.
+ final result = await Process.run(dart2native, [
+ '-o',
+ outputBinary,
+ '--packages=$packages',
+ '--extra-gen-snapshot-options=$flag=$sizesJson',
+ mainDart,
+ ]);
+
+ expect(result.exitCode, equals(0), reason: '''
+Compilation completed successfully.
+
+stdout: ${result.stdout}
+stderr: ${result.stderr}
+''');
+ expect(File(outputBinary).existsSync(), isTrue,
+ reason: 'Output binary exists');
+ expect(File(sizesJson).existsSync(), isTrue,
+ reason: 'Instruction sizes output exists');
+
+ await f(sizesJson);
+ });
+}
+
+Future withTempDir(String prefix, Future Function(String dir) f) async {
+ final tempDir =
+ Directory.systemTemp.createTempSync('instruction-sizes-test-${prefix}');
+ try {
+ await f(tempDir.path);
+ } finally {
+ tempDir.deleteSync(recursive: true);
+ }
+}
+
+// On Windows there is some issue with interpreting entry point URI as a package URI
+// it instead gets interpreted as a file URI - which breaks comparison. So we
+// simply ignore entry point library (main.dart).
+Map<String, dynamic> diffToJson(ProgramInfo diff,
+ {bool keepOnlyInputPackage = false}) {
+ final diffJson = diff.toJson();
+ diffJson.removeWhere((key, _) =>
+ keepOnlyInputPackage ? key != 'package:input' : key.startsWith('file:'));
+ return diffJson;
+}
diff --git a/pkg/vm/test/snapshot/name_test.dart b/pkg/vm_snapshot_analysis/test/name_test.dart
similarity index 95%
rename from pkg/vm/test/snapshot/name_test.dart
rename to pkg/vm_snapshot_analysis/test/name_test.dart
index 9bd1216..762f7f4 100644
--- a/pkg/vm/test/snapshot/name_test.dart
+++ b/pkg/vm_snapshot_analysis/test/name_test.dart
@@ -4,7 +4,7 @@
import 'package:test/test.dart';
-import 'package:vm/snapshot/name.dart';
+import 'package:vm_snapshot_analysis/name.dart';
void main() async {
group('name', () {
diff --git a/runtime/bin/crypto_macos.cc b/runtime/bin/crypto_macos.cc
index e5bf130..b3afc6f 100644
--- a/runtime/bin/crypto_macos.cc
+++ b/runtime/bin/crypto_macos.cc
@@ -16,16 +16,14 @@
namespace bin {
bool Crypto::GetRandomBytes(intptr_t count, uint8_t* buffer) {
- ThreadSignalBlocker signal_blocker(SIGPROF);
- intptr_t fd = TEMP_FAILURE_RETRY_NO_SIGNAL_BLOCKER(
- open("/dev/urandom", O_RDONLY | O_CLOEXEC));
+ intptr_t fd = TEMP_FAILURE_RETRY(open("/dev/urandom", O_RDONLY | O_CLOEXEC));
if (fd < 0) {
return false;
}
intptr_t bytes_read = 0;
do {
- int res = TEMP_FAILURE_RETRY_NO_SIGNAL_BLOCKER(
- read(fd, buffer + bytes_read, count - bytes_read));
+ int res =
+ TEMP_FAILURE_RETRY(read(fd, buffer + bytes_read, count - bytes_read));
if (res < 0) {
int err = errno;
close(fd);
diff --git a/runtime/bin/main_options.cc b/runtime/bin/main_options.cc
index aceeeca..c8ab364 100644
--- a/runtime/bin/main_options.cc
+++ b/runtime/bin/main_options.cc
@@ -13,6 +13,7 @@
#include "bin/error_exit.h"
#include "bin/options.h"
#include "bin/platform.h"
+#include "bin/utils.h"
#include "platform/syslog.h"
#if !defined(DART_IO_SECURE_SOCKET_DISABLED)
#include "bin/security_context.h"
@@ -34,8 +35,13 @@
NULL,
};
+static const char* kEnableExperiment1 = "--enable-experiment";
+static const char* kEnableExperiment2 = "--enable_experiment";
+
SnapshotKind Options::gen_snapshot_kind_ = kNone;
bool Options::enable_vm_service_ = false;
+MallocGrowableArray<const char*> Options::enabled_experiments_ =
+ MallocGrowableArray<const char*>(4);
#define OPTION_FIELD(variable) Options::variable##_
@@ -379,6 +385,27 @@
return true;
}
+bool Options::ProcessEnableExperimentOption(const char* arg,
+ CommandLineOptions* vm_options) {
+ const char* value =
+ OptionProcessor::ProcessOption(arg, "--enable_experiment=");
+ if (value == nullptr) {
+ value = OptionProcessor::ProcessOption(arg, "--enable-experiment=");
+ }
+ if (value == nullptr) {
+ return false;
+ }
+ vm_options->AddArgument(arg);
+ Utils::CStringUniquePtr tmp = Utils::CreateCStringUniquePtr(strdup(value));
+ char* save_ptr; // Needed for strtok_r.
+ char* token = strtok_r(const_cast<char*>(tmp.get()), ",", &save_ptr);
+ while (token != NULL) {
+ enabled_experiments_.Add(token);
+ token = strtok_r(NULL, ",", &save_ptr);
+ }
+ return true;
+}
+
static void ResolveDartDevSnapshotPath(const char* script,
char** snapshot_path) {
if (!DartDevUtils::TryResolveDartDevSnapshotPath(snapshot_path)) {
@@ -557,19 +584,61 @@
} else if (i > 1) {
// If we're running with DartDev, we're going to ignore the VM options for
// this VM instance and print a warning.
- Syslog::PrintErr(
- "Warning: The following flags were passed as VM options and are being "
- "ignored: ");
- for (int j = 1; j < script_or_cmd_index; ++j) {
- Syslog::PrintErr("%s", argv[j]);
- if (j + 1 < script_or_cmd_index) {
- Syslog::PrintErr(", ");
+
+ int num_experiment_flags = 0;
+ if (!enabled_experiments_.is_empty()) {
+ for (intptr_t j = 1; j < script_or_cmd_index; ++j) {
+ if ((strstr(argv[j], kEnableExperiment1) != nullptr) ||
+ (strstr(argv[j], kEnableExperiment2) != nullptr)) {
+ ++num_experiment_flags;
+ }
}
}
- Syslog::PrintErr(
- "\nThese flags should be passed after the dart command (e.g., 'dart "
- "run --enable-asserts foo.dart' instead of 'dart --enable-asserts run "
- "foo.dart').\n");
+ if (num_experiment_flags + 1 != script_or_cmd_index) {
+ Syslog::PrintErr(
+ "Warning: The following flags were passed as VM options and are "
+ "being "
+ "ignored:\n\n");
+ for (int j = 1; j < script_or_cmd_index; ++j) {
+ if ((strstr(argv[j], kEnableExperiment1) == nullptr) &&
+ (strstr(argv[j], kEnableExperiment2) == nullptr)) {
+ Syslog::PrintErr(" %s\n", argv[j]);
+ }
+ }
+ Syslog::PrintErr(
+ "\nThese flags should be passed after the dart command (e.g., 'dart "
+ "run --enable-asserts foo.dart' instead of 'dart --enable-asserts "
+ "run "
+ "foo.dart').\n\n");
+ }
+ }
+
+ if (!enabled_experiments_.is_empty() &&
+ !(Options::disable_dart_dev() || run_script)) {
+ intptr_t num_experiments = enabled_experiments_.length();
+ int option_size = strlen(kEnableExperiment1) + 1;
+ for (intptr_t i = 0; i < num_experiments; ++i) {
+ const char* flag = enabled_experiments_.At(i);
+ option_size += strlen(flag);
+ if (i + 1 != num_experiments) {
+ // Account for comma if there's more experiments to add.
+ ++option_size;
+ }
+ }
+ // Make room for null terminator
+ ++option_size;
+
+ char* enabled_experiments_arg = new char[option_size];
+ int offset = snprintf(enabled_experiments_arg, option_size,
+ "%s=", kEnableExperiment1);
+ for (intptr_t i = 0; i < num_experiments; ++i) {
+ const char* flag = enabled_experiments_.At(i);
+ const char* kFormat = (i + 1 != num_experiments) ? "%s," : "%s";
+ offset += snprintf(enabled_experiments_arg + offset, option_size - offset,
+ kFormat, flag);
+ ASSERT(offset < option_size);
+ }
+ dart_options->AddArgument(enabled_experiments_arg);
}
// Parse out options to be passed to dart main.
diff --git a/runtime/bin/main_options.h b/runtime/bin/main_options.h
index 9247863..4fe673c 100644
--- a/runtime/bin/main_options.h
+++ b/runtime/bin/main_options.h
@@ -8,6 +8,7 @@
#include "bin/dartutils.h"
#include "bin/dfe.h"
#include "platform/globals.h"
+#include "platform/growable_array.h"
#include "platform/hashmap.h"
namespace dart {
@@ -69,7 +70,8 @@
V(ProcessEnvironmentOption) \
V(ProcessEnableVmServiceOption) \
V(ProcessObserveOption) \
- V(ProcessAbiVersionOption)
+ V(ProcessAbiVersionOption) \
+ V(ProcessEnableExperimentOption)
// This enum must match the strings in kSnapshotKindNames in main_options.cc.
enum SnapshotKind {
@@ -177,6 +179,7 @@
const char* default_ip);
static int target_abi_version_;
+ static MallocGrowableArray<const char*> enabled_experiments_;
#define OPTION_FRIEND(flag, variable) friend class OptionProcessor_##flag;
STRING_OPTIONS_LIST(OPTION_FRIEND)
diff --git a/runtime/observatory/web/main.dart b/runtime/observatory/web/main.dart
index b09362d..dec58d7 100644
--- a/runtime/observatory/web/main.dart
+++ b/runtime/observatory/web/main.dart
@@ -7,6 +7,7 @@
import 'package:observatory/elements.dart';
import 'package:stack_trace/stack_trace.dart';
+
main() async {
Chain.capture(() async {
Logger.root.level = Level.INFO;
diff --git a/runtime/tests/vm/dart/generic_check_bound_slow_path_test.dart b/runtime/tests/vm/dart/generic_check_bound_slow_path_test.dart
new file mode 100644
index 0000000..7290b2f
--- /dev/null
+++ b/runtime/tests/vm/dart/generic_check_bound_slow_path_test.dart
@@ -0,0 +1,38 @@
+// 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.
+
+// VMOptions=--shared_slow_path_triggers_gc
+
+import 'dart:typed_data';
+
+import 'package:expect/expect.dart';
+
+Uint8List l = int.parse('1') == 1 ? Uint8List(10) : Uint8List(0);
+final int outOfRangeSmi = int.parse('-1');
+final int outOfRangeSmi2 = int.parse('10');
+final int outOfRangeMint = int.parse('${0x7fffffffffffffff}');
+final int outOfRangeMint2 = int.parse('${0x8000000000000000}');
+
+buildErrorMatcher(int outOfRangeValue) {
+ return (error) {
+ return error is RangeError &&
+ error.start == 0 &&
+ error.end == 9 &&
+ error.invalidValue == outOfRangeValue;
+ };
+}
+
+main() {
+ for (int i = 0; i < 10; ++i) l[i] = i;
+
+ Expect.throws(() => l[outOfRangeSmi], buildErrorMatcher(outOfRangeSmi));
+ Expect.throws(() => l[outOfRangeSmi2], buildErrorMatcher(outOfRangeSmi2));
+ Expect.throws(() => l[outOfRangeMint], buildErrorMatcher(outOfRangeMint));
+ Expect.throws(() => l[outOfRangeMint2], buildErrorMatcher(outOfRangeMint2));
+
+ if (int.parse('1') == 0) l = Uint8List(0);
+ for (int i = 0; i < 10; ++i) {
+ Expect.equals(i, l[i]);
+ }
+}
diff --git a/runtime/tests/vm/dart/type_casts_with_null_safety_autodetection_strong_script.dart b/runtime/tests/vm/dart/type_casts_with_null_safety_autodetection_strong_script.dart
new file mode 100644
index 0000000..0821461
--- /dev/null
+++ b/runtime/tests/vm/dart/type_casts_with_null_safety_autodetection_strong_script.dart
@@ -0,0 +1,29 @@
+// 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.
+
+// Test script for type_casts_with_null_safety_autodetection_test.dart which
+// is supposed to run in strong mode because it is opted-in.
+
+import 'package:expect/expect.dart';
+
+dynamic nullObj;
+
+@pragma('vm:never-inline')
+typeCast<T>(x) => x as T;
+
+doTests() {
+ typeCast<dynamic>(nullObj);
+ typeCast<void>(nullObj);
+ Expect.throwsTypeError(() => typeCast<Object>(nullObj));
+ Expect.throwsTypeError(() => typeCast<int>(nullObj));
+ typeCast<List<int>>(<int>[]);
+ Expect.throwsTypeError(() => typeCast<List<int>>(<Null>[]));
+}
+
+main() {
+ for (int i = 0; i < 20; ++i) {
+ doTests();
+ }
+ print('OK(strong)');
+}
diff --git a/runtime/tests/vm/dart/type_casts_with_null_safety_autodetection_test.dart b/runtime/tests/vm/dart/type_casts_with_null_safety_autodetection_test.dart
new file mode 100644
index 0000000..701f409
--- /dev/null
+++ b/runtime/tests/vm/dart/type_casts_with_null_safety_autodetection_test.dart
@@ -0,0 +1,43 @@
+// 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.
+
+// OtherResources=type_casts_with_null_safety_autodetection_strong_script.dart
+// OtherResources=type_casts_with_null_safety_autodetection_weak_script.dart
+
+// This test verifies that casts with type testing stubs work as expected
+// if null safety mode (weak/strong) is auto-detected.
+
+import 'dart:io' show File, Platform;
+
+import 'package:path/path.dart' as path;
+import 'snapshot_test_helper.dart';
+
+runTest(String script, String output, String temp) async {
+ // Need to copy test scripts out of Dart SDK to avoid hardcoded
+ // opted-in/opted-out status for Dart SDK tests.
+ final scriptInTemp = path.join(temp, script);
+ File.fromUri(Platform.script.resolve(script)).copySync(scriptInTemp);
+
+ // Do not add Platform.executableArguments into arguments to avoid passing
+ // --null-safety / --no-null-safety arguments.
+ final result = await runBinary("RUN $script", Platform.executable, [
+ '--enable-experiment=non-nullable',
+ '--deterministic',
+ '--optimization-counter-threshold=10',
+ '--packages=${Platform.packageConfig}',
+ scriptInTemp,
+ ]);
+ expectOutput(output, result);
+}
+
+main() async {
+ await withTempDir((String temp) async {
+ await runTest(
+ 'type_casts_with_null_safety_autodetection_strong_script.dart',
+ 'OK(strong)',
+ temp);
+ await runTest('type_casts_with_null_safety_autodetection_weak_script.dart',
+ 'OK(weak)', temp);
+ });
+}
diff --git a/runtime/tests/vm/dart/type_casts_with_null_safety_autodetection_weak_script.dart b/runtime/tests/vm/dart/type_casts_with_null_safety_autodetection_weak_script.dart
new file mode 100644
index 0000000..a12eacd
--- /dev/null
+++ b/runtime/tests/vm/dart/type_casts_with_null_safety_autodetection_weak_script.dart
@@ -0,0 +1,31 @@
+// 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.
+
+// Test script for type_casts_with_null_safety_autodetection_test.dart which
+// is supposed to run in weak mode because it is opted-out.
+
+// @dart=2.6
+
+import 'package:expect/expect.dart';
+
+dynamic nullObj;
+
+@pragma('vm:never-inline')
+typeCast<T>(x) => x as T;
+
+void doTests() {
+ typeCast<dynamic>(nullObj);
+ typeCast<void>(nullObj);
+ typeCast<Object>(nullObj);
+ typeCast<int>(nullObj);
+ typeCast<List<int>>(<int>[]);
+ typeCast<List<int>>(<Null>[]);
+}
+
+main() {
+ for (int i = 0; i < 20; ++i) {
+ doTests();
+ }
+ print('OK(weak)');
+}
diff --git a/runtime/tests/vm/dart/use_dwarf_stack_traces_flag_test.dart b/runtime/tests/vm/dart/use_dwarf_stack_traces_flag_test.dart
index 7a2ed6c..07a40c0 100644
--- a/runtime/tests/vm/dart/use_dwarf_stack_traces_flag_test.dart
+++ b/runtime/tests/vm/dart/use_dwarf_stack_traces_flag_test.dart
@@ -132,7 +132,16 @@
Expect.isTrue(translatedStackFrames.length > 0);
Expect.isTrue(originalStackFrames.length > 0);
- Expect.deepEquals(translatedStackFrames, originalStackFrames);
+ // In symbolic mode, we don't store column information to avoid an increase
+ // in size of CodeStackMaps. Thus, we need to strip any columns from the
+ // translated non-symbolic stack to compare them via equality.
+ final columnStrippedTranslated = removeColumns(translatedStackFrames);
+
+ print('Stack frames from translated non-symbolic stack trace, no columns:');
+ columnStrippedTranslated.forEach(print);
+ print('');
+
+ Expect.deepEquals(columnStrippedTranslated, originalStackFrames);
// Since we compiled directly to ELF, there should be a DSO base address
// in the stack trace header and 'virt' markers in the stack frames.
@@ -175,6 +184,19 @@
return lines.where((line) => _symbolicFrameRE.hasMatch(line));
}
+final _columnsRE = RegExp(r'[(](.*:\d+):\d+[)]');
+
+Iterable<String> removeColumns(Iterable<String> lines) sync* {
+ for (final line in lines) {
+ final match = _columnsRE.firstMatch(line);
+ if (match != null) {
+ yield line.replaceRange(match.start, match.end, '(${match.group(1)!})');
+ } else {
+ yield line;
+ }
+ }
+}
+
Iterable<int> parseUsingAddressRegExp(RegExp re, Iterable<String> lines) sync* {
for (final line in lines) {
final match = re.firstMatch(line);
diff --git a/runtime/tests/vm/dart/v8_snapshot_profile_writer_test.dart b/runtime/tests/vm/dart/v8_snapshot_profile_writer_test.dart
index ea385af..7ef3be5 100644
--- a/runtime/tests/vm/dart/v8_snapshot_profile_writer_test.dart
+++ b/runtime/tests/vm/dart/v8_snapshot_profile_writer_test.dart
@@ -2,12 +2,12 @@
// 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:convert';
+import 'dart:io';
-import "package:expect/expect.dart";
+import 'package:expect/expect.dart';
import 'package:path/path.dart' as path;
-import "package:vm/v8_snapshot_profile.dart";
+import 'package:vm_snapshot_analysis/v8_profile.dart';
import 'use_flag_test_helper.dart';
@@ -77,32 +77,52 @@
strippedPath = snapshotPath;
}
- final V8SnapshotProfile profile = V8SnapshotProfile.fromJson(
- JsonDecoder().convert(File(profilePath).readAsStringSync()));
+ final profile =
+ Snapshot.fromJson(jsonDecode(File(profilePath).readAsStringSync()));
// Verify that there are no "unknown" nodes. These are emitted when we see a
// reference to an some object but no other metadata about the object was
// recorded. We should at least record the type for every object in the
// graph (in some cases the shallow size can legitimately be 0, e.g. for
// "base objects").
- for (final int node in profile.nodes) {
- Expect.notEquals("Unknown", profile[node].type,
- "unknown node at ID ${profile[node].id}");
+ for (final node in profile.nodes) {
+ Expect.notEquals("Unknown", node.type, "unknown node at ID ${node.id}");
}
- // Verify that all nodes are reachable from the declared roots.
- int unreachableNodes = 0;
- Set<int> nodesReachableFromRoots = profile.preOrder(profile.root).toSet();
- for (final int node in profile.nodes) {
- Expect.isTrue(nodesReachableFromRoots.contains(node),
- "unreachable node at ID ${profile[node].id}");
+ // HeapSnapshotWorker.HeapSnapshot.calculateDistances (from HeapSnapshot.js)
+ // assumes that the root does not have more than one edge to any other node
+ // (most likely an oversight).
+ final Set<int> roots = <int>{};
+ for (final edge in profile.nodeAt(0).edges) {
+ Expect.isTrue(roots.add(edge.target.index));
+ }
+
+ // Check that all nodes are reachable from the root (index 0).
+ final Set<int> reachable = {0};
+ final dfs = <int>[0];
+ while (!dfs.isEmpty) {
+ final next = dfs.removeLast();
+ for (final edge in profile.nodeAt(next).edges) {
+ final target = edge.target;
+ if (!reachable.contains(target.index)) {
+ reachable.add(target.index);
+ dfs.add(target.index);
+ }
+ }
+ }
+
+ if (reachable.length != profile.nodeCount) {
+ for (final node in profile.nodes) {
+ Expect.isTrue(reachable.contains(node.index),
+ "unreachable node at ID ${node.id}");
+ }
}
// Verify that the actual size of the snapshot is close to the sum of the
// shallow sizes of all objects in the profile. They will not be exactly
// equal because of global headers and padding.
final actual = await File(strippedPath).length();
- final expected = profile.accountedBytes;
+ final expected = profile.nodes.fold<int>(0, (size, n) => size + n.selfSize);
final bareUsed = useBare ? "bare" : "non-bare";
final fileType = useAsm ? "assembly" : "ELF";
diff --git a/runtime/tests/vm/dart_2/generic_check_bound_slow_path_test.dart b/runtime/tests/vm/dart_2/generic_check_bound_slow_path_test.dart
new file mode 100644
index 0000000..7290b2f
--- /dev/null
+++ b/runtime/tests/vm/dart_2/generic_check_bound_slow_path_test.dart
@@ -0,0 +1,38 @@
+// 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.
+
+// VMOptions=--shared_slow_path_triggers_gc
+
+import 'dart:typed_data';
+
+import 'package:expect/expect.dart';
+
+Uint8List l = int.parse('1') == 1 ? Uint8List(10) : Uint8List(0);
+final int outOfRangeSmi = int.parse('-1');
+final int outOfRangeSmi2 = int.parse('10');
+final int outOfRangeMint = int.parse('${0x7fffffffffffffff}');
+final int outOfRangeMint2 = int.parse('${0x8000000000000000}');
+
+buildErrorMatcher(int outOfRangeValue) {
+ return (error) {
+ return error is RangeError &&
+ error.start == 0 &&
+ error.end == 9 &&
+ error.invalidValue == outOfRangeValue;
+ };
+}
+
+main() {
+ for (int i = 0; i < 10; ++i) l[i] = i;
+
+ Expect.throws(() => l[outOfRangeSmi], buildErrorMatcher(outOfRangeSmi));
+ Expect.throws(() => l[outOfRangeSmi2], buildErrorMatcher(outOfRangeSmi2));
+ Expect.throws(() => l[outOfRangeMint], buildErrorMatcher(outOfRangeMint));
+ Expect.throws(() => l[outOfRangeMint2], buildErrorMatcher(outOfRangeMint2));
+
+ if (int.parse('1') == 0) l = Uint8List(0);
+ for (int i = 0; i < 10; ++i) {
+ Expect.equals(i, l[i]);
+ }
+}
diff --git a/runtime/tests/vm/dart_2/use_dwarf_stack_traces_flag_test.dart b/runtime/tests/vm/dart_2/use_dwarf_stack_traces_flag_test.dart
index c67b5ef..40aa94a 100644
--- a/runtime/tests/vm/dart_2/use_dwarf_stack_traces_flag_test.dart
+++ b/runtime/tests/vm/dart_2/use_dwarf_stack_traces_flag_test.dart
@@ -132,7 +132,16 @@
Expect.isTrue(translatedStackFrames.length > 0);
Expect.isTrue(originalStackFrames.length > 0);
- Expect.deepEquals(translatedStackFrames, originalStackFrames);
+ // In symbolic mode, we don't store column information to avoid an increase
+ // in size of CodeStackMaps. Thus, we need to strip any columns from the
+ // translated non-symbolic stack to compare them via equality.
+ final columnStrippedTranslated = removeColumns(translatedStackFrames);
+
+ print('Stack frames from translated non-symbolic stack trace, no columns:');
+ columnStrippedTranslated.forEach(print);
+ print('');
+
+ Expect.deepEquals(columnStrippedTranslated, originalStackFrames);
// Since we compiled directly to ELF, there should be a DSO base address
// in the stack trace header and 'virt' markers in the stack frames.
@@ -175,6 +184,19 @@
return lines.where((line) => _symbolicFrameRE.hasMatch(line));
}
+final _columnsRE = RegExp(r'[(](.*:\d+):\d+[)]');
+
+Iterable<String> removeColumns(Iterable<String> lines) sync* {
+ for (final line in lines) {
+ final match = _columnsRE.firstMatch(line);
+ if (match != null) {
+ yield line.replaceRange(match.start, match.end, '(${match.group(1)})');
+ } else {
+ yield line;
+ }
+ }
+}
+
Iterable<int> parseUsingAddressRegExp(RegExp re, Iterable<String> lines) sync* {
for (final line in lines) {
final match = re.firstMatch(line);
diff --git a/runtime/tests/vm/dart_2/v8_snapshot_profile_writer_test.dart b/runtime/tests/vm/dart_2/v8_snapshot_profile_writer_test.dart
index de5facf..8778573 100644
--- a/runtime/tests/vm/dart_2/v8_snapshot_profile_writer_test.dart
+++ b/runtime/tests/vm/dart_2/v8_snapshot_profile_writer_test.dart
@@ -2,12 +2,12 @@
// 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:convert';
+import 'dart:io';
-import "package:expect/expect.dart";
+import 'package:expect/expect.dart';
import 'package:path/path.dart' as path;
-import "package:vm/v8_snapshot_profile.dart";
+import 'package:vm_snapshot_analysis/v8_profile.dart';
import 'use_flag_test_helper.dart';
@@ -77,32 +77,52 @@
strippedPath = snapshotPath;
}
- final V8SnapshotProfile profile = V8SnapshotProfile.fromJson(
- JsonDecoder().convert(File(profilePath).readAsStringSync()));
+ final profile =
+ Snapshot.fromJson(jsonDecode(File(profilePath).readAsStringSync()));
// Verify that there are no "unknown" nodes. These are emitted when we see a
// reference to an some object but no other metadata about the object was
// recorded. We should at least record the type for every object in the
// graph (in some cases the shallow size can legitimately be 0, e.g. for
// "base objects").
- for (final int node in profile.nodes) {
- Expect.notEquals("Unknown", profile[node].type,
- "unknown node at ID ${profile[node].id}");
+ for (final node in profile.nodes) {
+ Expect.notEquals("Unknown", node.type, "unknown node at ID ${node.id}");
}
- // Verify that all nodes are reachable from the declared roots.
- int unreachableNodes = 0;
- Set<int> nodesReachableFromRoots = profile.preOrder(profile.root).toSet();
- for (final int node in profile.nodes) {
- Expect.isTrue(nodesReachableFromRoots.contains(node),
- "unreachable node at ID ${profile[node].id}");
+ // HeapSnapshotWorker.HeapSnapshot.calculateDistances (from HeapSnapshot.js)
+ // assumes that the root does not have more than one edge to any other node
+ // (most likely an oversight).
+ final Set<int> roots = <int>{};
+ for (final edge in profile.nodeAt(0).edges) {
+ Expect.isTrue(roots.add(edge.target.index));
+ }
+
+ // Check that all nodes are reachable from the root (index 0).
+ final Set<int> reachable = {0};
+ final dfs = <int>[0];
+ while (!dfs.isEmpty) {
+ final next = dfs.removeLast();
+ for (final edge in profile.nodeAt(next).edges) {
+ final target = edge.target;
+ if (!reachable.contains(target.index)) {
+ reachable.add(target.index);
+ dfs.add(target.index);
+ }
+ }
+ }
+
+ if (reachable.length != profile.nodeCount) {
+ for (final node in profile.nodes) {
+ Expect.isTrue(reachable.contains(node.index),
+ "unreachable node at ID ${node.id}");
+ }
}
// Verify that the actual size of the snapshot is close to the sum of the
// shallow sizes of all objects in the profile. They will not be exactly
// equal because of global headers and padding.
final actual = await File(strippedPath).length();
- final expected = profile.accountedBytes;
+ final expected = profile.nodes.fold<int>(0, (size, n) => size + n.selfSize);
final bareUsed = useBare ? "bare" : "non-bare";
final fileType = useAsm ? "assembly" : "ELF";
diff --git a/runtime/tests/vm/vm.status b/runtime/tests/vm/vm.status
index c8912f5..72ce9c8 100644
--- a/runtime/tests/vm/vm.status
+++ b/runtime/tests/vm/vm.status
@@ -248,6 +248,7 @@
dart/minimal_kernel_test: SkipByDesign # Test needs to run from source
dart/null_safety_autodetection_in_kernel_compiler_test: SkipByDesign # Test needs to run from source
dart/snapshot_depfile_test: SkipByDesign # Test needs to run from source
+dart/type_casts_with_null_safety_autodetection_test: SkipByDesign # Test needs to run from source
dart_2/appjit*: SkipByDesign # Test needs to run from source
dart_2/bytecode_and_ast_mix_test: SkipByDesign # Test needs to run from source
dart_2/kernel_determinism_test: SkipByDesign # Test needs to run from source
diff --git a/runtime/vm/clustered_snapshot.cc b/runtime/vm/clustered_snapshot.cc
index f3ca74d..4bd04c8 100644
--- a/runtime/vm/clustered_snapshot.cc
+++ b/runtime/vm/clustered_snapshot.cc
@@ -6713,6 +6713,21 @@
#undef CHECK_FLAG
#undef SET_FLAG
+#if defined(DART_PRECOMPILED_RUNTIME)
+ if (FLAG_null_safety == kNullSafetyOptionUnspecified) {
+ if (strncmp(cursor, "null-safety", end - cursor) == 0) {
+ FLAG_null_safety = kNullSafetyOptionStrong;
+ cursor = end;
+ continue;
+ }
+ if (strncmp(cursor, "no-null-safety", end - cursor) == 0) {
+ FLAG_null_safety = kNullSafetyOptionWeak;
+ cursor = end;
+ continue;
+ }
+ }
+#endif // defined(DART_PRECOMPILED_RUNTIME)
+
cursor = end;
}
diff --git a/runtime/vm/code_descriptors.cc b/runtime/vm/code_descriptors.cc
index 1a5a5f2..aaaa159 100644
--- a/runtime/vm/code_descriptors.cc
+++ b/runtime/vm/code_descriptors.cc
@@ -573,18 +573,32 @@
void CodeSourceMapBuilder::WriteChangePosition(TokenPosition pos) {
stream_.Write<uint8_t>(kChangePosition);
+ intptr_t position_or_line = pos.value();
+#if defined(DART_PRECOMPILER)
+ intptr_t column = TokenPosition::kNoSourcePos;
if (FLAG_precompiled_mode) {
- intptr_t line = -1;
+ // Don't use the raw position value directly in precompiled mode. Instead,
+ // use the value of kNoSource as a fallback when no line or column
+ // information is found.
+ position_or_line = TokenPosition::kNoSourcePos;
intptr_t inline_id = buffered_inline_id_stack_.Last();
if (inline_id < inline_id_to_function_.length()) {
const Function* function = inline_id_to_function_[inline_id];
Script& script = Script::Handle(function->script());
- line = script.GetTokenLineUsingLineStarts(pos.SourcePosition());
+ script.GetTokenLocationUsingLineStarts(pos.SourcePosition(),
+ &position_or_line, &column);
}
- stream_.Write<int32_t>(static_cast<int32_t>(line));
- } else {
- stream_.Write<int32_t>(static_cast<int32_t>(pos.value()));
}
+#endif
+ stream_.Write<int32_t>(position_or_line);
+#if defined(DART_PRECOMPILER)
+ // For non-symbolic stack traces, the CodeSourceMaps are not serialized,
+ // so we need not worry about increasing snapshot size by including more
+ // information here.
+ if (FLAG_dwarf_stack_traces_mode) {
+ stream_.Write<int32_t>(column);
+ }
+#endif
written_token_pos_stack_.Last() = pos;
}
@@ -606,9 +620,8 @@
uint8_t opcode = stream.Read<uint8_t>();
switch (opcode) {
case CodeSourceMapBuilder::kChangePosition: {
- int32_t position = stream.Read<int32_t>();
(*token_positions)[token_positions->length() - 1] =
- TokenPosition(position);
+ ReadPosition(&stream);
break;
}
case CodeSourceMapBuilder::kAdvancePC: {
@@ -668,7 +681,7 @@
uint8_t opcode = stream.Read<uint8_t>();
switch (opcode) {
case CodeSourceMapBuilder::kChangePosition: {
- stream.Read<int32_t>();
+ ReadPosition(&stream);
break;
}
case CodeSourceMapBuilder::kAdvancePC: {
@@ -721,7 +734,7 @@
uint8_t opcode = stream.Read<uint8_t>();
switch (opcode) {
case CodeSourceMapBuilder::kChangePosition: {
- stream.Read<int32_t>();
+ ReadPosition(&stream);
break;
}
case CodeSourceMapBuilder::kAdvancePC: {
@@ -775,8 +788,7 @@
uint8_t opcode = stream.Read<uint8_t>();
switch (opcode) {
case CodeSourceMapBuilder::kChangePosition: {
- int32_t position = stream.Read<int32_t>();
- token_positions[token_positions.length() - 1] = TokenPosition(position);
+ token_positions[token_positions.length() - 1] = ReadPosition(&stream);
break;
}
case CodeSourceMapBuilder::kAdvancePC: {
@@ -830,7 +842,7 @@
uint8_t opcode = stream.Read<uint8_t>();
switch (opcode) {
case CodeSourceMapBuilder::kChangePosition: {
- stream.Read<int32_t>();
+ ReadPosition(&stream);
break;
}
case CodeSourceMapBuilder::kAdvancePC: {
@@ -862,4 +874,17 @@
return -1;
}
+TokenPosition CodeSourceMapReader::ReadPosition(ReadStream* stream) {
+ const intptr_t line = stream->Read<int32_t>();
+#if defined(DART_PRECOMPILER)
+ // The special handling for non-symbolic stack trace mode only needs to
+ // happen in the precompiler, because those CSMs are not serialized in
+ // precompiled snapshots.
+ if (FLAG_dwarf_stack_traces_mode) {
+ stream->Read<int32_t>(); // Discard the column information.
+ }
+#endif
+ return TokenPosition(line);
+}
+
} // namespace dart
diff --git a/runtime/vm/code_descriptors.h b/runtime/vm/code_descriptors.h
index 4ad6723..8b74bde 100644
--- a/runtime/vm/code_descriptors.h
+++ b/runtime/vm/code_descriptors.h
@@ -359,6 +359,10 @@
intptr_t GetNullCheckNameIndexAt(int32_t pc_offset);
private:
+ // Reads a TokenPosition value from a CSM, handling the different encoding for
+ // when non-symbolic stack traces are enabled.
+ static TokenPosition ReadPosition(ReadStream* stream);
+
const CodeSourceMap& map_;
const Array& functions_;
const Function& root_;
diff --git a/runtime/vm/compiler/backend/il.cc b/runtime/vm/compiler/backend/il.cc
index dc254d4..a7565cd 100644
--- a/runtime/vm/compiler/backend/il.cc
+++ b/runtime/vm/compiler/backend/il.cc
@@ -861,15 +861,16 @@
intptr_t CheckClassInstr::ComputeCidMask() const {
ASSERT(IsBitTest());
+ const uintptr_t one = 1;
intptr_t min = cids_.ComputeLowestCid();
intptr_t mask = 0;
for (intptr_t i = 0; i < cids_.length(); ++i) {
- intptr_t run;
- uintptr_t range = 1ul + cids_[i].Extent();
+ uintptr_t run;
+ uintptr_t range = one + cids_[i].Extent();
if (range >= static_cast<uintptr_t>(compiler::target::kBitsPerWord)) {
run = -1;
} else {
- run = (1 << range) - 1;
+ run = (one << range) - 1;
}
mask |= run << (cids_[i].cid_start - min);
}
@@ -1522,7 +1523,9 @@
bool Instruction::HasUnmatchedInputRepresentations() const {
for (intptr_t i = 0; i < InputCount(); i++) {
Definition* input = InputAt(i)->definition();
- if (RequiredInputRepresentation(i) != input->representation()) {
+ const Representation input_representation = RequiredInputRepresentation(i);
+ if (input_representation != kNoRepresentation &&
+ input_representation != input->representation()) {
return true;
}
}
@@ -5150,6 +5153,9 @@
}
void GenericCheckBoundInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
+ ASSERT(representation() == RequiredInputRepresentation(kIndexPos));
+ ASSERT(representation() == RequiredInputRepresentation(kLengthPos));
+
RangeErrorSlowPath* slow_path =
new RangeErrorSlowPath(this, compiler->CurrentTryIndex());
compiler->AddSlowPathCode(slow_path);
@@ -5158,8 +5164,15 @@
Register length = length_loc.reg();
Register index = index_loc.reg();
const intptr_t index_cid = this->index()->Type()->ToCid();
- if (index_cid != kSmiCid) {
- __ BranchIfNotSmi(index, slow_path->entry_label());
+
+ // The length comes from one of our variable-sized heap objects (e.g. typed
+ // data array) and is therefore guaranteed to be in the positive Smi range.
+ if (representation() == kTagged) {
+ if (index_cid != kSmiCid) {
+ __ BranchIfNotSmi(index, slow_path->entry_label());
+ }
+ } else {
+ ASSERT(representation() == kUnboxedInt64);
}
__ CompareRegisters(index, length);
__ BranchIf(UNSIGNED_GREATER_EQUAL, slow_path->entry_label());
@@ -5714,6 +5727,22 @@
SetInputAt(1, index);
}
+Definition* LoadIndexedInstr::Canonicalize(FlowGraph* flow_graph) {
+ auto Z = flow_graph->zone();
+ if (auto box = index()->definition()->AsBoxInt64()) {
+ // TODO(dartbug.com/39432): Make LoadIndexed fully suport unboxed indices.
+ if (!box->ComputeCanDeoptimize() && compiler::target::kWordSize == 8) {
+ auto load = new (Z) LoadIndexedInstr(
+ array()->CopyWithType(Z), box->value()->CopyWithType(Z),
+ /*index_unboxed=*/true, index_scale(), class_id(), alignment_,
+ GetDeoptId(), token_pos(), result_type_);
+ flow_graph->InsertBefore(this, load, env(), FlowGraph::kValue);
+ return load;
+ }
+ }
+ return this;
+}
+
StoreIndexedInstr::StoreIndexedInstr(Value* array,
Value* index,
Value* value,
@@ -5738,6 +5767,23 @@
SetInputAt(kValuePos, value);
}
+Instruction* StoreIndexedInstr::Canonicalize(FlowGraph* flow_graph) {
+ auto Z = flow_graph->zone();
+ if (auto box = index()->definition()->AsBoxInt64()) {
+ // TODO(dartbug.com/39432): Make StoreIndexed fully suport unboxed indices.
+ if (!box->ComputeCanDeoptimize() && compiler::target::kWordSize == 8) {
+ auto store = new (Z) StoreIndexedInstr(
+ array()->CopyWithType(Z), box->value()->CopyWithType(Z),
+ value()->CopyWithType(Z), emit_store_barrier_,
+ /*index_unboxed=*/true, index_scale(), class_id(), alignment_,
+ GetDeoptId(), token_pos(), speculative_mode_);
+ flow_graph->InsertBefore(this, store, env(), FlowGraph::kEffect);
+ return nullptr;
+ }
+ }
+ return this;
+}
+
bool Utf8ScanInstr::IsScanFlagsUnboxed() const {
return FlowGraphCompiler::IsUnboxedField(scan_flags_field_.field());
}
diff --git a/runtime/vm/compiler/backend/il.h b/runtime/vm/compiler/backend/il.h
index 833b344..1d28f31 100644
--- a/runtime/vm/compiler/backend/il.h
+++ b/runtime/vm/compiler/backend/il.h
@@ -5564,6 +5564,8 @@
virtual bool HasUnknownSideEffects() const { return false; }
+ virtual Definition* Canonicalize(FlowGraph* flow_graph);
+
ADD_EXTRA_INFO_TO_S_EXPRESSION_SUPPORT
private:
@@ -5864,6 +5866,8 @@
void PrintOperandsTo(BufferFormatter* f) const;
+ virtual Instruction* Canonicalize(FlowGraph* flow_graph);
+
ADD_EXTRA_INFO_TO_S_EXPRESSION_SUPPORT
private:
@@ -8697,6 +8701,12 @@
// or otherwise throws an out-of-bounds exception (viz. non-speculative).
class GenericCheckBoundInstr : public CheckBoundBase {
public:
+ // We prefer to have unboxed inputs on 64-bit where values can fit into a
+ // register.
+ static bool UseUnboxedRepresentation() {
+ return compiler::target::kWordSize == 8;
+ }
+
GenericCheckBoundInstr(Value* length, Value* index, intptr_t deopt_id)
: CheckBoundBase(length, index, deopt_id) {}
@@ -8707,6 +8717,21 @@
virtual CompileType ComputeType() const;
virtual bool RecomputeType();
+ virtual intptr_t DeoptimizationTarget() const { return DeoptId::kNone; }
+
+ virtual SpeculativeMode SpeculativeModeOfInput(intptr_t index) const {
+ return kNotSpeculative;
+ }
+
+ virtual Representation representation() const {
+ return UseUnboxedRepresentation() ? kUnboxedInt64 : kTagged;
+ }
+
+ virtual Representation RequiredInputRepresentation(intptr_t idx) const {
+ ASSERT(idx == kIndexPos || idx == kLengthPos);
+ return UseUnboxedRepresentation() ? kUnboxedInt64 : kTagged;
+ }
+
// GenericCheckBound can implicitly call Dart code (RangeError or
// ArgumentError constructor), so it can lazily deopt.
virtual bool ComputeCanDeoptimize() const {
diff --git a/runtime/vm/compiler/backend/il_test_helper.cc b/runtime/vm/compiler/backend/il_test_helper.cc
index 316025e..9c5788a 100644
--- a/runtime/vm/compiler/backend/il_test_helper.cc
+++ b/runtime/vm/compiler/backend/il_test_helper.cc
@@ -234,6 +234,11 @@
Instruction* cursor = cursor_;
for (size_t i = 0; i < qcodes.size(); ++i) {
Instruction** capture = qcodes[i].capture_;
+ if (parallel_moves_handling_ == ParallelMovesHandling::kSkip) {
+ while (cursor->IsParallelMove()) {
+ cursor = cursor->next();
+ }
+ }
if (trace_) {
OS::PrintErr(" matching %30s @ %s\n",
MatchOpCodeToCString(qcodes[i].opcode()),
@@ -274,6 +279,9 @@
if (branch == nullptr) return nullptr;
return branch->false_successor();
}
+ if (opcode == kNop) {
+ return cursor;
+ }
if (opcode == kMoveAny) {
return cursor->next();
}
@@ -342,6 +350,9 @@
if (opcode == kMatchAndMoveBranchFalse) {
return "kMatchAndMoveBranchFalse";
}
+ if (opcode == kNop) {
+ return "kNop";
+ }
if (opcode == kMoveAny) {
return "kMoveAny";
}
diff --git a/runtime/vm/compiler/backend/il_test_helper.h b/runtime/vm/compiler/backend/il_test_helper.h
index 903b94a..ec00bb7 100644
--- a/runtime/vm/compiler/backend/il_test_helper.h
+++ b/runtime/vm/compiler/backend/il_test_helper.h
@@ -110,6 +110,9 @@
// Matches a branch and moves right.
kMatchAndMoveBranchFalse,
+ // Is ignored.
+ kNop,
+
// Moves forward across any instruction.
kMoveAny,
@@ -150,6 +153,16 @@
Instruction** capture_;
};
+enum class ParallelMovesHandling {
+ // Matcher doesn't do anything special with ParallelMove instructions.
+ kDefault,
+ // All ParallelMove instructions are skipped.
+ // This mode is useful when matching a flow graph after the whole
+ // compiler pipeline, as it may have ParallelMove instructions
+ // at arbitrary architecture-dependent places.
+ kSkip,
+};
+
// Used for matching a sequence of IL instructions including capturing support.
//
// Example:
@@ -170,9 +183,14 @@
// value for the cursor.
class ILMatcher : public ValueObject {
public:
- ILMatcher(FlowGraph* flow_graph, Instruction* cursor, bool trace = true)
+ ILMatcher(FlowGraph* flow_graph,
+ Instruction* cursor,
+ bool trace = true,
+ ParallelMovesHandling parallel_moves_handling =
+ ParallelMovesHandling::kDefault)
: flow_graph_(flow_graph),
cursor_(cursor),
+ parallel_moves_handling_(parallel_moves_handling),
// clang-format off
#if !defined(PRODUCT)
trace_(trace) {}
@@ -202,6 +220,7 @@
FlowGraph* flow_graph_;
Instruction* cursor_;
+ ParallelMovesHandling parallel_moves_handling_;
bool trace_;
};
diff --git a/runtime/vm/compiler/backend/il_x64.cc b/runtime/vm/compiler/backend/il_x64.cc
index 449cfbd..88915ae 100644
--- a/runtime/vm/compiler/backend/il_x64.cc
+++ b/runtime/vm/compiler/backend/il_x64.cc
@@ -1617,19 +1617,17 @@
LocationSummary* locs = new (zone)
LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
locs->set_in(0, Location::RequiresRegister());
- // The smi index is either untagged (element size == 1), or it is left smi
- // tagged (for all element sizes > 1).
- if (index_scale() == 1) {
- locs->set_in(1,
- CanBeImmediateIndex(index(), class_id())
- ? Location::Constant(index()->definition()->AsConstant())
- : Location::WritableRegister());
- } else {
- locs->set_in(1,
- CanBeImmediateIndex(index(), class_id())
- ? Location::Constant(index()->definition()->AsConstant())
- : Location::RequiresRegister());
- }
+ // For tagged index with index_scale=1 as well as untagged index with
+ // index_scale=16 we need a writable register due to assdressing mode
+ // restrictions on X64.
+ const bool need_writable_index_register =
+ (index_scale() == 1 && !index_unboxed_) ||
+ (index_scale() == 16 && index_unboxed_);
+ locs->set_in(
+ 1, CanBeImmediateIndex(index(), class_id())
+ ? Location::Constant(index()->definition()->AsConstant())
+ : (need_writable_index_register ? Location::WritableRegister()
+ : Location::RequiresRegister()));
if ((representation() == kUnboxedDouble) ||
(representation() == kUnboxedFloat32x4) ||
(representation() == kUnboxedInt32x4) ||
@@ -1646,22 +1644,27 @@
const Register array = locs()->in(0).reg();
const Location index = locs()->in(1);
+ intptr_t index_scale = index_scale_;
+ if (index.IsRegister()) {
+ if (index_scale == 1 && !index_unboxed_) {
+ __ SmiUntag(index.reg());
+ } else if (index_scale == 16 && index_unboxed_) {
+ // X64 does not support addressing mode using TIMES_16.
+ __ SmiTag(index.reg());
+ index_scale >>= 1;
+ }
+ } else {
+ ASSERT(index.IsConstant());
+ }
+
compiler::Address element_address =
index.IsRegister() ? compiler::Assembler::ElementAddressForRegIndex(
- IsExternal(), class_id(), index_scale(),
+ IsExternal(), class_id(), index_scale,
index_unboxed_, array, index.reg())
: compiler::Assembler::ElementAddressForIntIndex(
- IsExternal(), class_id(), index_scale(), array,
+ IsExternal(), class_id(), index_scale, array,
Smi::Cast(index.constant()).Value());
- if (index_scale() == 1 && !index_unboxed_) {
- if (index.IsRegister()) {
- __ SmiUntag(index.reg());
- } else {
- ASSERT(index.IsConstant());
- }
- }
-
if (representation() == kUnboxedDouble ||
representation() == kUnboxedFloat32x4 ||
representation() == kUnboxedInt32x4 ||
@@ -1848,19 +1851,17 @@
LocationSummary* locs = new (zone)
LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
locs->set_in(0, Location::RequiresRegister());
- // The smi index is either untagged (element size == 1), or it is left smi
- // tagged (for all element sizes > 1).
- if (index_scale() == 1) {
- locs->set_in(1,
- CanBeImmediateIndex(index(), class_id())
- ? Location::Constant(index()->definition()->AsConstant())
- : Location::WritableRegister());
- } else {
- locs->set_in(1,
- CanBeImmediateIndex(index(), class_id())
- ? Location::Constant(index()->definition()->AsConstant())
- : Location::RequiresRegister());
- }
+ // For tagged index with index_scale=1 as well as untagged index with
+ // index_scale=16 we need a writable register due to assdressing mode
+ // restrictions on X64.
+ const bool need_writable_index_register =
+ (index_scale() == 1 && !index_unboxed_) ||
+ (index_scale() == 16 && index_unboxed_);
+ locs->set_in(
+ 1, CanBeImmediateIndex(index(), class_id())
+ ? Location::Constant(index()->definition()->AsConstant())
+ : (need_writable_index_register ? Location::WritableRegister()
+ : Location::RequiresRegister()));
switch (class_id()) {
case kArrayCid:
locs->set_in(2, ShouldEmitStoreBarrier()
@@ -1915,17 +1916,27 @@
const Register array = locs()->in(0).reg();
const Location index = locs()->in(1);
+ intptr_t index_scale = index_scale_;
+ if (index.IsRegister()) {
+ if (index_scale == 1 && !index_unboxed_) {
+ __ SmiUntag(index.reg());
+ } else if (index_scale == 16 && index_unboxed_) {
+ // X64 does not support addressing mode using TIMES_16.
+ __ SmiTag(index.reg());
+ index_scale >>= 1;
+ }
+ } else {
+ ASSERT(index.IsConstant());
+ }
+
compiler::Address element_address =
index.IsRegister() ? compiler::Assembler::ElementAddressForRegIndex(
- IsExternal(), class_id(), index_scale(),
+ IsExternal(), class_id(), index_scale,
index_unboxed_, array, index.reg())
: compiler::Assembler::ElementAddressForIntIndex(
- IsExternal(), class_id(), index_scale(), array,
+ IsExternal(), class_id(), index_scale, array,
Smi::Cast(index.constant()).Value());
- if ((index_scale() == 1) && index.IsRegister() && !index_unboxed_) {
- __ SmiUntag(index.reg());
- }
switch (class_id()) {
case kArrayCid:
if (ShouldEmitStoreBarrier()) {
diff --git a/runtime/vm/compiler/backend/inliner.cc b/runtime/vm/compiler/backend/inliner.cc
index 25c3e44..4b47da7 100644
--- a/runtime/vm/compiler/backend/inliner.cc
+++ b/runtime/vm/compiler/backend/inliner.cc
@@ -2436,6 +2436,8 @@
}
static bool InlineGetIndexed(FlowGraph* flow_graph,
+ bool can_speculate,
+ bool is_dynamic_call,
MethodRecognizer::Kind kind,
Definition* call,
Definition* receiver,
@@ -2447,6 +2449,11 @@
Definition* array = receiver;
Definition* index = call->ArgumentAt(1);
+
+ if (!can_speculate && is_dynamic_call && !index->Type()->IsInt()) {
+ return false;
+ }
+
*entry =
new (Z) FunctionEntryInstr(graph_entry, flow_graph->allocate_block_id(),
call->GetBlock()->try_index(), DeoptId::kNone);
@@ -3401,6 +3408,7 @@
}
static bool InlineSimdOp(FlowGraph* flow_graph,
+ bool is_dynamic_call,
Instruction* call,
Definition* receiver,
MethodRecognizer::Kind kind,
@@ -3411,13 +3419,6 @@
if (!ShouldInlineSimd()) {
return false;
}
- bool is_dynamic_call = false;
- if (auto instance_call = call->AsInstanceCallBase()) {
- is_dynamic_call = Function::IsDynamicInvocationForwarderName(
- instance_call->function_name());
- } else if (auto static_call = call->AsStaticCall()) {
- is_dynamic_call = static_call->function().IsDynamicInvocationForwarder();
- }
if (is_dynamic_call && call->ArgumentCount() > 1) {
// Issue(dartbug.com/37737): Dynamic invocation forwarders have the
// same recognized kind as the method they are forwarding to.
@@ -3666,6 +3667,8 @@
}
const bool can_speculate = policy->IsAllowedForInlining(call->deopt_id());
+ const bool is_dynamic_call = Function::IsDynamicInvocationForwarderName(
+ String::Handle(flow_graph->zone(), target.name()));
const MethodRecognizer::Kind kind = target.recognized_kind();
switch (kind) {
@@ -3680,36 +3683,36 @@
case MethodRecognizer::kExternalUint8ClampedArrayGetIndexed:
case MethodRecognizer::kInt16ArrayGetIndexed:
case MethodRecognizer::kUint16ArrayGetIndexed:
- return InlineGetIndexed(flow_graph, kind, call, receiver, graph_entry,
- entry, last, result);
+ return InlineGetIndexed(flow_graph, can_speculate, is_dynamic_call, kind,
+ call, receiver, graph_entry, entry, last, result);
case MethodRecognizer::kFloat32ArrayGetIndexed:
case MethodRecognizer::kFloat64ArrayGetIndexed:
if (!CanUnboxDouble()) {
return false;
}
- return InlineGetIndexed(flow_graph, kind, call, receiver, graph_entry,
- entry, last, result);
+ return InlineGetIndexed(flow_graph, can_speculate, is_dynamic_call, kind,
+ call, receiver, graph_entry, entry, last, result);
case MethodRecognizer::kFloat32x4ArrayGetIndexed:
case MethodRecognizer::kFloat64x2ArrayGetIndexed:
if (!ShouldInlineSimd()) {
return false;
}
- return InlineGetIndexed(flow_graph, kind, call, receiver, graph_entry,
- entry, last, result);
+ return InlineGetIndexed(flow_graph, can_speculate, is_dynamic_call, kind,
+ call, receiver, graph_entry, entry, last, result);
case MethodRecognizer::kInt32ArrayGetIndexed:
case MethodRecognizer::kUint32ArrayGetIndexed:
if (!CanUnboxInt32()) {
return false;
}
- return InlineGetIndexed(flow_graph, kind, call, receiver, graph_entry,
- entry, last, result);
+ return InlineGetIndexed(flow_graph, can_speculate, is_dynamic_call, kind,
+ call, receiver, graph_entry, entry, last, result);
case MethodRecognizer::kInt64ArrayGetIndexed:
case MethodRecognizer::kUint64ArrayGetIndexed:
if (!ShouldInlineInt64ArrayOps()) {
return false;
}
- return InlineGetIndexed(flow_graph, kind, call, receiver, graph_entry,
- entry, last, result);
+ return InlineGetIndexed(flow_graph, can_speculate, is_dynamic_call, kind,
+ call, receiver, graph_entry, entry, last, result);
case MethodRecognizer::kClassIDgetID:
return InlineLoadClassId(flow_graph, call, graph_entry, entry, last,
result);
@@ -4040,8 +4043,8 @@
case MethodRecognizer::kFloat64x2Div:
case MethodRecognizer::kFloat64x2Add:
case MethodRecognizer::kFloat64x2Sub:
- return InlineSimdOp(flow_graph, call, receiver, kind, graph_entry, entry,
- last, result);
+ return InlineSimdOp(flow_graph, is_dynamic_call, call, receiver, kind,
+ graph_entry, entry, last, result);
case MethodRecognizer::kMathSqrt:
case MethodRecognizer::kMathDoublePow:
diff --git a/runtime/vm/compiler/backend/inliner_test.cc b/runtime/vm/compiler/backend/inliner_test.cc
index 87e8029..2db1229 100644
--- a/runtime/vm/compiler/backend/inliner_test.cc
+++ b/runtime/vm/compiler/backend/inliner_test.cc
@@ -213,44 +213,80 @@
FlowGraph* flow_graph = pipeline.RunPasses({});
auto entry = flow_graph->graph_entry()->normal_entry();
- ILMatcher cursor(flow_graph, entry);
+ ILMatcher cursor(flow_graph, entry, /*trace=*/true,
+ ParallelMovesHandling::kSkip);
- RELEASE_ASSERT(cursor.TryMatch({
- kMoveGlob,
- kMatchAndMoveCreateArray,
- kMoveParallelMoves,
- kMatchAndMoveUnboxInt64,
- kMatchAndMoveUnboxedConstant,
- kMatchAndMoveUnboxedConstant,
- kMoveParallelMoves,
- kMatchAndMoveGoto,
+ if (function.is_declared_in_bytecode()) {
+ RELEASE_ASSERT(cursor.TryMatch({
+ kMoveGlob,
+ kMatchAndMoveCreateArray,
+ kWordSize == 8 ? kMatchAndMoveUnboxInt64 : kNop,
+ kMatchAndMoveGoto,
- // Loop header
- kMatchAndMoveJoinEntry,
- kMatchAndMoveCheckStackOverflow,
- kMatchAndMoveBranchTrue,
+ // Loop header
+ kMatchAndMoveJoinEntry,
+ kMatchAndMoveCheckStackOverflow,
+ kMatchAndMoveUnboxInt64,
+ kMatchAndMoveUnboxInt64,
+ kMatchAndMoveBranchTrue,
- // Loop body
- kMatchAndMoveTargetEntry,
- kMatchAndMoveBoxInt64,
- kMatchAndMoveBoxInt64,
- kMoveParallelMoves,
- kMatchAndMoveStoreIndexed,
- kMoveParallelMoves,
- kMatchAndMoveBinaryInt64Op,
- kMoveParallelMoves,
- kMatchAndMoveGoto,
+ // Loop body
+ kMatchAndMoveTargetEntry,
+ kWordSize == 8 ? kMatchAndMoveUnboxInt64 : kNop,
+ kMatchAndMoveGenericCheckBound,
+ kMatchAndMoveStoreIndexed,
+ kMatchAndMoveCheckedSmiOp,
+ kMatchAndMoveGoto,
- // Loop header once again
- kMatchAndMoveJoinEntry,
- kMatchAndMoveCheckStackOverflow,
- kMatchAndMoveBranchFalse,
+ // Loop header once again
+ kMatchAndMoveJoinEntry,
+ kMatchAndMoveCheckStackOverflow,
+ kMatchAndMoveUnboxInt64,
+ kMatchAndMoveUnboxInt64,
+ kMatchAndMoveBranchFalse,
- // After loop
- kMatchAndMoveTargetEntry,
- kMoveParallelMoves,
- kMatchReturn,
- }));
+ // After loop
+ kMatchAndMoveTargetEntry,
+ kMatchReturn,
+ }));
+ } else {
+ Instruction* unbox1 = nullptr;
+ Instruction* unbox2 = nullptr;
+
+ RELEASE_ASSERT(cursor.TryMatch({
+ kMoveGlob,
+ kMatchAndMoveCreateArray,
+ kMatchAndMoveUnboxInt64,
+ {kMoveAny, &unbox1},
+ {kMoveAny, &unbox2},
+ kMatchAndMoveGoto,
+
+ // Loop header
+ kMatchAndMoveJoinEntry,
+ kMatchAndMoveCheckStackOverflow,
+ kMatchAndMoveBranchTrue,
+
+ // Loop body
+ kMatchAndMoveTargetEntry,
+ kWordSize == 4 ? kMatchAndMoveBoxInt64 : kNop,
+ kMatchAndMoveBoxInt64,
+ kMatchAndMoveStoreIndexed,
+ kMatchAndMoveBinaryInt64Op,
+ kMatchAndMoveGoto,
+
+ // Loop header once again
+ kMatchAndMoveJoinEntry,
+ kMatchAndMoveCheckStackOverflow,
+ kMatchAndMoveBranchFalse,
+
+ // After loop
+ kMatchAndMoveTargetEntry,
+ kMatchReturn,
+ }));
+
+ EXPECT(unbox1->IsUnboxedConstant() || unbox1->IsUnboxInt64());
+ EXPECT(unbox2->IsUnboxedConstant() || unbox2->IsUnboxInt64());
+ }
}
#endif // defined(DART_PRECOMPILER)
diff --git a/runtime/vm/compiler/backend/typed_data_aot_test.cc b/runtime/vm/compiler/backend/typed_data_aot_test.cc
index b1dd7c0..d978e86 100644
--- a/runtime/vm/compiler/backend/typed_data_aot_test.cc
+++ b/runtime/vm/compiler/backend/typed_data_aot_test.cc
@@ -77,7 +77,9 @@
}
EXPECT(load_field->InputAt(0)->definition()->IsParameter());
- EXPECT(bounds_check->InputAt(0)->definition() == load_field);
+ EXPECT(bounds_check->length()
+ ->definition()
+ ->OriginalDefinitionIgnoreBoxingAndConstraints() == load_field);
EXPECT(load_untagged->InputAt(0)->definition()->IsParameter());
EXPECT(load_indexed->InputAt(0)->definition() == load_untagged);
}
diff --git a/runtime/vm/compiler/stub_code_compiler.cc b/runtime/vm/compiler/stub_code_compiler.cc
index c02a246..87f0a60 100644
--- a/runtime/vm/compiler/stub_code_compiler.cc
+++ b/runtime/vm/compiler/stub_code_compiler.cc
@@ -20,6 +20,22 @@
namespace compiler {
+intptr_t StubCodeCompiler::WordOffsetFromFpToCpuRegister(
+ Register cpu_register) {
+ ASSERT(RegisterSet::Contains(kDartAvailableCpuRegs, cpu_register));
+
+ // Skip FP + saved PC.
+ intptr_t slots_from_fp = 2;
+ for (intptr_t i = 0; i < kNumberOfCpuRegisters; i++) {
+ Register reg = static_cast<Register>(i);
+ if (reg == cpu_register) break;
+ if (RegisterSet::Contains(kDartAvailableCpuRegs, reg)) {
+ slots_from_fp++;
+ }
+ }
+ return slots_from_fp;
+}
+
void StubCodeCompiler::GenerateInitStaticFieldStub(Assembler* assembler) {
__ EnterStubFrame();
__ PushObject(NullObject()); // Make room for result.
diff --git a/runtime/vm/compiler/stub_code_compiler.h b/runtime/vm/compiler/stub_code_compiler.h
index cf4e58d..875ea17 100644
--- a/runtime/vm/compiler/stub_code_compiler.h
+++ b/runtime/vm/compiler/stub_code_compiler.h
@@ -125,6 +125,18 @@
static void GenerateJITCallbackTrampolines(Assembler* assembler,
intptr_t next_callback_id);
+ // Calculates the offset (in words) from FP to the provided [cpu_register].
+ //
+ // Assumes
+ // * all [kDartAvailableCpuRegs] followed by saved-PC, saved-FP were
+ // pushed on the stack
+ // * [cpu_register] is in [kDartAvailableCpuRegs]
+ //
+ // The intended use of this function is to find registers on the stack which
+ // were spilled in the
+ // `StubCode::*<stub-name>Shared{With,Without}FpuRegsStub()`
+ static intptr_t WordOffsetFromFpToCpuRegister(Register cpu_register);
+
private:
// Common function for generating InitLateInstanceField and
// InitLateFinalInstanceField stubs.
diff --git a/runtime/vm/compiler/stub_code_compiler_arm.cc b/runtime/vm/compiler/stub_code_compiler_arm.cc
index 24a50e7..5d9365d 100644
--- a/runtime/vm/compiler/stub_code_compiler_arm.cc
+++ b/runtime/vm/compiler/stub_code_compiler_arm.cc
@@ -6,6 +6,7 @@
#include "vm/globals.h"
// For `AllocateObjectInstr::WillAllocateNewOrRemembered`
+// For `GenericCheckBoundInstr::UseUnboxedRepresentation`
#include "vm/compiler/backend/il.h"
#define SHOULD_NOT_INCLUDE_RUNTIME
@@ -164,14 +165,11 @@
__ Ret();
}
-void GenerateSharedStub(
- Assembler* assembler,
- bool save_fpu_registers,
- const RuntimeEntry* target,
- intptr_t self_code_stub_offset_from_thread,
- bool allow_return,
- bool store_runtime_result_in_r0 = false,
- std::initializer_list<Register> runtime_call_arguments = {}) {
+void GenerateSharedStubGeneric(Assembler* assembler,
+ bool save_fpu_registers,
+ intptr_t self_code_stub_offset_from_thread,
+ bool allow_return,
+ std::function<void()> perform_runtime_call) {
// If the target CPU does not support VFP the caller should always use the
// non-FPU stub.
if (save_fpu_registers && !TargetCPUFeatures::vfp_supported()) {
@@ -187,44 +185,45 @@
// To make the stack map calculation architecture independent we do the same
// as on intel.
__ Push(LR);
-
__ PushRegisters(all_registers);
__ ldr(CODE_REG, Address(THR, self_code_stub_offset_from_thread));
__ EnterStubFrame();
-
- if (store_runtime_result_in_r0) {
- ASSERT(all_registers.ContainsRegister(R0));
- ASSERT(allow_return);
-
- // Push an even value so it will not be seen as a pointer
- __ Push(LR);
- }
-
- for (Register argument_reg : runtime_call_arguments) {
- __ PushRegister(argument_reg);
- }
- __ CallRuntime(*target, /*argument_count=*/runtime_call_arguments.size());
+ perform_runtime_call();
if (!allow_return) {
__ Breakpoint();
return;
}
-
- __ Drop(runtime_call_arguments.size());
- if (store_runtime_result_in_r0) {
- __ Pop(R0);
- }
__ LeaveStubFrame();
- if (store_runtime_result_in_r0) {
- // Stores the runtime result in stack where R0 was pushed ( R0 is the very
- // last register to be pushed by __ PushRegisters(all_registers) )
- __ str(R0, Address(SP));
- }
-
__ PopRegisters(all_registers);
__ Pop(LR);
__ bx(LR);
}
+static void GenerateSharedStub(Assembler* assembler,
+ bool save_fpu_registers,
+ const RuntimeEntry* target,
+ intptr_t self_code_stub_offset_from_thread,
+ bool allow_return,
+ bool store_runtime_result_in_r0 = false) {
+ ASSERT(!store_runtime_result_in_r0 || allow_return);
+ auto perform_runtime_call = [&]() {
+ if (store_runtime_result_in_r0) {
+ __ PushRegister(LR); // Push an even register.
+ }
+ __ CallRuntime(*target, /*argument_count=*/0);
+ if (store_runtime_result_in_r0) {
+ __ PopRegister(R0);
+ __ str(
+ R0,
+ Address(FP, target::kWordSize *
+ StubCodeCompiler::WordOffsetFromFpToCpuRegister(R0)));
+ }
+ };
+ GenerateSharedStubGeneric(assembler, save_fpu_registers,
+ self_code_stub_offset_from_thread, allow_return,
+ perform_runtime_call);
+}
+
// R1: The extracted method.
// R4: The type_arguments_field_offset (or 0)
// SP+0: The object from which we are tearing a method off.
@@ -497,7 +496,7 @@
GenerateSharedStub(
assembler, /*save_fpu_registers=*/false, &kNullErrorRuntimeEntry,
target::Thread::null_error_shared_without_fpu_regs_stub_offset(),
- /*allow_return=*/false, {});
+ /*allow_return=*/false);
}
void StubCodeCompiler::GenerateNullErrorSharedWithFPURegsStub(
@@ -505,7 +504,7 @@
GenerateSharedStub(
assembler, /*save_fpu_registers=*/true, &kNullErrorRuntimeEntry,
target::Thread::null_error_shared_with_fpu_regs_stub_offset(),
- /*allow_return=*/false, {});
+ /*allow_return=*/false);
}
void StubCodeCompiler::GenerateNullArgErrorSharedWithoutFPURegsStub(
@@ -513,7 +512,7 @@
GenerateSharedStub(
assembler, /*save_fpu_registers=*/false, &kArgumentNullErrorRuntimeEntry,
target::Thread::null_arg_error_shared_without_fpu_regs_stub_offset(),
- /*allow_return=*/false, {});
+ /*allow_return=*/false);
}
void StubCodeCompiler::GenerateNullArgErrorSharedWithFPURegsStub(
@@ -521,27 +520,34 @@
GenerateSharedStub(
assembler, /*save_fpu_registers=*/true, &kArgumentNullErrorRuntimeEntry,
target::Thread::null_arg_error_shared_with_fpu_regs_stub_offset(),
- /*allow_return=*/false, {});
+ /*allow_return=*/false);
+}
+
+static void GenerateRangeError(Assembler* assembler, bool with_fpu_regs) {
+ auto perform_runtime_call = [&]() {
+ ASSERT(!GenericCheckBoundInstr::UseUnboxedRepresentation());
+ __ PushRegister(RangeErrorABI::kLengthReg);
+ __ PushRegister(RangeErrorABI::kIndexReg);
+ __ CallRuntime(kRangeErrorRuntimeEntry, /*argument_count=*/2);
+ __ Breakpoint();
+ };
+
+ GenerateSharedStubGeneric(
+ assembler, /*save_fpu_registers=*/with_fpu_regs,
+ with_fpu_regs
+ ? target::Thread::range_error_shared_with_fpu_regs_stub_offset()
+ : target::Thread::range_error_shared_without_fpu_regs_stub_offset(),
+ /*allow_return=*/false, perform_runtime_call);
}
void StubCodeCompiler::GenerateRangeErrorSharedWithoutFPURegsStub(
Assembler* assembler) {
- GenerateSharedStub(
- assembler, /*save_fpu_registers=*/false, &kRangeErrorRuntimeEntry,
- target::Thread::range_error_shared_without_fpu_regs_stub_offset(),
- /*allow_return=*/false,
- /*store_runtime_result_in_r0=*/false,
- {RangeErrorABI::kLengthReg, RangeErrorABI::kIndexReg});
+ GenerateRangeError(assembler, /*with_fpu_regs=*/false);
}
void StubCodeCompiler::GenerateRangeErrorSharedWithFPURegsStub(
Assembler* assembler) {
- GenerateSharedStub(
- assembler, /*save_fpu_registers=*/true, &kRangeErrorRuntimeEntry,
- target::Thread::range_error_shared_with_fpu_regs_stub_offset(),
- /*allow_return=*/false,
- /*store_runtime_result_in_r0=*/false,
- {RangeErrorABI::kLengthReg, RangeErrorABI::kIndexReg});
+ GenerateRangeError(assembler, /*with_fpu_regs=*/true);
}
void StubCodeCompiler::GenerateStackOverflowSharedWithoutFPURegsStub(
@@ -549,7 +555,7 @@
GenerateSharedStub(
assembler, /*save_fpu_registers=*/false, &kStackOverflowRuntimeEntry,
target::Thread::stack_overflow_shared_without_fpu_regs_stub_offset(),
- /*allow_return=*/true, {});
+ /*allow_return=*/true);
}
void StubCodeCompiler::GenerateStackOverflowSharedWithFPURegsStub(
@@ -557,7 +563,7 @@
GenerateSharedStub(
assembler, /*save_fpu_registers=*/true, &kStackOverflowRuntimeEntry,
target::Thread::stack_overflow_shared_with_fpu_regs_stub_offset(),
- /*allow_return=*/true, {});
+ /*allow_return=*/true);
}
// Input parameters:
@@ -1204,7 +1210,7 @@
&kAllocateMintRuntimeEntry,
target::Thread::allocate_mint_with_fpu_regs_stub_offset(),
/*allow_return=*/true,
- /*store_runtime_result_in_r0=*/true, {});
+ /*store_runtime_result_in_r0=*/true);
}
// Called for allocation of Mint.
@@ -1223,7 +1229,7 @@
assembler, /*save_fpu_registers=*/false, &kAllocateMintRuntimeEntry,
target::Thread::allocate_mint_without_fpu_regs_stub_offset(),
/*allow_return=*/true,
- /*store_runtime_result_in_r0=*/true, {});
+ /*store_runtime_result_in_r0=*/true);
}
// Called when invoking Dart code from C++ (VM code).
diff --git a/runtime/vm/compiler/stub_code_compiler_arm64.cc b/runtime/vm/compiler/stub_code_compiler_arm64.cc
index f8c009c..08d5931 100644
--- a/runtime/vm/compiler/stub_code_compiler_arm64.cc
+++ b/runtime/vm/compiler/stub_code_compiler_arm64.cc
@@ -5,6 +5,7 @@
#include "vm/globals.h"
// For `AllocateObjectInstr::WillAllocateNewOrRemembered`
+// For `GenericCheckBoundInstr::UseUnboxedRepresentation`
#include "vm/compiler/backend/il.h"
#define SHOULD_NOT_INCLUDE_RUNTIME
@@ -184,14 +185,12 @@
__ ret();
}
-void GenerateSharedStub(
+static void GenerateSharedStubGeneric(
Assembler* assembler,
bool save_fpu_registers,
- const RuntimeEntry* target,
intptr_t self_code_stub_offset_from_thread,
bool allow_return,
- bool store_runtime_result_in_r0 = false,
- std::initializer_list<Register> runtime_call_arguments = {}) {
+ std::function<void()> perform_runtime_call) {
// We want the saved registers to appear like part of the caller's frame, so
// we push them before calling EnterStubFrame.
RegisterSet all_registers;
@@ -203,37 +202,42 @@
__ PushRegisters(all_registers);
__ ldr(CODE_REG, Address(THR, self_code_stub_offset_from_thread));
__ EnterStubFrame();
- if (store_runtime_result_in_r0) {
- ASSERT(all_registers.ContainsRegister(R0));
- ASSERT(allow_return);
-
- // Push an even value so it will not be seen as a pointer
- __ Push(LR);
- }
- for (Register argument_reg : runtime_call_arguments) {
- __ PushRegister(argument_reg);
- }
- __ CallRuntime(*target, /*argument_count=*/runtime_call_arguments.size());
+ perform_runtime_call();
if (!allow_return) {
__ Breakpoint();
return;
}
-
- __ Drop(runtime_call_arguments.size());
- if (store_runtime_result_in_r0) {
- __ Pop(R0);
- }
__ LeaveStubFrame();
- if (store_runtime_result_in_r0) {
- // Stores the runtime result in stack where R0 was pushed ( R0 is the very
- // last register to be pushed by __ PushRegisters(all_registers) )
- __ str(R0, Address(SP));
- }
__ PopRegisters(all_registers);
__ Pop(LR);
__ ret(LR);
}
+static void GenerateSharedStub(Assembler* assembler,
+ bool save_fpu_registers,
+ const RuntimeEntry* target,
+ intptr_t self_code_stub_offset_from_thread,
+ bool allow_return,
+ bool store_runtime_result_in_r0 = false) {
+ ASSERT(!store_runtime_result_in_r0 || allow_return);
+ auto perform_runtime_call = [&]() {
+ if (store_runtime_result_in_r0) {
+ __ PushRegister(NULL_REG);
+ }
+ __ CallRuntime(*target, /*argument_count=*/0);
+ if (store_runtime_result_in_r0) {
+ __ PopRegister(R0);
+ __ str(
+ R0,
+ Address(FP, target::kWordSize *
+ StubCodeCompiler::WordOffsetFromFpToCpuRegister(R0)));
+ }
+ };
+ GenerateSharedStubGeneric(assembler, save_fpu_registers,
+ self_code_stub_offset_from_thread, allow_return,
+ perform_runtime_call);
+}
+
void StubCodeCompiler::GenerateEnterSafepointStub(Assembler* assembler) {
RegisterSet all_registers;
all_registers.AddAllGeneralRegisters();
@@ -549,7 +553,7 @@
GenerateSharedStub(
assembler, /*save_fpu_registers=*/false, &kNullErrorRuntimeEntry,
target::Thread::null_error_shared_without_fpu_regs_stub_offset(),
- /*allow_return=*/false, {});
+ /*allow_return=*/false);
}
void StubCodeCompiler::GenerateNullErrorSharedWithFPURegsStub(
@@ -557,7 +561,7 @@
GenerateSharedStub(
assembler, /*save_fpu_registers=*/true, &kNullErrorRuntimeEntry,
target::Thread::null_error_shared_with_fpu_regs_stub_offset(),
- /*allow_return=*/false, {});
+ /*allow_return=*/false);
}
void StubCodeCompiler::GenerateNullArgErrorSharedWithoutFPURegsStub(
@@ -565,7 +569,7 @@
GenerateSharedStub(
assembler, /*save_fpu_registers=*/false, &kArgumentNullErrorRuntimeEntry,
target::Thread::null_arg_error_shared_without_fpu_regs_stub_offset(),
- /*allow_return=*/false, {});
+ /*allow_return=*/false);
}
void StubCodeCompiler::GenerateNullArgErrorSharedWithFPURegsStub(
@@ -573,27 +577,64 @@
GenerateSharedStub(
assembler, /*save_fpu_registers=*/true, &kArgumentNullErrorRuntimeEntry,
target::Thread::null_arg_error_shared_with_fpu_regs_stub_offset(),
- /*allow_return=*/false, {});
+ /*allow_return=*/false);
+}
+
+static void GenerateRangeError(Assembler* assembler, bool with_fpu_regs) {
+ auto perform_runtime_call = [&]() {
+ // If the generated code has unboxed index/length we need to box them before
+ // calling the runtime entry.
+ if (GenericCheckBoundInstr::UseUnboxedRepresentation()) {
+ Label length, smi_case;
+
+ // The user-controlled index might not fit into a Smi.
+ __ adds(RangeErrorABI::kIndexReg, RangeErrorABI::kIndexReg,
+ compiler::Operand(RangeErrorABI::kIndexReg));
+ __ BranchIf(NO_OVERFLOW, &length);
+ {
+ // Allocate a mint, reload the two registers and popualte the mint.
+ __ PushRegister(NULL_REG);
+ __ CallRuntime(kAllocateMintRuntimeEntry, /*argument_count=*/0);
+ __ PopRegister(RangeErrorABI::kIndexReg);
+ __ ldr(TMP,
+ Address(FP, target::kWordSize *
+ StubCodeCompiler::WordOffsetFromFpToCpuRegister(
+ RangeErrorABI::kIndexReg)));
+ __ str(TMP, FieldAddress(RangeErrorABI::kIndexReg,
+ target::Mint::value_offset()));
+ __ ldr(RangeErrorABI::kLengthReg,
+ Address(FP, target::kWordSize *
+ StubCodeCompiler::WordOffsetFromFpToCpuRegister(
+ RangeErrorABI::kLengthReg)));
+ }
+
+ // Length is guaranteed to be in positive Smi range (it comes from a load
+ // of a vm recognized array).
+ __ Bind(&length);
+ __ SmiTag(RangeErrorABI::kLengthReg);
+ }
+ __ PushRegister(RangeErrorABI::kLengthReg);
+ __ PushRegister(RangeErrorABI::kIndexReg);
+ __ CallRuntime(kRangeErrorRuntimeEntry, /*argument_count=*/2);
+ __ Breakpoint();
+ };
+
+ GenerateSharedStubGeneric(
+ assembler, /*save_fpu_registers=*/with_fpu_regs,
+ with_fpu_regs
+ ? target::Thread::range_error_shared_with_fpu_regs_stub_offset()
+ : target::Thread::range_error_shared_without_fpu_regs_stub_offset(),
+ /*allow_return=*/false, perform_runtime_call);
}
void StubCodeCompiler::GenerateRangeErrorSharedWithoutFPURegsStub(
Assembler* assembler) {
- GenerateSharedStub(
- assembler, /*save_fpu_registers=*/false, &kRangeErrorRuntimeEntry,
- target::Thread::range_error_shared_without_fpu_regs_stub_offset(),
- /*allow_return=*/false,
- /*store_runtime_result_in_r0=*/false,
- {RangeErrorABI::kLengthReg, RangeErrorABI::kIndexReg});
+ GenerateRangeError(assembler, /*with_fpu_regs=*/false);
}
void StubCodeCompiler::GenerateRangeErrorSharedWithFPURegsStub(
Assembler* assembler) {
- GenerateSharedStub(
- assembler, /*save_fpu_registers=*/true, &kRangeErrorRuntimeEntry,
- target::Thread::range_error_shared_with_fpu_regs_stub_offset(),
- /*allow_return=*/false,
- /*store_runtime_result_in_r0=*/false,
- {RangeErrorABI::kLengthReg, RangeErrorABI::kIndexReg});
+ GenerateRangeError(assembler, /*with_fpu_regs=*/true);
}
void StubCodeCompiler::GenerateStackOverflowSharedWithoutFPURegsStub(
@@ -601,7 +642,7 @@
GenerateSharedStub(
assembler, /*save_fpu_registers=*/false, &kStackOverflowRuntimeEntry,
target::Thread::stack_overflow_shared_without_fpu_regs_stub_offset(),
- /*allow_return=*/true, {});
+ /*allow_return=*/true);
}
void StubCodeCompiler::GenerateStackOverflowSharedWithFPURegsStub(
@@ -609,7 +650,7 @@
GenerateSharedStub(
assembler, /*save_fpu_registers=*/true, &kStackOverflowRuntimeEntry,
target::Thread::stack_overflow_shared_with_fpu_regs_stub_offset(),
- /*allow_return=*/true, {});
+ /*allow_return=*/true);
}
void StubCodeCompiler::GeneratePrintStopMessageStub(Assembler* assembler) {
@@ -1288,7 +1329,7 @@
&kAllocateMintRuntimeEntry,
target::Thread::allocate_mint_with_fpu_regs_stub_offset(),
/*allow_return=*/true,
- /*store_runtime_result_in_r0=*/true, {});
+ /*store_runtime_result_in_r0=*/true);
}
void StubCodeCompiler::GenerateAllocateMintSharedWithoutFPURegsStub(
@@ -1306,7 +1347,7 @@
assembler, /*save_fpu_registers=*/false, &kAllocateMintRuntimeEntry,
target::Thread::allocate_mint_without_fpu_regs_stub_offset(),
/*allow_return=*/true,
- /*store_runtime_result_in_r0=*/true, {});
+ /*store_runtime_result_in_r0=*/true);
}
// Called when invoking Dart code from C++ (VM code).
diff --git a/runtime/vm/compiler/stub_code_compiler_x64.cc b/runtime/vm/compiler/stub_code_compiler_x64.cc
index 67261a1..4d4637a 100644
--- a/runtime/vm/compiler/stub_code_compiler_x64.cc
+++ b/runtime/vm/compiler/stub_code_compiler_x64.cc
@@ -6,6 +6,7 @@
#include "vm/globals.h"
// For `AllocateObjectInstr::WillAllocateNewOrRemembered`
+// For `GenericCheckBoundInstr::UseUnboxedRepresentation`
#include "vm/compiler/backend/il.h"
#define SHOULD_NOT_INCLUDE_RUNTIME
@@ -163,13 +164,12 @@
__ ret();
}
-void GenerateSharedStub(
+static void GenerateSharedStubGeneric(
Assembler* assembler,
bool save_fpu_registers,
- const RuntimeEntry* target,
intptr_t self_code_stub_offset_from_thread,
bool allow_return,
- std::initializer_list<Register> runtime_call_arguments) {
+ std::function<void()> perform_runtime_call) {
// We want the saved registers to appear like part of the caller's frame, so
// we push them before calling EnterStubFrame.
__ PushRegisters(kDartAvailableCpuRegs,
@@ -177,30 +177,22 @@
const intptr_t kSavedCpuRegisterSlots =
Utils::CountOneBitsWord(kDartAvailableCpuRegs);
-
const intptr_t kSavedFpuRegisterSlots =
save_fpu_registers
? kNumberOfFpuRegisters * kFpuRegisterSize / target::kWordSize
: 0;
-
const intptr_t kAllSavedRegistersSlots =
kSavedCpuRegisterSlots + kSavedFpuRegisterSlots;
// Copy down the return address so the stack layout is correct.
__ pushq(Address(RSP, kAllSavedRegistersSlots * target::kWordSize));
-
__ movq(CODE_REG, Address(THR, self_code_stub_offset_from_thread));
-
__ EnterStubFrame();
- for (Register argument_reg : runtime_call_arguments) {
- __ PushRegister(argument_reg);
- }
- __ CallRuntime(*target, /*argument_count=*/runtime_call_arguments.size());
+ perform_runtime_call();
if (!allow_return) {
__ Breakpoint();
return;
}
- __ Drop(runtime_call_arguments.size());
__ LeaveStubFrame();
// Drop "official" return address -- we can just use the one stored above the
@@ -213,6 +205,19 @@
__ ret();
}
+static void GenerateSharedStub(Assembler* assembler,
+ bool save_fpu_registers,
+ const RuntimeEntry* target,
+ intptr_t self_code_stub_offset_from_thread,
+ bool allow_return) {
+ auto perform_runtime_call = [&]() {
+ __ CallRuntime(*target, /*argument_count=*/0);
+ };
+ GenerateSharedStubGeneric(assembler, save_fpu_registers,
+ self_code_stub_offset_from_thread, allow_return,
+ perform_runtime_call);
+}
+
void StubCodeCompiler::GenerateEnterSafepointStub(Assembler* assembler) {
RegisterSet all_registers;
all_registers.AddAllGeneralRegisters();
@@ -484,7 +489,7 @@
GenerateSharedStub(
assembler, /*save_fpu_registers=*/false, &kNullErrorRuntimeEntry,
target::Thread::null_error_shared_without_fpu_regs_stub_offset(),
- /*allow_return=*/false, {});
+ /*allow_return=*/false);
}
void StubCodeCompiler::GenerateNullErrorSharedWithFPURegsStub(
@@ -492,7 +497,7 @@
GenerateSharedStub(
assembler, /*save_fpu_registers=*/true, &kNullErrorRuntimeEntry,
target::Thread::null_error_shared_with_fpu_regs_stub_offset(),
- /*allow_return=*/false, {});
+ /*allow_return=*/false);
}
void StubCodeCompiler::GenerateNullArgErrorSharedWithoutFPURegsStub(
@@ -500,7 +505,7 @@
GenerateSharedStub(
assembler, /*save_fpu_registers=*/false, &kArgumentNullErrorRuntimeEntry,
target::Thread::null_arg_error_shared_without_fpu_regs_stub_offset(),
- /*allow_return=*/false, {});
+ /*allow_return=*/false);
}
void StubCodeCompiler::GenerateNullArgErrorSharedWithFPURegsStub(
@@ -508,25 +513,66 @@
GenerateSharedStub(
assembler, /*save_fpu_registers=*/true, &kArgumentNullErrorRuntimeEntry,
target::Thread::null_arg_error_shared_with_fpu_regs_stub_offset(),
- /*allow_return=*/false, {});
+ /*allow_return=*/false);
+}
+
+static void GenerateRangeError(Assembler* assembler, bool with_fpu_regs) {
+ auto perform_runtime_call = [&]() {
+ // If the generated code has unboxed index/length we need to box them before
+ // calling the runtime entry.
+ if (GenericCheckBoundInstr::UseUnboxedRepresentation()) {
+ Label length, smi_case;
+
+ // The user-controlled index might not fit into a Smi.
+ __ addq(RangeErrorABI::kIndexReg, RangeErrorABI::kIndexReg);
+ __ BranchIf(NO_OVERFLOW, &length);
+ {
+ // Allocate a mint, reload the two registers and popualte the mint.
+ __ PushImmediate(Immediate(0));
+ __ CallRuntime(kAllocateMintRuntimeEntry, /*argument_count=*/0);
+ __ PopRegister(RangeErrorABI::kIndexReg);
+ __ movq(
+ TMP,
+ Address(RBP, target::kWordSize *
+ StubCodeCompiler::WordOffsetFromFpToCpuRegister(
+ RangeErrorABI::kIndexReg)));
+ __ movq(FieldAddress(RangeErrorABI::kIndexReg,
+ target::Mint::value_offset()),
+ TMP);
+ __ movq(
+ RangeErrorABI::kLengthReg,
+ Address(RBP, target::kWordSize *
+ StubCodeCompiler::WordOffsetFromFpToCpuRegister(
+ RangeErrorABI::kLengthReg)));
+ }
+
+ // Length is guaranteed to be in positive Smi range (it comes from a load
+ // of a vm recognized array).
+ __ Bind(&length);
+ __ SmiTag(RangeErrorABI::kLengthReg);
+ }
+ __ PushRegister(RangeErrorABI::kLengthReg);
+ __ PushRegister(RangeErrorABI::kIndexReg);
+ __ CallRuntime(kRangeErrorRuntimeEntry, /*argument_count=*/2);
+ __ Breakpoint();
+ };
+
+ GenerateSharedStubGeneric(
+ assembler, /*save_fpu_registers=*/with_fpu_regs,
+ with_fpu_regs
+ ? target::Thread::range_error_shared_with_fpu_regs_stub_offset()
+ : target::Thread::range_error_shared_without_fpu_regs_stub_offset(),
+ /*allow_return=*/false, perform_runtime_call);
}
void StubCodeCompiler::GenerateRangeErrorSharedWithoutFPURegsStub(
Assembler* assembler) {
- GenerateSharedStub(
- assembler, /*save_fpu_registers=*/false, &kRangeErrorRuntimeEntry,
- target::Thread::range_error_shared_without_fpu_regs_stub_offset(),
- /*allow_return=*/false,
- {RangeErrorABI::kLengthReg, RangeErrorABI::kIndexReg});
+ GenerateRangeError(assembler, /*with_fpu_regs=*/false);
}
void StubCodeCompiler::GenerateRangeErrorSharedWithFPURegsStub(
Assembler* assembler) {
- GenerateSharedStub(
- assembler, /*save_fpu_registers=*/true, &kRangeErrorRuntimeEntry,
- target::Thread::range_error_shared_with_fpu_regs_stub_offset(),
- /*allow_return=*/false,
- {RangeErrorABI::kLengthReg, RangeErrorABI::kIndexReg});
+ GenerateRangeError(assembler, /*with_fpu_regs=*/true);
}
void StubCodeCompiler::GenerateStackOverflowSharedWithoutFPURegsStub(
@@ -534,7 +580,7 @@
GenerateSharedStub(
assembler, /*save_fpu_registers=*/false, &kStackOverflowRuntimeEntry,
target::Thread::stack_overflow_shared_without_fpu_regs_stub_offset(),
- /*allow_return=*/true, {});
+ /*allow_return=*/true);
}
void StubCodeCompiler::GenerateStackOverflowSharedWithFPURegsStub(
@@ -542,7 +588,7 @@
GenerateSharedStub(
assembler, /*save_fpu_registers=*/true, &kStackOverflowRuntimeEntry,
target::Thread::stack_overflow_shared_with_fpu_regs_stub_offset(),
- /*allow_return=*/true, {});
+ /*allow_return=*/true);
}
// Input parameters:
diff --git a/runtime/vm/dart.cc b/runtime/vm/dart.cc
index 3ec567d..31f2973 100644
--- a/runtime/vm/dart.cc
+++ b/runtime/vm/dart.cc
@@ -753,12 +753,13 @@
intptr_t kernel_buffer_size,
const char* package_config,
const char* original_working_directory) {
+#if !defined(DART_PRECOMPILED_RUNTIME)
// Before creating the isolate we first determine the null safety mode
// in which the isolate needs to run based on one of these factors :
// - if loading from source, based on opt-in status of the source
// - if loading from a kernel file, based on the mode used when
// generating the kernel file
- // - if loading from an appJIT or AOT snapshot, based on the mode used
+ // - if loading from an appJIT, based on the mode used
// when generating the snapshot.
ASSERT(FLAG_null_safety == kNullSafetyOptionUnspecified);
@@ -772,7 +773,6 @@
}
}
-#if !defined(DART_PRECOMPILED_RUNTIME)
// If kernel_buffer is specified, it could be a self contained
// kernel file or the kernel file of the application,
// figure out the null safety mode by sniffing the kernel file.
@@ -791,8 +791,10 @@
return KernelIsolate::DetectNullSafety(script_uri, package_config,
original_working_directory);
}
-#endif // !defined(DART_PRECOMPILED_RUNTIME)
return false;
+#else
+ UNREACHABLE();
+#endif // !defined(DART_PRECOMPILED_RUNTIME)
}
#if defined(DART_PRECOMPILED_RUNTIME)
diff --git a/runtime/vm/dart_api_impl.cc b/runtime/vm/dart_api_impl.cc
index b3800b5..7893030 100644
--- a/runtime/vm/dart_api_impl.cc
+++ b/runtime/vm/dart_api_impl.cc
@@ -6032,6 +6032,10 @@
const uint8_t* snapshot_instructions,
const uint8_t* kernel_buffer,
intptr_t kernel_buffer_size) {
+#if defined(DART_PRECOMPILED_RUNTIME)
+ ASSERT(FLAG_null_safety != kNullSafetyOptionUnspecified);
+ return (FLAG_null_safety == kNullSafetyOptionStrong);
+#else
bool null_safety;
if (FLAG_null_safety == kNullSafetyOptionUnspecified) {
null_safety = Dart::DetectNullSafety(
@@ -6041,6 +6045,7 @@
null_safety = (FLAG_null_safety == kNullSafetyOptionStrong);
}
return null_safety;
+#endif // defined(DART_PRECOMPILED_RUNTIME)
}
// --- Service support ---
diff --git a/runtime/vm/dart_entry.cc b/runtime/vm/dart_entry.cc
index 6c306ac..8a1350e 100644
--- a/runtime/vm/dart_entry.cc
+++ b/runtime/vm/dart_entry.cc
@@ -188,6 +188,7 @@
Thread* thread) {
ASSERT(!code.IsNull());
ASSERT(thread->no_callback_scope_depth() == 0);
+ ASSERT(!Isolate::Current()->null_safety_not_set());
invokestub entrypoint =
reinterpret_cast<invokestub>(StubCode::InvokeDartCode().EntryPoint());
diff --git a/runtime/vm/dwarf.cc b/runtime/vm/dwarf.cc
index cdee3d3..db73b08 100644
--- a/runtime/vm/dwarf.cc
+++ b/runtime/vm/dwarf.cc
@@ -11,15 +11,36 @@
namespace dart {
-#ifdef DART_PRECOMPILER
+#if defined(DART_PRECOMPILER)
+
+class DwarfPosition {
+ public:
+ // The DWARF standard uses 0 to denote missing line or column information.
+ DwarfPosition(intptr_t line, intptr_t column)
+ : line_(line > 0 ? line : 0), column_(column > 0 ? column : 0) {
+ // Should only have no line information if also no column information.
+ ASSERT(line_ > 0 || column_ == 0);
+ }
+ explicit DwarfPosition(intptr_t line) : DwarfPosition(line, 0) {}
+ constexpr DwarfPosition() : line_(0), column_(0) {}
+
+ intptr_t line() const { return line_; }
+ intptr_t column() const { return column_; }
+
+ private:
+ intptr_t line_;
+ intptr_t column_;
+};
+
+static constexpr auto kNoDwarfPositionInfo = DwarfPosition();
class InliningNode : public ZoneAllocated {
public:
InliningNode(const Function& function,
- TokenPosition call_pos,
+ const DwarfPosition& position,
int32_t start_pc_offset)
: function(function),
- call_pos(call_pos),
+ position(position),
start_pc_offset(start_pc_offset),
end_pc_offset(-1),
children_head(NULL),
@@ -39,7 +60,7 @@
}
const Function& function;
- TokenPosition call_pos;
+ DwarfPosition position;
int32_t start_pc_offset;
int32_t end_pc_offset;
InliningNode* children_head;
@@ -170,14 +191,6 @@
return index;
}
-intptr_t Dwarf::TokenPositionToLine(const TokenPosition& token_pos) {
- // By the point we're creating the DWARF information, the values of
- // non-special token positions have been converted to line numbers, so
- // we just need to handle special (negative) token positions.
- ASSERT(token_pos.value() > TokenPosition::kLast.value());
- return token_pos.value() < 0 ? kNoLineInformation : token_pos.value();
-}
-
intptr_t Dwarf::AddFunction(const Function& function) {
RELEASE_ASSERT(!function.IsNull());
FunctionIndexPair* pair = function_to_index_.Lookup(&function);
@@ -257,8 +270,6 @@
stream->uleb128(DW_FORM_string);
stream->uleb128(DW_AT_decl_file);
stream->uleb128(DW_FORM_udata);
- stream->uleb128(DW_AT_decl_line);
- stream->uleb128(DW_FORM_udata);
stream->uleb128(DW_AT_inline);
stream->uleb128(DW_FORM_udata);
stream->uleb128(0);
@@ -289,6 +300,8 @@
stream->uleb128(DW_FORM_udata);
stream->uleb128(DW_AT_call_line);
stream->uleb128(DW_FORM_udata);
+ stream->uleb128(DW_AT_call_column);
+ stream->uleb128(DW_FORM_udata);
stream->uleb128(0);
stream->uleb128(0); // End of attributes.
@@ -354,8 +367,8 @@
String& name = String::Handle(zone_);
stream->InitializeAbstractOrigins(functions_.length());
// By the point we're creating DWARF information, scripts have already lost
- // their token stream, so we can't look up their line number information.
- auto const line = kNoLineInformation;
+ // their token stream and we can't look up their line number or column
+ // information, hence the lack of DW_AT_decl_line and DW_AT_decl_column.
for (intptr_t i = 0; i < functions_.length(); i++) {
const Function& function = *(functions_[i]);
name = function.QualifiedUserVisibleName();
@@ -367,7 +380,6 @@
stream->uleb128(kAbstractFunction);
stream->string(name_cstr); // DW_AT_name
stream->uleb128(file); // DW_AT_decl_file
- stream->uleb128(line); // DW_AT_decl_line
stream->uleb128(DW_INL_inlined); // DW_AT_inline
stream->uleb128(0); // End of children.
}
@@ -411,6 +423,15 @@
}
}
+static DwarfPosition ReadPosition(ReadStream* stream) {
+ const intptr_t line = stream->Read<int32_t>();
+ if (!FLAG_dwarf_stack_traces_mode) {
+ return DwarfPosition(line);
+ }
+ const intptr_t column = stream->Read<int32_t>();
+ return DwarfPosition(line, column);
+}
+
// Our state machine encodes position metadata such that we don't know the
// end pc for an inlined function until it is popped, but DWARF DIEs encode
// it where the function is pushed. We expand the state transitions into
@@ -428,24 +449,23 @@
}
GrowableArray<InliningNode*> node_stack(zone_, 4);
- GrowableArray<TokenPosition> token_positions(zone_, 4);
+ GrowableArray<DwarfPosition> token_positions(zone_, 4);
NoSafepointScope no_safepoint;
ReadStream stream(map.Data(), map.Length());
int32_t current_pc_offset = 0;
+ token_positions.Add(kNoDwarfPositionInfo);
InliningNode* root_node =
- new (zone_) InliningNode(root_function, TokenPosition(), 0);
+ new (zone_) InliningNode(root_function, token_positions.Last(), 0);
root_node->end_pc_offset = code.Size();
node_stack.Add(root_node);
- token_positions.Add(CodeSourceMapBuilder::kInitialPosition);
while (stream.PendingBytes() > 0) {
uint8_t opcode = stream.Read<uint8_t>();
switch (opcode) {
case CodeSourceMapBuilder::kChangePosition: {
- int32_t position = stream.Read<int32_t>();
- token_positions[token_positions.length() - 1] = TokenPosition(position);
+ token_positions[token_positions.length() - 1] = ReadPosition(&stream);
break;
}
case CodeSourceMapBuilder::kAdvancePC: {
@@ -457,12 +477,11 @@
int32_t func = stream.Read<int32_t>();
const Function& child_func =
Function::ZoneHandle(zone_, Function::RawCast(functions.At(func)));
- TokenPosition call_pos = token_positions.Last();
- InliningNode* child_node =
- new (zone_) InliningNode(child_func, call_pos, current_pc_offset);
+ InliningNode* child_node = new (zone_)
+ InliningNode(child_func, token_positions.Last(), current_pc_offset);
node_stack.Last()->AppendChild(child_node);
node_stack.Add(child_node);
- token_positions.Add(CodeSourceMapBuilder::kInitialPosition);
+ token_positions.Add(kNoDwarfPositionInfo);
break;
}
case CodeSourceMapBuilder::kPopFunction: {
@@ -498,7 +517,6 @@
const Script& parent_script,
SnapshotTextObjectNamer* namer) {
intptr_t file = LookupScript(parent_script);
- const auto& token_pos = node->call_pos;
intptr_t function_index = LookupFunction(node->function);
const Script& script = Script::Handle(zone_, node->function.script());
@@ -514,7 +532,9 @@
// DW_AT_call_file
stream->uleb128(file);
// DW_AT_call_line
- stream->uleb128(TokenPositionToLine(token_pos));
+ stream->uleb128(node->position.line());
+ // DW_at_call_column
+ stream->uleb128(node->position.column());
for (InliningNode* child = node->children_head; child != NULL;
child = child->children_next) {
@@ -582,17 +602,21 @@
// 6.2.5 The Line Number Program
+ // The initial values for the line number program state machine registers
+ // according to the DWARF standard.
+ intptr_t previous_pc_offset = 0;
intptr_t previous_file = 1;
intptr_t previous_line = 1;
+ intptr_t previous_column = 0;
+ // Other info not stored in the state machine registers.
const char* previous_asm_name = nullptr;
- intptr_t previous_pc_offset = 0;
Function& root_function = Function::Handle(zone_);
Script& script = Script::Handle(zone_);
CodeSourceMap& map = CodeSourceMap::Handle(zone_);
Array& functions = Array::Handle(zone_);
GrowableArray<const Function*> function_stack(zone_, 8);
- GrowableArray<TokenPosition> token_positions(zone_, 8);
+ GrowableArray<DwarfPosition> token_positions(zone_, 8);
SnapshotTextObjectNamer namer(zone_);
for (intptr_t i = 0; i < codes_.length(); i++) {
@@ -614,15 +638,14 @@
int32_t current_pc_offset = 0;
function_stack.Add(&root_function);
- token_positions.Add(CodeSourceMapBuilder::kInitialPosition);
+ token_positions.Add(kNoDwarfPositionInfo);
while (code_map_stream.PendingBytes() > 0) {
uint8_t opcode = code_map_stream.Read<uint8_t>();
switch (opcode) {
case CodeSourceMapBuilder::kChangePosition: {
- int32_t position = code_map_stream.Read<int32_t>();
token_positions[token_positions.length() - 1] =
- TokenPosition(position);
+ ReadPosition(&code_map_stream);
break;
}
case CodeSourceMapBuilder::kAdvancePC: {
@@ -641,12 +664,18 @@
}
// 2. Update LNP line.
- const auto line = TokenPositionToLine(token_positions.Last());
+ const intptr_t line = token_positions.Last().line();
+ const intptr_t column = token_positions.Last().column();
if (line != previous_line) {
stream->u1(DW_LNS_advance_line);
stream->sleb128(line - previous_line);
previous_line = line;
}
+ if (column != previous_column) {
+ stream->u1(DW_LNS_set_column);
+ stream->uleb128(column);
+ previous_column = column;
+ }
// 3. Emit LNP row if the address register has been updated to a
// non-zero value (dartbug.com/41756).
@@ -676,7 +705,7 @@
const Function& child_func = Function::Handle(
zone_, Function::RawCast(functions.At(func_index)));
function_stack.Add(&child_func);
- token_positions.Add(CodeSourceMapBuilder::kInitialPosition);
+ token_positions.Add(kNoDwarfPositionInfo);
break;
}
case CodeSourceMapBuilder::kPopFunction: {
diff --git a/runtime/vm/dwarf.h b/runtime/vm/dwarf.h
index dc1692c..65d612c 100644
--- a/runtime/vm/dwarf.h
+++ b/runtime/vm/dwarf.h
@@ -313,6 +313,7 @@
static const intptr_t DW_LNS_advance_pc = 0x2;
static const intptr_t DW_LNS_advance_line = 0x3;
static const intptr_t DW_LNS_set_file = 0x4;
+ static const intptr_t DW_LNS_set_column = 0x5;
static const intptr_t DW_LNE_end_sequence = 0x01;
static const intptr_t DW_LNE_set_address = 0x02;
@@ -324,12 +325,6 @@
kInlinedFunction,
};
- static constexpr intptr_t kNoLineInformation = 0;
-
- // Returns the line number or kNoLineInformation if there is no line
- // information available for the given token position.
- static intptr_t TokenPositionToLine(const TokenPosition& token_pos);
-
void WriteAbstractFunctions(DwarfWriteStream* stream);
void WriteConcreteFunctions(DwarfWriteStream* stream);
InliningNode* ExpandInliningTree(const Code& code);
diff --git a/runtime/vm/kernel.cc b/runtime/vm/kernel.cc
index d6663e3..84f456c 100644
--- a/runtime/vm/kernel.cc
+++ b/runtime/vm/kernel.cc
@@ -36,26 +36,6 @@
}
}
-intptr_t KernelLineStartsReader::LineNumberForPosition(
- intptr_t position) const {
- intptr_t line_count = line_starts_data_.Length();
- intptr_t current_start = 0;
- for (intptr_t i = 0; i < line_count; ++i) {
- current_start += helper_->At(line_starts_data_, i);
- if (current_start > position) {
- // If current_start is greater than the desired position, it means that
- // it is for the line after |position|. However, since line numbers
- // start at 1, we just return |i|.
- return i;
- }
-
- if (current_start == position) {
- return i + 1;
- }
- }
- return line_count;
-}
-
void KernelLineStartsReader::LocationForPosition(intptr_t position,
intptr_t* line,
intptr_t* col) const {
diff --git a/runtime/vm/kernel.h b/runtime/vm/kernel.h
index f612540..2ac42a1 100644
--- a/runtime/vm/kernel.h
+++ b/runtime/vm/kernel.h
@@ -140,8 +140,6 @@
return helper_->At(line_starts_data_, index);
}
- intptr_t LineNumberForPosition(intptr_t position) const;
-
void LocationForPosition(intptr_t position,
intptr_t* line,
intptr_t* col) const;
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 8e58e0f..29e4c97 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -10757,24 +10757,28 @@
// Specialized for AOT compilation, which does this lookup for every token
// position that could be part of a stack trace.
-intptr_t Script::GetTokenLineUsingLineStarts(
- TokenPosition target_token_pos) const {
- if (target_token_pos.IsNoSource()) {
- return 0;
- }
+bool Script::GetTokenLocationUsingLineStarts(TokenPosition target_token_pos,
+ intptr_t* line,
+ intptr_t* column) const {
+#if defined(DART_PRECOMPILED_RUNTIME)
+ return false;
+#else
+ // Negative positions denote positions that do not correspond to Dart code.
+ if (target_token_pos.value() < 0) return false;
+
Zone* zone = Thread::Current()->zone();
TypedData& line_starts_data = TypedData::Handle(zone, line_starts());
// Scripts loaded from bytecode may have null line_starts().
- if (line_starts_data.IsNull()) {
- return 0;
- }
+ if (line_starts_data.IsNull()) return false;
-#if !defined(DART_PRECOMPILED_RUNTIME)
kernel::KernelLineStartsReader line_starts_reader(line_starts_data, zone);
- return line_starts_reader.LineNumberForPosition(target_token_pos.value());
-#else
- return 0;
-#endif // !defined(DART_PRECOMPILED_RUNTIME)
+ line_starts_reader.LocationForPosition(target_token_pos.value(), line,
+ column);
+ // The line and column numbers returned are ordinals, so we shouldn't get 0.
+ ASSERT(*line > 0);
+ ASSERT(*column > 0);
+ return true;
+#endif
}
#if !defined(DART_PRECOMPILED_RUNTIME)
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index d87f411..0193fcd 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -4495,7 +4495,9 @@
void SetLocationOffset(intptr_t line_offset, intptr_t col_offset) const;
- intptr_t GetTokenLineUsingLineStarts(TokenPosition token_pos) const;
+ bool GetTokenLocationUsingLineStarts(TokenPosition token_pos,
+ intptr_t* line,
+ intptr_t* column) const;
void GetTokenLocation(TokenPosition token_pos,
intptr_t* line,
intptr_t* column,
diff --git a/sdk/lib/_http/http_headers.dart b/sdk/lib/_http/http_headers.dart
index ae8b482..a2edfbd 100644
--- a/sdk/lib/_http/http_headers.dart
+++ b/sdk/lib/_http/http_headers.dart
@@ -80,6 +80,9 @@
String lowercaseName = _validateField(name);
_headers.remove(lowercaseName);
_originalHeaderNames?.remove(lowercaseName);
+ if (lowercaseName == HttpHeaders.contentLengthHeader) {
+ _contentLength = -1;
+ }
if (lowercaseName == HttpHeaders.transferEncodingHeader) {
_chunkedTransferEncoding = false;
}
diff --git a/sdk/lib/_http/http_impl.dart b/sdk/lib/_http/http_impl.dart
index 766b567..162461c 100644
--- a/sdk/lib/_http/http_impl.dart
+++ b/sdk/lib/_http/http_impl.dart
@@ -1808,7 +1808,11 @@
startTimer();
return;
}
- if (closed) return;
+ // Keep the connection open if the CONNECT request was successful.
+ if (closed ||
+ (method == 'CONNECT' && incoming.statusCode == HttpStatus.ok)) {
+ return;
+ }
if (!closing &&
!_dispose &&
incoming.headers.persistentConnection &&
@@ -1895,10 +1899,8 @@
timeline?.instant('Establishing proxy tunnel', arguments: {
'proxyInfo': {
if (proxy.host != null) 'host': proxy.host,
- if (proxy.port != null)
- 'port': proxy.port,
- if (proxy.username != null)
- 'username': proxy.username,
+ if (proxy.port != null) 'port': proxy.port,
+ if (proxy.username != null) 'username': proxy.username,
// TODO(bkonyi): is this something we would want to surface? Initial
// thought is no.
// if (proxy.password != null)
diff --git a/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/runtime.dart b/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/runtime.dart
index 4867462..68c6691 100644
--- a/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/runtime.dart
+++ b/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/runtime.dart
@@ -202,7 +202,10 @@
for (var m in _cacheMaps) JS('', '#.clear()', m);
_cacheMaps.clear();
JS('', '#.clear()', _nullComparisonSet);
+ JS('', '#.clear()', constants);
+ JS('', '#.clear()', constantLists);
JS('', '#.clear()', constantMaps);
+ JS('', '#.clear()', constantSets);
}
/// Marks enqueuing an async operation.
diff --git a/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/types.dart b/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/types.dart
index f5f25a0..ad77d63 100644
--- a/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/types.dart
+++ b/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/types.dart
@@ -530,7 +530,24 @@
var formals = _getCanonicalTypeFormals(type.typeFormals.length);
var normBounds =
type.instantiateTypeBounds(formals).map(normalizeHelper).toList();
- var normFunc = normalizeHelper(type.instantiate(formals)) as FunctionType;
+
+ // Normalize type arguments that are bounded by Never to Never at their
+ // use site in the function type signature.
+ var substitutedTypes = [];
+ for (var i = 0; i < formals.length; i++) {
+ var substitutedType = normBounds[i];
+ while (formals.contains(substitutedType)) {
+ substitutedType = normBounds[formals.indexOf(substitutedType)];
+ }
+ if (substitutedType == _never) {
+ substitutedTypes.add(_never);
+ continue;
+ }
+ substitutedTypes.add(formals[i]);
+ }
+
+ var normFunc =
+ normalizeHelper(type.instantiate(substitutedTypes)) as FunctionType;
// Create a comparison key for structural identity.
var typeObjectIdKey = JS('', '[]');
JS('', '#.push(...#)', typeObjectIdKey, normBounds);
diff --git a/sdk/lib/_internal/js_runtime/lib/js_helper.dart b/sdk/lib/_internal/js_runtime/lib/js_helper.dart
index bf335946..7e3b6f1 100644
--- a/sdk/lib/_internal/js_runtime/lib/js_helper.dart
+++ b/sdk/lib/_internal/js_runtime/lib/js_helper.dart
@@ -82,22 +82,6 @@
/// them.
abstract class InternalMap {}
-/// Extracts the JavaScript-constructor name from the given isCheckProperty.
-// TODO(floitsch): move this to foreign_helper.dart or similar.
-@pragma('dart2js:tryInline')
-String isCheckPropertyToJsConstructorName(String isCheckProperty) {
- return JS_BUILTIN('returns:String;depends:none;effects:none',
- JsBuiltin.isCheckPropertyToJsConstructorName, isCheckProperty);
-}
-
-/// Retrieves the class name from type information stored on the constructor of
-/// [type].
-// TODO(floitsch): move this to foreign_helper.dart or similar.
-@pragma('dart2js:tryInline')
-String rawRtiToJsConstructorName(Object rti) {
- return JS_BUILTIN('String', JsBuiltin.rawRtiToJsConstructorName, rti);
-}
-
/// Given a raw constructor name, return the unminified name, if available,
/// otherwise tag the name with `minified:`.
String unminifyOrTag(String rawClassName) {
@@ -107,25 +91,6 @@
return rawClassName;
}
-/// Returns the rti from the given [constructorName].
-// TODO(floitsch): make this a builtin.
-jsConstructorNameToRti(String constructorName) {
- var getTypeFromName = JS_EMBEDDED_GLOBAL('', GET_TYPE_FROM_NAME);
- return JS('', '#(#)', getTypeFromName, constructorName);
-}
-
-/// Returns the raw runtime type of the given object [o].
-///
-/// The argument [o] must be the interceptor for primitive types. If
-/// necessary run it through [getInterceptor] first.
-// TODO(floitsch): move this to foreign_helper.dart or similar.
-// TODO(floitsch): we should call getInterceptor ourselves, but currently
-// getInterceptor is not GVNed.
-@pragma('dart2js:tryInline')
-Object getRawRuntimeType(Object o) {
- return JS_BUILTIN('', JsBuiltin.rawRuntimeType, o);
-}
-
/// Returns the metadata of the given [index].
// TODO(floitsch): move this to foreign_helper.dart or similar.
@pragma('dart2js:tryInline')
@@ -416,72 +381,6 @@
return _objectTypeNameNewRti(object);
}
- static String _objectClassName(Object object) {
- var interceptor = getInterceptor(object);
- // The interceptor is either an object (self-intercepting plain Dart class),
- // the prototype of the constructor for an Interceptor class (like
- // `JSString.prototype`, `JSNull.prototype`), or an Interceptor object
- // instance (`const JSString()`, should use `JSString.prototype`).
- //
- // These all should have a `constructor` property with a `name` property.
- String? name;
- var interceptorConstructor = JS('', '#.constructor', interceptor);
- if (JS('bool', 'typeof # == "function"', interceptorConstructor)) {
- var interceptorConstructorName = JS('', '#.name', interceptorConstructor);
- if (interceptorConstructorName is String) {
- name = interceptorConstructorName;
- }
- }
-
- if (name == null ||
- identical(interceptor, JS_INTERCEPTOR_CONSTANT(Interceptor)) ||
- object is UnknownJavaScriptObject) {
- // Try to do better. If we do not find something better, leave the name
- // as 'UnknownJavaScriptObject' or 'Interceptor' (or the minified name).
- //
- // When we get here via the UnknownJavaScriptObject test (for JavaScript
- // objects from outside the program), the object's constructor has a
- // better name that 'UnknownJavaScriptObject'.
- //
- // When we get here the Interceptor test (for Native classes that are
- // declared in the Dart program but have been 'folded' into Interceptor),
- // the native class's constructor name is better than the generic
- // 'Interceptor' (an abstract class).
-
- // Try the [constructorNameFallback]. This gets the constructor name for
- // any browser (used by [getNativeInterceptor]).
- String dispatchName = constructorNameFallback(object);
- name ??= dispatchName;
- if (dispatchName == 'Object') {
- // Try to decompile the constructor by turning it into a string and get
- // the name out of that. If the decompiled name is a string containing
- // an identifier, we use that instead of the very generic 'Object'.
- var objectConstructor = JS('', '#.constructor', object);
- if (JS('bool', 'typeof # == "function"', objectConstructor)) {
- var match = JS('var', r'#.match(/^\s*function\s*([\w$]*)\s*\(/)',
- JS('var', r'String(#)', objectConstructor));
- var decompiledName = match == null ? null : JS('var', r'#[1]', match);
- if (decompiledName is String &&
- JS('bool', r'/^\w+$/.test(#)', decompiledName)) {
- name = decompiledName;
- }
- }
- }
- return JS('String', '#', name);
- }
-
- // Type inference does not understand that [name] is now always a non-null
- // String. (There is some imprecision in the negation of the disjunction.)
- name = JS<String>('String', '#', name);
-
- // TODO(kasperl): If the namer gave us a fresh global name, we may
- // want to remove the numeric suffix that makes it unique too.
- if (name.length > 1 && identical(name.codeUnitAt(0), DOLLAR_CHAR_VALUE)) {
- name = name.substring(1);
- }
- return unminifyOrTag(name);
- }
-
/// Returns the type of [object] as a string (including type arguments).
/// Tries to return a sensible name for non-Dart objects.
///
@@ -1261,18 +1160,10 @@
JS('void', 'throw #', wrapException(ex));
}
-throwRuntimeError(message) {
- throw new RuntimeError(message);
-}
-
throwUnsupportedError(message) {
throw new UnsupportedError(message);
}
-throwAbstractClassInstantiationError(className) {
- throw new AbstractClassInstantiationError(className);
-}
-
// This is used in open coded for-in loops on arrays.
//
// checkConcurrentModificationError(a.length == startLength, a)
@@ -2138,45 +2029,6 @@
return constructor;
}
- static _computeSignatureFunctionLegacy(
- Object functionType, bool isStatic, bool isIntercepted) {
- if (JS('bool', 'typeof # == "number"', functionType)) {
- // We cannot call [getType] here, since the types-metadata might not be
- // set yet. This is, because fromTearOff might be called for constants
- // when the program isn't completely set up yet.
- //
- // Note that we cannot just textually inline the call
- // `getType(functionType)` since we cannot guarantee that the (then)
- // captured variable `functionType` isn't reused.
- return JS(
- '',
- '''(function(getType, t) {
- return function(){ return getType(t); };
- })(#, #)''',
- RAW_DART_FUNCTION_REF(getType),
- functionType);
- }
- if (JS('bool', 'typeof # == "function"', functionType)) {
- if (isStatic) {
- return functionType;
- } else {
- var getReceiver = isIntercepted
- ? RAW_DART_FUNCTION_REF(BoundClosure.receiverOf)
- : RAW_DART_FUNCTION_REF(BoundClosure.selfOf);
- return JS(
- '',
- 'function(f,r){'
- 'return function(){'
- 'return f.apply({\$receiver:r(this)},arguments)'
- '}'
- '}(#,#)',
- functionType,
- getReceiver);
- }
- }
- throw 'Error in functionType of tearoff';
- }
-
static _computeSignatureFunctionNewRti(
Object functionType, bool isStatic, bool isIntercepted) {
if (JS('bool', 'typeof # == "number"', functionType)) {
diff --git a/sdk/lib/_internal/js_runtime/lib/shared/embedded_names.dart b/sdk/lib/_internal/js_runtime/lib/shared/embedded_names.dart
index c50d99c..e80fb6a 100644
--- a/sdk/lib/_internal/js_runtime/lib/shared/embedded_names.dart
+++ b/sdk/lib/_internal/js_runtime/lib/shared/embedded_names.dart
@@ -11,29 +11,6 @@
/// This library is shared between the compiler and the runtime system.
library dart2js._embedded_names;
-/// The name of the property that is used to mark a type as typedef.
-///
-/// Without reflection typedefs are removed (expanded to their function type)
-/// but with reflection an object is needed to have the typedef's name. The
-/// object is marked with this property.
-///
-/// This property name only lives on internal type-objects and is only used
-/// when reflection is enabled.
-const TYPEDEF_PREDICATE_PROPERTY_NAME = r"$$isTypedef";
-
-/// The name of the property that is used to find the function type of a
-/// typedef.
-///
-/// Without reflection typedefs are removed (expanded to their function type)
-/// but with reflection an object is needed to have the typedef's name.
-///
-/// The typedef's object contains a pointer to its function type (as an index
-/// into the embedded global [TYPES]) in this property.
-///
-/// This property name only lives on internal type-objects and is only used
-/// when reflection is enabled.
-const TYPEDEF_TYPE_PROPERTY_NAME = r"$typedefType";
-
/// The name of the property that is used to find the native superclass of
/// an extended class.
///
@@ -206,51 +183,6 @@
/// This embedded global is used for --experiment-new-rti.
const RTI_UNIVERSE = 'typeUniverse';
-/// Returns a function that creates all precompiled functions (in particular
-/// constructors).
-///
-/// That is, the function returns the array that the full emitter would
-/// otherwise build dynamically when it finishes all classes.
-///
-/// This constant is only used in CSP mode.
-///
-/// This global is an emitter-internal embedded global, and not used by the
-/// runtime. The constant remains in this file to make sure that other embedded
-/// globals don't clash with it.
-const PRECOMPILED = 'precompiled';
-
-/// An emitter-internal embedded global. This global is not used by the runtime.
-const FINISHED_CLASSES = 'finishedClasses';
-
-/// A JavaScript object literal that maps the (minified) JavaScript constructor
-/// name (as given by [JsBuiltin.rawRtiToJsConstructorName] to the
-/// JavaScript constructor.
-///
-/// This embedded global is only used by reflection.
-const ALL_CLASSES = 'allClasses';
-
-/// A map from element to type information.
-///
-/// This embedded global is only used by reflection.
-const TYPE_INFORMATION = 'typeInformation';
-
-/// A map from statics to their descriptors.
-///
-/// This embedded global is only used by reflection.
-const STATICS = 'statics';
-
-/// An array of library descriptors.
-///
-/// The descriptor contains information such as name, uri, classes, ...
-///
-/// This embedded global is only used by reflection.
-const LIBRARIES = 'libraries';
-
-/// A map from lazy statics to their initializers.
-///
-/// This embedded global is only used by reflection.
-const LAZIES = 'lazies';
-
/// Names that are supported by [JS_GET_NAME].
// TODO(herhut): Make entries lower case (as in fields) and find a better name.
enum JsGetName {
@@ -264,8 +196,6 @@
CALL_PREFIX4,
CALL_PREFIX5,
CALL_CATCH_ALL,
- REFLECTABLE,
- CLASS_DESCRIPTOR_PROPERTY,
REQUIRED_PARAMETER_PROPERTY,
DEFAULT_VALUES_PROPERTY,
CALL_NAME_PROPERTY,
@@ -297,12 +227,6 @@
/// String representation of the type of the object class.
OBJECT_CLASS_TYPE_NAME,
- /// String representation of the type of the function class.
- FUNCTION_CLASS_TYPE_NAME,
-
- /// String representation of the type of the JavaScriptFunction class.
- JS_FUNCTION_CLASS_TYPE_NAME,
-
/// Property name for Rti._as field.
RTI_FIELD_AS,
@@ -328,34 +252,12 @@
/// ...
dartClosureConstructor,
- /// Returns the JavaScript-constructor name given an [isCheckProperty].
- ///
- /// This relies on a deterministic encoding of is-check properties (for
- /// example `$isFoo` for a class `Foo`). In minified code the returned
- /// classname is the minified name of the class.
- ///
- /// JS_BUILTIN('returns:String;depends:none;effects:none',
- /// JsBuiltin.isCheckPropertyToJsConstructorName,
- /// isCheckProperty);
- isCheckPropertyToJsConstructorName,
-
/// Returns true if the given type is a type argument of a js-interop class
/// or a supertype of a js-interop class.
///
/// JS_BUILTIN('bool', JsBuiltin.isJsInteropTypeArgument, o)
isJsInteropTypeArgument,
- /// Returns the JavaScript-constructor name given an rti encoding.
- ///
- /// JS_BUILTIN('String', JsBuiltin.rawRtiToJsConstructorName, rti)
- rawRtiToJsConstructorName,
-
- /// Returns the raw runtime type of the given object. The given argument
- /// [o] should be the interceptor (for non-Dart objects).
- ///
- /// JS_BUILTIN('', JsBuiltin.rawRuntimeType, o)
- rawRuntimeType,
-
/// Returns the metadata of the given [index].
///
/// JS_BUILTIN('returns:var;effects:none;depends:none',
diff --git a/sdk/lib/_internal/vm/lib/ffi_patch.dart b/sdk/lib/_internal/vm/lib/ffi_patch.dart
index 735c7e2..a04c737 100644
--- a/sdk/lib/_internal/vm/lib/ffi_patch.dart
+++ b/sdk/lib/_internal/vm/lib/ffi_patch.dart
@@ -68,7 +68,7 @@
// Function objects returned by this native method are not Dart instances,
// so we need to use top type as a return type to avoid type check.
dynamic _nativeCallbackFunction<NS extends Function>(Function target,
- Object exceptionalReturn) native "Ffi_nativeCallbackFunction";
+ Object? exceptionalReturn) native "Ffi_nativeCallbackFunction";
Pointer<NS> _pointerFromFunction<NS extends NativeFunction>(dynamic function)
native "Ffi_pointerFromFunction";
diff --git a/sdk/lib/_internal/vm/lib/mirrors_impl.dart b/sdk/lib/_internal/vm/lib/mirrors_impl.dart
index a7f1f13..712d2c8 100644
--- a/sdk/lib/_internal/vm/lib/mirrors_impl.dart
+++ b/sdk/lib/_internal/vm/lib/mirrors_impl.dart
@@ -1160,7 +1160,7 @@
}
class _MethodMirror extends _DeclarationMirror implements MethodMirror {
- final Type _instantiator;
+ final Type? _instantiator;
final bool isStatic;
final int _kindFlags;
@@ -1353,7 +1353,7 @@
false // Not extension member.
);
- Object _defaultValueReflectee;
+ Object? _defaultValueReflectee;
InstanceMirror? _defaultValue;
InstanceMirror? get defaultValue {
if (!isOptional) {
diff --git a/sdk/lib/_internal/vm/lib/string_patch.dart b/sdk/lib/_internal/vm/lib/string_patch.dart
index 55fc053..5d6612d 100644
--- a/sdk/lib/_internal/vm/lib/string_patch.dart
+++ b/sdk/lib/_internal/vm/lib/string_patch.dart
@@ -824,7 +824,7 @@
// Convert single object to string.
@pragma("vm:entry-point", "call")
- static String _interpolateSingle(Object o) {
+ static String _interpolateSingle(Object? o) {
if (o is String) return o;
final s = o.toString();
if (s is! String) {
diff --git a/tests/dart2js/internal/mock_libraries.dart b/tests/dart2js/internal/mock_libraries.dart
index 28f6f01..f747443 100644
--- a/tests/dart2js/internal/mock_libraries.dart
+++ b/tests/dart2js/internal/mock_libraries.dart
@@ -275,8 +275,6 @@
'stringTypeCheck': 'stringTypeCheck(x) {}',
'subtypeCast': 'subtypeCast(object, isField, checks, asField) {}',
'subtypeOfRuntimeTypeCast': 'subtypeOfRuntimeTypeCast(object, type) {}',
- 'throwAbstractClassInstantiationError':
- 'throwAbstractClassInstantiationError(className) {}',
'checkConcurrentModificationError':
'checkConcurrentModificationError(collection) {}',
'throwConcurrentModificationError':
@@ -285,7 +283,6 @@
'throwExpression': 'throwExpression(e) {}',
'throwNoSuchMethod':
'throwNoSuchMethod(obj, name, arguments, expectedArgumentNames) {}',
- 'throwRuntimeError': 'throwRuntimeError(message) {}',
'throwUnsupportedError': 'throwUnsupportedError(message) {}',
'throwTypeError': 'throwTypeError(message) {}',
'unwrapException': 'unwrapException(e) {}',
diff --git a/tests/dart2js_2/internal/mock_libraries.dart b/tests/dart2js_2/internal/mock_libraries.dart
index edd762b..adec88a 100644
--- a/tests/dart2js_2/internal/mock_libraries.dart
+++ b/tests/dart2js_2/internal/mock_libraries.dart
@@ -234,8 +234,6 @@
'S': 'S() {}',
'setRuntimeTypeInfo': 'setRuntimeTypeInfo(a, b) {}',
'subtypeOfRuntimeTypeCast': 'subtypeOfRuntimeTypeCast(object, type) {}',
- 'throwAbstractClassInstantiationError':
- 'throwAbstractClassInstantiationError(className) {}',
'checkConcurrentModificationError':
'checkConcurrentModificationError(collection) {}',
'throwConcurrentModificationError':
@@ -244,7 +242,6 @@
'throwExpression': 'throwExpression(e) {}',
'throwNoSuchMethod':
'throwNoSuchMethod(obj, name, arguments, expectedArgumentNames) {}',
- 'throwRuntimeError': 'throwRuntimeError(message) {}',
'throwUnsupportedError': 'throwUnsupportedError(message) {}',
'unwrapException': 'unwrapException(e) {}',
'voidTypeCheck': 'voidTypeCheck(value) {}',
diff --git a/tests/language/covariant/subtyping_test.dart b/tests/language/covariant/subtyping_test.dart
index 51aa58f..43d665e 100644
--- a/tests/language/covariant/subtyping_test.dart
+++ b/tests/language/covariant/subtyping_test.dart
@@ -217,20 +217,25 @@
method7(T Function() Function() returnsReturnsT) => null; // needs check
}
+typedef F1 = dynamic Function(Object?);
+typedef F2 = dynamic Function(dynamic Function(int));
+typedef F3 = dynamic Function(dynamic Function(int Function()));
+typedef F4 = dynamic Function(dynamic Function(int) Function());
+
testTearOffRuntimeType() {
- expectRTTI(tearoff, type) => Expect.equals('${tearoff.runtimeType}', type,
+ expectRTTI(tearoff, type) => Expect.equals(tearoff.runtimeType, type,
'covariant params should reify with Object? as their type');
TearOff<num> t = new TearOff<int>();
- expectRTTI(t.method1, '(Object?) => dynamic');
+ expectRTTI(t.method1, F1);
- expectRTTI(t.method2, '((int) => dynamic) => dynamic');
- expectRTTI(t.method3, '(Object?) => dynamic');
+ expectRTTI(t.method2, F2);
+ expectRTTI(t.method3, F1);
- expectRTTI(t.method4, '(Object?) => dynamic');
- expectRTTI(t.method5, '((() => int) => dynamic) => dynamic');
- expectRTTI(t.method6, '(() => (int) => dynamic) => dynamic');
- expectRTTI(t.method7, '(Object?) => dynamic');
+ expectRTTI(t.method4, F1);
+ expectRTTI(t.method5, F3);
+ expectRTTI(t.method6, F4);
+ expectRTTI(t.method7, F1);
}
main() {
diff --git a/tests/language/interface/implements_futureor_test.dart b/tests/language/interface/implements_futureor_test.dart
index 1ec6779..abe2e64 100644
--- a/tests/language/interface/implements_futureor_test.dart
+++ b/tests/language/interface/implements_futureor_test.dart
@@ -6,7 +6,7 @@
class A<T> implements FutureOr<T> {}
// ^^^^^^^^^^^
-// [analyzer] COMPILE_TIME_ERROR.IMPLEMENTS_DISALLOWED_CLASS
+// [analyzer] COMPILE_TIME_ERROR.SUBTYPE_OF_DISALLOWED_TYPE
// ^
// [cfe] The type 'FutureOr' can't be used in an 'implements' clause.
diff --git a/tests/language/null_aware/access_runtime_1_test.dart b/tests/language/null_aware/access_runtime_1_test.dart
new file mode 100644
index 0000000..ba92ba5
--- /dev/null
+++ b/tests/language/null_aware/access_runtime_1_test.dart
@@ -0,0 +1,58 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2015, 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.
+
+// Verify semantics of the ?. operator when it does not appear on the LHS of an
+// assignment.
+
+import "package:expect/expect.dart";
+import "conditional_access_helper.dart" as h;
+
+class B {}
+
+class C extends B {
+ int? v;
+ C(this.v);
+ static int? staticInt;
+}
+
+C? nullC() => null;
+
+main() {
+ // Make sure the "none" test fails if property access using "?." is not
+ // implemented. This makes status files easier to maintain.
+ nullC()?.v;
+
+ // e1?.id is equivalent to ((x) => x == null ? null : x.id)(e1).
+ Expect.equals(null, nullC()?.v);
+
+
+ // C?.id is equivalent to C.id.
+
+
+
+ // The static type of e1?.d is the static type of e1.id.
+
+
+
+
+
+
+
+ // Let T be the static type of e1 and let y be a fresh variable of type T.
+ // Exactly the same static warnings that would be caused by y.id are also
+ // generated in the case of e1?.id.
+
+
+
+ // '?.' cannot be used to access toplevel properties in libraries imported via
+ // prefix.
+
+
+ // Nor can it be used to access the hashCode getter on the class Type.
+
+
+}
diff --git a/tests/language/null_aware/access_runtime_2_test.dart b/tests/language/null_aware/access_runtime_2_test.dart
new file mode 100644
index 0000000..b67de99
--- /dev/null
+++ b/tests/language/null_aware/access_runtime_2_test.dart
@@ -0,0 +1,59 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2015, 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.
+
+// Verify semantics of the ?. operator when it does not appear on the LHS of an
+// assignment.
+
+import "package:expect/expect.dart";
+import "conditional_access_helper.dart" as h;
+
+class B {}
+
+class C extends B {
+ int? v;
+ C(this.v);
+ static int? staticInt;
+}
+
+C? nullC() => null;
+
+main() {
+ // Make sure the "none" test fails if property access using "?." is not
+ // implemented. This makes status files easier to maintain.
+ nullC()?.v;
+
+ // e1?.id is equivalent to ((x) => x == null ? null : x.id)(e1).
+
+ C? c = new C(1);
+ Expect.equals(1, c?.v);
+
+ // C?.id is equivalent to C.id.
+
+
+
+ // The static type of e1?.d is the static type of e1.id.
+
+
+
+
+
+
+
+ // Let T be the static type of e1 and let y be a fresh variable of type T.
+ // Exactly the same static warnings that would be caused by y.id are also
+ // generated in the case of e1?.id.
+
+
+
+ // '?.' cannot be used to access toplevel properties in libraries imported via
+ // prefix.
+
+
+ // Nor can it be used to access the hashCode getter on the class Type.
+
+
+}
diff --git a/tests/language/null_aware/access_runtime_3_test.dart b/tests/language/null_aware/access_runtime_3_test.dart
new file mode 100644
index 0000000..633aa36
--- /dev/null
+++ b/tests/language/null_aware/access_runtime_3_test.dart
@@ -0,0 +1,58 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2015, 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.
+
+// Verify semantics of the ?. operator when it does not appear on the LHS of an
+// assignment.
+
+import "package:expect/expect.dart";
+import "conditional_access_helper.dart" as h;
+
+class B {}
+
+class C extends B {
+ int? v;
+ C(this.v);
+ static int? staticInt;
+}
+
+C? nullC() => null;
+
+main() {
+ // Make sure the "none" test fails if property access using "?." is not
+ // implemented. This makes status files easier to maintain.
+ nullC()?.v;
+
+ // e1?.id is equivalent to ((x) => x == null ? null : x.id)(e1).
+
+
+
+ // C?.id is equivalent to C.id.
+ { C.staticInt = 1; Expect.equals(1, C?.staticInt); }
+
+
+ // The static type of e1?.d is the static type of e1.id.
+
+
+
+
+
+
+
+ // Let T be the static type of e1 and let y be a fresh variable of type T.
+ // Exactly the same static warnings that would be caused by y.id are also
+ // generated in the case of e1?.id.
+
+
+
+ // '?.' cannot be used to access toplevel properties in libraries imported via
+ // prefix.
+
+
+ // Nor can it be used to access the hashCode getter on the class Type.
+
+
+}
diff --git a/tests/language/null_aware/access_runtime_4_test.dart b/tests/language/null_aware/access_runtime_4_test.dart
new file mode 100644
index 0000000..96462c6
--- /dev/null
+++ b/tests/language/null_aware/access_runtime_4_test.dart
@@ -0,0 +1,58 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2015, 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.
+
+// Verify semantics of the ?. operator when it does not appear on the LHS of an
+// assignment.
+
+import "package:expect/expect.dart";
+import "conditional_access_helper.dart" as h;
+
+class B {}
+
+class C extends B {
+ int? v;
+ C(this.v);
+ static int? staticInt;
+}
+
+C? nullC() => null;
+
+main() {
+ // Make sure the "none" test fails if property access using "?." is not
+ // implemented. This makes status files easier to maintain.
+ nullC()?.v;
+
+ // e1?.id is equivalent to ((x) => x == null ? null : x.id)(e1).
+
+
+
+ // C?.id is equivalent to C.id.
+
+ { h.C.staticInt = 1; Expect.equals(1, h.C?.staticInt); }
+
+ // The static type of e1?.d is the static type of e1.id.
+
+
+
+
+
+
+
+ // Let T be the static type of e1 and let y be a fresh variable of type T.
+ // Exactly the same static warnings that would be caused by y.id are also
+ // generated in the case of e1?.id.
+
+
+
+ // '?.' cannot be used to access toplevel properties in libraries imported via
+ // prefix.
+
+
+ // Nor can it be used to access the hashCode getter on the class Type.
+
+
+}
diff --git a/tests/language/null_aware/access_runtime_5_test.dart b/tests/language/null_aware/access_runtime_5_test.dart
new file mode 100644
index 0000000..5984802
--- /dev/null
+++ b/tests/language/null_aware/access_runtime_5_test.dart
@@ -0,0 +1,59 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2015, 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.
+
+// Verify semantics of the ?. operator when it does not appear on the LHS of an
+// assignment.
+
+import "package:expect/expect.dart";
+import "conditional_access_helper.dart" as h;
+
+class B {}
+
+class C extends B {
+ int? v;
+ C(this.v);
+ static int? staticInt;
+}
+
+C? nullC() => null;
+
+main() {
+ // Make sure the "none" test fails if property access using "?." is not
+ // implemented. This makes status files easier to maintain.
+ nullC()?.v;
+
+ // e1?.id is equivalent to ((x) => x == null ? null : x.id)(e1).
+
+
+
+ // C?.id is equivalent to C.id.
+
+
+
+ // The static type of e1?.d is the static type of e1.id.
+ C? c = new C(1);
+ { int? i = c?.v; Expect.equals(1, i); }
+
+
+
+
+
+
+ // Let T be the static type of e1 and let y be a fresh variable of type T.
+ // Exactly the same static warnings that would be caused by y.id are also
+ // generated in the case of e1?.id.
+
+
+
+ // '?.' cannot be used to access toplevel properties in libraries imported via
+ // prefix.
+
+
+ // Nor can it be used to access the hashCode getter on the class Type.
+
+
+}
diff --git a/tests/language/null_aware/access_runtime_6_test.dart b/tests/language/null_aware/access_runtime_6_test.dart
new file mode 100644
index 0000000..5b4a999
--- /dev/null
+++ b/tests/language/null_aware/access_runtime_6_test.dart
@@ -0,0 +1,58 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2015, 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.
+
+// Verify semantics of the ?. operator when it does not appear on the LHS of an
+// assignment.
+
+import "package:expect/expect.dart";
+import "conditional_access_helper.dart" as h;
+
+class B {}
+
+class C extends B {
+ int? v;
+ C(this.v);
+ static int? staticInt;
+}
+
+C? nullC() => null;
+
+main() {
+ // Make sure the "none" test fails if property access using "?." is not
+ // implemented. This makes status files easier to maintain.
+ nullC()?.v;
+
+ // e1?.id is equivalent to ((x) => x == null ? null : x.id)(e1).
+
+
+
+ // C?.id is equivalent to C.id.
+
+
+
+ // The static type of e1?.d is the static type of e1.id.
+
+
+ { C.staticInt = 1; int? i = C?.staticInt; Expect.equals(1, i); }
+
+
+
+
+ // Let T be the static type of e1 and let y be a fresh variable of type T.
+ // Exactly the same static warnings that would be caused by y.id are also
+ // generated in the case of e1?.id.
+
+
+
+ // '?.' cannot be used to access toplevel properties in libraries imported via
+ // prefix.
+
+
+ // Nor can it be used to access the hashCode getter on the class Type.
+
+
+}
diff --git a/tests/language/null_aware/access_runtime_7_test.dart b/tests/language/null_aware/access_runtime_7_test.dart
new file mode 100644
index 0000000..7f53dc1
--- /dev/null
+++ b/tests/language/null_aware/access_runtime_7_test.dart
@@ -0,0 +1,58 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2015, 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.
+
+// Verify semantics of the ?. operator when it does not appear on the LHS of an
+// assignment.
+
+import "package:expect/expect.dart";
+import "conditional_access_helper.dart" as h;
+
+class B {}
+
+class C extends B {
+ int? v;
+ C(this.v);
+ static int? staticInt;
+}
+
+C? nullC() => null;
+
+main() {
+ // Make sure the "none" test fails if property access using "?." is not
+ // implemented. This makes status files easier to maintain.
+ nullC()?.v;
+
+ // e1?.id is equivalent to ((x) => x == null ? null : x.id)(e1).
+
+
+
+ // C?.id is equivalent to C.id.
+
+
+
+ // The static type of e1?.d is the static type of e1.id.
+
+
+
+ { h.C.staticInt = 1; int? i = h.C?.staticInt; Expect.equals(1, i); }
+
+
+
+ // Let T be the static type of e1 and let y be a fresh variable of type T.
+ // Exactly the same static warnings that would be caused by y.id are also
+ // generated in the case of e1?.id.
+
+
+
+ // '?.' cannot be used to access toplevel properties in libraries imported via
+ // prefix.
+
+
+ // Nor can it be used to access the hashCode getter on the class Type.
+
+
+}
diff --git a/tests/language/null_aware/access_runtime_test.dart b/tests/language/null_aware/access_runtime_test.dart
new file mode 100644
index 0000000..5d8a2c8
--- /dev/null
+++ b/tests/language/null_aware/access_runtime_test.dart
@@ -0,0 +1,58 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2015, 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.
+
+// Verify semantics of the ?. operator when it does not appear on the LHS of an
+// assignment.
+
+import "package:expect/expect.dart";
+import "conditional_access_helper.dart" as h;
+
+class B {}
+
+class C extends B {
+ int? v;
+ C(this.v);
+ static int? staticInt;
+}
+
+C? nullC() => null;
+
+main() {
+ // Make sure the "none" test fails if property access using "?." is not
+ // implemented. This makes status files easier to maintain.
+ nullC()?.v;
+
+ // e1?.id is equivalent to ((x) => x == null ? null : x.id)(e1).
+
+
+
+ // C?.id is equivalent to C.id.
+
+
+
+ // The static type of e1?.d is the static type of e1.id.
+
+
+
+
+
+
+
+ // Let T be the static type of e1 and let y be a fresh variable of type T.
+ // Exactly the same static warnings that would be caused by y.id are also
+ // generated in the case of e1?.id.
+
+
+
+ // '?.' cannot be used to access toplevel properties in libraries imported via
+ // prefix.
+
+
+ // Nor can it be used to access the hashCode getter on the class Type.
+
+
+}
diff --git a/tests/language/null_aware/access_test.dart b/tests/language/null_aware/access_test.dart
new file mode 100644
index 0000000..deaa991
--- /dev/null
+++ b/tests/language/null_aware/access_test.dart
@@ -0,0 +1,88 @@
+// Copyright (c) 2015, 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.
+
+// Verify semantics of the ?. operator when it does not appear on the LHS of an
+// assignment.
+
+import "package:expect/expect.dart";
+import "conditional_access_helper.dart" as h;
+
+class B {}
+
+class C extends B {
+ int? v;
+ C(this.v);
+ static int? staticInt;
+}
+
+C? nullC() => null;
+
+main() {
+ // Make sure the "none" test fails if property access using "?." is not
+ // implemented. This makes status files easier to maintain.
+ nullC()?.v;
+
+ // e1?.id is equivalent to ((x) => x == null ? null : x.id)(e1).
+ Expect.equals(null, nullC()?.v);
+ Expect.equals(1, new C(1)?.v);
+ // ^^
+ // [analyzer] STATIC_WARNING.INVALID_NULL_AWARE_OPERATOR
+
+ // C?.id is equivalent to C.id.
+ { C.staticInt = 1; Expect.equals(1, C?.staticInt); }
+ { h.C.staticInt = 1; Expect.equals(1, h.C?.staticInt); }
+
+ // The static type of e1?.d is the static type of e1.id.
+ { int? i = new C(1)?.v; Expect.equals(1, i); }
+ // ^^
+ // [analyzer] STATIC_WARNING.INVALID_NULL_AWARE_OPERATOR
+ { String? s = new C(null)?.v; Expect.equals(null, s); }
+ // ^^^^^^^^^^^^^^
+ // [analyzer] STATIC_TYPE_WARNING.INVALID_ASSIGNMENT
+ // ^
+ // [cfe] A value of type 'int?' can't be assigned to a variable of type 'String?'.
+ // ^^
+ // [analyzer] STATIC_WARNING.INVALID_NULL_AWARE_OPERATOR
+ { C.staticInt = 1; int? i = C?.staticInt; Expect.equals(1, i); }
+ { h.C.staticInt = 1; int? i = h.C?.staticInt; Expect.equals(1, i); }
+ { C.staticInt = null; String? s = C?.staticInt; Expect.equals(null, s); }
+ // ^^^^^^^^^^^^
+ // [analyzer] STATIC_TYPE_WARNING.INVALID_ASSIGNMENT
+ // ^
+ // [cfe] A value of type 'int?' can't be assigned to a variable of type 'String?'.
+ { h.C.staticNullable = null; String? s = h.C?.staticNullable; Expect.equals(null, s); }
+ // ^^^^^^^^^^^^^^^^^^^
+ // [analyzer] STATIC_TYPE_WARNING.INVALID_ASSIGNMENT
+ // ^
+ // [cfe] A value of type 'int?' can't be assigned to a variable of type 'String?'.
+
+ // Let T be the static type of e1 and let y be a fresh variable of type T.
+ // Exactly the same static warnings that would be caused by y.id are also
+ // generated in the case of e1?.id.
+ Expect.equals(null, nullC()?.bad);
+ // ^^^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'bad' isn't defined for the class 'C'.
+ { B? b = new C(1); Expect.equals(1, b?.v); }
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'v' isn't defined for the class 'B'.
+
+ // '?.' cannot be used to access toplevel properties in libraries imported via
+ // prefix.
+ var x = h?.topLevelVar;
+ // ^
+ // [analyzer] COMPILE_TIME_ERROR.PREFIX_IDENTIFIER_NOT_FOLLOWED_BY_DOT
+ // [cfe] A prefix can't be used with null-aware operators.
+
+ // Nor can it be used to access the hashCode getter on the class Type.
+ Expect.throwsNoSuchMethodError(() => C?.hashCode);
+ // ^^^^^^^^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] Getter not found: 'hashCode'.
+ Expect.throwsNoSuchMethodError(() => h.C?.hashCode);
+ // ^^^^^^^^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] Getter not found: 'hashCode'.
+}
diff --git a/tests/language/null_aware/assignment_runtime_10_test.dart b/tests/language/null_aware/assignment_runtime_10_test.dart
new file mode 100644
index 0000000..91abae7
--- /dev/null
+++ b/tests/language/null_aware/assignment_runtime_10_test.dart
@@ -0,0 +1,101 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2015, 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.
+
+// Verify semantics of the ?. operator when it appears on the LHS of an
+// assignment.
+
+import "package:expect/expect.dart";
+import "conditional_access_helper.dart" as h;
+
+bad() {
+ Expect.fail('Should not be executed');
+}
+
+class B {}
+
+class C extends B {
+ int v;
+ C(this.v);
+ static late int staticInt;
+}
+
+class D {
+ E v;
+ D(this.v);
+ static late E staticE;
+}
+
+class E {
+ G operator +(int i) => new I();
+}
+
+class F {}
+
+class G extends E implements F {}
+
+class H {}
+
+class I extends G implements H {}
+
+C? nullC() => null;
+
+main() {
+ // Make sure the "none" test fails if assignment to "?." is not implemented.
+ // This makes status files easier to maintain.
+ nullC()?.v = 1;
+
+ // e1?.v = e2 is equivalent to ((x) => x == null ? null : x.v = e2)(e1).
+
+
+
+ // C?.v = e2 is equivalent to C.v = e2.
+
+
+
+ // The static type of e1?.v = e2 is the static type of e2.
+
+
+
+
+
+
+
+ // Exactly the same errors that would be caused by e1.v = e2 are
+ // also generated in the case of e1?.v = e2.
+
+
+
+ // e1?.v op= e2 is equivalent to ((x) => x?.v = x.v op e2)(e1).
+
+
+
+ // C?.v op= e2 is equivalent to C.v op= e2.
+ { C.staticInt = 1; Expect.equals(3, C?.staticInt += 2); Expect.equals(3, C?.staticInt); }
+
+ // The static type of e1?.v op= e2 is the static type of e1.v op e2.
+
+
+
+
+ // Let T be the static type of e1 and let y be a fresh variable of type T.
+ // Exactly the same errors that would be caused by y.v op e2 are
+ // also generated in the case of e1?.v op= e2.
+
+
+
+
+
+
+
+
+
+ // '?.' cannot be used to assign to toplevel properties in libraries imported
+ // via prefix.
+
+
+
+}
diff --git a/tests/language/null_aware/assignment_runtime_11_test.dart b/tests/language/null_aware/assignment_runtime_11_test.dart
new file mode 100644
index 0000000..05b84a6
--- /dev/null
+++ b/tests/language/null_aware/assignment_runtime_11_test.dart
@@ -0,0 +1,101 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2015, 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.
+
+// Verify semantics of the ?. operator when it appears on the LHS of an
+// assignment.
+
+import "package:expect/expect.dart";
+import "conditional_access_helper.dart" as h;
+
+bad() {
+ Expect.fail('Should not be executed');
+}
+
+class B {}
+
+class C extends B {
+ int v;
+ C(this.v);
+ static late int staticInt;
+}
+
+class D {
+ E v;
+ D(this.v);
+ static late E staticE;
+}
+
+class E {
+ G operator +(int i) => new I();
+}
+
+class F {}
+
+class G extends E implements F {}
+
+class H {}
+
+class I extends G implements H {}
+
+C? nullC() => null;
+
+main() {
+ // Make sure the "none" test fails if assignment to "?." is not implemented.
+ // This makes status files easier to maintain.
+ nullC()?.v = 1;
+
+ // e1?.v = e2 is equivalent to ((x) => x == null ? null : x.v = e2)(e1).
+
+
+
+ // C?.v = e2 is equivalent to C.v = e2.
+
+
+
+ // The static type of e1?.v = e2 is the static type of e2.
+
+
+
+
+
+
+
+ // Exactly the same errors that would be caused by e1.v = e2 are
+ // also generated in the case of e1?.v = e2.
+
+
+
+ // e1?.v op= e2 is equivalent to ((x) => x?.v = x.v op e2)(e1).
+
+
+
+ // C?.v op= e2 is equivalent to C.v op= e2.
+
+
+ // The static type of e1?.v op= e2 is the static type of e1.v op e2.
+ { D? d = new D(new E()); F? f = (d?.v += 1); Expect.identical(d!.v, f); }
+
+
+
+ // Let T be the static type of e1 and let y be a fresh variable of type T.
+ // Exactly the same errors that would be caused by y.v op e2 are
+ // also generated in the case of e1?.v op= e2.
+
+
+
+
+
+
+
+
+
+ // '?.' cannot be used to assign to toplevel properties in libraries imported
+ // via prefix.
+
+
+
+}
diff --git a/tests/language/null_aware/assignment_runtime_12_test.dart b/tests/language/null_aware/assignment_runtime_12_test.dart
new file mode 100644
index 0000000..c17f848
--- /dev/null
+++ b/tests/language/null_aware/assignment_runtime_12_test.dart
@@ -0,0 +1,101 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2015, 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.
+
+// Verify semantics of the ?. operator when it appears on the LHS of an
+// assignment.
+
+import "package:expect/expect.dart";
+import "conditional_access_helper.dart" as h;
+
+bad() {
+ Expect.fail('Should not be executed');
+}
+
+class B {}
+
+class C extends B {
+ int v;
+ C(this.v);
+ static late int staticInt;
+}
+
+class D {
+ E v;
+ D(this.v);
+ static late E staticE;
+}
+
+class E {
+ G operator +(int i) => new I();
+}
+
+class F {}
+
+class G extends E implements F {}
+
+class H {}
+
+class I extends G implements H {}
+
+C? nullC() => null;
+
+main() {
+ // Make sure the "none" test fails if assignment to "?." is not implemented.
+ // This makes status files easier to maintain.
+ nullC()?.v = 1;
+
+ // e1?.v = e2 is equivalent to ((x) => x == null ? null : x.v = e2)(e1).
+
+
+
+ // C?.v = e2 is equivalent to C.v = e2.
+
+
+
+ // The static type of e1?.v = e2 is the static type of e2.
+
+
+
+
+
+
+
+ // Exactly the same errors that would be caused by e1.v = e2 are
+ // also generated in the case of e1?.v = e2.
+
+
+
+ // e1?.v op= e2 is equivalent to ((x) => x?.v = x.v op e2)(e1).
+
+
+
+ // C?.v op= e2 is equivalent to C.v op= e2.
+
+
+ // The static type of e1?.v op= e2 is the static type of e1.v op e2.
+
+ { D.staticE = new E(); F? f = (D?.staticE += 1); Expect.identical(D.staticE, f); }
+
+
+ // Let T be the static type of e1 and let y be a fresh variable of type T.
+ // Exactly the same errors that would be caused by y.v op e2 are
+ // also generated in the case of e1?.v op= e2.
+
+
+
+
+
+
+
+
+
+ // '?.' cannot be used to assign to toplevel properties in libraries imported
+ // via prefix.
+
+
+
+}
diff --git a/tests/language/null_aware/assignment_runtime_13_test.dart b/tests/language/null_aware/assignment_runtime_13_test.dart
new file mode 100644
index 0000000..961aff3
--- /dev/null
+++ b/tests/language/null_aware/assignment_runtime_13_test.dart
@@ -0,0 +1,101 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2015, 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.
+
+// Verify semantics of the ?. operator when it appears on the LHS of an
+// assignment.
+
+import "package:expect/expect.dart";
+import "conditional_access_helper.dart" as h;
+
+bad() {
+ Expect.fail('Should not be executed');
+}
+
+class B {}
+
+class C extends B {
+ int v;
+ C(this.v);
+ static late int staticInt;
+}
+
+class D {
+ E v;
+ D(this.v);
+ static late E staticE;
+}
+
+class E {
+ G operator +(int i) => new I();
+}
+
+class F {}
+
+class G extends E implements F {}
+
+class H {}
+
+class I extends G implements H {}
+
+C? nullC() => null;
+
+main() {
+ // Make sure the "none" test fails if assignment to "?." is not implemented.
+ // This makes status files easier to maintain.
+ nullC()?.v = 1;
+
+ // e1?.v = e2 is equivalent to ((x) => x == null ? null : x.v = e2)(e1).
+
+
+
+ // C?.v = e2 is equivalent to C.v = e2.
+
+
+
+ // The static type of e1?.v = e2 is the static type of e2.
+
+
+
+
+
+
+
+ // Exactly the same errors that would be caused by e1.v = e2 are
+ // also generated in the case of e1?.v = e2.
+
+
+
+ // e1?.v op= e2 is equivalent to ((x) => x?.v = x.v op e2)(e1).
+
+
+
+ // C?.v op= e2 is equivalent to C.v op= e2.
+
+
+ // The static type of e1?.v op= e2 is the static type of e1.v op e2.
+
+
+ { h.D.staticE = new h.E(); h.F? f = (h.D?.staticE += 1); Expect.identical(h.D.staticE, f); }
+
+ // Let T be the static type of e1 and let y be a fresh variable of type T.
+ // Exactly the same errors that would be caused by y.v op e2 are
+ // also generated in the case of e1?.v op= e2.
+
+
+
+
+
+
+
+
+
+ // '?.' cannot be used to assign to toplevel properties in libraries imported
+ // via prefix.
+
+
+
+}
diff --git a/tests/language/null_aware/assignment_runtime_1_test.dart b/tests/language/null_aware/assignment_runtime_1_test.dart
new file mode 100644
index 0000000..121302d
--- /dev/null
+++ b/tests/language/null_aware/assignment_runtime_1_test.dart
@@ -0,0 +1,101 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2015, 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.
+
+// Verify semantics of the ?. operator when it appears on the LHS of an
+// assignment.
+
+import "package:expect/expect.dart";
+import "conditional_access_helper.dart" as h;
+
+bad() {
+ Expect.fail('Should not be executed');
+}
+
+class B {}
+
+class C extends B {
+ int v;
+ C(this.v);
+ static late int staticInt;
+}
+
+class D {
+ E v;
+ D(this.v);
+ static late E staticE;
+}
+
+class E {
+ G operator +(int i) => new I();
+}
+
+class F {}
+
+class G extends E implements F {}
+
+class H {}
+
+class I extends G implements H {}
+
+C? nullC() => null;
+
+main() {
+ // Make sure the "none" test fails if assignment to "?." is not implemented.
+ // This makes status files easier to maintain.
+ nullC()?.v = 1;
+
+ // e1?.v = e2 is equivalent to ((x) => x == null ? null : x.v = e2)(e1).
+ Expect.equals(null, nullC()?.v = bad());
+
+
+ // C?.v = e2 is equivalent to C.v = e2.
+
+
+
+ // The static type of e1?.v = e2 is the static type of e2.
+
+
+
+
+
+
+
+ // Exactly the same errors that would be caused by e1.v = e2 are
+ // also generated in the case of e1?.v = e2.
+
+
+
+ // e1?.v op= e2 is equivalent to ((x) => x?.v = x.v op e2)(e1).
+
+
+
+ // C?.v op= e2 is equivalent to C.v op= e2.
+
+
+ // The static type of e1?.v op= e2 is the static type of e1.v op e2.
+
+
+
+
+ // Let T be the static type of e1 and let y be a fresh variable of type T.
+ // Exactly the same errors that would be caused by y.v op e2 are
+ // also generated in the case of e1?.v op= e2.
+
+
+
+
+
+
+
+
+
+ // '?.' cannot be used to assign to toplevel properties in libraries imported
+ // via prefix.
+
+
+
+}
diff --git a/tests/language/null_aware/assignment_runtime_2_test.dart b/tests/language/null_aware/assignment_runtime_2_test.dart
new file mode 100644
index 0000000..825bfc6
--- /dev/null
+++ b/tests/language/null_aware/assignment_runtime_2_test.dart
@@ -0,0 +1,101 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2015, 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.
+
+// Verify semantics of the ?. operator when it appears on the LHS of an
+// assignment.
+
+import "package:expect/expect.dart";
+import "conditional_access_helper.dart" as h;
+
+bad() {
+ Expect.fail('Should not be executed');
+}
+
+class B {}
+
+class C extends B {
+ int v;
+ C(this.v);
+ static late int staticInt;
+}
+
+class D {
+ E v;
+ D(this.v);
+ static late E staticE;
+}
+
+class E {
+ G operator +(int i) => new I();
+}
+
+class F {}
+
+class G extends E implements F {}
+
+class H {}
+
+class I extends G implements H {}
+
+C? nullC() => null;
+
+main() {
+ // Make sure the "none" test fails if assignment to "?." is not implemented.
+ // This makes status files easier to maintain.
+ nullC()?.v = 1;
+
+ // e1?.v = e2 is equivalent to ((x) => x == null ? null : x.v = e2)(e1).
+
+ { C? c = new C(1); Expect.equals(2, c?.v = 2); Expect.equals(2, c!.v); }
+
+ // C?.v = e2 is equivalent to C.v = e2.
+
+
+
+ // The static type of e1?.v = e2 is the static type of e2.
+
+
+
+
+
+
+
+ // Exactly the same errors that would be caused by e1.v = e2 are
+ // also generated in the case of e1?.v = e2.
+
+
+
+ // e1?.v op= e2 is equivalent to ((x) => x?.v = x.v op e2)(e1).
+
+
+
+ // C?.v op= e2 is equivalent to C.v op= e2.
+
+
+ // The static type of e1?.v op= e2 is the static type of e1.v op e2.
+
+
+
+
+ // Let T be the static type of e1 and let y be a fresh variable of type T.
+ // Exactly the same errors that would be caused by y.v op e2 are
+ // also generated in the case of e1?.v op= e2.
+
+
+
+
+
+
+
+
+
+ // '?.' cannot be used to assign to toplevel properties in libraries imported
+ // via prefix.
+
+
+
+}
diff --git a/tests/language/null_aware/assignment_runtime_3_test.dart b/tests/language/null_aware/assignment_runtime_3_test.dart
new file mode 100644
index 0000000..bc50d98
--- /dev/null
+++ b/tests/language/null_aware/assignment_runtime_3_test.dart
@@ -0,0 +1,101 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2015, 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.
+
+// Verify semantics of the ?. operator when it appears on the LHS of an
+// assignment.
+
+import "package:expect/expect.dart";
+import "conditional_access_helper.dart" as h;
+
+bad() {
+ Expect.fail('Should not be executed');
+}
+
+class B {}
+
+class C extends B {
+ int v;
+ C(this.v);
+ static late int staticInt;
+}
+
+class D {
+ E v;
+ D(this.v);
+ static late E staticE;
+}
+
+class E {
+ G operator +(int i) => new I();
+}
+
+class F {}
+
+class G extends E implements F {}
+
+class H {}
+
+class I extends G implements H {}
+
+C? nullC() => null;
+
+main() {
+ // Make sure the "none" test fails if assignment to "?." is not implemented.
+ // This makes status files easier to maintain.
+ nullC()?.v = 1;
+
+ // e1?.v = e2 is equivalent to ((x) => x == null ? null : x.v = e2)(e1).
+
+
+
+ // C?.v = e2 is equivalent to C.v = e2.
+ { C.staticInt = 1; Expect.equals(2, C?.staticInt = 2); Expect.equals(2, C.staticInt); }
+
+
+ // The static type of e1?.v = e2 is the static type of e2.
+
+
+
+
+
+
+
+ // Exactly the same errors that would be caused by e1.v = e2 are
+ // also generated in the case of e1?.v = e2.
+
+
+
+ // e1?.v op= e2 is equivalent to ((x) => x?.v = x.v op e2)(e1).
+
+
+
+ // C?.v op= e2 is equivalent to C.v op= e2.
+
+
+ // The static type of e1?.v op= e2 is the static type of e1.v op e2.
+
+
+
+
+ // Let T be the static type of e1 and let y be a fresh variable of type T.
+ // Exactly the same errors that would be caused by y.v op e2 are
+ // also generated in the case of e1?.v op= e2.
+
+
+
+
+
+
+
+
+
+ // '?.' cannot be used to assign to toplevel properties in libraries imported
+ // via prefix.
+
+
+
+}
diff --git a/tests/language/null_aware/assignment_runtime_4_test.dart b/tests/language/null_aware/assignment_runtime_4_test.dart
new file mode 100644
index 0000000..dcf3ab0
--- /dev/null
+++ b/tests/language/null_aware/assignment_runtime_4_test.dart
@@ -0,0 +1,101 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2015, 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.
+
+// Verify semantics of the ?. operator when it appears on the LHS of an
+// assignment.
+
+import "package:expect/expect.dart";
+import "conditional_access_helper.dart" as h;
+
+bad() {
+ Expect.fail('Should not be executed');
+}
+
+class B {}
+
+class C extends B {
+ int v;
+ C(this.v);
+ static late int staticInt;
+}
+
+class D {
+ E v;
+ D(this.v);
+ static late E staticE;
+}
+
+class E {
+ G operator +(int i) => new I();
+}
+
+class F {}
+
+class G extends E implements F {}
+
+class H {}
+
+class I extends G implements H {}
+
+C? nullC() => null;
+
+main() {
+ // Make sure the "none" test fails if assignment to "?." is not implemented.
+ // This makes status files easier to maintain.
+ nullC()?.v = 1;
+
+ // e1?.v = e2 is equivalent to ((x) => x == null ? null : x.v = e2)(e1).
+
+
+
+ // C?.v = e2 is equivalent to C.v = e2.
+
+ { h.C.staticInt = 1; Expect.equals(2, h.C?.staticInt = 2); Expect.equals(2, h.C.staticInt); }
+
+ // The static type of e1?.v = e2 is the static type of e2.
+
+
+
+
+
+
+
+ // Exactly the same errors that would be caused by e1.v = e2 are
+ // also generated in the case of e1?.v = e2.
+
+
+
+ // e1?.v op= e2 is equivalent to ((x) => x?.v = x.v op e2)(e1).
+
+
+
+ // C?.v op= e2 is equivalent to C.v op= e2.
+
+
+ // The static type of e1?.v op= e2 is the static type of e1.v op e2.
+
+
+
+
+ // Let T be the static type of e1 and let y be a fresh variable of type T.
+ // Exactly the same errors that would be caused by y.v op e2 are
+ // also generated in the case of e1?.v op= e2.
+
+
+
+
+
+
+
+
+
+ // '?.' cannot be used to assign to toplevel properties in libraries imported
+ // via prefix.
+
+
+
+}
diff --git a/tests/language/null_aware/assignment_runtime_5_test.dart b/tests/language/null_aware/assignment_runtime_5_test.dart
new file mode 100644
index 0000000..abcf60a
--- /dev/null
+++ b/tests/language/null_aware/assignment_runtime_5_test.dart
@@ -0,0 +1,101 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2015, 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.
+
+// Verify semantics of the ?. operator when it appears on the LHS of an
+// assignment.
+
+import "package:expect/expect.dart";
+import "conditional_access_helper.dart" as h;
+
+bad() {
+ Expect.fail('Should not be executed');
+}
+
+class B {}
+
+class C extends B {
+ int v;
+ C(this.v);
+ static late int staticInt;
+}
+
+class D {
+ E v;
+ D(this.v);
+ static late E staticE;
+}
+
+class E {
+ G operator +(int i) => new I();
+}
+
+class F {}
+
+class G extends E implements F {}
+
+class H {}
+
+class I extends G implements H {}
+
+C? nullC() => null;
+
+main() {
+ // Make sure the "none" test fails if assignment to "?." is not implemented.
+ // This makes status files easier to maintain.
+ nullC()?.v = 1;
+
+ // e1?.v = e2 is equivalent to ((x) => x == null ? null : x.v = e2)(e1).
+
+
+
+ // C?.v = e2 is equivalent to C.v = e2.
+
+
+
+ // The static type of e1?.v = e2 is the static type of e2.
+ { D? d = new D(new E()); G g = new G(); F? f = (d?.v = g); Expect.identical(f, g); }
+
+
+
+
+
+
+ // Exactly the same errors that would be caused by e1.v = e2 are
+ // also generated in the case of e1?.v = e2.
+
+
+
+ // e1?.v op= e2 is equivalent to ((x) => x?.v = x.v op e2)(e1).
+
+
+
+ // C?.v op= e2 is equivalent to C.v op= e2.
+
+
+ // The static type of e1?.v op= e2 is the static type of e1.v op e2.
+
+
+
+
+ // Let T be the static type of e1 and let y be a fresh variable of type T.
+ // Exactly the same errors that would be caused by y.v op e2 are
+ // also generated in the case of e1?.v op= e2.
+
+
+
+
+
+
+
+
+
+ // '?.' cannot be used to assign to toplevel properties in libraries imported
+ // via prefix.
+
+
+
+}
diff --git a/tests/language/null_aware/assignment_runtime_6_test.dart b/tests/language/null_aware/assignment_runtime_6_test.dart
new file mode 100644
index 0000000..e1a732b
--- /dev/null
+++ b/tests/language/null_aware/assignment_runtime_6_test.dart
@@ -0,0 +1,101 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2015, 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.
+
+// Verify semantics of the ?. operator when it appears on the LHS of an
+// assignment.
+
+import "package:expect/expect.dart";
+import "conditional_access_helper.dart" as h;
+
+bad() {
+ Expect.fail('Should not be executed');
+}
+
+class B {}
+
+class C extends B {
+ int v;
+ C(this.v);
+ static late int staticInt;
+}
+
+class D {
+ E v;
+ D(this.v);
+ static late E staticE;
+}
+
+class E {
+ G operator +(int i) => new I();
+}
+
+class F {}
+
+class G extends E implements F {}
+
+class H {}
+
+class I extends G implements H {}
+
+C? nullC() => null;
+
+main() {
+ // Make sure the "none" test fails if assignment to "?." is not implemented.
+ // This makes status files easier to maintain.
+ nullC()?.v = 1;
+
+ // e1?.v = e2 is equivalent to ((x) => x == null ? null : x.v = e2)(e1).
+
+
+
+ // C?.v = e2 is equivalent to C.v = e2.
+
+
+
+ // The static type of e1?.v = e2 is the static type of e2.
+
+
+ { D.staticE = new E(); G g = new G(); F? f = (D?.staticE = g); Expect.identical(f, g); }
+
+
+
+
+ // Exactly the same errors that would be caused by e1.v = e2 are
+ // also generated in the case of e1?.v = e2.
+
+
+
+ // e1?.v op= e2 is equivalent to ((x) => x?.v = x.v op e2)(e1).
+
+
+
+ // C?.v op= e2 is equivalent to C.v op= e2.
+
+
+ // The static type of e1?.v op= e2 is the static type of e1.v op e2.
+
+
+
+
+ // Let T be the static type of e1 and let y be a fresh variable of type T.
+ // Exactly the same errors that would be caused by y.v op e2 are
+ // also generated in the case of e1?.v op= e2.
+
+
+
+
+
+
+
+
+
+ // '?.' cannot be used to assign to toplevel properties in libraries imported
+ // via prefix.
+
+
+
+}
diff --git a/tests/language/null_aware/assignment_runtime_7_test.dart b/tests/language/null_aware/assignment_runtime_7_test.dart
new file mode 100644
index 0000000..8a5ba98
--- /dev/null
+++ b/tests/language/null_aware/assignment_runtime_7_test.dart
@@ -0,0 +1,101 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2015, 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.
+
+// Verify semantics of the ?. operator when it appears on the LHS of an
+// assignment.
+
+import "package:expect/expect.dart";
+import "conditional_access_helper.dart" as h;
+
+bad() {
+ Expect.fail('Should not be executed');
+}
+
+class B {}
+
+class C extends B {
+ int v;
+ C(this.v);
+ static late int staticInt;
+}
+
+class D {
+ E v;
+ D(this.v);
+ static late E staticE;
+}
+
+class E {
+ G operator +(int i) => new I();
+}
+
+class F {}
+
+class G extends E implements F {}
+
+class H {}
+
+class I extends G implements H {}
+
+C? nullC() => null;
+
+main() {
+ // Make sure the "none" test fails if assignment to "?." is not implemented.
+ // This makes status files easier to maintain.
+ nullC()?.v = 1;
+
+ // e1?.v = e2 is equivalent to ((x) => x == null ? null : x.v = e2)(e1).
+
+
+
+ // C?.v = e2 is equivalent to C.v = e2.
+
+
+
+ // The static type of e1?.v = e2 is the static type of e2.
+
+
+
+ { h.D.staticE = new h.E(); h.G g = new h.G(); h.F? f = (h.D?.staticE = g); Expect.identical(f, g); }
+
+
+
+ // Exactly the same errors that would be caused by e1.v = e2 are
+ // also generated in the case of e1?.v = e2.
+
+
+
+ // e1?.v op= e2 is equivalent to ((x) => x?.v = x.v op e2)(e1).
+
+
+
+ // C?.v op= e2 is equivalent to C.v op= e2.
+
+
+ // The static type of e1?.v op= e2 is the static type of e1.v op e2.
+
+
+
+
+ // Let T be the static type of e1 and let y be a fresh variable of type T.
+ // Exactly the same errors that would be caused by y.v op e2 are
+ // also generated in the case of e1?.v op= e2.
+
+
+
+
+
+
+
+
+
+ // '?.' cannot be used to assign to toplevel properties in libraries imported
+ // via prefix.
+
+
+
+}
diff --git a/tests/language/null_aware/assignment_runtime_8_test.dart b/tests/language/null_aware/assignment_runtime_8_test.dart
new file mode 100644
index 0000000..34f7359
--- /dev/null
+++ b/tests/language/null_aware/assignment_runtime_8_test.dart
@@ -0,0 +1,102 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2015, 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.
+
+// Verify semantics of the ?. operator when it appears on the LHS of an
+// assignment.
+
+import "package:expect/expect.dart";
+import "conditional_access_helper.dart" as h;
+
+int bad() {
+ Expect.fail('Should not be executed');
+ return -1;
+}
+
+class B {}
+
+class C extends B {
+ int v;
+ C(this.v);
+ static late int staticInt;
+}
+
+class D {
+ E v;
+ D(this.v);
+ static late E staticE;
+}
+
+class E {
+ G operator +(int i) => new I();
+}
+
+class F {}
+
+class G extends E implements F {}
+
+class H {}
+
+class I extends G implements H {}
+
+C? nullC() => null;
+
+main() {
+ // Make sure the "none" test fails if assignment to "?." is not implemented.
+ // This makes status files easier to maintain.
+ nullC()?.v = 1;
+
+ // e1?.v = e2 is equivalent to ((x) => x == null ? null : x.v = e2)(e1).
+
+
+
+ // C?.v = e2 is equivalent to C.v = e2.
+
+
+
+ // The static type of e1?.v = e2 is the static type of e2.
+
+
+
+
+
+
+
+ // Exactly the same errors that would be caused by e1.v = e2 are
+ // also generated in the case of e1?.v = e2.
+
+
+
+ // e1?.v op= e2 is equivalent to ((x) => x?.v = x.v op e2)(e1).
+ Expect.equals(null, nullC()?.v += bad());
+
+
+ // C?.v op= e2 is equivalent to C.v op= e2.
+
+
+ // The static type of e1?.v op= e2 is the static type of e1.v op e2.
+
+
+
+
+ // Let T be the static type of e1 and let y be a fresh variable of type T.
+ // Exactly the same errors that would be caused by y.v op e2 are
+ // also generated in the case of e1?.v op= e2.
+
+
+
+
+
+
+
+
+
+ // '?.' cannot be used to assign to toplevel properties in libraries imported
+ // via prefix.
+
+
+
+}
diff --git a/tests/language/null_aware/assignment_runtime_9_test.dart b/tests/language/null_aware/assignment_runtime_9_test.dart
new file mode 100644
index 0000000..3c2d6c0
--- /dev/null
+++ b/tests/language/null_aware/assignment_runtime_9_test.dart
@@ -0,0 +1,101 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2015, 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.
+
+// Verify semantics of the ?. operator when it appears on the LHS of an
+// assignment.
+
+import "package:expect/expect.dart";
+import "conditional_access_helper.dart" as h;
+
+bad() {
+ Expect.fail('Should not be executed');
+}
+
+class B {}
+
+class C extends B {
+ int v;
+ C(this.v);
+ static late int staticInt;
+}
+
+class D {
+ E v;
+ D(this.v);
+ static late E staticE;
+}
+
+class E {
+ G operator +(int i) => new I();
+}
+
+class F {}
+
+class G extends E implements F {}
+
+class H {}
+
+class I extends G implements H {}
+
+C? nullC() => null;
+
+main() {
+ // Make sure the "none" test fails if assignment to "?." is not implemented.
+ // This makes status files easier to maintain.
+ nullC()?.v = 1;
+
+ // e1?.v = e2 is equivalent to ((x) => x == null ? null : x.v = e2)(e1).
+
+
+
+ // C?.v = e2 is equivalent to C.v = e2.
+
+
+
+ // The static type of e1?.v = e2 is the static type of e2.
+
+
+
+
+
+
+
+ // Exactly the same errors that would be caused by e1.v = e2 are
+ // also generated in the case of e1?.v = e2.
+
+
+
+ // e1?.v op= e2 is equivalent to ((x) => x?.v = x.v op e2)(e1).
+
+ { C? c = new C(1); Expect.equals(3, c?.v += 2); Expect.equals(3, c!.v); }
+
+ // C?.v op= e2 is equivalent to C.v op= e2.
+
+
+ // The static type of e1?.v op= e2 is the static type of e1.v op e2.
+
+
+
+
+ // Let T be the static type of e1 and let y be a fresh variable of type T.
+ // Exactly the same errors that would be caused by y.v op e2 are
+ // also generated in the case of e1?.v op= e2.
+
+
+
+
+
+
+
+
+
+ // '?.' cannot be used to assign to toplevel properties in libraries imported
+ // via prefix.
+
+
+
+}
diff --git a/tests/language/null_aware/assignment_runtime_test.dart b/tests/language/null_aware/assignment_runtime_test.dart
new file mode 100644
index 0000000..af673ef
--- /dev/null
+++ b/tests/language/null_aware/assignment_runtime_test.dart
@@ -0,0 +1,101 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2015, 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.
+
+// Verify semantics of the ?. operator when it appears on the LHS of an
+// assignment.
+
+import "package:expect/expect.dart";
+import "conditional_access_helper.dart" as h;
+
+bad() {
+ Expect.fail('Should not be executed');
+}
+
+class B {}
+
+class C extends B {
+ int v;
+ C(this.v);
+ static late int staticInt;
+}
+
+class D {
+ E v;
+ D(this.v);
+ static late E staticE;
+}
+
+class E {
+ G operator +(int i) => new I();
+}
+
+class F {}
+
+class G extends E implements F {}
+
+class H {}
+
+class I extends G implements H {}
+
+C? nullC() => null;
+
+main() {
+ // Make sure the "none" test fails if assignment to "?." is not implemented.
+ // This makes status files easier to maintain.
+ nullC()?.v = 1;
+
+ // e1?.v = e2 is equivalent to ((x) => x == null ? null : x.v = e2)(e1).
+
+
+
+ // C?.v = e2 is equivalent to C.v = e2.
+
+
+
+ // The static type of e1?.v = e2 is the static type of e2.
+
+
+
+
+
+
+
+ // Exactly the same errors that would be caused by e1.v = e2 are
+ // also generated in the case of e1?.v = e2.
+
+
+
+ // e1?.v op= e2 is equivalent to ((x) => x?.v = x.v op e2)(e1).
+
+
+
+ // C?.v op= e2 is equivalent to C.v op= e2.
+
+
+ // The static type of e1?.v op= e2 is the static type of e1.v op e2.
+
+
+
+
+ // Let T be the static type of e1 and let y be a fresh variable of type T.
+ // Exactly the same errors that would be caused by y.v op e2 are
+ // also generated in the case of e1?.v op= e2.
+
+
+
+
+
+
+
+
+
+ // '?.' cannot be used to assign to toplevel properties in libraries imported
+ // via prefix.
+
+
+
+}
diff --git a/tests/language/null_aware/assignment_test.dart b/tests/language/null_aware/assignment_test.dart
new file mode 100644
index 0000000..4b5f5de
--- /dev/null
+++ b/tests/language/null_aware/assignment_test.dart
@@ -0,0 +1,176 @@
+// Copyright (c) 2015, 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.
+
+// Verify semantics of the ?. operator when it appears on the LHS of an
+// assignment.
+
+import "package:expect/expect.dart";
+import "conditional_access_helper.dart" as h;
+
+int bad() {
+ Expect.fail('Should not be executed');
+ return -1;
+}
+
+class B {}
+
+class C extends B {
+ int v;
+ C(this.v);
+ static late int staticInt;
+}
+
+class D {
+ E v;
+ D(this.v);
+ static late E staticE;
+}
+
+class E {
+ G operator +(int i) => new I();
+}
+
+class F {}
+
+class G extends E implements F {}
+
+class H {}
+
+class I extends G implements H {}
+
+C? nullC() => null;
+
+main() {
+ // Make sure the "none" test fails if assignment to "?." is not implemented.
+ // This makes status files easier to maintain.
+ nullC()?.v = 1;
+
+ // e1?.v = e2 is equivalent to ((x) => x == null ? null : x.v = e2)(e1).
+ Expect.equals(null, nullC()?.v = bad());
+ { C c = new C(1); Expect.equals(2, c?.v = 2); Expect.equals(2, c.v); }
+ // ^^
+ // [analyzer] STATIC_WARNING.INVALID_NULL_AWARE_OPERATOR
+
+ // C?.v = e2 is equivalent to C.v = e2.
+ { C.staticInt = 1; Expect.equals(2, C?.staticInt = 2); Expect.equals(2, C.staticInt); }
+ { h.C.staticInt = 1; Expect.equals(2, h.C?.staticInt = 2); Expect.equals(2, h.C.staticInt); }
+
+ // The static type of e1?.v = e2 is the static type of e2.
+ { D? d = new D(new E()); G g = new G(); F? f = (d?.v = g); Expect.identical(f, g); }
+ { D? d = new D(new E()); E e = new G(); F? f = (d?.v = e); }
+ // ^^^^^^^^^^
+ // [analyzer] STATIC_TYPE_WARNING.INVALID_ASSIGNMENT
+ // ^
+ // [cfe] A value of type 'E?' can't be assigned to a variable of type 'F?'.
+ { D.staticE = new E(); G g = new G(); F? f = (D?.staticE = g); Expect.identical(f, g); }
+ { h.D.staticE = new h.E(); h.G g = new h.G(); h.F? f = (h.D?.staticE = g); Expect.identical(f, g); }
+ { D.staticE = new E(); E e = new G(); F? f = (D?.staticE = e); }
+ // ^^^^^^^^^^^^^^^^
+ // [analyzer] STATIC_TYPE_WARNING.INVALID_ASSIGNMENT
+ // ^
+ // [cfe] A value of type 'E' can't be assigned to a variable of type 'F?'.
+ { h.D.staticE = new h.E(); h.E e = new h.G(); h.F f = (h.D?.staticE = e); }
+ // ^^^^^^^^^^^^^^^^^^
+ // [analyzer] STATIC_TYPE_WARNING.INVALID_ASSIGNMENT
+ // ^
+ // [cfe] A value of type 'E' can't be assigned to a variable of type 'F'.
+
+ // Exactly the same errors that would be caused by e1.v = e2 are
+ // also generated in the case of e1?.v = e2.
+ Expect.equals(null, nullC()?.bad = bad());
+ // ^^^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_SETTER
+ // [cfe] The setter 'bad' isn't defined for the class 'C'.
+ { B b = new C(1); Expect.equals(2, b?.v = 2); }
+ // ^^
+ // [analyzer] STATIC_WARNING.INVALID_NULL_AWARE_OPERATOR
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_SETTER
+ // [cfe] The setter 'v' isn't defined for the class 'B'.
+
+ // e1?.v op= e2 is equivalent to ((x) => x?.v = x.v op e2)(e1).
+ Expect.equals(null, nullC()?.v += bad());
+ { C c = new C(1); Expect.equals(3, c?.v += 2); Expect.equals(3, c.v); }
+ // ^^
+ // [analyzer] STATIC_WARNING.INVALID_NULL_AWARE_OPERATOR
+
+ // C?.v op= e2 is equivalent to C.v op= e2.
+ { C.staticInt = 1; Expect.equals(3, C?.staticInt += 2); Expect.equals(3, C?.staticInt); }
+
+ // The static type of e1?.v op= e2 is the static type of e1.v op e2.
+ { D d = new D(new E()); F? f = (d?.v += 1); Expect.identical(d.v, f); }
+ // ^^
+ // [analyzer] STATIC_WARNING.INVALID_NULL_AWARE_OPERATOR
+ { D.staticE = new E(); F? f = (D?.staticE += 1); Expect.identical(D.staticE, f); }
+ { h.D.staticE = new h.E(); h.F? f = (h.D?.staticE += 1); Expect.identical(h.D.staticE, f); }
+
+ // Let T be the static type of e1 and let y be a fresh variable of type T.
+ // Exactly the same errors that would be caused by y.v op e2 are
+ // also generated in the case of e1?.v op= e2.
+ nullC()?.bad = bad();
+ // ^^^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_SETTER
+ // [cfe] The setter 'bad' isn't defined for the class 'C'.
+ { B b = new C(1); b?.v += 2; }
+ // ^^
+ // [analyzer] STATIC_WARNING.INVALID_NULL_AWARE_OPERATOR
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'v' isn't defined for the class 'B'.
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_SETTER
+ // [cfe] The setter 'v' isn't defined for the class 'B'.
+ { D d = new D(new E()); F? f = (d?.v += nullC()); }
+ // ^^
+ // [analyzer] STATIC_WARNING.INVALID_NULL_AWARE_OPERATOR
+ // ^^^^^^^
+ // [analyzer] STATIC_WARNING.ARGUMENT_TYPE_NOT_ASSIGNABLE
+ // [cfe] A value of type 'C?' can't be assigned to a variable of type 'int'.
+ { D d = new D(new E()); H? h = (d?.v += 1); }
+ // ^^^^^^^^^^^
+ // [analyzer] STATIC_TYPE_WARNING.INVALID_ASSIGNMENT
+ // ^
+ // [cfe] A value of type 'G?' can't be assigned to a variable of type 'H?'.
+ // ^^
+ // [analyzer] STATIC_WARNING.INVALID_NULL_AWARE_OPERATOR
+ { D.staticE = new E(); F? f = (D?.staticE += nullC()); }
+ // ^^^^^^^
+ // [analyzer] STATIC_WARNING.ARGUMENT_TYPE_NOT_ASSIGNABLE
+ // [cfe] A value of type 'C?' can't be assigned to a variable of type 'int'.
+ { h.D.staticE = new h.E(); h.F? f = (h.D?.staticE += h.nullC()); }
+ // ^^^^^^^^^
+ // [analyzer] STATIC_WARNING.ARGUMENT_TYPE_NOT_ASSIGNABLE
+ // [cfe] A value of type 'C?' can't be assigned to a variable of type 'int'.
+ { D.staticE = new E(); H? h = (D?.staticE += 1); }
+ // ^^^^^^^^^^^^^^^^^
+ // [analyzer] STATIC_TYPE_WARNING.INVALID_ASSIGNMENT
+ // ^
+ // [cfe] A value of type 'G' can't be assigned to a variable of type 'H?'.
+ { h.D.staticE = new h.E(); h.H? hh = (h.D?.staticE += 1); }
+ // ^^^^^^^^^^^^^^^^^^^
+ // [analyzer] STATIC_TYPE_WARNING.INVALID_ASSIGNMENT
+ // ^
+ // [cfe] A value of type 'G' can't be assigned to a variable of type 'H?'.
+
+ // '?.' cannot be used to assign to toplevel properties in libraries imported
+ // via prefix.
+ h?.topLevelVar = null;
+//^
+// [analyzer] COMPILE_TIME_ERROR.PREFIX_IDENTIFIER_NOT_FOLLOWED_BY_DOT
+// [cfe] A prefix can't be used with null-aware operators.
+// ^
+// [cfe] Can't assign to this.
+ h?.topLevelVar += null;
+//^
+// [analyzer] COMPILE_TIME_ERROR.PREFIX_IDENTIFIER_NOT_FOLLOWED_BY_DOT
+// [cfe] A prefix can't be used with null-aware operators.
+// ^
+// [cfe] Can't assign to this.
+ h?.topLevelVar ??= null;
+//^
+// [analyzer] COMPILE_TIME_ERROR.PREFIX_IDENTIFIER_NOT_FOLLOWED_BY_DOT
+// [cfe] A prefix can't be used with null-aware operators.
+// ^
+// [cfe] Can't assign to this.
+}
diff --git a/tests/language/null_aware/conditional_access_helper.dart b/tests/language/null_aware/conditional_access_helper.dart
new file mode 100644
index 0000000..3227005
--- /dev/null
+++ b/tests/language/null_aware/conditional_access_helper.dart
@@ -0,0 +1,40 @@
+// Copyright (c) 2015, 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.
+
+// Library used by conditional_property_assignment_test.dart,
+// conditional_property_access_test.dart, and
+// conditional_method_invocation_test.dart, all of which import it using the
+// prefix "h.".
+
+library lib;
+
+var topLevelVar;
+
+void topLevelFunction() {}
+
+class C {
+ static late int staticInt;
+ static int? staticNullable;
+ static staticF(callback()) => callback();
+ static int? staticG(int? callback()) => callback();
+}
+
+C? nullC() => null;
+
+class D {
+ static late E staticE;
+}
+
+class E {
+ G operator +(int i) => new I();
+ G operator -(int i) => new I();
+}
+
+class F {}
+
+class G extends E implements F {}
+
+class H {}
+
+class I extends G implements H {}
diff --git a/tests/language/null_aware/dynamic_test.dart b/tests/language/null_aware/dynamic_test.dart
new file mode 100644
index 0000000..36dfc10
--- /dev/null
+++ b/tests/language/null_aware/dynamic_test.dart
@@ -0,0 +1,17 @@
+// 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 'package:expect/expect.dart';
+
+void main() {
+ dynamic l;
+ var val = l?.first?.abs();
+ Expect.equals(null, val);
+ l = [null, -2, -3];
+ val = l?.first?.abs();
+ Expect.equals(null, val);
+ l = [-1, -2, -3];
+ val = l?.first?.abs();
+ Expect.equals(1, val);
+}
diff --git a/tests/language/null_aware/increment_decrement_runtime_10_test.dart b/tests/language/null_aware/increment_decrement_runtime_10_test.dart
new file mode 100644
index 0000000..1ee42a7
--- /dev/null
+++ b/tests/language/null_aware/increment_decrement_runtime_10_test.dart
@@ -0,0 +1,109 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2015, 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.
+
+// Verify semantics of the ?. operator when it appears in a postincrement or
+// preincrement expression (or a postdecrement or predecrement expression).
+
+import "package:expect/expect.dart";
+import "conditional_access_helper.dart" as h;
+
+class C {
+ int v;
+ C(this.v);
+ static late int staticInt;
+}
+
+class D {
+ E v;
+ D(this.v);
+ static late E staticE;
+}
+
+class E {
+ G operator +(int i) => new I();
+ G operator -(int i) => new I();
+}
+
+class F {}
+
+class G extends E implements F {}
+
+class H {}
+
+class I extends G implements H {}
+
+C? nullC() => null;
+
+main() {
+ // Make sure the "none" test fails if assignment to "?." is not implemented.
+ // This makes status files easier to maintain.
+ nullC()?.v = 1;
+
+ // e1?.v++ is equivalent to ((x) => x == null ? null : x.v++)(e1).
+
+
+
+ // C?.v++ is equivalent to C.v++.
+
+
+
+ // The static type of e1?.v++ is the same as the static type of e1.v.
+
+
+
+
+
+
+
+ // e1?.v-- is equivalent to ((x) => x == null ? null : x.v--)(e1).
+
+
+
+ // C?.v-- is equivalent to C.v--.
+ { C.staticInt = 1; Expect.equals(1, C?.staticInt--); Expect.equals(0, C.staticInt); }
+
+
+ // The static type of e1?.v-- is the same as the static type of e1.v.
+
+
+
+
+
+
+
+ // ++e1?.v is equivalent to e1?.v += 1.
+
+
+
+ // ++C?.v is equivalent to C?.v += 1.
+
+
+
+ // The static type of ++e1?.v is the same as the static type of e1.v + 1.
+
+
+
+
+
+
+
+ // --e1?.v is equivalent to e1?.v -= 1.
+
+
+
+ // --C?.v is equivalent to C?.v -= 1.
+
+
+
+ // The static type of --e1?.v is the same as the static type of e1.v - 1.
+
+
+
+
+
+
+}
diff --git a/tests/language/null_aware/increment_decrement_runtime_11_test.dart b/tests/language/null_aware/increment_decrement_runtime_11_test.dart
new file mode 100644
index 0000000..2a96102
--- /dev/null
+++ b/tests/language/null_aware/increment_decrement_runtime_11_test.dart
@@ -0,0 +1,109 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2015, 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.
+
+// Verify semantics of the ?. operator when it appears in a postincrement or
+// preincrement expression (or a postdecrement or predecrement expression).
+
+import "package:expect/expect.dart";
+import "conditional_access_helper.dart" as h;
+
+class C {
+ int v;
+ C(this.v);
+ static late int staticInt;
+}
+
+class D {
+ E v;
+ D(this.v);
+ static late E staticE;
+}
+
+class E {
+ G operator +(int i) => new I();
+ G operator -(int i) => new I();
+}
+
+class F {}
+
+class G extends E implements F {}
+
+class H {}
+
+class I extends G implements H {}
+
+C? nullC() => null;
+
+main() {
+ // Make sure the "none" test fails if assignment to "?." is not implemented.
+ // This makes status files easier to maintain.
+ nullC()?.v = 1;
+
+ // e1?.v++ is equivalent to ((x) => x == null ? null : x.v++)(e1).
+
+
+
+ // C?.v++ is equivalent to C.v++.
+
+
+
+ // The static type of e1?.v++ is the same as the static type of e1.v.
+
+
+
+
+
+
+
+ // e1?.v-- is equivalent to ((x) => x == null ? null : x.v--)(e1).
+
+
+
+ // C?.v-- is equivalent to C.v--.
+
+ { h.C.staticInt = 1; Expect.equals(1, h.C?.staticInt--); Expect.equals(0, h.C.staticInt); }
+
+ // The static type of e1?.v-- is the same as the static type of e1.v.
+
+
+
+
+
+
+
+ // ++e1?.v is equivalent to e1?.v += 1.
+
+
+
+ // ++C?.v is equivalent to C?.v += 1.
+
+
+
+ // The static type of ++e1?.v is the same as the static type of e1.v + 1.
+
+
+
+
+
+
+
+ // --e1?.v is equivalent to e1?.v -= 1.
+
+
+
+ // --C?.v is equivalent to C?.v -= 1.
+
+
+
+ // The static type of --e1?.v is the same as the static type of e1.v - 1.
+
+
+
+
+
+
+}
diff --git a/tests/language/null_aware/increment_decrement_runtime_12_test.dart b/tests/language/null_aware/increment_decrement_runtime_12_test.dart
new file mode 100644
index 0000000..03986fb
--- /dev/null
+++ b/tests/language/null_aware/increment_decrement_runtime_12_test.dart
@@ -0,0 +1,109 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2015, 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.
+
+// Verify semantics of the ?. operator when it appears in a postincrement or
+// preincrement expression (or a postdecrement or predecrement expression).
+
+import "package:expect/expect.dart";
+import "conditional_access_helper.dart" as h;
+
+class C {
+ int v;
+ C(this.v);
+ static late int staticInt;
+}
+
+class D {
+ E v;
+ D(this.v);
+ static late E staticE;
+}
+
+class E {
+ G operator +(int i) => new I();
+ G operator -(int i) => new I();
+}
+
+class F {}
+
+class G extends E implements F {}
+
+class H {}
+
+class I extends G implements H {}
+
+C? nullC() => null;
+
+main() {
+ // Make sure the "none" test fails if assignment to "?." is not implemented.
+ // This makes status files easier to maintain.
+ nullC()?.v = 1;
+
+ // e1?.v++ is equivalent to ((x) => x == null ? null : x.v++)(e1).
+
+
+
+ // C?.v++ is equivalent to C.v++.
+
+
+
+ // The static type of e1?.v++ is the same as the static type of e1.v.
+
+
+
+
+
+
+
+ // e1?.v-- is equivalent to ((x) => x == null ? null : x.v--)(e1).
+
+
+
+ // C?.v-- is equivalent to C.v--.
+
+
+
+ // The static type of e1?.v-- is the same as the static type of e1.v.
+ { E e1 = new E(); D? d = new D(e1); E? e2 = d?.v--; Expect.identical(e1, e2); }
+
+
+
+
+
+
+ // ++e1?.v is equivalent to e1?.v += 1.
+
+
+
+ // ++C?.v is equivalent to C?.v += 1.
+
+
+
+ // The static type of ++e1?.v is the same as the static type of e1.v + 1.
+
+
+
+
+
+
+
+ // --e1?.v is equivalent to e1?.v -= 1.
+
+
+
+ // --C?.v is equivalent to C?.v -= 1.
+
+
+
+ // The static type of --e1?.v is the same as the static type of e1.v - 1.
+
+
+
+
+
+
+}
diff --git a/tests/language/null_aware/increment_decrement_runtime_13_test.dart b/tests/language/null_aware/increment_decrement_runtime_13_test.dart
new file mode 100644
index 0000000..54a60b5
--- /dev/null
+++ b/tests/language/null_aware/increment_decrement_runtime_13_test.dart
@@ -0,0 +1,109 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2015, 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.
+
+// Verify semantics of the ?. operator when it appears in a postincrement or
+// preincrement expression (or a postdecrement or predecrement expression).
+
+import "package:expect/expect.dart";
+import "conditional_access_helper.dart" as h;
+
+class C {
+ int v;
+ C(this.v);
+ static late int staticInt;
+}
+
+class D {
+ E v;
+ D(this.v);
+ static late E staticE;
+}
+
+class E {
+ G operator +(int i) => new I();
+ G operator -(int i) => new I();
+}
+
+class F {}
+
+class G extends E implements F {}
+
+class H {}
+
+class I extends G implements H {}
+
+C? nullC() => null;
+
+main() {
+ // Make sure the "none" test fails if assignment to "?." is not implemented.
+ // This makes status files easier to maintain.
+ nullC()?.v = 1;
+
+ // e1?.v++ is equivalent to ((x) => x == null ? null : x.v++)(e1).
+
+
+
+ // C?.v++ is equivalent to C.v++.
+
+
+
+ // The static type of e1?.v++ is the same as the static type of e1.v.
+
+
+
+
+
+
+
+ // e1?.v-- is equivalent to ((x) => x == null ? null : x.v--)(e1).
+
+
+
+ // C?.v-- is equivalent to C.v--.
+
+
+
+ // The static type of e1?.v-- is the same as the static type of e1.v.
+
+
+ { E e1 = new E(); D.staticE = e1; E? e2 = D?.staticE--; Expect.identical(e1, e2); }
+
+
+
+
+ // ++e1?.v is equivalent to e1?.v += 1.
+
+
+
+ // ++C?.v is equivalent to C?.v += 1.
+
+
+
+ // The static type of ++e1?.v is the same as the static type of e1.v + 1.
+
+
+
+
+
+
+
+ // --e1?.v is equivalent to e1?.v -= 1.
+
+
+
+ // --C?.v is equivalent to C?.v -= 1.
+
+
+
+ // The static type of --e1?.v is the same as the static type of e1.v - 1.
+
+
+
+
+
+
+}
diff --git a/tests/language/null_aware/increment_decrement_runtime_14_test.dart b/tests/language/null_aware/increment_decrement_runtime_14_test.dart
new file mode 100644
index 0000000..aedf157
--- /dev/null
+++ b/tests/language/null_aware/increment_decrement_runtime_14_test.dart
@@ -0,0 +1,109 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2015, 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.
+
+// Verify semantics of the ?. operator when it appears in a postincrement or
+// preincrement expression (or a postdecrement or predecrement expression).
+
+import "package:expect/expect.dart";
+import "conditional_access_helper.dart" as h;
+
+class C {
+ int v;
+ C(this.v);
+ static late int staticInt;
+}
+
+class D {
+ E v;
+ D(this.v);
+ static late E staticE;
+}
+
+class E {
+ G operator +(int i) => new I();
+ G operator -(int i) => new I();
+}
+
+class F {}
+
+class G extends E implements F {}
+
+class H {}
+
+class I extends G implements H {}
+
+C? nullC() => null;
+
+main() {
+ // Make sure the "none" test fails if assignment to "?." is not implemented.
+ // This makes status files easier to maintain.
+ nullC()?.v = 1;
+
+ // e1?.v++ is equivalent to ((x) => x == null ? null : x.v++)(e1).
+
+
+
+ // C?.v++ is equivalent to C.v++.
+
+
+
+ // The static type of e1?.v++ is the same as the static type of e1.v.
+
+
+
+
+
+
+
+ // e1?.v-- is equivalent to ((x) => x == null ? null : x.v--)(e1).
+
+
+
+ // C?.v-- is equivalent to C.v--.
+
+
+
+ // The static type of e1?.v-- is the same as the static type of e1.v.
+
+
+
+ { h.E e1 = new h.E(); h.D.staticE = e1; h.E? e2 = h.D?.staticE--; Expect.identical(e1, e2); }
+
+
+
+ // ++e1?.v is equivalent to e1?.v += 1.
+
+
+
+ // ++C?.v is equivalent to C?.v += 1.
+
+
+
+ // The static type of ++e1?.v is the same as the static type of e1.v + 1.
+
+
+
+
+
+
+
+ // --e1?.v is equivalent to e1?.v -= 1.
+
+
+
+ // --C?.v is equivalent to C?.v -= 1.
+
+
+
+ // The static type of --e1?.v is the same as the static type of e1.v - 1.
+
+
+
+
+
+
+}
diff --git a/tests/language/null_aware/increment_decrement_runtime_15_test.dart b/tests/language/null_aware/increment_decrement_runtime_15_test.dart
new file mode 100644
index 0000000..674833c
--- /dev/null
+++ b/tests/language/null_aware/increment_decrement_runtime_15_test.dart
@@ -0,0 +1,109 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2015, 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.
+
+// Verify semantics of the ?. operator when it appears in a postincrement or
+// preincrement expression (or a postdecrement or predecrement expression).
+
+import "package:expect/expect.dart";
+import "conditional_access_helper.dart" as h;
+
+class C {
+ int v;
+ C(this.v);
+ static late int staticInt;
+}
+
+class D {
+ E v;
+ D(this.v);
+ static late E staticE;
+}
+
+class E {
+ G operator +(int i) => new I();
+ G operator -(int i) => new I();
+}
+
+class F {}
+
+class G extends E implements F {}
+
+class H {}
+
+class I extends G implements H {}
+
+C? nullC() => null;
+
+main() {
+ // Make sure the "none" test fails if assignment to "?." is not implemented.
+ // This makes status files easier to maintain.
+ nullC()?.v = 1;
+
+ // e1?.v++ is equivalent to ((x) => x == null ? null : x.v++)(e1).
+
+
+
+ // C?.v++ is equivalent to C.v++.
+
+
+
+ // The static type of e1?.v++ is the same as the static type of e1.v.
+
+
+
+
+
+
+
+ // e1?.v-- is equivalent to ((x) => x == null ? null : x.v--)(e1).
+
+
+
+ // C?.v-- is equivalent to C.v--.
+
+
+
+ // The static type of e1?.v-- is the same as the static type of e1.v.
+
+
+
+
+
+
+
+ // ++e1?.v is equivalent to e1?.v += 1.
+ Expect.equals(null, ++nullC()?.v);
+
+
+ // ++C?.v is equivalent to C?.v += 1.
+
+
+
+ // The static type of ++e1?.v is the same as the static type of e1.v + 1.
+
+
+
+
+
+
+
+ // --e1?.v is equivalent to e1?.v -= 1.
+
+
+
+ // --C?.v is equivalent to C?.v -= 1.
+
+
+
+ // The static type of --e1?.v is the same as the static type of e1.v - 1.
+
+
+
+
+
+
+}
diff --git a/tests/language/null_aware/increment_decrement_runtime_16_test.dart b/tests/language/null_aware/increment_decrement_runtime_16_test.dart
new file mode 100644
index 0000000..1016d22
--- /dev/null
+++ b/tests/language/null_aware/increment_decrement_runtime_16_test.dart
@@ -0,0 +1,109 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2015, 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.
+
+// Verify semantics of the ?. operator when it appears in a postincrement or
+// preincrement expression (or a postdecrement or predecrement expression).
+
+import "package:expect/expect.dart";
+import "conditional_access_helper.dart" as h;
+
+class C {
+ int v;
+ C(this.v);
+ static late int staticInt;
+}
+
+class D {
+ E v;
+ D(this.v);
+ static late E staticE;
+}
+
+class E {
+ G operator +(int i) => new I();
+ G operator -(int i) => new I();
+}
+
+class F {}
+
+class G extends E implements F {}
+
+class H {}
+
+class I extends G implements H {}
+
+C? nullC() => null;
+
+main() {
+ // Make sure the "none" test fails if assignment to "?." is not implemented.
+ // This makes status files easier to maintain.
+ nullC()?.v = 1;
+
+ // e1?.v++ is equivalent to ((x) => x == null ? null : x.v++)(e1).
+
+
+
+ // C?.v++ is equivalent to C.v++.
+
+
+
+ // The static type of e1?.v++ is the same as the static type of e1.v.
+
+
+
+
+
+
+
+ // e1?.v-- is equivalent to ((x) => x == null ? null : x.v--)(e1).
+
+
+
+ // C?.v-- is equivalent to C.v--.
+
+
+
+ // The static type of e1?.v-- is the same as the static type of e1.v.
+
+
+
+
+
+
+
+ // ++e1?.v is equivalent to e1?.v += 1.
+
+ { C? c = new C(1); Expect.equals(2, ++c?.v); Expect.equals(2, c!.v); }
+
+ // ++C?.v is equivalent to C?.v += 1.
+
+
+
+ // The static type of ++e1?.v is the same as the static type of e1.v + 1.
+
+
+
+
+
+
+
+ // --e1?.v is equivalent to e1?.v -= 1.
+
+
+
+ // --C?.v is equivalent to C?.v -= 1.
+
+
+
+ // The static type of --e1?.v is the same as the static type of e1.v - 1.
+
+
+
+
+
+
+}
diff --git a/tests/language/null_aware/increment_decrement_runtime_17_test.dart b/tests/language/null_aware/increment_decrement_runtime_17_test.dart
new file mode 100644
index 0000000..4997a21
--- /dev/null
+++ b/tests/language/null_aware/increment_decrement_runtime_17_test.dart
@@ -0,0 +1,109 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2015, 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.
+
+// Verify semantics of the ?. operator when it appears in a postincrement or
+// preincrement expression (or a postdecrement or predecrement expression).
+
+import "package:expect/expect.dart";
+import "conditional_access_helper.dart" as h;
+
+class C {
+ int v;
+ C(this.v);
+ static late int staticInt;
+}
+
+class D {
+ E v;
+ D(this.v);
+ static late E staticE;
+}
+
+class E {
+ G operator +(int i) => new I();
+ G operator -(int i) => new I();
+}
+
+class F {}
+
+class G extends E implements F {}
+
+class H {}
+
+class I extends G implements H {}
+
+C? nullC() => null;
+
+main() {
+ // Make sure the "none" test fails if assignment to "?." is not implemented.
+ // This makes status files easier to maintain.
+ nullC()?.v = 1;
+
+ // e1?.v++ is equivalent to ((x) => x == null ? null : x.v++)(e1).
+
+
+
+ // C?.v++ is equivalent to C.v++.
+
+
+
+ // The static type of e1?.v++ is the same as the static type of e1.v.
+
+
+
+
+
+
+
+ // e1?.v-- is equivalent to ((x) => x == null ? null : x.v--)(e1).
+
+
+
+ // C?.v-- is equivalent to C.v--.
+
+
+
+ // The static type of e1?.v-- is the same as the static type of e1.v.
+
+
+
+
+
+
+
+ // ++e1?.v is equivalent to e1?.v += 1.
+
+
+
+ // ++C?.v is equivalent to C?.v += 1.
+ { C.staticInt = 1; Expect.equals(2, ++C?.staticInt); Expect.equals(2, C.staticInt); }
+
+
+ // The static type of ++e1?.v is the same as the static type of e1.v + 1.
+
+
+
+
+
+
+
+ // --e1?.v is equivalent to e1?.v -= 1.
+
+
+
+ // --C?.v is equivalent to C?.v -= 1.
+
+
+
+ // The static type of --e1?.v is the same as the static type of e1.v - 1.
+
+
+
+
+
+
+}
diff --git a/tests/language/null_aware/increment_decrement_runtime_18_test.dart b/tests/language/null_aware/increment_decrement_runtime_18_test.dart
new file mode 100644
index 0000000..c42e93a
--- /dev/null
+++ b/tests/language/null_aware/increment_decrement_runtime_18_test.dart
@@ -0,0 +1,109 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2015, 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.
+
+// Verify semantics of the ?. operator when it appears in a postincrement or
+// preincrement expression (or a postdecrement or predecrement expression).
+
+import "package:expect/expect.dart";
+import "conditional_access_helper.dart" as h;
+
+class C {
+ int v;
+ C(this.v);
+ static late int staticInt;
+}
+
+class D {
+ E v;
+ D(this.v);
+ static late E staticE;
+}
+
+class E {
+ G operator +(int i) => new I();
+ G operator -(int i) => new I();
+}
+
+class F {}
+
+class G extends E implements F {}
+
+class H {}
+
+class I extends G implements H {}
+
+C? nullC() => null;
+
+main() {
+ // Make sure the "none" test fails if assignment to "?." is not implemented.
+ // This makes status files easier to maintain.
+ nullC()?.v = 1;
+
+ // e1?.v++ is equivalent to ((x) => x == null ? null : x.v++)(e1).
+
+
+
+ // C?.v++ is equivalent to C.v++.
+
+
+
+ // The static type of e1?.v++ is the same as the static type of e1.v.
+
+
+
+
+
+
+
+ // e1?.v-- is equivalent to ((x) => x == null ? null : x.v--)(e1).
+
+
+
+ // C?.v-- is equivalent to C.v--.
+
+
+
+ // The static type of e1?.v-- is the same as the static type of e1.v.
+
+
+
+
+
+
+
+ // ++e1?.v is equivalent to e1?.v += 1.
+
+
+
+ // ++C?.v is equivalent to C?.v += 1.
+
+ { h.C.staticInt = 1; Expect.equals(2, ++h.C?.staticInt); Expect.equals(2, h.C.staticInt); }
+
+ // The static type of ++e1?.v is the same as the static type of e1.v + 1.
+
+
+
+
+
+
+
+ // --e1?.v is equivalent to e1?.v -= 1.
+
+
+
+ // --C?.v is equivalent to C?.v -= 1.
+
+
+
+ // The static type of --e1?.v is the same as the static type of e1.v - 1.
+
+
+
+
+
+
+}
diff --git a/tests/language/null_aware/increment_decrement_runtime_19_test.dart b/tests/language/null_aware/increment_decrement_runtime_19_test.dart
new file mode 100644
index 0000000..bb8c565
--- /dev/null
+++ b/tests/language/null_aware/increment_decrement_runtime_19_test.dart
@@ -0,0 +1,109 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2015, 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.
+
+// Verify semantics of the ?. operator when it appears in a postincrement or
+// preincrement expression (or a postdecrement or predecrement expression).
+
+import "package:expect/expect.dart";
+import "conditional_access_helper.dart" as h;
+
+class C {
+ int v;
+ C(this.v);
+ static late int staticInt;
+}
+
+class D {
+ E v;
+ D(this.v);
+ static late E staticE;
+}
+
+class E {
+ G operator +(int i) => new I();
+ G operator -(int i) => new I();
+}
+
+class F {}
+
+class G extends E implements F {}
+
+class H {}
+
+class I extends G implements H {}
+
+C? nullC() => null;
+
+main() {
+ // Make sure the "none" test fails if assignment to "?." is not implemented.
+ // This makes status files easier to maintain.
+ nullC()?.v = 1;
+
+ // e1?.v++ is equivalent to ((x) => x == null ? null : x.v++)(e1).
+
+
+
+ // C?.v++ is equivalent to C.v++.
+
+
+
+ // The static type of e1?.v++ is the same as the static type of e1.v.
+
+
+
+
+
+
+
+ // e1?.v-- is equivalent to ((x) => x == null ? null : x.v--)(e1).
+
+
+
+ // C?.v-- is equivalent to C.v--.
+
+
+
+ // The static type of e1?.v-- is the same as the static type of e1.v.
+
+
+
+
+
+
+
+ // ++e1?.v is equivalent to e1?.v += 1.
+
+
+
+ // ++C?.v is equivalent to C?.v += 1.
+
+
+
+ // The static type of ++e1?.v is the same as the static type of e1.v + 1.
+ { D? d = new D(new E()); F? f = ++d?.v; Expect.identical(d!.v, f); }
+
+
+
+
+
+
+ // --e1?.v is equivalent to e1?.v -= 1.
+
+
+
+ // --C?.v is equivalent to C?.v -= 1.
+
+
+
+ // The static type of --e1?.v is the same as the static type of e1.v - 1.
+
+
+
+
+
+
+}
diff --git a/tests/language/null_aware/increment_decrement_runtime_1_test.dart b/tests/language/null_aware/increment_decrement_runtime_1_test.dart
new file mode 100644
index 0000000..d23da62
--- /dev/null
+++ b/tests/language/null_aware/increment_decrement_runtime_1_test.dart
@@ -0,0 +1,109 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2015, 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.
+
+// Verify semantics of the ?. operator when it appears in a postincrement or
+// preincrement expression (or a postdecrement or predecrement expression).
+
+import "package:expect/expect.dart";
+import "conditional_access_helper.dart" as h;
+
+class C {
+ int v;
+ C(this.v);
+ static late int staticInt;
+}
+
+class D {
+ E v;
+ D(this.v);
+ static late E staticE;
+}
+
+class E {
+ G operator +(int i) => new I();
+ G operator -(int i) => new I();
+}
+
+class F {}
+
+class G extends E implements F {}
+
+class H {}
+
+class I extends G implements H {}
+
+C? nullC() => null;
+
+main() {
+ // Make sure the "none" test fails if assignment to "?." is not implemented.
+ // This makes status files easier to maintain.
+ nullC()?.v = 1;
+
+ // e1?.v++ is equivalent to ((x) => x == null ? null : x.v++)(e1).
+ Expect.equals(null, nullC()?.v++);
+
+
+ // C?.v++ is equivalent to C.v++.
+
+
+
+ // The static type of e1?.v++ is the same as the static type of e1.v.
+
+
+
+
+
+
+
+ // e1?.v-- is equivalent to ((x) => x == null ? null : x.v--)(e1).
+
+
+
+ // C?.v-- is equivalent to C.v--.
+
+
+
+ // The static type of e1?.v-- is the same as the static type of e1.v.
+
+
+
+
+
+
+
+ // ++e1?.v is equivalent to e1?.v += 1.
+
+
+
+ // ++C?.v is equivalent to C?.v += 1.
+
+
+
+ // The static type of ++e1?.v is the same as the static type of e1.v + 1.
+
+
+
+
+
+
+
+ // --e1?.v is equivalent to e1?.v -= 1.
+
+
+
+ // --C?.v is equivalent to C?.v -= 1.
+
+
+
+ // The static type of --e1?.v is the same as the static type of e1.v - 1.
+
+
+
+
+
+
+}
diff --git a/tests/language/null_aware/increment_decrement_runtime_20_test.dart b/tests/language/null_aware/increment_decrement_runtime_20_test.dart
new file mode 100644
index 0000000..d3dc17e
--- /dev/null
+++ b/tests/language/null_aware/increment_decrement_runtime_20_test.dart
@@ -0,0 +1,109 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2015, 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.
+
+// Verify semantics of the ?. operator when it appears in a postincrement or
+// preincrement expression (or a postdecrement or predecrement expression).
+
+import "package:expect/expect.dart";
+import "conditional_access_helper.dart" as h;
+
+class C {
+ int v;
+ C(this.v);
+ static late int staticInt;
+}
+
+class D {
+ E v;
+ D(this.v);
+ static late E staticE;
+}
+
+class E {
+ G operator +(int i) => new I();
+ G operator -(int i) => new I();
+}
+
+class F {}
+
+class G extends E implements F {}
+
+class H {}
+
+class I extends G implements H {}
+
+C? nullC() => null;
+
+main() {
+ // Make sure the "none" test fails if assignment to "?." is not implemented.
+ // This makes status files easier to maintain.
+ nullC()?.v = 1;
+
+ // e1?.v++ is equivalent to ((x) => x == null ? null : x.v++)(e1).
+
+
+
+ // C?.v++ is equivalent to C.v++.
+
+
+
+ // The static type of e1?.v++ is the same as the static type of e1.v.
+
+
+
+
+
+
+
+ // e1?.v-- is equivalent to ((x) => x == null ? null : x.v--)(e1).
+
+
+
+ // C?.v-- is equivalent to C.v--.
+
+
+
+ // The static type of e1?.v-- is the same as the static type of e1.v.
+
+
+
+
+
+
+
+ // ++e1?.v is equivalent to e1?.v += 1.
+
+
+
+ // ++C?.v is equivalent to C?.v += 1.
+
+
+
+ // The static type of ++e1?.v is the same as the static type of e1.v + 1.
+
+
+ { D.staticE = new E(); F? f = ++D?.staticE; Expect.identical(D.staticE, f); }
+
+
+
+
+ // --e1?.v is equivalent to e1?.v -= 1.
+
+
+
+ // --C?.v is equivalent to C?.v -= 1.
+
+
+
+ // The static type of --e1?.v is the same as the static type of e1.v - 1.
+
+
+
+
+
+
+}
diff --git a/tests/language/null_aware/increment_decrement_runtime_21_test.dart b/tests/language/null_aware/increment_decrement_runtime_21_test.dart
new file mode 100644
index 0000000..b12232c
--- /dev/null
+++ b/tests/language/null_aware/increment_decrement_runtime_21_test.dart
@@ -0,0 +1,109 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2015, 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.
+
+// Verify semantics of the ?. operator when it appears in a postincrement or
+// preincrement expression (or a postdecrement or predecrement expression).
+
+import "package:expect/expect.dart";
+import "conditional_access_helper.dart" as h;
+
+class C {
+ int v;
+ C(this.v);
+ static late int staticInt;
+}
+
+class D {
+ E v;
+ D(this.v);
+ static late E staticE;
+}
+
+class E {
+ G operator +(int i) => new I();
+ G operator -(int i) => new I();
+}
+
+class F {}
+
+class G extends E implements F {}
+
+class H {}
+
+class I extends G implements H {}
+
+C? nullC() => null;
+
+main() {
+ // Make sure the "none" test fails if assignment to "?." is not implemented.
+ // This makes status files easier to maintain.
+ nullC()?.v = 1;
+
+ // e1?.v++ is equivalent to ((x) => x == null ? null : x.v++)(e1).
+
+
+
+ // C?.v++ is equivalent to C.v++.
+
+
+
+ // The static type of e1?.v++ is the same as the static type of e1.v.
+
+
+
+
+
+
+
+ // e1?.v-- is equivalent to ((x) => x == null ? null : x.v--)(e1).
+
+
+
+ // C?.v-- is equivalent to C.v--.
+
+
+
+ // The static type of e1?.v-- is the same as the static type of e1.v.
+
+
+
+
+
+
+
+ // ++e1?.v is equivalent to e1?.v += 1.
+
+
+
+ // ++C?.v is equivalent to C?.v += 1.
+
+
+
+ // The static type of ++e1?.v is the same as the static type of e1.v + 1.
+
+
+
+ { h.D.staticE = new h.E(); h.F? f = ++h.D?.staticE; Expect.identical(h.D.staticE, f); }
+
+
+
+ // --e1?.v is equivalent to e1?.v -= 1.
+
+
+
+ // --C?.v is equivalent to C?.v -= 1.
+
+
+
+ // The static type of --e1?.v is the same as the static type of e1.v - 1.
+
+
+
+
+
+
+}
diff --git a/tests/language/null_aware/increment_decrement_runtime_22_test.dart b/tests/language/null_aware/increment_decrement_runtime_22_test.dart
new file mode 100644
index 0000000..59b625c
--- /dev/null
+++ b/tests/language/null_aware/increment_decrement_runtime_22_test.dart
@@ -0,0 +1,109 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2015, 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.
+
+// Verify semantics of the ?. operator when it appears in a postincrement or
+// preincrement expression (or a postdecrement or predecrement expression).
+
+import "package:expect/expect.dart";
+import "conditional_access_helper.dart" as h;
+
+class C {
+ int v;
+ C(this.v);
+ static late int staticInt;
+}
+
+class D {
+ E v;
+ D(this.v);
+ static late E staticE;
+}
+
+class E {
+ G operator +(int i) => new I();
+ G operator -(int i) => new I();
+}
+
+class F {}
+
+class G extends E implements F {}
+
+class H {}
+
+class I extends G implements H {}
+
+C? nullC() => null;
+
+main() {
+ // Make sure the "none" test fails if assignment to "?." is not implemented.
+ // This makes status files easier to maintain.
+ nullC()?.v = 1;
+
+ // e1?.v++ is equivalent to ((x) => x == null ? null : x.v++)(e1).
+
+
+
+ // C?.v++ is equivalent to C.v++.
+
+
+
+ // The static type of e1?.v++ is the same as the static type of e1.v.
+
+
+
+
+
+
+
+ // e1?.v-- is equivalent to ((x) => x == null ? null : x.v--)(e1).
+
+
+
+ // C?.v-- is equivalent to C.v--.
+
+
+
+ // The static type of e1?.v-- is the same as the static type of e1.v.
+
+
+
+
+
+
+
+ // ++e1?.v is equivalent to e1?.v += 1.
+
+
+
+ // ++C?.v is equivalent to C?.v += 1.
+
+
+
+ // The static type of ++e1?.v is the same as the static type of e1.v + 1.
+
+
+
+
+
+
+
+ // --e1?.v is equivalent to e1?.v -= 1.
+ Expect.equals(null, --nullC()?.v);
+
+
+ // --C?.v is equivalent to C?.v -= 1.
+
+
+
+ // The static type of --e1?.v is the same as the static type of e1.v - 1.
+
+
+
+
+
+
+}
diff --git a/tests/language/null_aware/increment_decrement_runtime_23_test.dart b/tests/language/null_aware/increment_decrement_runtime_23_test.dart
new file mode 100644
index 0000000..b2dc6b5
--- /dev/null
+++ b/tests/language/null_aware/increment_decrement_runtime_23_test.dart
@@ -0,0 +1,109 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2015, 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.
+
+// Verify semantics of the ?. operator when it appears in a postincrement or
+// preincrement expression (or a postdecrement or predecrement expression).
+
+import "package:expect/expect.dart";
+import "conditional_access_helper.dart" as h;
+
+class C {
+ int v;
+ C(this.v);
+ static late int staticInt;
+}
+
+class D {
+ E v;
+ D(this.v);
+ static late E staticE;
+}
+
+class E {
+ G operator +(int i) => new I();
+ G operator -(int i) => new I();
+}
+
+class F {}
+
+class G extends E implements F {}
+
+class H {}
+
+class I extends G implements H {}
+
+C? nullC() => null;
+
+main() {
+ // Make sure the "none" test fails if assignment to "?." is not implemented.
+ // This makes status files easier to maintain.
+ nullC()?.v = 1;
+
+ // e1?.v++ is equivalent to ((x) => x == null ? null : x.v++)(e1).
+
+
+
+ // C?.v++ is equivalent to C.v++.
+
+
+
+ // The static type of e1?.v++ is the same as the static type of e1.v.
+
+
+
+
+
+
+
+ // e1?.v-- is equivalent to ((x) => x == null ? null : x.v--)(e1).
+
+
+
+ // C?.v-- is equivalent to C.v--.
+
+
+
+ // The static type of e1?.v-- is the same as the static type of e1.v.
+
+
+
+
+
+
+
+ // ++e1?.v is equivalent to e1?.v += 1.
+
+
+
+ // ++C?.v is equivalent to C?.v += 1.
+
+
+
+ // The static type of ++e1?.v is the same as the static type of e1.v + 1.
+
+
+
+
+
+
+
+ // --e1?.v is equivalent to e1?.v -= 1.
+
+ { C? c = new C(1); Expect.equals(0, --c?.v); Expect.equals(0, c!.v); }
+
+ // --C?.v is equivalent to C?.v -= 1.
+
+
+
+ // The static type of --e1?.v is the same as the static type of e1.v - 1.
+
+
+
+
+
+
+}
diff --git a/tests/language/null_aware/increment_decrement_runtime_24_test.dart b/tests/language/null_aware/increment_decrement_runtime_24_test.dart
new file mode 100644
index 0000000..f80ef6e
--- /dev/null
+++ b/tests/language/null_aware/increment_decrement_runtime_24_test.dart
@@ -0,0 +1,109 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2015, 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.
+
+// Verify semantics of the ?. operator when it appears in a postincrement or
+// preincrement expression (or a postdecrement or predecrement expression).
+
+import "package:expect/expect.dart";
+import "conditional_access_helper.dart" as h;
+
+class C {
+ int v;
+ C(this.v);
+ static late int staticInt;
+}
+
+class D {
+ E v;
+ D(this.v);
+ static late E staticE;
+}
+
+class E {
+ G operator +(int i) => new I();
+ G operator -(int i) => new I();
+}
+
+class F {}
+
+class G extends E implements F {}
+
+class H {}
+
+class I extends G implements H {}
+
+C? nullC() => null;
+
+main() {
+ // Make sure the "none" test fails if assignment to "?." is not implemented.
+ // This makes status files easier to maintain.
+ nullC()?.v = 1;
+
+ // e1?.v++ is equivalent to ((x) => x == null ? null : x.v++)(e1).
+
+
+
+ // C?.v++ is equivalent to C.v++.
+
+
+
+ // The static type of e1?.v++ is the same as the static type of e1.v.
+
+
+
+
+
+
+
+ // e1?.v-- is equivalent to ((x) => x == null ? null : x.v--)(e1).
+
+
+
+ // C?.v-- is equivalent to C.v--.
+
+
+
+ // The static type of e1?.v-- is the same as the static type of e1.v.
+
+
+
+
+
+
+
+ // ++e1?.v is equivalent to e1?.v += 1.
+
+
+
+ // ++C?.v is equivalent to C?.v += 1.
+
+
+
+ // The static type of ++e1?.v is the same as the static type of e1.v + 1.
+
+
+
+
+
+
+
+ // --e1?.v is equivalent to e1?.v -= 1.
+
+
+
+ // --C?.v is equivalent to C?.v -= 1.
+ { C.staticInt = 1; Expect.equals(0, --C?.staticInt); Expect.equals(0, C.staticInt); }
+
+
+ // The static type of --e1?.v is the same as the static type of e1.v - 1.
+
+
+
+
+
+
+}
diff --git a/tests/language/null_aware/increment_decrement_runtime_25_test.dart b/tests/language/null_aware/increment_decrement_runtime_25_test.dart
new file mode 100644
index 0000000..83ef846
--- /dev/null
+++ b/tests/language/null_aware/increment_decrement_runtime_25_test.dart
@@ -0,0 +1,109 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2015, 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.
+
+// Verify semantics of the ?. operator when it appears in a postincrement or
+// preincrement expression (or a postdecrement or predecrement expression).
+
+import "package:expect/expect.dart";
+import "conditional_access_helper.dart" as h;
+
+class C {
+ int v;
+ C(this.v);
+ static late int staticInt;
+}
+
+class D {
+ E v;
+ D(this.v);
+ static late E staticE;
+}
+
+class E {
+ G operator +(int i) => new I();
+ G operator -(int i) => new I();
+}
+
+class F {}
+
+class G extends E implements F {}
+
+class H {}
+
+class I extends G implements H {}
+
+C? nullC() => null;
+
+main() {
+ // Make sure the "none" test fails if assignment to "?." is not implemented.
+ // This makes status files easier to maintain.
+ nullC()?.v = 1;
+
+ // e1?.v++ is equivalent to ((x) => x == null ? null : x.v++)(e1).
+
+
+
+ // C?.v++ is equivalent to C.v++.
+
+
+
+ // The static type of e1?.v++ is the same as the static type of e1.v.
+
+
+
+
+
+
+
+ // e1?.v-- is equivalent to ((x) => x == null ? null : x.v--)(e1).
+
+
+
+ // C?.v-- is equivalent to C.v--.
+
+
+
+ // The static type of e1?.v-- is the same as the static type of e1.v.
+
+
+
+
+
+
+
+ // ++e1?.v is equivalent to e1?.v += 1.
+
+
+
+ // ++C?.v is equivalent to C?.v += 1.
+
+
+
+ // The static type of ++e1?.v is the same as the static type of e1.v + 1.
+
+
+
+
+
+
+
+ // --e1?.v is equivalent to e1?.v -= 1.
+
+
+
+ // --C?.v is equivalent to C?.v -= 1.
+
+ { h.C.staticInt = 1; Expect.equals(0, --h.C?.staticInt); Expect.equals(0, h.C.staticInt); }
+
+ // The static type of --e1?.v is the same as the static type of e1.v - 1.
+
+
+
+
+
+
+}
diff --git a/tests/language/null_aware/increment_decrement_runtime_26_test.dart b/tests/language/null_aware/increment_decrement_runtime_26_test.dart
new file mode 100644
index 0000000..4201d15
--- /dev/null
+++ b/tests/language/null_aware/increment_decrement_runtime_26_test.dart
@@ -0,0 +1,109 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2015, 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.
+
+// Verify semantics of the ?. operator when it appears in a postincrement or
+// preincrement expression (or a postdecrement or predecrement expression).
+
+import "package:expect/expect.dart";
+import "conditional_access_helper.dart" as h;
+
+class C {
+ int v;
+ C(this.v);
+ static late int staticInt;
+}
+
+class D {
+ E v;
+ D(this.v);
+ static late E staticE;
+}
+
+class E {
+ G operator +(int i) => new I();
+ G operator -(int i) => new I();
+}
+
+class F {}
+
+class G extends E implements F {}
+
+class H {}
+
+class I extends G implements H {}
+
+C? nullC() => null;
+
+main() {
+ // Make sure the "none" test fails if assignment to "?." is not implemented.
+ // This makes status files easier to maintain.
+ nullC()?.v = 1;
+
+ // e1?.v++ is equivalent to ((x) => x == null ? null : x.v++)(e1).
+
+
+
+ // C?.v++ is equivalent to C.v++.
+
+
+
+ // The static type of e1?.v++ is the same as the static type of e1.v.
+
+
+
+
+
+
+
+ // e1?.v-- is equivalent to ((x) => x == null ? null : x.v--)(e1).
+
+
+
+ // C?.v-- is equivalent to C.v--.
+
+
+
+ // The static type of e1?.v-- is the same as the static type of e1.v.
+
+
+
+
+
+
+
+ // ++e1?.v is equivalent to e1?.v += 1.
+
+
+
+ // ++C?.v is equivalent to C?.v += 1.
+
+
+
+ // The static type of ++e1?.v is the same as the static type of e1.v + 1.
+
+
+
+
+
+
+
+ // --e1?.v is equivalent to e1?.v -= 1.
+
+
+
+ // --C?.v is equivalent to C?.v -= 1.
+
+
+
+ // The static type of --e1?.v is the same as the static type of e1.v - 1.
+ { D? d = new D(new E()); F? f = --d?.v; Expect.identical(d!.v, f); }
+
+
+
+
+
+}
diff --git a/tests/language/null_aware/increment_decrement_runtime_27_test.dart b/tests/language/null_aware/increment_decrement_runtime_27_test.dart
new file mode 100644
index 0000000..c753b8a
--- /dev/null
+++ b/tests/language/null_aware/increment_decrement_runtime_27_test.dart
@@ -0,0 +1,109 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2015, 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.
+
+// Verify semantics of the ?. operator when it appears in a postincrement or
+// preincrement expression (or a postdecrement or predecrement expression).
+
+import "package:expect/expect.dart";
+import "conditional_access_helper.dart" as h;
+
+class C {
+ int v;
+ C(this.v);
+ static late int staticInt;
+}
+
+class D {
+ E v;
+ D(this.v);
+ static late E staticE;
+}
+
+class E {
+ G operator +(int i) => new I();
+ G operator -(int i) => new I();
+}
+
+class F {}
+
+class G extends E implements F {}
+
+class H {}
+
+class I extends G implements H {}
+
+C? nullC() => null;
+
+main() {
+ // Make sure the "none" test fails if assignment to "?." is not implemented.
+ // This makes status files easier to maintain.
+ nullC()?.v = 1;
+
+ // e1?.v++ is equivalent to ((x) => x == null ? null : x.v++)(e1).
+
+
+
+ // C?.v++ is equivalent to C.v++.
+
+
+
+ // The static type of e1?.v++ is the same as the static type of e1.v.
+
+
+
+
+
+
+
+ // e1?.v-- is equivalent to ((x) => x == null ? null : x.v--)(e1).
+
+
+
+ // C?.v-- is equivalent to C.v--.
+
+
+
+ // The static type of e1?.v-- is the same as the static type of e1.v.
+
+
+
+
+
+
+
+ // ++e1?.v is equivalent to e1?.v += 1.
+
+
+
+ // ++C?.v is equivalent to C?.v += 1.
+
+
+
+ // The static type of ++e1?.v is the same as the static type of e1.v + 1.
+
+
+
+
+
+
+
+ // --e1?.v is equivalent to e1?.v -= 1.
+
+
+
+ // --C?.v is equivalent to C?.v -= 1.
+
+
+
+ // The static type of --e1?.v is the same as the static type of e1.v - 1.
+
+
+ { D.staticE = new E(); F? f = --D?.staticE; Expect.identical(D.staticE, f); }
+
+
+
+}
diff --git a/tests/language/null_aware/increment_decrement_runtime_28_test.dart b/tests/language/null_aware/increment_decrement_runtime_28_test.dart
new file mode 100644
index 0000000..fbec38a
--- /dev/null
+++ b/tests/language/null_aware/increment_decrement_runtime_28_test.dart
@@ -0,0 +1,109 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2015, 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.
+
+// Verify semantics of the ?. operator when it appears in a postincrement or
+// preincrement expression (or a postdecrement or predecrement expression).
+
+import "package:expect/expect.dart";
+import "conditional_access_helper.dart" as h;
+
+class C {
+ int v;
+ C(this.v);
+ static late int staticInt;
+}
+
+class D {
+ E v;
+ D(this.v);
+ static late E staticE;
+}
+
+class E {
+ G operator +(int i) => new I();
+ G operator -(int i) => new I();
+}
+
+class F {}
+
+class G extends E implements F {}
+
+class H {}
+
+class I extends G implements H {}
+
+C? nullC() => null;
+
+main() {
+ // Make sure the "none" test fails if assignment to "?." is not implemented.
+ // This makes status files easier to maintain.
+ nullC()?.v = 1;
+
+ // e1?.v++ is equivalent to ((x) => x == null ? null : x.v++)(e1).
+
+
+
+ // C?.v++ is equivalent to C.v++.
+
+
+
+ // The static type of e1?.v++ is the same as the static type of e1.v.
+
+
+
+
+
+
+
+ // e1?.v-- is equivalent to ((x) => x == null ? null : x.v--)(e1).
+
+
+
+ // C?.v-- is equivalent to C.v--.
+
+
+
+ // The static type of e1?.v-- is the same as the static type of e1.v.
+
+
+
+
+
+
+
+ // ++e1?.v is equivalent to e1?.v += 1.
+
+
+
+ // ++C?.v is equivalent to C?.v += 1.
+
+
+
+ // The static type of ++e1?.v is the same as the static type of e1.v + 1.
+
+
+
+
+
+
+
+ // --e1?.v is equivalent to e1?.v -= 1.
+
+
+
+ // --C?.v is equivalent to C?.v -= 1.
+
+
+
+ // The static type of --e1?.v is the same as the static type of e1.v - 1.
+
+
+
+ { h.D.staticE = new h.E(); h.F? f = --h.D?.staticE; Expect.identical(h.D.staticE, f); }
+
+
+}
diff --git a/tests/language/null_aware/increment_decrement_runtime_2_test.dart b/tests/language/null_aware/increment_decrement_runtime_2_test.dart
new file mode 100644
index 0000000..67bca4a
--- /dev/null
+++ b/tests/language/null_aware/increment_decrement_runtime_2_test.dart
@@ -0,0 +1,109 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2015, 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.
+
+// Verify semantics of the ?. operator when it appears in a postincrement or
+// preincrement expression (or a postdecrement or predecrement expression).
+
+import "package:expect/expect.dart";
+import "conditional_access_helper.dart" as h;
+
+class C {
+ int v;
+ C(this.v);
+ static late int staticInt;
+}
+
+class D {
+ E v;
+ D(this.v);
+ static late E staticE;
+}
+
+class E {
+ G operator +(int i) => new I();
+ G operator -(int i) => new I();
+}
+
+class F {}
+
+class G extends E implements F {}
+
+class H {}
+
+class I extends G implements H {}
+
+C? nullC() => null;
+
+main() {
+ // Make sure the "none" test fails if assignment to "?." is not implemented.
+ // This makes status files easier to maintain.
+ nullC()?.v = 1;
+
+ // e1?.v++ is equivalent to ((x) => x == null ? null : x.v++)(e1).
+
+ { C? c = new C(1); Expect.equals(1, c?.v++); Expect.equals(2, c!.v); }
+
+ // C?.v++ is equivalent to C.v++.
+
+
+
+ // The static type of e1?.v++ is the same as the static type of e1.v.
+
+
+
+
+
+
+
+ // e1?.v-- is equivalent to ((x) => x == null ? null : x.v--)(e1).
+
+
+
+ // C?.v-- is equivalent to C.v--.
+
+
+
+ // The static type of e1?.v-- is the same as the static type of e1.v.
+
+
+
+
+
+
+
+ // ++e1?.v is equivalent to e1?.v += 1.
+
+
+
+ // ++C?.v is equivalent to C?.v += 1.
+
+
+
+ // The static type of ++e1?.v is the same as the static type of e1.v + 1.
+
+
+
+
+
+
+
+ // --e1?.v is equivalent to e1?.v -= 1.
+
+
+
+ // --C?.v is equivalent to C?.v -= 1.
+
+
+
+ // The static type of --e1?.v is the same as the static type of e1.v - 1.
+
+
+
+
+
+
+}
diff --git a/tests/language/null_aware/increment_decrement_runtime_3_test.dart b/tests/language/null_aware/increment_decrement_runtime_3_test.dart
new file mode 100644
index 0000000..d28f21e
--- /dev/null
+++ b/tests/language/null_aware/increment_decrement_runtime_3_test.dart
@@ -0,0 +1,109 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2015, 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.
+
+// Verify semantics of the ?. operator when it appears in a postincrement or
+// preincrement expression (or a postdecrement or predecrement expression).
+
+import "package:expect/expect.dart";
+import "conditional_access_helper.dart" as h;
+
+class C {
+ int v;
+ C(this.v);
+ static late int staticInt;
+}
+
+class D {
+ E v;
+ D(this.v);
+ static late E staticE;
+}
+
+class E {
+ G operator +(int i) => new I();
+ G operator -(int i) => new I();
+}
+
+class F {}
+
+class G extends E implements F {}
+
+class H {}
+
+class I extends G implements H {}
+
+C? nullC() => null;
+
+main() {
+ // Make sure the "none" test fails if assignment to "?." is not implemented.
+ // This makes status files easier to maintain.
+ nullC()?.v = 1;
+
+ // e1?.v++ is equivalent to ((x) => x == null ? null : x.v++)(e1).
+
+
+
+ // C?.v++ is equivalent to C.v++.
+ { C.staticInt = 1; Expect.equals(1, C?.staticInt++); Expect.equals(2, C.staticInt); }
+
+
+ // The static type of e1?.v++ is the same as the static type of e1.v.
+
+
+
+
+
+
+
+ // e1?.v-- is equivalent to ((x) => x == null ? null : x.v--)(e1).
+
+
+
+ // C?.v-- is equivalent to C.v--.
+
+
+
+ // The static type of e1?.v-- is the same as the static type of e1.v.
+
+
+
+
+
+
+
+ // ++e1?.v is equivalent to e1?.v += 1.
+
+
+
+ // ++C?.v is equivalent to C?.v += 1.
+
+
+
+ // The static type of ++e1?.v is the same as the static type of e1.v + 1.
+
+
+
+
+
+
+
+ // --e1?.v is equivalent to e1?.v -= 1.
+
+
+
+ // --C?.v is equivalent to C?.v -= 1.
+
+
+
+ // The static type of --e1?.v is the same as the static type of e1.v - 1.
+
+
+
+
+
+
+}
diff --git a/tests/language/null_aware/increment_decrement_runtime_4_test.dart b/tests/language/null_aware/increment_decrement_runtime_4_test.dart
new file mode 100644
index 0000000..d5a21b7
--- /dev/null
+++ b/tests/language/null_aware/increment_decrement_runtime_4_test.dart
@@ -0,0 +1,109 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2015, 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.
+
+// Verify semantics of the ?. operator when it appears in a postincrement or
+// preincrement expression (or a postdecrement or predecrement expression).
+
+import "package:expect/expect.dart";
+import "conditional_access_helper.dart" as h;
+
+class C {
+ int v;
+ C(this.v);
+ static late int staticInt;
+}
+
+class D {
+ E v;
+ D(this.v);
+ static late E staticE;
+}
+
+class E {
+ G operator +(int i) => new I();
+ G operator -(int i) => new I();
+}
+
+class F {}
+
+class G extends E implements F {}
+
+class H {}
+
+class I extends G implements H {}
+
+C? nullC() => null;
+
+main() {
+ // Make sure the "none" test fails if assignment to "?." is not implemented.
+ // This makes status files easier to maintain.
+ nullC()?.v = 1;
+
+ // e1?.v++ is equivalent to ((x) => x == null ? null : x.v++)(e1).
+
+
+
+ // C?.v++ is equivalent to C.v++.
+
+ { h.C.staticInt = 1; Expect.equals(1, h.C?.staticInt++); Expect.equals(2, h.C.staticInt); }
+
+ // The static type of e1?.v++ is the same as the static type of e1.v.
+
+
+
+
+
+
+
+ // e1?.v-- is equivalent to ((x) => x == null ? null : x.v--)(e1).
+
+
+
+ // C?.v-- is equivalent to C.v--.
+
+
+
+ // The static type of e1?.v-- is the same as the static type of e1.v.
+
+
+
+
+
+
+
+ // ++e1?.v is equivalent to e1?.v += 1.
+
+
+
+ // ++C?.v is equivalent to C?.v += 1.
+
+
+
+ // The static type of ++e1?.v is the same as the static type of e1.v + 1.
+
+
+
+
+
+
+
+ // --e1?.v is equivalent to e1?.v -= 1.
+
+
+
+ // --C?.v is equivalent to C?.v -= 1.
+
+
+
+ // The static type of --e1?.v is the same as the static type of e1.v - 1.
+
+
+
+
+
+
+}
diff --git a/tests/language/null_aware/increment_decrement_runtime_5_test.dart b/tests/language/null_aware/increment_decrement_runtime_5_test.dart
new file mode 100644
index 0000000..8ca6c72
--- /dev/null
+++ b/tests/language/null_aware/increment_decrement_runtime_5_test.dart
@@ -0,0 +1,109 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2015, 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.
+
+// Verify semantics of the ?. operator when it appears in a postincrement or
+// preincrement expression (or a postdecrement or predecrement expression).
+
+import "package:expect/expect.dart";
+import "conditional_access_helper.dart" as h;
+
+class C {
+ int v;
+ C(this.v);
+ static late int staticInt;
+}
+
+class D {
+ E v;
+ D(this.v);
+ static late E staticE;
+}
+
+class E {
+ G operator +(int i) => new I();
+ G operator -(int i) => new I();
+}
+
+class F {}
+
+class G extends E implements F {}
+
+class H {}
+
+class I extends G implements H {}
+
+C? nullC() => null;
+
+main() {
+ // Make sure the "none" test fails if assignment to "?." is not implemented.
+ // This makes status files easier to maintain.
+ nullC()?.v = 1;
+
+ // e1?.v++ is equivalent to ((x) => x == null ? null : x.v++)(e1).
+
+
+
+ // C?.v++ is equivalent to C.v++.
+
+
+
+ // The static type of e1?.v++ is the same as the static type of e1.v.
+ { E e1 = new E(); D? d = new D(e1); E? e2 = d?.v++; Expect.identical(e1, e2); }
+
+
+
+
+
+
+ // e1?.v-- is equivalent to ((x) => x == null ? null : x.v--)(e1).
+
+
+
+ // C?.v-- is equivalent to C.v--.
+
+
+
+ // The static type of e1?.v-- is the same as the static type of e1.v.
+
+
+
+
+
+
+
+ // ++e1?.v is equivalent to e1?.v += 1.
+
+
+
+ // ++C?.v is equivalent to C?.v += 1.
+
+
+
+ // The static type of ++e1?.v is the same as the static type of e1.v + 1.
+
+
+
+
+
+
+
+ // --e1?.v is equivalent to e1?.v -= 1.
+
+
+
+ // --C?.v is equivalent to C?.v -= 1.
+
+
+
+ // The static type of --e1?.v is the same as the static type of e1.v - 1.
+
+
+
+
+
+
+}
diff --git a/tests/language/null_aware/increment_decrement_runtime_6_test.dart b/tests/language/null_aware/increment_decrement_runtime_6_test.dart
new file mode 100644
index 0000000..3d4f812
--- /dev/null
+++ b/tests/language/null_aware/increment_decrement_runtime_6_test.dart
@@ -0,0 +1,109 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2015, 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.
+
+// Verify semantics of the ?. operator when it appears in a postincrement or
+// preincrement expression (or a postdecrement or predecrement expression).
+
+import "package:expect/expect.dart";
+import "conditional_access_helper.dart" as h;
+
+class C {
+ int v;
+ C(this.v);
+ static late int staticInt;
+}
+
+class D {
+ E v;
+ D(this.v);
+ static late E staticE;
+}
+
+class E {
+ G operator +(int i) => new I();
+ G operator -(int i) => new I();
+}
+
+class F {}
+
+class G extends E implements F {}
+
+class H {}
+
+class I extends G implements H {}
+
+C? nullC() => null;
+
+main() {
+ // Make sure the "none" test fails if assignment to "?." is not implemented.
+ // This makes status files easier to maintain.
+ nullC()?.v = 1;
+
+ // e1?.v++ is equivalent to ((x) => x == null ? null : x.v++)(e1).
+
+
+
+ // C?.v++ is equivalent to C.v++.
+
+
+
+ // The static type of e1?.v++ is the same as the static type of e1.v.
+
+
+ { E e1 = new E(); D.staticE = e1; E? e2 = D?.staticE++; Expect.identical(e1, e2); }
+
+
+
+
+ // e1?.v-- is equivalent to ((x) => x == null ? null : x.v--)(e1).
+
+
+
+ // C?.v-- is equivalent to C.v--.
+
+
+
+ // The static type of e1?.v-- is the same as the static type of e1.v.
+
+
+
+
+
+
+
+ // ++e1?.v is equivalent to e1?.v += 1.
+
+
+
+ // ++C?.v is equivalent to C?.v += 1.
+
+
+
+ // The static type of ++e1?.v is the same as the static type of e1.v + 1.
+
+
+
+
+
+
+
+ // --e1?.v is equivalent to e1?.v -= 1.
+
+
+
+ // --C?.v is equivalent to C?.v -= 1.
+
+
+
+ // The static type of --e1?.v is the same as the static type of e1.v - 1.
+
+
+
+
+
+
+}
diff --git a/tests/language/null_aware/increment_decrement_runtime_7_test.dart b/tests/language/null_aware/increment_decrement_runtime_7_test.dart
new file mode 100644
index 0000000..b9ae91d
--- /dev/null
+++ b/tests/language/null_aware/increment_decrement_runtime_7_test.dart
@@ -0,0 +1,109 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2015, 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.
+
+// Verify semantics of the ?. operator when it appears in a postincrement or
+// preincrement expression (or a postdecrement or predecrement expression).
+
+import "package:expect/expect.dart";
+import "conditional_access_helper.dart" as h;
+
+class C {
+ int v;
+ C(this.v);
+ static late int staticInt;
+}
+
+class D {
+ E v;
+ D(this.v);
+ static late E staticE;
+}
+
+class E {
+ G operator +(int i) => new I();
+ G operator -(int i) => new I();
+}
+
+class F {}
+
+class G extends E implements F {}
+
+class H {}
+
+class I extends G implements H {}
+
+C? nullC() => null;
+
+main() {
+ // Make sure the "none" test fails if assignment to "?." is not implemented.
+ // This makes status files easier to maintain.
+ nullC()?.v = 1;
+
+ // e1?.v++ is equivalent to ((x) => x == null ? null : x.v++)(e1).
+
+
+
+ // C?.v++ is equivalent to C.v++.
+
+
+
+ // The static type of e1?.v++ is the same as the static type of e1.v.
+
+
+
+ { h.E e1 = new h.E(); h.D.staticE = e1; h.E? e2 = h.D?.staticE++; Expect.identical(e1, e2); }
+
+
+
+ // e1?.v-- is equivalent to ((x) => x == null ? null : x.v--)(e1).
+
+
+
+ // C?.v-- is equivalent to C.v--.
+
+
+
+ // The static type of e1?.v-- is the same as the static type of e1.v.
+
+
+
+
+
+
+
+ // ++e1?.v is equivalent to e1?.v += 1.
+
+
+
+ // ++C?.v is equivalent to C?.v += 1.
+
+
+
+ // The static type of ++e1?.v is the same as the static type of e1.v + 1.
+
+
+
+
+
+
+
+ // --e1?.v is equivalent to e1?.v -= 1.
+
+
+
+ // --C?.v is equivalent to C?.v -= 1.
+
+
+
+ // The static type of --e1?.v is the same as the static type of e1.v - 1.
+
+
+
+
+
+
+}
diff --git a/tests/language/null_aware/increment_decrement_runtime_8_test.dart b/tests/language/null_aware/increment_decrement_runtime_8_test.dart
new file mode 100644
index 0000000..669b7ed
--- /dev/null
+++ b/tests/language/null_aware/increment_decrement_runtime_8_test.dart
@@ -0,0 +1,109 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2015, 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.
+
+// Verify semantics of the ?. operator when it appears in a postincrement or
+// preincrement expression (or a postdecrement or predecrement expression).
+
+import "package:expect/expect.dart";
+import "conditional_access_helper.dart" as h;
+
+class C {
+ int v;
+ C(this.v);
+ static late int staticInt;
+}
+
+class D {
+ E v;
+ D(this.v);
+ static late E staticE;
+}
+
+class E {
+ G operator +(int i) => new I();
+ G operator -(int i) => new I();
+}
+
+class F {}
+
+class G extends E implements F {}
+
+class H {}
+
+class I extends G implements H {}
+
+C? nullC() => null;
+
+main() {
+ // Make sure the "none" test fails if assignment to "?." is not implemented.
+ // This makes status files easier to maintain.
+ nullC()?.v = 1;
+
+ // e1?.v++ is equivalent to ((x) => x == null ? null : x.v++)(e1).
+
+
+
+ // C?.v++ is equivalent to C.v++.
+
+
+
+ // The static type of e1?.v++ is the same as the static type of e1.v.
+
+
+
+
+
+
+
+ // e1?.v-- is equivalent to ((x) => x == null ? null : x.v--)(e1).
+ Expect.equals(null, nullC()?.v--);
+
+
+ // C?.v-- is equivalent to C.v--.
+
+
+
+ // The static type of e1?.v-- is the same as the static type of e1.v.
+
+
+
+
+
+
+
+ // ++e1?.v is equivalent to e1?.v += 1.
+
+
+
+ // ++C?.v is equivalent to C?.v += 1.
+
+
+
+ // The static type of ++e1?.v is the same as the static type of e1.v + 1.
+
+
+
+
+
+
+
+ // --e1?.v is equivalent to e1?.v -= 1.
+
+
+
+ // --C?.v is equivalent to C?.v -= 1.
+
+
+
+ // The static type of --e1?.v is the same as the static type of e1.v - 1.
+
+
+
+
+
+
+}
diff --git a/tests/language/null_aware/increment_decrement_runtime_9_test.dart b/tests/language/null_aware/increment_decrement_runtime_9_test.dart
new file mode 100644
index 0000000..cd3bd72
--- /dev/null
+++ b/tests/language/null_aware/increment_decrement_runtime_9_test.dart
@@ -0,0 +1,109 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2015, 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.
+
+// Verify semantics of the ?. operator when it appears in a postincrement or
+// preincrement expression (or a postdecrement or predecrement expression).
+
+import "package:expect/expect.dart";
+import "conditional_access_helper.dart" as h;
+
+class C {
+ int v;
+ C(this.v);
+ static late int staticInt;
+}
+
+class D {
+ E v;
+ D(this.v);
+ static late E staticE;
+}
+
+class E {
+ G operator +(int i) => new I();
+ G operator -(int i) => new I();
+}
+
+class F {}
+
+class G extends E implements F {}
+
+class H {}
+
+class I extends G implements H {}
+
+C? nullC() => null;
+
+main() {
+ // Make sure the "none" test fails if assignment to "?." is not implemented.
+ // This makes status files easier to maintain.
+ nullC()?.v = 1;
+
+ // e1?.v++ is equivalent to ((x) => x == null ? null : x.v++)(e1).
+
+
+
+ // C?.v++ is equivalent to C.v++.
+
+
+
+ // The static type of e1?.v++ is the same as the static type of e1.v.
+
+
+
+
+
+
+
+ // e1?.v-- is equivalent to ((x) => x == null ? null : x.v--)(e1).
+
+ { C? c = new C(1); Expect.equals(1, c?.v--); Expect.equals(0, c!.v); }
+
+ // C?.v-- is equivalent to C.v--.
+
+
+
+ // The static type of e1?.v-- is the same as the static type of e1.v.
+
+
+
+
+
+
+
+ // ++e1?.v is equivalent to e1?.v += 1.
+
+
+
+ // ++C?.v is equivalent to C?.v += 1.
+
+
+
+ // The static type of ++e1?.v is the same as the static type of e1.v + 1.
+
+
+
+
+
+
+
+ // --e1?.v is equivalent to e1?.v -= 1.
+
+
+
+ // --C?.v is equivalent to C?.v -= 1.
+
+
+
+ // The static type of --e1?.v is the same as the static type of e1.v - 1.
+
+
+
+
+
+
+}
diff --git a/tests/language/null_aware/increment_decrement_runtime_test.dart b/tests/language/null_aware/increment_decrement_runtime_test.dart
new file mode 100644
index 0000000..0ccc804
--- /dev/null
+++ b/tests/language/null_aware/increment_decrement_runtime_test.dart
@@ -0,0 +1,109 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2015, 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.
+
+// Verify semantics of the ?. operator when it appears in a postincrement or
+// preincrement expression (or a postdecrement or predecrement expression).
+
+import "package:expect/expect.dart";
+import "conditional_access_helper.dart" as h;
+
+class C {
+ int v;
+ C(this.v);
+ static late int staticInt;
+}
+
+class D {
+ E v;
+ D(this.v);
+ static late E staticE;
+}
+
+class E {
+ G operator +(int i) => new I();
+ G operator -(int i) => new I();
+}
+
+class F {}
+
+class G extends E implements F {}
+
+class H {}
+
+class I extends G implements H {}
+
+C? nullC() => null;
+
+main() {
+ // Make sure the "none" test fails if assignment to "?." is not implemented.
+ // This makes status files easier to maintain.
+ nullC()?.v = 1;
+
+ // e1?.v++ is equivalent to ((x) => x == null ? null : x.v++)(e1).
+
+
+
+ // C?.v++ is equivalent to C.v++.
+
+
+
+ // The static type of e1?.v++ is the same as the static type of e1.v.
+
+
+
+
+
+
+
+ // e1?.v-- is equivalent to ((x) => x == null ? null : x.v--)(e1).
+
+
+
+ // C?.v-- is equivalent to C.v--.
+
+
+
+ // The static type of e1?.v-- is the same as the static type of e1.v.
+
+
+
+
+
+
+
+ // ++e1?.v is equivalent to e1?.v += 1.
+
+
+
+ // ++C?.v is equivalent to C?.v += 1.
+
+
+
+ // The static type of ++e1?.v is the same as the static type of e1.v + 1.
+
+
+
+
+
+
+
+ // --e1?.v is equivalent to e1?.v -= 1.
+
+
+
+ // --C?.v is equivalent to C?.v -= 1.
+
+
+
+ // The static type of --e1?.v is the same as the static type of e1.v - 1.
+
+
+
+
+
+
+}
diff --git a/tests/language/null_aware/increment_decrement_test.dart b/tests/language/null_aware/increment_decrement_test.dart
new file mode 100644
index 0000000..1a5ea4e
--- /dev/null
+++ b/tests/language/null_aware/increment_decrement_test.dart
@@ -0,0 +1,152 @@
+// Copyright (c) 2015, 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.
+
+// Verify semantics of the ?. operator when it appears in a postincrement or
+// preincrement expression (or a postdecrement or predecrement expression).
+
+import "package:expect/expect.dart";
+import "conditional_access_helper.dart" as h;
+
+class C {
+ int v;
+ C(this.v);
+ static late int staticInt;
+}
+
+class D {
+ E v;
+ D(this.v);
+ static late E staticE;
+}
+
+class E {
+ G operator +(int i) => new I();
+ G operator -(int i) => new I();
+}
+
+class F {}
+
+class G extends E implements F {}
+
+class H {}
+
+class I extends G implements H {}
+
+C? nullC() => null;
+
+main() {
+ // Make sure the "none" test fails if assignment to "?." is not implemented.
+ // This makes status files easier to maintain.
+ nullC()?.v = 1;
+
+ // e1?.v++ is equivalent to ((x) => x == null ? null : x.v++)(e1).
+ Expect.equals(null, nullC()?.v++);
+ { C? c = new C(1); Expect.equals(1, c?.v++); Expect.equals(2, c!.v); }
+
+ // C?.v++ is equivalent to C.v++.
+ { C.staticInt = 1; Expect.equals(1, C?.staticInt++); Expect.equals(2, C.staticInt); }
+ { h.C.staticInt = 1; Expect.equals(1, h.C?.staticInt++); Expect.equals(2, h.C.staticInt); }
+
+ // The static type of e1?.v++ is the same as the static type of e1.v.
+ { E e1 = new E(); D? d = new D(e1); E? e2 = d?.v++; Expect.identical(e1, e2); }
+ { G g = new G(); D? d = new D(g); F? f = d?.v++; Expect.identical(f, g); }
+ // ^^^^^^
+ // [analyzer] STATIC_TYPE_WARNING.INVALID_ASSIGNMENT
+ // [cfe] A value of type 'E?' can't be assigned to a variable of type 'F?'.
+ { E e1 = new E(); D.staticE = e1; E? e2 = D?.staticE++; Expect.identical(e1, e2); }
+ { h.E e1 = new h.E(); h.D.staticE = e1; h.E? e2 = h.D?.staticE++; Expect.identical(e1, e2); }
+ { G g = new G(); D.staticE = g; F? f = D?.staticE++; Expect.identical(f, g); }
+ // ^^^^^^^^^^^^
+ // [analyzer] STATIC_TYPE_WARNING.INVALID_ASSIGNMENT
+ // ^
+ // [cfe] A value of type 'E' can't be assigned to a variable of type 'F?'.
+ { h.G g = new h.G(); h.D.staticE = g; h.F? f = h.D?.staticE++; Expect.identical(f, g); }
+ // ^^^^^^^^^^^^^^
+ // [analyzer] STATIC_TYPE_WARNING.INVALID_ASSIGNMENT
+ // ^
+ // [cfe] A value of type 'E' can't be assigned to a variable of type 'F?'.
+
+ // e1?.v-- is equivalent to ((x) => x == null ? null : x.v--)(e1).
+ Expect.equals(null, nullC()?.v--);
+ { C? c = new C(1); Expect.equals(1, c?.v--); Expect.equals(0, c!.v); }
+
+ // C?.v-- is equivalent to C.v--.
+ { C.staticInt = 1; Expect.equals(1, C?.staticInt--); Expect.equals(0, C.staticInt); }
+ { h.C.staticInt = 1; Expect.equals(1, h.C?.staticInt--); Expect.equals(0, h.C.staticInt); }
+
+ // The static type of e1?.v-- is the same as the static type of e1.v.
+ { E e1 = new E(); D? d = new D(e1); E? e2 = d?.v--; Expect.identical(e1, e2); }
+ { G g = new G(); D? d = new D(g); F? f = d?.v--; Expect.identical(f, g); }
+ // ^^^^^^
+ // [analyzer] STATIC_TYPE_WARNING.INVALID_ASSIGNMENT
+ // [cfe] A value of type 'E?' can't be assigned to a variable of type 'F?'.
+ { E e1 = new E(); D.staticE = e1; E? e2 = D?.staticE--; Expect.identical(e1, e2); }
+ { h.E e1 = new h.E(); h.D.staticE = e1; h.E? e2 = h.D?.staticE--; Expect.identical(e1, e2); }
+ { G g = new G(); D.staticE = g; F? f = D?.staticE--; Expect.identical(f, g); }
+ // ^^^^^^^^^^^^
+ // [analyzer] STATIC_TYPE_WARNING.INVALID_ASSIGNMENT
+ // ^
+ // [cfe] A value of type 'E' can't be assigned to a variable of type 'F?'.
+ { h.G g = new h.G(); h.D.staticE = g; h.F? f = h.D?.staticE--; Expect.identical(f, g); }
+ // ^^^^^^^^^^^^^^
+ // [analyzer] STATIC_TYPE_WARNING.INVALID_ASSIGNMENT
+ // ^
+ // [cfe] A value of type 'E' can't be assigned to a variable of type 'F?'.
+
+ // ++e1?.v is equivalent to e1?.v += 1.
+ Expect.equals(null, ++nullC()?.v);
+ { C? c = new C(1); Expect.equals(2, ++c?.v); Expect.equals(2, c!.v); }
+
+ // ++C?.v is equivalent to C?.v += 1.
+ { C.staticInt = 1; Expect.equals(2, ++C?.staticInt); Expect.equals(2, C.staticInt); }
+ { h.C.staticInt = 1; Expect.equals(2, ++h.C?.staticInt); Expect.equals(2, h.C.staticInt); }
+
+ // The static type of ++e1?.v is the same as the static type of e1.v + 1.
+ { D? d = new D(new E()); F? f = ++d?.v; Expect.identical(d!.v, f); }
+ { D? d = new D(new E()); H? h = ++d?.v; Expect.identical(d!.v, h); }
+ // ^^^^^^
+ // [analyzer] STATIC_TYPE_WARNING.INVALID_ASSIGNMENT
+ // ^
+ // [cfe] A value of type 'G?' can't be assigned to a variable of type 'H?'.
+ { D.staticE = new E(); F? f = ++D?.staticE; Expect.identical(D.staticE, f); }
+ { h.D.staticE = new h.E(); h.F? f = ++h.D?.staticE; Expect.identical(h.D.staticE, f); }
+ { D.staticE = new E(); H? h = ++D?.staticE; Expect.identical(D.staticE, h); }
+ // ^^^^^^^^^^^^
+ // [analyzer] STATIC_TYPE_WARNING.INVALID_ASSIGNMENT
+ // ^
+ // [cfe] A value of type 'G' can't be assigned to a variable of type 'H?'.
+ { h.D.staticE = new h.E(); h.H? hh = ++h.D?.staticE; Expect.identical(h.D.staticE, hh); }
+ // ^^^^^^^^^^^^^^
+ // [analyzer] STATIC_TYPE_WARNING.INVALID_ASSIGNMENT
+ // ^
+ // [cfe] A value of type 'G' can't be assigned to a variable of type 'H?'.
+
+ // --e1?.v is equivalent to e1?.v -= 1.
+ Expect.equals(null, --nullC()?.v);
+ { C? c = new C(1); Expect.equals(0, --c?.v); Expect.equals(0, c!.v); }
+
+ // --C?.v is equivalent to C?.v -= 1.
+ { C.staticInt = 1; Expect.equals(0, --C?.staticInt); Expect.equals(0, C.staticInt); }
+ { h.C.staticInt = 1; Expect.equals(0, --h.C?.staticInt); Expect.equals(0, h.C.staticInt); }
+
+ // The static type of --e1?.v is the same as the static type of e1.v - 1.
+ { D? d = new D(new E()); F? f = --d?.v; Expect.identical(d!.v, f); }
+ { D? d = new D(new E()); H? h = --d?.v; Expect.identical(d!.v, h); }
+ // ^^^^^^
+ // [analyzer] STATIC_TYPE_WARNING.INVALID_ASSIGNMENT
+ // ^
+ // [cfe] A value of type 'G?' can't be assigned to a variable of type 'H?'.
+ { D.staticE = new E(); F? f = --D?.staticE; Expect.identical(D.staticE, f); }
+ { h.D.staticE = new h.E(); h.F? f = --h.D?.staticE; Expect.identical(h.D.staticE, f); }
+ { D.staticE = new E(); H? h = --D?.staticE; Expect.identical(D.staticE, h); }
+ // ^^^^^^^^^^^^
+ // [analyzer] STATIC_TYPE_WARNING.INVALID_ASSIGNMENT
+ // ^
+ // [cfe] A value of type 'G' can't be assigned to a variable of type 'H?'.
+ { h.D.staticE = new h.E(); h.H? hh = --h.D?.staticE; Expect.identical(h.D.staticE, hh); }
+ // ^^^^^^^^^^^^^^
+ // [analyzer] STATIC_TYPE_WARNING.INVALID_ASSIGNMENT
+ // ^
+ // [cfe] A value of type 'G' can't be assigned to a variable of type 'H?'.
+}
diff --git a/tests/language/null_aware/invocation_runtime_1_test.dart b/tests/language/null_aware/invocation_runtime_1_test.dart
new file mode 100644
index 0000000..e5e1ed7
--- /dev/null
+++ b/tests/language/null_aware/invocation_runtime_1_test.dart
@@ -0,0 +1,65 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2015, 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.
+
+// Verify semantics of the ?. operator when it is used to invoke a method.
+
+import "package:expect/expect.dart";
+import "conditional_access_helper.dart" as h;
+
+bad() {
+ Expect.fail('Should not be executed');
+}
+
+class B {}
+
+class C extends B {
+ f(callback()?) => callback!();
+ int g(int callback()) => callback();
+ static staticF(callback()) => callback();
+ static int staticG(int callback()) => callback();
+}
+
+C? nullC() => null;
+
+main() {
+ // Make sure the "none" test fails if method invocation using "?." is not
+ // implemented. This makes status files easier to maintain.
+ nullC()?.f(null);
+
+ // o?.m(...) is equivalent to ((x) => x == null ? null : x.m(...))(o).
+ Expect.equals(null, nullC()?.f(bad()));
+
+
+ // C?.m(...) is equivalent to C.m(...).
+
+
+
+ // The static type of o?.m(...) is the same as the static type of
+ // o.m(...).
+
+
+
+
+
+
+
+
+
+ // Let T be the static type of o and let y be a fresh variable of type T.
+ // Exactly the same static warnings that would be caused by y.m(...) are also
+ // generated in the case of o?.m(...).
+
+
+
+ // '?.' can't be used to access toplevel functions in libraries imported via
+ // prefix.
+
+
+ // Nor can it be used to access the toString method on the class Type.
+
+
+}
diff --git a/tests/language/null_aware/invocation_runtime_2_test.dart b/tests/language/null_aware/invocation_runtime_2_test.dart
new file mode 100644
index 0000000..70a36d1
--- /dev/null
+++ b/tests/language/null_aware/invocation_runtime_2_test.dart
@@ -0,0 +1,66 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2015, 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.
+
+// Verify semantics of the ?. operator when it is used to invoke a method.
+
+import "package:expect/expect.dart";
+import "conditional_access_helper.dart" as h;
+
+bad() {
+ Expect.fail('Should not be executed');
+}
+
+class B {}
+
+class C extends B {
+ f(callback()?) => callback!();
+ int g(int callback()) => callback();
+ static staticF(callback()) => callback();
+ static int staticG(int callback()) => callback();
+}
+
+C? nullC() => null;
+
+main() {
+ // Make sure the "none" test fails if method invocation using "?." is not
+ // implemented. This makes status files easier to maintain.
+ nullC()?.f(null);
+
+ // o?.m(...) is equivalent to ((x) => x == null ? null : x.m(...))(o).
+
+ C? c = C();
+ Expect.equals(1, c?.f(() => 1));
+
+ // C?.m(...) is equivalent to C.m(...).
+
+
+
+ // The static type of o?.m(...) is the same as the static type of
+ // o.m(...).
+
+
+
+
+
+
+
+
+
+ // Let T be the static type of o and let y be a fresh variable of type T.
+ // Exactly the same static warnings that would be caused by y.m(...) are also
+ // generated in the case of o?.m(...).
+
+
+
+ // '?.' can't be used to access toplevel functions in libraries imported via
+ // prefix.
+
+
+ // Nor can it be used to access the toString method on the class Type.
+
+
+}
diff --git a/tests/language/null_aware/invocation_runtime_3_test.dart b/tests/language/null_aware/invocation_runtime_3_test.dart
new file mode 100644
index 0000000..13f50a1
--- /dev/null
+++ b/tests/language/null_aware/invocation_runtime_3_test.dart
@@ -0,0 +1,65 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2015, 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.
+
+// Verify semantics of the ?. operator when it is used to invoke a method.
+
+import "package:expect/expect.dart";
+import "conditional_access_helper.dart" as h;
+
+bad() {
+ Expect.fail('Should not be executed');
+}
+
+class B {}
+
+class C extends B {
+ f(callback()?) => callback!();
+ int g(int callback()) => callback();
+ static staticF(callback()) => callback();
+ static int staticG(int callback()) => callback();
+}
+
+C? nullC() => null;
+
+main() {
+ // Make sure the "none" test fails if method invocation using "?." is not
+ // implemented. This makes status files easier to maintain.
+ nullC()?.f(null);
+
+ // o?.m(...) is equivalent to ((x) => x == null ? null : x.m(...))(o).
+
+
+
+ // C?.m(...) is equivalent to C.m(...).
+ Expect.equals(1, C?.staticF(() => 1));
+
+
+ // The static type of o?.m(...) is the same as the static type of
+ // o.m(...).
+
+
+
+
+
+
+
+
+
+ // Let T be the static type of o and let y be a fresh variable of type T.
+ // Exactly the same static warnings that would be caused by y.m(...) are also
+ // generated in the case of o?.m(...).
+
+
+
+ // '?.' can't be used to access toplevel functions in libraries imported via
+ // prefix.
+
+
+ // Nor can it be used to access the toString method on the class Type.
+
+
+}
diff --git a/tests/language/null_aware/invocation_runtime_4_test.dart b/tests/language/null_aware/invocation_runtime_4_test.dart
new file mode 100644
index 0000000..ea6b6b1
--- /dev/null
+++ b/tests/language/null_aware/invocation_runtime_4_test.dart
@@ -0,0 +1,65 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2015, 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.
+
+// Verify semantics of the ?. operator when it is used to invoke a method.
+
+import "package:expect/expect.dart";
+import "conditional_access_helper.dart" as h;
+
+bad() {
+ Expect.fail('Should not be executed');
+}
+
+class B {}
+
+class C extends B {
+ f(callback()?) => callback!();
+ int g(int callback()) => callback();
+ static staticF(callback()) => callback();
+ static int staticG(int callback()) => callback();
+}
+
+C? nullC() => null;
+
+main() {
+ // Make sure the "none" test fails if method invocation using "?." is not
+ // implemented. This makes status files easier to maintain.
+ nullC()?.f(null);
+
+ // o?.m(...) is equivalent to ((x) => x == null ? null : x.m(...))(o).
+
+
+
+ // C?.m(...) is equivalent to C.m(...).
+
+ Expect.equals(1, h.C?.staticF(() => 1));
+
+ // The static type of o?.m(...) is the same as the static type of
+ // o.m(...).
+
+
+
+
+
+
+
+
+
+ // Let T be the static type of o and let y be a fresh variable of type T.
+ // Exactly the same static warnings that would be caused by y.m(...) are also
+ // generated in the case of o?.m(...).
+
+
+
+ // '?.' can't be used to access toplevel functions in libraries imported via
+ // prefix.
+
+
+ // Nor can it be used to access the toString method on the class Type.
+
+
+}
diff --git a/tests/language/null_aware/invocation_runtime_5_test.dart b/tests/language/null_aware/invocation_runtime_5_test.dart
new file mode 100644
index 0000000..b95b1e5
--- /dev/null
+++ b/tests/language/null_aware/invocation_runtime_5_test.dart
@@ -0,0 +1,65 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2015, 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.
+
+// Verify semantics of the ?. operator when it is used to invoke a method.
+
+import "package:expect/expect.dart";
+import "conditional_access_helper.dart" as h;
+
+bad() {
+ Expect.fail('Should not be executed');
+}
+
+class B {}
+
+class C extends B {
+ f(callback()?) => callback!();
+ int g(int callback()) => callback();
+ static staticF(callback()) => callback();
+ static int staticG(int callback()) => callback();
+}
+
+C? nullC() => null;
+
+main() {
+ // Make sure the "none" test fails if method invocation using "?." is not
+ // implemented. This makes status files easier to maintain.
+ nullC()?.f(null);
+
+ // o?.m(...) is equivalent to ((x) => x == null ? null : x.m(...))(o).
+
+
+
+ // C?.m(...) is equivalent to C.m(...).
+
+
+
+ // The static type of o?.m(...) is the same as the static type of
+ // o.m(...).
+ { int? i = nullC()?.g(bad()); Expect.equals(null, i); }
+
+
+
+
+
+
+
+
+ // Let T be the static type of o and let y be a fresh variable of type T.
+ // Exactly the same static warnings that would be caused by y.m(...) are also
+ // generated in the case of o?.m(...).
+
+
+
+ // '?.' can't be used to access toplevel functions in libraries imported via
+ // prefix.
+
+
+ // Nor can it be used to access the toString method on the class Type.
+
+
+}
diff --git a/tests/language/null_aware/invocation_runtime_6_test.dart b/tests/language/null_aware/invocation_runtime_6_test.dart
new file mode 100644
index 0000000..23559d8
--- /dev/null
+++ b/tests/language/null_aware/invocation_runtime_6_test.dart
@@ -0,0 +1,66 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2015, 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.
+
+// Verify semantics of the ?. operator when it is used to invoke a method.
+
+import "package:expect/expect.dart";
+import "conditional_access_helper.dart" as h;
+
+bad() {
+ Expect.fail('Should not be executed');
+}
+
+class B {}
+
+class C extends B {
+ f(callback()?) => callback!();
+ int g(int callback()) => callback();
+ static staticF(callback()) => callback();
+ static int staticG(int callback()) => callback();
+}
+
+C? nullC() => null;
+
+main() {
+ // Make sure the "none" test fails if method invocation using "?." is not
+ // implemented. This makes status files easier to maintain.
+ nullC()?.f(null);
+
+ // o?.m(...) is equivalent to ((x) => x == null ? null : x.m(...))(o).
+
+
+
+ // C?.m(...) is equivalent to C.m(...).
+
+
+
+ // The static type of o?.m(...) is the same as the static type of
+ // o.m(...).
+
+ C? c = new C();
+ { int? i = c?.g(() => 1); Expect.equals(1, i); }
+
+
+
+
+
+
+
+ // Let T be the static type of o and let y be a fresh variable of type T.
+ // Exactly the same static warnings that would be caused by y.m(...) are also
+ // generated in the case of o?.m(...).
+
+
+
+ // '?.' can't be used to access toplevel functions in libraries imported via
+ // prefix.
+
+
+ // Nor can it be used to access the toString method on the class Type.
+
+
+}
diff --git a/tests/language/null_aware/invocation_runtime_7_test.dart b/tests/language/null_aware/invocation_runtime_7_test.dart
new file mode 100644
index 0000000..8bed5f0
--- /dev/null
+++ b/tests/language/null_aware/invocation_runtime_7_test.dart
@@ -0,0 +1,65 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2015, 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.
+
+// Verify semantics of the ?. operator when it is used to invoke a method.
+
+import "package:expect/expect.dart";
+import "conditional_access_helper.dart" as h;
+
+bad() {
+ Expect.fail('Should not be executed');
+}
+
+class B {}
+
+class C extends B {
+ f(callback()?) => callback!();
+ int g(int callback()) => callback();
+ static staticF(callback()) => callback();
+ static int staticG(int callback()) => callback();
+}
+
+C? nullC() => null;
+
+main() {
+ // Make sure the "none" test fails if method invocation using "?." is not
+ // implemented. This makes status files easier to maintain.
+ nullC()?.f(null);
+
+ // o?.m(...) is equivalent to ((x) => x == null ? null : x.m(...))(o).
+
+
+
+ // C?.m(...) is equivalent to C.m(...).
+
+
+
+ // The static type of o?.m(...) is the same as the static type of
+ // o.m(...).
+
+
+
+
+ { int? i = C?.staticG(() => 1); Expect.equals(1, i); }
+
+
+
+
+ // Let T be the static type of o and let y be a fresh variable of type T.
+ // Exactly the same static warnings that would be caused by y.m(...) are also
+ // generated in the case of o?.m(...).
+
+
+
+ // '?.' can't be used to access toplevel functions in libraries imported via
+ // prefix.
+
+
+ // Nor can it be used to access the toString method on the class Type.
+
+
+}
diff --git a/tests/language/null_aware/invocation_runtime_8_test.dart b/tests/language/null_aware/invocation_runtime_8_test.dart
new file mode 100644
index 0000000..62f8c4a
--- /dev/null
+++ b/tests/language/null_aware/invocation_runtime_8_test.dart
@@ -0,0 +1,65 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2015, 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.
+
+// Verify semantics of the ?. operator when it is used to invoke a method.
+
+import "package:expect/expect.dart";
+import "conditional_access_helper.dart" as h;
+
+bad() {
+ Expect.fail('Should not be executed');
+}
+
+class B {}
+
+class C extends B {
+ f(callback()?) => callback!();
+ int g(int callback()) => callback();
+ static staticF(callback()) => callback();
+ static int staticG(int callback()) => callback();
+}
+
+C? nullC() => null;
+
+main() {
+ // Make sure the "none" test fails if method invocation using "?." is not
+ // implemented. This makes status files easier to maintain.
+ nullC()?.f(null);
+
+ // o?.m(...) is equivalent to ((x) => x == null ? null : x.m(...))(o).
+
+
+
+ // C?.m(...) is equivalent to C.m(...).
+
+
+
+ // The static type of o?.m(...) is the same as the static type of
+ // o.m(...).
+
+
+
+
+
+ { int? i = h.C?.staticG(() => 1); Expect.equals(1, i); }
+
+
+
+ // Let T be the static type of o and let y be a fresh variable of type T.
+ // Exactly the same static warnings that would be caused by y.m(...) are also
+ // generated in the case of o?.m(...).
+
+
+
+ // '?.' can't be used to access toplevel functions in libraries imported via
+ // prefix.
+
+
+ // Nor can it be used to access the toString method on the class Type.
+
+
+}
diff --git a/tests/language/null_aware/invocation_runtime_test.dart b/tests/language/null_aware/invocation_runtime_test.dart
new file mode 100644
index 0000000..8956053
--- /dev/null
+++ b/tests/language/null_aware/invocation_runtime_test.dart
@@ -0,0 +1,65 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2015, 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.
+
+// Verify semantics of the ?. operator when it is used to invoke a method.
+
+import "package:expect/expect.dart";
+import "conditional_access_helper.dart" as h;
+
+bad() {
+ Expect.fail('Should not be executed');
+}
+
+class B {}
+
+class C extends B {
+ f(callback()?) => callback!();
+ int g(int callback()) => callback();
+ static staticF(callback()) => callback();
+ static int staticG(int callback()) => callback();
+}
+
+C? nullC() => null;
+
+main() {
+ // Make sure the "none" test fails if method invocation using "?." is not
+ // implemented. This makes status files easier to maintain.
+ nullC()?.f(null);
+
+ // o?.m(...) is equivalent to ((x) => x == null ? null : x.m(...))(o).
+
+
+
+ // C?.m(...) is equivalent to C.m(...).
+
+
+
+ // The static type of o?.m(...) is the same as the static type of
+ // o.m(...).
+
+
+
+
+
+
+
+
+
+ // Let T be the static type of o and let y be a fresh variable of type T.
+ // Exactly the same static warnings that would be caused by y.m(...) are also
+ // generated in the case of o?.m(...).
+
+
+
+ // '?.' can't be used to access toplevel functions in libraries imported via
+ // prefix.
+
+
+ // Nor can it be used to access the toString method on the class Type.
+
+
+}
diff --git a/tests/language/null_aware/invocation_test.dart b/tests/language/null_aware/invocation_test.dart
new file mode 100644
index 0000000..4131cdf
--- /dev/null
+++ b/tests/language/null_aware/invocation_test.dart
@@ -0,0 +1,94 @@
+// Copyright (c) 2015, 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.
+
+// Verify semantics of the ?. operator when it is used to invoke a method.
+
+import "package:expect/expect.dart";
+import "conditional_access_helper.dart" as h;
+
+bad() {
+ Expect.fail('Should not be executed');
+}
+
+class B {}
+
+class C extends B {
+ f(callback()?) => callback!();
+ int? g(int? callback()) => callback();
+ static staticF(callback()) => callback();
+ static int? staticG(int? callback()) => callback();
+}
+
+C? nullC() => null;
+
+main() {
+ C? c = C();
+
+ // Make sure the "none" test fails if method invocation using "?." is not
+ // implemented. This makes status files easier to maintain.
+ nullC()?.f(null);
+
+ // o?.m(...) is equivalent to ((x) => x == null ? null : x.m(...))(o).
+ Expect.equals(null, nullC()?.f(bad()));
+ Expect.equals(1, c?.f(() => 1));
+
+ // C?.m(...) is equivalent to C.m(...).
+ Expect.equals(1, C?.staticF(() => 1));
+ Expect.equals(1, h.C?.staticF(() => 1));
+
+ // The static type of o?.m(...) is the same as the static type of
+ // o.m(...).
+ { int? i = nullC()?.g(bad()); Expect.equals(null, i); }
+ { int? i = c?.g(() => 1); Expect.equals(1, i); }
+ { String? s = nullC()?.g(bad()); Expect.equals(null, s); }
+ // ^^^^^^^^^^^^^^^^^
+ // [analyzer] STATIC_TYPE_WARNING.INVALID_ASSIGNMENT
+ // [cfe] A value of type 'int?' can't be assigned to a variable of type 'String?'.
+ { String? s = c?.g(() => null); Expect.equals(null, s); }
+ // ^^^^^^^^^^^^^^^^
+ // [analyzer] STATIC_TYPE_WARNING.INVALID_ASSIGNMENT
+ // ^
+ // [cfe] A value of type 'int?' can't be assigned to a variable of type 'String?'.
+ { int? i = C?.staticG(() => 1); Expect.equals(1, i); }
+ { int? i = h.C?.staticG(() => 1); Expect.equals(1, i); }
+ { String? s = C?.staticG(() => null); Expect.equals(null, s); }
+ // ^^^^^^^^^^^^^^^^^^^^^^
+ // [analyzer] STATIC_TYPE_WARNING.INVALID_ASSIGNMENT
+ // ^
+ // [cfe] A value of type 'int?' can't be assigned to a variable of type 'String?'.
+ { String? s = h.C?.staticG(() => null); Expect.equals(null, s); }
+ // ^^^^^^^^^^^^^^^^^^^^^^^^
+ // [analyzer] STATIC_TYPE_WARNING.INVALID_ASSIGNMENT
+ // ^
+ // [cfe] A value of type 'int?' can't be assigned to a variable of type 'String?'.
+
+ // Let T be the static type of o and let y be a fresh variable of type T.
+ // Exactly the same static warnings that would be caused by y.m(...) are also
+ // generated in the case of o?.m(...).
+ { B? b = new C(); Expect.equals(1, b?.f(() => 1)); }
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_METHOD
+ // [cfe] The method 'f' isn't defined for the class 'B'.
+ { int? i = 1; Expect.equals(null, nullC()?.f(i)); }
+ // ^
+ // [analyzer] STATIC_WARNING.ARGUMENT_TYPE_NOT_ASSIGNABLE
+ // [cfe] The argument type 'int?' can't be assigned to the parameter type 'dynamic Function()?'.
+
+ // '?.' can't be used to access toplevel functions in libraries imported via
+ // prefix.
+ h?.topLevelFunction();
+//^
+// [analyzer] COMPILE_TIME_ERROR.PREFIX_IDENTIFIER_NOT_FOLLOWED_BY_DOT
+// [cfe] A prefix can't be used with null-aware operators.
+
+ // Nor can it be used to access the toString method on the class Type.
+ Expect.throwsNoSuchMethodError(() => C?.toString());
+ // ^^^^^^^^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_METHOD
+ // [cfe] Method not found: 'C.toString'.
+ Expect.throwsNoSuchMethodError(() => h.C?.toString());
+ // ^^^^^^^^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_METHOD
+ // [cfe] Method not found: 'C.toString'.
+}
diff --git a/tests/language/null_aware/opt_test.dart b/tests/language/null_aware/opt_test.dart
new file mode 100644
index 0000000..c5bb8a7
--- /dev/null
+++ b/tests/language/null_aware/opt_test.dart
@@ -0,0 +1,103 @@
+// Copyright (c) 2015, 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.
+//
+// VMOptions=--optimization_counter_threshold=5
+//
+// Basic null-aware operator test that invokes the optimizing compiler.
+
+import "package:expect/expect.dart";
+
+class C {
+ C(this.f);
+ var f;
+ m(a) => a;
+}
+
+bomb() {
+ Expect.fail('Should not be executed');
+ return 100;
+}
+
+getNull() => null;
+
+test() {
+ var c;
+ C? d = new C(5);
+ Expect.equals(null, c?.m(bomb()));
+ Expect.equals(null, getNull()?.anything(bomb()));
+ Expect.equals(1, d?.m(1));
+
+ C? c2 = new C(1);
+ Expect.equals(1, c2?.f);
+ Expect.equals(null, c?.v);
+ Expect.equals(10, c ?? 10);
+ Expect.equals(d, d ?? bomb());
+ List<List<int>>? list = [[3]];
+ Expect.equals(
+ 3,
+ list?.expand((i) => i).toList()[0]);
+ Expect.equals(null, (null as dynamic)?.expand((i) => i)?.toList());
+
+ var e;
+ // The assignment to e is not executed since d != null.
+ d ??= e ??= new C(100);
+ Expect.equals(null, e);
+ e ??= new C(100);
+ Expect.equals(100, e?.f);
+ e?.f ??= 200;
+ Expect.equals(100, e?.f);
+
+ e.f = null;
+ e?.f ??= 200;
+ Expect.equals(200, e?.f);
+
+ c?.f ??= 400;
+ Expect.equals(null, c?.f);
+ Expect.equals(null, c?.f++);
+ e?.f++;
+ Expect.equals(201, e.f);
+
+ int? five = 5;
+ var x = five ?? bomb();
+}
+
+// Check that instructions without result do not crash.
+test2() {
+ var c;
+ c?.v;
+ c?.m(bomb());
+}
+
+class Bar {
+ String? s;
+}
+
+class Foo {
+ Bar? _bar;
+ String? str;
+
+ Foo(this._bar) : str = _bar?.s;
+}
+
+// Check that ?? isn't incorrectly optimized as non-nullable
+// (DDC regression test)
+test3() {
+ List? n = null;
+ var func = n?.add;
+ var result = func ?? 1;
+ Expect.equals(result, 1);
+
+ String? x = null;
+ Expect.equals(x?.split(',') ?? 'hello', 'hello');
+}
+
+main() {
+ for (int i = 0; i < 10; i++) {
+ test();
+ test2();
+ test3();
+ }
+
+ Expect.equals(null, new Foo(new Bar()).str);
+}
diff --git a/tests/language/type_object/constant_type_literal_runtime_test.dart b/tests/language/type_object/constant_type_literal_runtime_test.dart
new file mode 100644
index 0000000..30fdd50
--- /dev/null
+++ b/tests/language/type_object/constant_type_literal_runtime_test.dart
@@ -0,0 +1,20 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2013, 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.
+
+// Test for type literals as compile-time constants.
+
+class C<T> {
+ void m() {
+ const List lst = const [
+
+ ];
+ }
+}
+
+main() {
+ new C().m();
+}
diff --git a/tests/language/type_object/constant_type_literal_test.dart b/tests/language/type_object/constant_type_literal_test.dart
new file mode 100644
index 0000000..91e07f4
--- /dev/null
+++ b/tests/language/type_object/constant_type_literal_test.dart
@@ -0,0 +1,22 @@
+// Copyright (c) 2013, 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.
+
+// Test for type literals as compile-time constants.
+
+class C<T> {
+ void m() {
+ const List lst = const [
+ T
+// ^
+// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
+// [cfe] Type variables can't be used as constants.
+// ^
+// [analyzer] COMPILE_TIME_ERROR.NON_CONSTANT_LIST_ELEMENT
+ ];
+ }
+}
+
+main() {
+ new C().m();
+}
diff --git a/tests/language/type_object/first_class_types_constants_test.dart b/tests/language/type_object/first_class_types_constants_test.dart
new file mode 100644
index 0000000..f0447f7
--- /dev/null
+++ b/tests/language/type_object/first_class_types_constants_test.dart
@@ -0,0 +1,30 @@
+// Copyright (c) 2012, 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:expect/expect.dart";
+
+class C<T> {
+ final T t;
+ const C(this.t);
+}
+
+typedef int Fun(bool, String);
+
+const c0 = C;
+const c1 = const C<Type>(C);
+const c2 = Fun;
+const c3 = const C<Type>(Fun);
+
+main() {
+ Expect.identical(C, C);
+ Expect.identical(C, c0);
+ Expect.identical(c1, c1);
+ Expect.notEquals(c0, c1);
+ Expect.notEquals(c1, c2);
+ Expect.identical(c1.t, c0);
+ Expect.notEquals(C, Fun);
+ Expect.identical(Fun, Fun);
+ Expect.identical(Fun, c2);
+ Expect.identical(c3.t, c2);
+}
diff --git a/tests/language/type_object/first_class_types_lib1.dart b/tests/language/type_object/first_class_types_lib1.dart
new file mode 100644
index 0000000..baadc66
--- /dev/null
+++ b/tests/language/type_object/first_class_types_lib1.dart
@@ -0,0 +1,7 @@
+// Copyright (c) 2012, 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.
+
+library lib1;
+
+class A {}
diff --git a/tests/language/type_object/first_class_types_lib2.dart b/tests/language/type_object/first_class_types_lib2.dart
new file mode 100644
index 0000000..de943b7
--- /dev/null
+++ b/tests/language/type_object/first_class_types_lib2.dart
@@ -0,0 +1,7 @@
+// Copyright (c) 2012, 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.
+
+library lib2;
+
+class A {}
diff --git a/tests/language/type_object/first_class_types_libraries_test.dart b/tests/language/type_object/first_class_types_libraries_test.dart
new file mode 100644
index 0000000..ad5f552
--- /dev/null
+++ b/tests/language/type_object/first_class_types_libraries_test.dart
@@ -0,0 +1,25 @@
+// Copyright (c) 2012, 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.
+
+library firstClassLibrariestest;
+
+import "package:expect/expect.dart";
+import 'first_class_types_lib1.dart' as lib1;
+import 'first_class_types_lib2.dart' as lib2;
+
+class C<X> {}
+
+sameType(a, b) {
+ Expect.equals(a.runtimeType, b.runtimeType);
+}
+
+differentType(a, b) {
+ Expect.notEquals(a.runtimeType, b.runtimeType);
+}
+
+main() {
+ sameType(new lib1.A(), new lib1.A());
+ differentType(new lib1.A(), new lib2.A());
+ differentType(new C<lib1.A>(), new C<lib2.A>());
+}
diff --git a/tests/language/type_object/first_class_types_literals_runtime_1_test.dart b/tests/language/type_object/first_class_types_literals_runtime_1_test.dart
new file mode 100644
index 0000000..b415e99
--- /dev/null
+++ b/tests/language/type_object/first_class_types_literals_runtime_1_test.dart
@@ -0,0 +1,67 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2012, 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:expect/expect.dart";
+
+class C<T, U, V> {}
+
+class D {}
+
+typedef int Foo(bool b);
+
+sameType(a, b) {
+ Expect.equals(a.runtimeType, b.runtimeType);
+}
+
+main() {
+ void foo(a) {}
+
+ // Test that literals can be used in different contexts.
+ [int];
+ ([int]);
+ foo([int]);
+ [int].length;
+ ({1: int});
+ foo({1: int});
+ ({1: int}).keys;
+
+ // Test type literals.
+ Expect.equals(int, int);
+ Expect.notEquals(int, num);
+ Expect.equals(Foo, Foo);
+ Expect.equals(dynamic, dynamic);
+
+ // Test that class literals return instances of Type.
+ Expect.isTrue((D).runtimeType is Type);
+ Expect.isTrue((dynamic).runtimeType is Type);
+
+ // Test that types from runtimeType and literals agree.
+ Expect.equals(int, 1.runtimeType);
+ Expect.equals(String, 'hest'.runtimeType);
+ Expect.equals(double, (0.5).runtimeType);
+ Expect.equals(bool, true.runtimeType);
+ Expect.equals(Object, Object().runtimeType);
+ Expect.equals(C, new C().runtimeType);
+
+
+ // runtimeType on type is idempotent.
+ Expect.equals((D).runtimeType, (D).runtimeType.runtimeType);
+
+ // Test that operator calls on class literals go to Type.
+
+
+
+
+
+
+
+
+
+
+
+ Expect.equals((dynamic).toString(), 'dynamic');
+}
diff --git a/tests/language/type_object/first_class_types_literals_runtime_2_test.dart b/tests/language/type_object/first_class_types_literals_runtime_2_test.dart
new file mode 100644
index 0000000..e5c8442
--- /dev/null
+++ b/tests/language/type_object/first_class_types_literals_runtime_2_test.dart
@@ -0,0 +1,67 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2012, 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:expect/expect.dart";
+
+class C<T, U, V> {}
+
+class D {}
+
+typedef int Foo(bool b);
+
+sameType(a, b) {
+ Expect.equals(a.runtimeType, b.runtimeType);
+}
+
+main() {
+ void foo(a) {}
+
+ // Test that literals can be used in different contexts.
+ [int];
+ ([int]);
+ foo([int]);
+ [int].length;
+ ({1: int});
+ foo({1: int});
+ ({1: int}).keys;
+
+ // Test type literals.
+ Expect.equals(int, int);
+ Expect.notEquals(int, num);
+ Expect.equals(Foo, Foo);
+ Expect.equals(dynamic, dynamic);
+
+ // Test that class literals return instances of Type.
+ Expect.isTrue((D).runtimeType is Type);
+ Expect.isTrue((dynamic).runtimeType is Type);
+
+ // Test that types from runtimeType and literals agree.
+ Expect.equals(int, 1.runtimeType);
+ Expect.equals(String, 'hest'.runtimeType);
+ Expect.equals(double, (0.5).runtimeType);
+ Expect.equals(bool, true.runtimeType);
+ Expect.equals(Object, Object().runtimeType);
+
+ Expect.equals(D, new D().runtimeType);
+
+ // runtimeType on type is idempotent.
+ Expect.equals((D).runtimeType, (D).runtimeType.runtimeType);
+
+ // Test that operator calls on class literals go to Type.
+
+
+
+
+
+
+
+
+
+
+
+ Expect.equals((dynamic).toString(), 'dynamic');
+}
diff --git a/tests/language/type_object/first_class_types_literals_runtime_test.dart b/tests/language/type_object/first_class_types_literals_runtime_test.dart
new file mode 100644
index 0000000..3df083a
--- /dev/null
+++ b/tests/language/type_object/first_class_types_literals_runtime_test.dart
@@ -0,0 +1,67 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2012, 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:expect/expect.dart";
+
+class C<T, U, V> {}
+
+class D {}
+
+typedef int Foo(bool b);
+
+sameType(a, b) {
+ Expect.equals(a.runtimeType, b.runtimeType);
+}
+
+main() {
+ void foo(a) {}
+
+ // Test that literals can be used in different contexts.
+ [int];
+ ([int]);
+ foo([int]);
+ [int].length;
+ ({1: int});
+ foo({1: int});
+ ({1: int}).keys;
+
+ // Test type literals.
+ Expect.equals(int, int);
+ Expect.notEquals(int, num);
+ Expect.equals(Foo, Foo);
+ Expect.equals(dynamic, dynamic);
+
+ // Test that class literals return instances of Type.
+ Expect.isTrue((D).runtimeType is Type);
+ Expect.isTrue((dynamic).runtimeType is Type);
+
+ // Test that types from runtimeType and literals agree.
+ Expect.equals(int, 1.runtimeType);
+ Expect.equals(String, 'hest'.runtimeType);
+ Expect.equals(double, (0.5).runtimeType);
+ Expect.equals(bool, true.runtimeType);
+ Expect.equals(Object, Object().runtimeType);
+
+
+
+ // runtimeType on type is idempotent.
+ Expect.equals((D).runtimeType, (D).runtimeType.runtimeType);
+
+ // Test that operator calls on class literals go to Type.
+
+
+
+
+
+
+
+
+
+
+
+ Expect.equals((dynamic).toString(), 'dynamic');
+}
diff --git a/tests/language/type_object/first_class_types_literals_test.dart b/tests/language/type_object/first_class_types_literals_test.dart
new file mode 100644
index 0000000..aa02a4d
--- /dev/null
+++ b/tests/language/type_object/first_class_types_literals_test.dart
@@ -0,0 +1,100 @@
+// Copyright (c) 2012, 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:expect/expect.dart";
+
+class C<T, U, V> {}
+
+class D {}
+
+typedef int Foo(bool b);
+
+sameType(a, b) {
+ Expect.equals(a.runtimeType, b.runtimeType);
+}
+
+main() {
+ void foo(a) {}
+
+ // Test that literals can be used in different contexts.
+ [int];
+ ([int]);
+ foo([int]);
+ [int].length;
+ ({1: int});
+ foo({1: int});
+ ({1: int}).keys;
+
+ // Test type literals.
+ Expect.equals(int, int);
+ Expect.notEquals(int, num);
+ Expect.equals(Foo, Foo);
+ Expect.equals(dynamic, dynamic);
+
+ // Test that class literals return instances of Type.
+ Expect.isTrue((D).runtimeType is Type);
+ Expect.isTrue((dynamic).runtimeType is Type);
+
+ // Test that types from runtimeType and literals agree.
+ Expect.equals(int, 1.runtimeType);
+ Expect.equals(String, 'hest'.runtimeType);
+ Expect.equals(double, (0.5).runtimeType);
+ Expect.equals(bool, true.runtimeType);
+ Expect.equals(Object, Object().runtimeType);
+ Expect.equals(C, new C().runtimeType);
+ Expect.equals(D, new D().runtimeType);
+
+ // runtimeType on type is idempotent.
+ Expect.equals((D).runtimeType, (D).runtimeType.runtimeType);
+
+ // Test that operator calls on class literals go to Type.
+ Expect.throwsNoSuchMethodError(() => C = 1);
+ // ^
+ // [analyzer] STATIC_WARNING.ASSIGNMENT_TO_TYPE
+ // [cfe] Can't assign to a type literal.
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.INVALID_ASSIGNMENT
+ Expect.throwsNoSuchMethodError(() => C++);
+ // ^
+ // [analyzer] STATIC_WARNING.ASSIGNMENT_TO_TYPE
+ // [cfe] Can't assign to a type literal.
+ // ^^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_OPERATOR
+ Expect.throwsNoSuchMethodError(() => C + 1);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_OPERATOR
+ // [cfe] The operator '+' isn't defined for the class 'Type'.
+ Expect.throwsNoSuchMethodError(() => C[2]);
+ // ^^^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_OPERATOR
+ // [cfe] The operator '[]' isn't defined for the class 'Type'.
+ Expect.throwsNoSuchMethodError(() => C[2] = 'hest');
+ // ^^^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_OPERATOR
+ // [cfe] The operator '[]=' isn't defined for the class 'Type'.
+ Expect.throwsNoSuchMethodError(() => dynamic = 1);
+ // ^
+ // [cfe] Can't assign to a type literal.
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.INVALID_ASSIGNMENT
+ Expect.throwsNoSuchMethodError(() => dynamic++);
+ // ^
+ // [cfe] Can't assign to a type literal.
+ // ^^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_OPERATOR
+ Expect.throwsNoSuchMethodError(() => dynamic + 1);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_OPERATOR
+ // [cfe] The operator '+' isn't defined for the class 'Type'.
+ Expect.throwsNoSuchMethodError(() => dynamic[2]);
+ // ^^^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_OPERATOR
+ // [cfe] The operator '[]' isn't defined for the class 'Type'.
+ Expect.throwsNoSuchMethodError(() => dynamic[2] = 'hest');
+ // ^^^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_OPERATOR
+ // [cfe] The operator '[]=' isn't defined for the class 'Type'.
+
+ Expect.equals((dynamic).toString(), 'dynamic');
+}
diff --git a/tests/language/type_object/first_class_types_test.dart b/tests/language/type_object/first_class_types_test.dart
new file mode 100644
index 0000000..d2eca28
--- /dev/null
+++ b/tests/language/type_object/first_class_types_test.dart
@@ -0,0 +1,44 @@
+// Copyright (c) 2012, 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:expect/expect.dart";
+
+class C<T> {}
+
+sameType(a, b) {
+ Expect.equals(a.runtimeType, b.runtimeType);
+}
+
+differentType(a, b) {
+ Expect.isFalse(a.runtimeType == b.runtimeType);
+}
+
+main() {
+ // Test types obtained by calling runtimeType.
+ var v1 = new C<int>();
+ var v2 = new C<int>();
+ sameType(v1, v2);
+
+ var v3 = new C<num>();
+ differentType(v1, v3);
+
+ var i = 1;
+ var s = 'string';
+ var d = 3.14;
+ var b = true;
+ sameType(2, i);
+ sameType('hest', s);
+ sameType(1.2, d);
+ sameType(false, b);
+
+ List l = [1, 2, 3];
+ Map m = {'a': 1, 'b': 2};
+ sameType([], l);
+ sameType({}, m);
+
+ // Test parameterized lists.
+ sameType(<int>[], <int>[]);
+ differentType(<int>[], <num>[]);
+ differentType(<int>[], []);
+}
diff --git a/tests/language/type_object/literal_canonicalization_test.dart b/tests/language/type_object/literal_canonicalization_test.dart
new file mode 100644
index 0000000..790f1b6
--- /dev/null
+++ b/tests/language/type_object/literal_canonicalization_test.dart
@@ -0,0 +1,26 @@
+// 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:expect/expect.dart";
+
+class Foo {}
+
+class Box<T> {
+ Type get typeArg => T;
+}
+
+/// A typedef that defines a non-generic function type.
+typedef int Func1(bool b);
+
+/// Semantically identical to [Func], but using the Dart 2 syntax.
+typedef Func2 = int Function(bool);
+
+main() {
+ // Literals are canonicalized.
+ Expect.identical(Foo, Foo);
+ Expect.identical(Box, Box);
+ Expect.identical(new Box<Foo>().typeArg, new Box<Foo>().typeArg);
+ Expect.identical(Func1, Func1);
+ Expect.identical(Func2, Func2);
+}
diff --git a/tests/language/type_object/literal_prefix_call_test.dart b/tests/language/type_object/literal_prefix_call_test.dart
new file mode 100644
index 0000000..6467fdb
--- /dev/null
+++ b/tests/language/type_object/literal_prefix_call_test.dart
@@ -0,0 +1,10 @@
+// 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:expect/expect.dart";
+import 'dart:core' as core;
+
+main() {
+ core.Map();
+}
diff --git a/tests/language/type_object/literal_type_literal_test.dart b/tests/language/type_object/literal_type_literal_test.dart
new file mode 100644
index 0000000..92da452
--- /dev/null
+++ b/tests/language/type_object/literal_type_literal_test.dart
@@ -0,0 +1,119 @@
+// Copyright (c) 2016, 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:expect/expect.dart";
+
+import 'literal_type_literal_test.dart' as prefix;
+
+// TODO(rnystrom): This test has a lot of overlap with some other language
+// tests, but those are sort of all over the place so I thought it useful to
+// test all of the relevant bits in one place here.
+
+class Foo {
+ static var property;
+ static method() => "result";
+}
+
+class Box<T> {
+ Type get typeArg => T;
+}
+
+class G<A, B> {}
+
+/// A typedef that defines a non-generic function type.
+typedef int Func(bool b);
+
+/// Semantically identical to [Func], but using the Dart 2 syntax.
+typedef Func2 = int Function(bool);
+
+/// A typedef that defines a generic function type.
+typedef GenericFunc = int Function<T>(T);
+
+/// A typedef with a type paramter that defines a non-generic function type.
+typedef int GenericTypedef<T>(T t);
+
+/// Semantically identical to [GenericTypedef], but using the Dart 2 syntax.
+typedef GenericTypedef2<T> = int Function(T);
+
+/// A typedef with a type paramter that defines a generic function type.
+typedef GenericTypedefAndFunc<S> = S Function<T>(T);
+
+main() {
+ // Primitive types.
+ testType(Object, "Object");
+ testType(Null, "Null");
+ testType(bool, "bool");
+ testType(double, "double");
+ testType(int, "int");
+ testType(num, "num");
+ testType(String, "String");
+
+ // Class types.
+ testType(Foo, "Foo");
+
+ // Generic classes.
+ testType(Box, ["Box", "Box<dynamic>"]);
+ testType(new Box<Foo>().typeArg, "Foo");
+ testType(new Box<dynamic>().typeArg, "dynamic");
+ testType(new Box<Box<Foo>>().typeArg, "Box<Foo>");
+ testType(G, ["G", "G<dynamic, dynamic>"]);
+ testType(new Box<G<int, String>>().typeArg, "G<int, String>");
+
+ // Typedef.
+ testType(Func, ["Func", "(bool) => int"]);
+ testType(Func2, ["Func2", "(bool) => int"]);
+ testType(GenericTypedef,
+ ["GenericTypedef", "GenericTypedef<dynamic>", "(dynamic) => int"]);
+ testType(GenericTypedef2,
+ ["GenericTypedef2", "GenericTypedef2<dynamic>", "(dynamic) => int"]);
+ testType(new Box<GenericTypedef<int>>().typeArg,
+ ["GenericTypedef<int>", "(int) => int"]);
+ testType(GenericFunc, ["GenericFunc", RegExp(r'<(\w+)>\((\1)\) => int')]);
+ testType(GenericTypedefAndFunc, [
+ "GenericTypedefAndFunc",
+ "GenericTypedefAndFunc<dynamic>",
+ RegExp(r'<(\w+)>\((\1)\) => dynamic')
+ ]);
+
+ // Literals are canonicalized.
+ // See type_literal_canonicalization_test.dart
+
+ // Static member uses are not type literals.
+ Foo.property = "value";
+ Expect.equals("value", Foo.property);
+ Expect.equals("result", Foo.method());
+
+ // Prefixed types are type literals.
+ testType(prefix.Foo, "Foo");
+
+ // Prefix member uses are not.
+ prefix.Foo.property = "value2";
+ Expect.equals("value2", prefix.Foo.property);
+ Expect.equals("result", prefix.Foo.method());
+}
+
+void testType(Type type, Object expectedToStringValues) {
+ Expect.isTrue(type is Type);
+ String text = type.toString();
+
+ // dart2js minified names should be tagged. We can still test types that don't
+ // contain minified names.
+ if (text.contains('minified:')) return;
+
+ if (expectedToStringValues is List) {
+ var matched = false;
+ for (var value in expectedToStringValues) {
+ if (value is String) {
+ matched = matched || value == text;
+ } else if (value is RegExp) {
+ matched = matched || value.hasMatch(text);
+ }
+ }
+ Expect.isTrue(matched,
+ 'type `$type`.toString() should be one of: $expectedToStringValues.');
+ } else {
+ var string = expectedToStringValues as String;
+ Expect.equals(string, text);
+ }
+}
diff --git a/tests/language/type_object/reify_type_variable_test.dart b/tests/language/type_object/reify_type_variable_test.dart
new file mode 100644
index 0000000..50c5e41
--- /dev/null
+++ b/tests/language/type_object/reify_type_variable_test.dart
@@ -0,0 +1,16 @@
+// 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:expect/expect.dart";
+
+class Foo<T> {
+ reify() {
+ return T;
+ }
+}
+
+main() {
+ Expect.equals(int, new Foo<int>().reify());
+ Expect.equals(Foo, new Foo().runtimeType);
+}
diff --git a/tests/language/type_object/runtime_type_function_helper.dart b/tests/language/type_object/runtime_type_function_helper.dart
new file mode 100644
index 0000000..c9f9822
--- /dev/null
+++ b/tests/language/type_object/runtime_type_function_helper.dart
@@ -0,0 +1,40 @@
+// 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:expect/expect.dart";
+
+typedef String F(String returns, String arguments, [Map<String, String> named]);
+
+/// Formats a type like `(String, [int], {bool name}) => double`.
+String fn(String returns, String positional,
+ [Map<String, String> named = const {}]) {
+ var result = new StringBuffer();
+ result.write("($positional");
+ if (positional != "" && named.isNotEmpty) result.write(", ");
+ if (named.isNotEmpty) {
+ result.write("{");
+ bool first = true;
+ named.forEach((name, type) {
+ if (first) {
+ first = false;
+ } else {
+ result.write(", ");
+ }
+ result.write("$type $name");
+ });
+ result.write("}");
+ }
+ result.write(") => $returns");
+ return result.toString();
+}
+
+void check(String text, var thing) {
+ var type = thing.runtimeType.toString();
+ if (type == text) return;
+ Expect.fail("""
+Type print string does not match expectation
+ Expected: '$text'
+ Actual: '$type'
+""");
+}
diff --git a/tests/language/type_object/runtime_type_function_legacy_lib.dart b/tests/language/type_object/runtime_type_function_legacy_lib.dart
new file mode 100644
index 0000000..26e01de
--- /dev/null
+++ b/tests/language/type_object/runtime_type_function_legacy_lib.dart
@@ -0,0 +1,28 @@
+// 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.
+
+// @dart=2.6
+
+typedef String F(String returns, String arguments, [Map<String, String> named]);
+
+legacyMain() {}
+
+class Xyzzy {
+ static void foo() {}
+ static String opt(String x, [String a, b]) => "";
+ static String nam(String x, {String a, b}) => "";
+ void intAdd(int x) {}
+}
+
+// Using 'MyList' instead of core lib 'List' keeps covariant parameter type of
+// tear-offs 'Object' (legacy lib) instead of 'Object?' (opted-in lib).
+class MyList<E> {
+ void add(E value) {}
+}
+
+class G<U, V> {
+ U foo(V x) => null;
+ U moo(V f(U x)) => null;
+ U higherOrder(int f(U x)) => null;
+}
diff --git a/tests/language/type_object/runtime_type_function_test.dart b/tests/language/type_object/runtime_type_function_test.dart
new file mode 100644
index 0000000..1ca042d
--- /dev/null
+++ b/tests/language/type_object/runtime_type_function_test.dart
@@ -0,0 +1,67 @@
+// 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 "package:expect/expect.dart";
+
+import "runtime_type_function_helper.dart";
+
+main() {
+ // Types that do not use class names - these can be checked on dart2js in
+ // minified mode.
+
+ check(fn('dynamic', ''), main); // Top-level tear-off.
+ check(fn('void', ''), Xyzzy.foo); // Class static member tear-off.
+ check(fn('void', 'Object?'), new MyList().add); // Instance tear-off.
+ check(fn('int', ''), () => 1); // closure.
+
+ var s = new Xyzzy().runtimeType.toString();
+ if (s.length <= 3) return; // dart2js --minify has minified names.
+
+ Expect.equals('Xyzzy', s, 'runtime type of plain class prints as class name');
+
+ check(fn('void', 'String, dynamic'), check);
+
+ // Class static member tear-offs.
+ check(fn('String', 'String, [String?, dynamic]'), Xyzzy.opt);
+ check(fn('String', 'String', {'a': 'String?', 'b': 'dynamic'}), Xyzzy.nam);
+
+ // Instance method tear-offs.
+ check(fn('void', 'Object?'), new MyList<String>().add);
+ check(fn('void', 'Object?'), new MyList<int>().add);
+ check(fn('void', 'int'), new Xyzzy().intAdd);
+
+ check(fn('String', 'Object?'), new G<String, int>().foo);
+
+ // Instance method with function parameter.
+ var string2int = fn('int', 'String');
+ check(fn('String', 'Object?'), new G<String, int>().moo);
+ check(fn('String', '$string2int'), new G<String, int>().higherOrder);
+
+ // Closures.
+ String localFunc(String a, String b, [Map<String, String>? named]) => a + b;
+ void localFunc2(int a) {
+ print(a);
+ }
+
+ Expect.isTrue(localFunc is F);
+ check(fn('String', 'String, String, [Map<String, String>?]'), localFunc);
+ check(fn('void', 'int'), localFunc2);
+}
+
+class Xyzzy {
+ static void foo() {}
+ static String opt(String x, [String? a, b]) => "";
+ static String nam(String x, {String? a, b}) => "";
+ void intAdd(int x) {}
+}
+
+class MyList<E> {
+ void add(E value) {}
+}
+
+class G<U, V> {
+ U foo(V x) => throw "uncalled";
+ U moo(V f(U x)) => throw "uncalled";
+ U higherOrder(int f(U x)) => throw "uncalled";
+}
diff --git a/tests/language/type_object/runtime_type_function_weak_test.dart b/tests/language/type_object/runtime_type_function_weak_test.dart
new file mode 100644
index 0000000..c280da3
--- /dev/null
+++ b/tests/language/type_object/runtime_type_function_weak_test.dart
@@ -0,0 +1,45 @@
+// 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.
+
+// Requirements=nnbd-weak
+
+/// Test string representations of legacy types accessed from a null safe
+/// library.
+import "package:expect/expect.dart";
+
+import "runtime_type_function_helper.dart";
+import "runtime_type_function_legacy_lib.dart";
+
+main() {
+ // Types that do not use class names - these can be checked on dart2js in
+ // minified mode.
+
+ check(fn('dynamic', ''), legacyMain); // Top-level tear-off.
+ check(fn('void', ''), Xyzzy.foo); // Class static member tear-off.
+ check(fn('void', 'Object'), new MyList().add); // Instance tear-off.
+ check(fn('int', ''), () => 1); // closure.
+
+ var s = new Xyzzy().runtimeType.toString();
+ if (s.length <= 3) return; // dart2js --minify has minified names.
+
+ Expect.equals('Xyzzy', s, 'runtime type of plain class prints as class name');
+
+ check(fn('void', 'String, dynamic'), check);
+
+ // Class static member tear-offs.
+ check(fn('String', 'String, [String, dynamic]'), Xyzzy.opt);
+ check(fn('String', 'String', {'a': 'String', 'b': 'dynamic'}), Xyzzy.nam);
+
+ // Instance method tear-offs.
+ check(fn('void', 'Object'), new MyList<String>().add);
+ check(fn('void', 'Object'), new MyList<int>().add);
+ check(fn('void', 'int'), new Xyzzy().intAdd);
+
+ check(fn('String', 'Object'), new G<String, int>().foo);
+
+ // Instance method with function parameter.
+ var string2int = fn('int', 'String');
+ check(fn('String', 'Object'), new G<String, int>().moo);
+ check(fn('String', '$string2int'), new G<String, int>().higherOrder);
+}
diff --git a/tests/language/type_object/runtime_type_test.dart b/tests/language/type_object/runtime_type_test.dart
new file mode 100644
index 0000000..f0b66db
--- /dev/null
+++ b/tests/language/type_object/runtime_type_test.dart
@@ -0,0 +1,36 @@
+// Copyright (c) 2012, 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:expect/expect.dart";
+
+class A {}
+
+class B {}
+
+main() {
+ var a = new A();
+ var b = new B();
+ var f = () => null;
+ var funcType = f.runtimeType;
+ var classA = A;
+ var classB = B;
+
+ Expect.isTrue(a.runtimeType is Type);
+ Expect.equals(A, a.runtimeType);
+ Expect.notEquals(B, a.runtimeType);
+ Expect.notEquals(A, b.runtimeType);
+
+ Expect.isTrue(f.runtimeType is Type);
+ Expect.isFalse(f.runtimeType == a.runtimeType);
+
+ Expect.isTrue(classA.runtimeType is Type);
+ Expect.isTrue(classA.runtimeType == classB.runtimeType);
+ Expect.isFalse(classA.runtimeType == a.runtimeType);
+ Expect.isFalse(classA.runtimeType == f.runtimeType);
+
+ Expect.isTrue(funcType.runtimeType == classA.runtimeType);
+
+ Expect.isTrue(null.runtimeType is Type);
+ Expect.equals(Null, null.runtimeType);
+}
diff --git a/tests/language/type_promotion/assign_runtime_test.dart b/tests/language/type_promotion/assign_runtime_test.dart
new file mode 100644
index 0000000..244ab17
--- /dev/null
+++ b/tests/language/type_promotion/assign_runtime_test.dart
@@ -0,0 +1,54 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2013, 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.
+
+// Test type promotion of assigned locals.
+
+class A {
+ var a = "a";
+}
+
+class B extends A {
+ var b = "b";
+}
+
+class C extends B {
+ var c = "c";
+}
+
+class D extends A {
+ var d = "d";
+}
+
+class E implements C, D {
+ var a = "";
+ var b = "";
+ var c = "";
+ var d = "";
+}
+
+void main() {
+ A a = new E();
+ if (a is B) {
+ print(a.a);
+
+ a = A();
+ }
+ if (a is B) {
+ a = A();
+ print(a.a);
+
+ }
+ if (a is B) {
+ print(a.a);
+
+ {
+ a = A();
+ }
+ print(a.a);
+
+ }
+}
diff --git a/tests/language/type_promotion/assign_test.dart b/tests/language/type_promotion/assign_test.dart
new file mode 100644
index 0000000..737a750
--- /dev/null
+++ b/tests/language/type_promotion/assign_test.dart
@@ -0,0 +1,57 @@
+// Copyright (c) 2013, 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.
+
+// Test type promotion of assigned locals.
+
+class A {
+ var a = "a";
+}
+
+class B extends A {
+ var b = "b";
+}
+
+class C extends B {
+ var c = "c";
+}
+
+class D extends A {
+ var d = "d";
+}
+
+class E implements C, D {
+ var a = "";
+ var b = "";
+ var c = "";
+ var d = "";
+}
+
+void main() {
+ A a = new E();
+ if (a is B) {
+ print(a.a);
+ print(a.b);
+ a = A();
+ }
+ if (a is B) {
+ a = A();
+ print(a.a);
+ print(a.b);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'b' isn't defined for the class 'A'.
+ }
+ if (a is B) {
+ print(a.a);
+ print(a.b);
+ {
+ a = A();
+ }
+ print(a.a);
+ print(a.b);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'b' isn't defined for the class 'A'.
+ }
+}
diff --git a/tests/language/type_promotion/closure_runtime_1_test.dart b/tests/language/type_promotion/closure_runtime_1_test.dart
new file mode 100644
index 0000000..9700e47
--- /dev/null
+++ b/tests/language/type_promotion/closure_runtime_1_test.dart
@@ -0,0 +1,203 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2013, 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.
+
+// Test type promotion of locals potentially mutated in closures.
+
+import "package:meta/meta.dart" show virtual;
+
+class A {
+ var a = "a";
+ A operator +(int i) => this;
+}
+
+class B extends A {
+ var b = "b";
+}
+
+class C extends B {
+ var c = "c";
+}
+
+class D extends A {
+ var d = "d";
+}
+
+class E extends D implements C {
+ var a = "";
+ var b = "";
+ var c = "";
+ var d = "";
+}
+
+func(x) => true;
+
+void main() {
+ test1();
+ test2();
+ test3();
+ test3a();
+ test4();
+ test5();
+ test6();
+ test6a();
+ test7();
+ test8();
+ test9();
+ test10();
+ test11();
+ test12();
+}
+
+void test1() {
+ A a = new E();
+ if (a is B) {
+ print(a.a);
+
+ }
+ void foo() {
+ a = new D();
+ }
+}
+
+void test2() {
+ A a = new E();
+ void foo() {
+ a = new D();
+ }
+
+ if (a is B) {
+ print(a.a);
+
+ }
+}
+
+void test3() {
+ A a = new E();
+ void foo() {
+ a = new D();
+ }
+
+ if (a is B) {
+ print(a.a);
+
+ void foo() {
+ a = new D();
+ }
+
+ print(a.a);
+
+ }
+}
+
+void test3a() {
+ A a = new E();
+ void foo() {
+ a = new D();
+ }
+
+ if ((((a)) is B)) {
+ print(a.a);
+
+ void foo() {
+ a = new D();
+ }
+
+ print(a.a);
+
+ }
+}
+
+void test4() {
+ A a = new E();
+ if (a is B) {
+ func(() => a.b);
+ print(a.a);
+ print(a.b);
+ }
+}
+
+void test5() {
+ A a = new E();
+ if (a is B) {
+
+ print(a.a);
+ }
+ a = A();
+}
+
+void test6() {
+ A a = new E();
+ if (a is B) {
+ func(() => a);
+ print(a.a);
+
+ }
+ a = A();
+}
+
+void test6a() {
+ A a = new E();
+ if (((a) is B)) {
+ func(() => a);
+ print(a.a);
+
+ }
+ a = A();
+}
+
+void test7() {
+ A a = new E();
+ if (a is B && func(() => a)) {
+ print(a.a);
+
+ }
+ a = A();
+}
+
+void test8() {
+ A a = new E();
+ if (a is B
+
+ ) {
+ print(a.a);
+ }
+ a = A();
+}
+
+void test9() {
+ A a = new E();
+
+ a = A();
+}
+
+void test10() {
+ List<A> a = <E>[new E()];
+ if (a is List<B>) {
+ func(() => a[0]);
+
+ }
+ a = [];
+}
+
+void test11() {
+ List<A> a = <E>[new E()];
+ if (a is List<B>) {
+ func(() => a[0] = E());
+
+ }
+ a = [];
+}
+
+void test12() {
+ A a = new E();
+ if (a is B) {
+ func(() => a++);
+ print(a.a);
+
+ }
+ a = A();
+}
diff --git a/tests/language/type_promotion/closure_runtime_2_test.dart b/tests/language/type_promotion/closure_runtime_2_test.dart
new file mode 100644
index 0000000..52dc86f
--- /dev/null
+++ b/tests/language/type_promotion/closure_runtime_2_test.dart
@@ -0,0 +1,203 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2013, 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.
+
+// Test type promotion of locals potentially mutated in closures.
+
+import "package:meta/meta.dart" show virtual;
+
+class A {
+ var a = "a";
+ A operator +(int i) => this;
+}
+
+class B extends A {
+ var b = "b";
+}
+
+class C extends B {
+ var c = "c";
+}
+
+class D extends A {
+ var d = "d";
+}
+
+class E extends D implements C {
+ var a = "";
+ var b = "";
+ var c = "";
+ var d = "";
+}
+
+func(x) => true;
+
+void main() {
+ test1();
+ test2();
+ test3();
+ test3a();
+ test4();
+ test5();
+ test6();
+ test6a();
+ test7();
+ test8();
+ test9();
+ test10();
+ test11();
+ test12();
+}
+
+void test1() {
+ A a = new E();
+ if (a is B) {
+ print(a.a);
+
+ }
+ void foo() {
+ a = new D();
+ }
+}
+
+void test2() {
+ A a = new E();
+ void foo() {
+ a = new D();
+ }
+
+ if (a is B) {
+ print(a.a);
+
+ }
+}
+
+void test3() {
+ A a = new E();
+ void foo() {
+ a = new D();
+ }
+
+ if (a is B) {
+ print(a.a);
+
+ void foo() {
+ a = new D();
+ }
+
+ print(a.a);
+
+ }
+}
+
+void test3a() {
+ A a = new E();
+ void foo() {
+ a = new D();
+ }
+
+ if ((((a)) is B)) {
+ print(a.a);
+
+ void foo() {
+ a = new D();
+ }
+
+ print(a.a);
+
+ }
+}
+
+void test4() {
+ A a = new E();
+ if (a is B) {
+
+ print(a.a);
+ print(a.b);
+ }
+}
+
+void test5() {
+ A a = new E();
+ if (a is B) {
+
+ print(a.a);
+ }
+ a = A();
+}
+
+void test6() {
+ A a = new E();
+ if (a is B) {
+ func(() => a);
+ print(a.a);
+
+ }
+ a = A();
+}
+
+void test6a() {
+ A a = new E();
+ if (((a) is B)) {
+ func(() => a);
+ print(a.a);
+
+ }
+ a = A();
+}
+
+void test7() {
+ A a = new E();
+ if (a is B && func(() => a)) {
+ print(a.a);
+ print(a.b);
+ }
+ a = A();
+}
+
+void test8() {
+ A a = new E();
+ if (a is B
+
+ ) {
+ print(a.a);
+ }
+ a = A();
+}
+
+void test9() {
+ A a = new E();
+
+ a = A();
+}
+
+void test10() {
+ List<A> a = <E>[new E()];
+ if (a is List<B>) {
+ func(() => a[0]);
+
+ }
+ a = [];
+}
+
+void test11() {
+ List<A> a = <E>[new E()];
+ if (a is List<B>) {
+ func(() => a[0] = E());
+
+ }
+ a = [];
+}
+
+void test12() {
+ A a = new E();
+ if (a is B) {
+ func(() => a++);
+ print(a.a);
+
+ }
+ a = A();
+}
diff --git a/tests/language/type_promotion/closure_runtime_test.dart b/tests/language/type_promotion/closure_runtime_test.dart
new file mode 100644
index 0000000..07b6caa
--- /dev/null
+++ b/tests/language/type_promotion/closure_runtime_test.dart
@@ -0,0 +1,203 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2013, 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.
+
+// Test type promotion of locals potentially mutated in closures.
+
+import "package:meta/meta.dart" show virtual;
+
+class A {
+ var a = "a";
+ A operator +(int i) => this;
+}
+
+class B extends A {
+ var b = "b";
+}
+
+class C extends B {
+ var c = "c";
+}
+
+class D extends A {
+ var d = "d";
+}
+
+class E extends D implements C {
+ var a = "";
+ var b = "";
+ var c = "";
+ var d = "";
+}
+
+func(x) => true;
+
+void main() {
+ test1();
+ test2();
+ test3();
+ test3a();
+ test4();
+ test5();
+ test6();
+ test6a();
+ test7();
+ test8();
+ test9();
+ test10();
+ test11();
+ test12();
+}
+
+void test1() {
+ A a = new E();
+ if (a is B) {
+ print(a.a);
+
+ }
+ void foo() {
+ a = new D();
+ }
+}
+
+void test2() {
+ A a = new E();
+ void foo() {
+ a = new D();
+ }
+
+ if (a is B) {
+ print(a.a);
+
+ }
+}
+
+void test3() {
+ A a = new E();
+ void foo() {
+ a = new D();
+ }
+
+ if (a is B) {
+ print(a.a);
+
+ void foo() {
+ a = new D();
+ }
+
+ print(a.a);
+
+ }
+}
+
+void test3a() {
+ A a = new E();
+ void foo() {
+ a = new D();
+ }
+
+ if ((((a)) is B)) {
+ print(a.a);
+
+ void foo() {
+ a = new D();
+ }
+
+ print(a.a);
+
+ }
+}
+
+void test4() {
+ A a = new E();
+ if (a is B) {
+
+ print(a.a);
+ print(a.b);
+ }
+}
+
+void test5() {
+ A a = new E();
+ if (a is B) {
+
+ print(a.a);
+ }
+ a = A();
+}
+
+void test6() {
+ A a = new E();
+ if (a is B) {
+ func(() => a);
+ print(a.a);
+
+ }
+ a = A();
+}
+
+void test6a() {
+ A a = new E();
+ if (((a) is B)) {
+ func(() => a);
+ print(a.a);
+
+ }
+ a = A();
+}
+
+void test7() {
+ A a = new E();
+ if (a is B && func(() => a)) {
+ print(a.a);
+
+ }
+ a = A();
+}
+
+void test8() {
+ A a = new E();
+ if (a is B
+
+ ) {
+ print(a.a);
+ }
+ a = A();
+}
+
+void test9() {
+ A a = new E();
+
+ a = A();
+}
+
+void test10() {
+ List<A> a = <E>[new E()];
+ if (a is List<B>) {
+ func(() => a[0]);
+
+ }
+ a = [];
+}
+
+void test11() {
+ List<A> a = <E>[new E()];
+ if (a is List<B>) {
+ func(() => a[0] = E());
+
+ }
+ a = [];
+}
+
+void test12() {
+ A a = new E();
+ if (a is B) {
+ func(() => a++);
+ print(a.a);
+
+ }
+ a = A();
+}
diff --git a/tests/language/type_promotion/closure_test.dart b/tests/language/type_promotion/closure_test.dart
new file mode 100644
index 0000000..17246dc
--- /dev/null
+++ b/tests/language/type_promotion/closure_test.dart
@@ -0,0 +1,227 @@
+// Copyright (c) 2013, 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.
+
+// Test type promotion of locals potentially mutated in closures.
+
+import "package:meta/meta.dart" show virtual;
+
+class A {
+ var a = "a";
+ A operator +(int i) => this;
+}
+
+class B extends A {
+ var b = "b";
+}
+
+class C extends B {
+ var c = "c";
+}
+
+class D extends A {
+ var d = "d";
+}
+
+class E extends D implements C {
+ var a = "";
+ var b = "";
+ var c = "";
+ var d = "";
+}
+
+func(x) => true;
+
+void main() {
+ test1();
+ test2();
+ test3();
+ test3a();
+ test4();
+ test5();
+ test6();
+ test6a();
+ test7();
+ test8();
+ test9();
+ test10();
+ test11();
+ test12();
+}
+
+void test1() {
+ A a = new E();
+ if (a is B) {
+ print(a.a);
+ print(a.b);
+ }
+ void foo() {
+ a = new D();
+ }
+}
+
+void test2() {
+ A a = new E();
+ void foo() {
+ a = new D();
+ }
+
+ if (a is B) {
+ print(a.a);
+ print(a.b);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'b' isn't defined for the class 'A'.
+ }
+}
+
+void test3() {
+ A a = new E();
+ void foo() {
+ a = new D();
+ }
+
+ if (a is B) {
+ print(a.a);
+ print(a.b);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'b' isn't defined for the class 'A'.
+ void foo() {
+ a = new D();
+ }
+
+ print(a.a);
+ print(a.b);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'b' isn't defined for the class 'A'.
+ }
+}
+
+void test3a() {
+ A a = new E();
+ void foo() {
+ a = new D();
+ }
+
+ if ((((a)) is B)) {
+ print(a.a);
+ print(a.b);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'b' isn't defined for the class 'A'.
+ void foo() {
+ a = new D();
+ }
+
+ print(a.a);
+ print(a.b);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'b' isn't defined for the class 'A'.
+ }
+}
+
+void test4() {
+ A a = new E();
+ if (a is B) {
+ func(() => a.b);
+ print(a.a);
+ print(a.b);
+ }
+}
+
+void test5() {
+ A a = new E();
+ if (a is B) {
+ func(() => a.b);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'b' isn't defined for the class 'A'.
+ print(a.a);
+ }
+ a = A();
+}
+
+void test6() {
+ A a = new E();
+ if (a is B) {
+ func(() => a);
+ print(a.a);
+ print(a.b);
+ }
+ a = A();
+}
+
+void test6a() {
+ A a = new E();
+ if (((a) is B)) {
+ func(() => a);
+ print(a.a);
+ print(a.b);
+ }
+ a = A();
+}
+
+void test7() {
+ A a = new E();
+ if (a is B && func(() => a)) {
+ print(a.a);
+ print(a.b);
+ }
+ a = A();
+}
+
+void test8() {
+ A a = new E();
+ if (a is B
+ && func(() => a.b)
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'b' isn't defined for the class 'A'.
+ ) {
+ print(a.a);
+ }
+ a = A();
+}
+
+void test9() {
+ A a = new E();
+ var b = a is B ? func(() => a.b) : false;
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'b' isn't defined for the class 'A'.
+ a = A();
+}
+
+void test10() {
+ List<A> a = <E>[new E()];
+ if (a is List<B>) {
+ func(() => a[0]);
+ print(a[0].b);
+ }
+ a = [];
+}
+
+void test11() {
+ List<A> a = <E>[new E()];
+ if (a is List<B>) {
+ func(() => a[0] = E());
+ print(a[0].b);
+ }
+ a = [];
+}
+
+void test12() {
+ A a = new E();
+ if (a is B) {
+ func(() => a++);
+ print(a.a);
+ print(a.b);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'b' isn't defined for the class 'A'.
+ }
+ a = A();
+}
diff --git a/tests/language/type_promotion/functions_runtime_test.dart b/tests/language/type_promotion/functions_runtime_test.dart
new file mode 100644
index 0000000..5e462be
--- /dev/null
+++ b/tests/language/type_promotion/functions_runtime_test.dart
@@ -0,0 +1,127 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2013, 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.
+
+// Test type promotion of functions.
+
+class A {}
+
+class B extends A {}
+
+class C {}
+
+// Subtype relations:
+//
+// FuncDynToA == A Function(dynamic) <:
+// FuncDynToDyn == dynamic Function(dynamic) <:> // "void == dynamic"
+// FuncDynToVoid == void Function(dynamic) <:
+// FuncAtoDyn == dynamic Function(A).
+//
+// Declarations ordered by "super before sub", as is common for classes:
+
+typedef FuncAtoDyn(A a);
+typedef void FuncDynToVoid(x);
+typedef FuncDynToDyn(x);
+typedef A FuncDynToA(x);
+
+dynamic func(x) => x;
+
+late A a;
+late B b;
+late C c;
+
+main() {
+ testFuncAtoDyn();
+ testFuncDynToDyn();
+ testFuncDynToVoid();
+ testFuncDynToA();
+}
+
+testFuncAtoDyn() {
+ FuncAtoDyn funcAtoDyn = func;
+ a = funcAtoDyn(new A());
+ b = funcAtoDyn(new B());
+
+
+ if (funcAtoDyn is FuncDynToDyn) {
+ // Promotion: FuncDynToDyn <: FuncAtoDyn.
+ a = funcAtoDyn(new A());
+ b = funcAtoDyn(new B());
+ c = funcAtoDyn(new C());
+ }
+}
+
+testFuncDynToDyn() {
+ FuncDynToDyn funcDynToDyn = func;
+ a = funcDynToDyn(new A());
+ b = funcDynToDyn(new B());
+ c = funcDynToDyn(new C());
+
+ if (funcDynToDyn is FuncAtoDyn) {
+ // No promotion: FuncAtoDyn <\: FuncDynToDyn.
+ a = funcDynToDyn(new A());
+ b = funcDynToDyn(new B());
+ c = funcDynToDyn(new C());
+ }
+
+ if (funcDynToDyn is FuncDynToVoid) {
+ // Promotion: FuncDynToVoid <: FuncDynToDyn.
+ funcDynToDyn(new A());
+ funcDynToDyn(new B());
+ funcDynToDyn(new C());
+ // Returned value has type `void`, usage is restricted.
+
+ }
+
+ if (funcDynToDyn is FuncDynToA) {
+ // Promotion: FuncDynToA <: FuncDynToDyn.
+ a = funcDynToDyn(new A());
+ b = funcDynToDyn(new B()) as B;
+
+ }
+}
+
+testFuncDynToVoid() {
+ FuncDynToVoid funcDynToVoid = func;
+
+
+
+
+ if (funcDynToVoid is FuncDynToDyn) {
+ // Promotion: FuncDynToDyn <:> FuncDynToVoid.
+ a = funcDynToVoid(new A());
+ b = funcDynToVoid(new B());
+ c = funcDynToVoid(new C());
+ }
+
+ if (funcDynToVoid is FuncDynToA) {
+ // Promotion: FuncDynToA <: FuncDynToVoid.
+ a = funcDynToVoid(new A());
+ b = funcDynToVoid(new B()) as B;
+
+ }
+}
+
+testFuncDynToA() {
+ FuncDynToA funcDynToA = (x) => x as A;
+ a = funcDynToA(new A());
+ b = funcDynToA(new B()) as B;
+
+
+ if (funcDynToA is FuncDynToDyn) {
+ // No promotion: FuncDynToDyn <\: FuncDynToA.
+ a = funcDynToA(new A());
+ b = funcDynToA(new B()) as B;
+
+ }
+
+ if (funcDynToA is FuncDynToVoid) {
+ // No promotion: FuncDynToVoid <\: FuncDynToA.
+ a = funcDynToA(new A());
+ b = funcDynToA(new B()) as B;
+
+ }
+}
diff --git a/tests/language/type_promotion/functions_test.dart b/tests/language/type_promotion/functions_test.dart
new file mode 100644
index 0000000..1e0b4ae
--- /dev/null
+++ b/tests/language/type_promotion/functions_test.dart
@@ -0,0 +1,164 @@
+// Copyright (c) 2013, 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.
+
+// Test type promotion of functions.
+
+class A {}
+
+class B extends A {}
+
+class C {}
+
+// Subtype relations:
+//
+// FuncDynToA == A Function(dynamic) <:
+// FuncDynToDyn == dynamic Function(dynamic) <:> // "void == dynamic"
+// FuncDynToVoid == void Function(dynamic) <:
+// FuncAtoDyn == dynamic Function(A).
+//
+// Declarations ordered by "super before sub", as is common for classes:
+
+typedef FuncAtoDyn(A a);
+typedef void FuncDynToVoid(x);
+typedef FuncDynToDyn(x);
+typedef A FuncDynToA(x);
+
+dynamic func(x) => x;
+
+late A a;
+late B b;
+late C c;
+
+main() {
+ testFuncAtoDyn();
+ testFuncDynToDyn();
+ testFuncDynToVoid();
+ testFuncDynToA();
+}
+
+testFuncAtoDyn() {
+ FuncAtoDyn funcAtoDyn = func;
+ a = funcAtoDyn(new A());
+ b = funcAtoDyn(new B());
+ c = funcAtoDyn(new C());
+ // ^^^^^^^
+ // [analyzer] STATIC_WARNING.ARGUMENT_TYPE_NOT_ASSIGNABLE
+ // ^
+ // [cfe] The argument type 'C' can't be assigned to the parameter type 'A'.
+
+ if (funcAtoDyn is FuncDynToDyn) {
+ // Promotion: FuncDynToDyn <: FuncAtoDyn.
+ a = funcAtoDyn(new A());
+ b = funcAtoDyn(new B());
+ c = funcAtoDyn(new C());
+ }
+}
+
+testFuncDynToDyn() {
+ FuncDynToDyn funcDynToDyn = func;
+ a = funcDynToDyn(new A());
+ b = funcDynToDyn(new B());
+ c = funcDynToDyn(new C());
+
+ if (funcDynToDyn is FuncAtoDyn) {
+ // No promotion: FuncAtoDyn <\: FuncDynToDyn.
+ a = funcDynToDyn(new A());
+ b = funcDynToDyn(new B());
+ c = funcDynToDyn(new C());
+ }
+
+ if (funcDynToDyn is FuncDynToVoid) {
+ // Promotion: FuncDynToVoid <: FuncDynToDyn.
+ funcDynToDyn(new A());
+ funcDynToDyn(new B());
+ funcDynToDyn(new C());
+ // Returned value has type `void`, usage is restricted.
+ Object o = funcDynToDyn(null);
+ // ^^^^^^^^^^^^^^^^^^
+ // [analyzer] STATIC_WARNING.USE_OF_VOID_RESULT
+ // ^
+ // [cfe] This expression has type 'void' and can't be used.
+ }
+
+ if (funcDynToDyn is FuncDynToA) {
+ // Promotion: FuncDynToA <: FuncDynToDyn.
+ a = funcDynToDyn(new A());
+ b = funcDynToDyn(new B()) as B;
+ c = funcDynToDyn(new C());
+ // ^^^^^^^^^^^^^^^^^^^^^
+ // [analyzer] STATIC_TYPE_WARNING.INVALID_ASSIGNMENT
+ // ^
+ // [cfe] A value of type 'A' can't be assigned to a variable of type 'C'.
+ }
+}
+
+testFuncDynToVoid() {
+ FuncDynToVoid funcDynToVoid = func;
+ a = funcDynToVoid(new A());
+ // ^^^^^^^^^^^^^^^^^^^^^^
+ // [analyzer] STATIC_WARNING.USE_OF_VOID_RESULT
+ // ^
+ // [cfe] This expression has type 'void' and can't be used.
+ b = funcDynToVoid(new B());
+ // ^^^^^^^^^^^^^^^^^^^^^^
+ // [analyzer] STATIC_WARNING.USE_OF_VOID_RESULT
+ // ^
+ // [cfe] This expression has type 'void' and can't be used.
+ c = funcDynToVoid(new C());
+ // ^^^^^^^^^^^^^^^^^^^^^^
+ // [analyzer] STATIC_WARNING.USE_OF_VOID_RESULT
+ // ^
+ // [cfe] This expression has type 'void' and can't be used.
+
+ if (funcDynToVoid is FuncDynToDyn) {
+ // Promotion: FuncDynToDyn <:> FuncDynToVoid.
+ a = funcDynToVoid(new A());
+ b = funcDynToVoid(new B());
+ c = funcDynToVoid(new C());
+ }
+
+ if (funcDynToVoid is FuncDynToA) {
+ // Promotion: FuncDynToA <: FuncDynToVoid.
+ a = funcDynToVoid(new A());
+ b = funcDynToVoid(new B()) as B;
+ c = funcDynToVoid(new C());
+ // ^^^^^^^^^^^^^^^^^^^^^^
+ // [analyzer] STATIC_TYPE_WARNING.INVALID_ASSIGNMENT
+ // ^
+ // [cfe] A value of type 'A' can't be assigned to a variable of type 'C'.
+ }
+}
+
+testFuncDynToA() {
+ FuncDynToA funcDynToA = (x) => x as A;
+ a = funcDynToA(new A());
+ b = funcDynToA(new B()) as B;
+ c = funcDynToA(new C());
+ // ^^^^^^^^^^^^^^^^^^^
+ // [analyzer] STATIC_TYPE_WARNING.INVALID_ASSIGNMENT
+ // ^
+ // [cfe] A value of type 'A' can't be assigned to a variable of type 'C'.
+
+ if (funcDynToA is FuncDynToDyn) {
+ // No promotion: FuncDynToDyn <\: FuncDynToA.
+ a = funcDynToA(new A());
+ b = funcDynToA(new B()) as B;
+ c = funcDynToA(new C());
+ // ^^^^^^^^^^^^^^^^^^^
+ // [analyzer] STATIC_TYPE_WARNING.INVALID_ASSIGNMENT
+ // ^
+ // [cfe] A value of type 'A' can't be assigned to a variable of type 'C'.
+ }
+
+ if (funcDynToA is FuncDynToVoid) {
+ // No promotion: FuncDynToVoid <\: FuncDynToA.
+ a = funcDynToA(new A());
+ b = funcDynToA(new B()) as B;
+ c = funcDynToA(new C());
+ // ^^^^^^^^^^^^^^^^^^^
+ // [analyzer] STATIC_TYPE_WARNING.INVALID_ASSIGNMENT
+ // ^
+ // [cfe] A value of type 'A' can't be assigned to a variable of type 'C'.
+ }
+}
diff --git a/tests/language/type_promotion/local_runtime_test.dart b/tests/language/type_promotion/local_runtime_test.dart
new file mode 100644
index 0000000..b010208
--- /dev/null
+++ b/tests/language/type_promotion/local_runtime_test.dart
@@ -0,0 +1,163 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2013, 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.
+
+// Test type promotion of locals.
+
+class A {
+ var a = "a";
+}
+
+class B extends A {
+ var b = "b";
+}
+
+class C extends B {
+ var c = "c";
+}
+
+class D extends A {
+ var d = "d";
+}
+
+class E implements C, D {
+ var a = "";
+ var b = "";
+ var c = "";
+ var d = "";
+}
+
+void main() {
+ A a = new E();
+ print(a.a);
+
+
+
+
+ if (a is B) {
+ print(a.a);
+ print(a.b);
+
+
+
+ if (a is C) {
+ print(a.a);
+ print(a.b);
+ print(a.c);
+
+ }
+
+ print(a.a);
+ print(a.b);
+
+
+ }
+ if (a is C) {
+ print(a.a);
+ print(a.b);
+ print(a.c);
+
+
+ if (a is B) {
+ print(a.a);
+ print(a.b);
+ print(a.c);
+
+ }
+ if (a is D) {
+ print(a.a);
+ print(a.b);
+ print(a.c);
+
+ }
+
+ print(a.a);
+ print(a.b);
+ print(a.c);
+
+ }
+
+ print(a.a);
+
+
+
+
+ if (a is D) {
+ print(a.a);
+
+
+ print(a.d);
+ }
+
+ print(a.a);
+
+
+
+
+ var o1 = a is B
+ ? '${a.a}'
+ '${a.b}'
+
+
+ : '${a.a}'
+
+
+
+ ;
+
+ var o2 = a is C
+ ? '${a.a}'
+ '${a.b}'
+ '${a.c}'
+
+ : '${a.a}'
+
+
+
+ ;
+
+ var o3 = a is D
+ ? '${a.a}'
+
+
+ '${a.d}'
+ : '${a.a}'
+
+
+
+ ;
+
+ if (a is B && a is B) {
+ print(a.a);
+ print(a.b);
+
+
+ }
+ if (a is B && a is C) {
+ print(a.a);
+ print(a.b);
+ print(a.c);
+
+ }
+ if (a is C && a is B) {
+ print(a.a);
+ print(a.b);
+ print(a.c);
+
+ }
+ if (a is C && a is D) {
+ print(a.a);
+ print(a.b);
+ print(a.c);
+
+ }
+ if (a is D && a is C) {
+ print(a.a);
+
+
+ print(a.d);
+ }
+}
diff --git a/tests/language/type_promotion/local_test.dart b/tests/language/type_promotion/local_test.dart
new file mode 100644
index 0000000..c0fe87d
--- /dev/null
+++ b/tests/language/type_promotion/local_test.dart
@@ -0,0 +1,283 @@
+// Copyright (c) 2013, 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.
+
+// Test type promotion of locals.
+
+class A {
+ var a = "a";
+}
+
+class B extends A {
+ var b = "b";
+}
+
+class C extends B {
+ var c = "c";
+}
+
+class D extends A {
+ var d = "d";
+}
+
+class E implements C, D {
+ var a = "";
+ var b = "";
+ var c = "";
+ var d = "";
+}
+
+void main() {
+ A a = new E();
+ print(a.a);
+ print(a.b);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'b' isn't defined for the class 'A'.
+ print(a.c);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'c' isn't defined for the class 'A'.
+ print(a.d);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'd' isn't defined for the class 'A'.
+
+ if (a is B) {
+ print(a.a);
+ print(a.b);
+ print(a.c);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'c' isn't defined for the class 'B'.
+ print(a.d);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'd' isn't defined for the class 'B'.
+
+ if (a is C) {
+ print(a.a);
+ print(a.b);
+ print(a.c);
+ print(a.d);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'd' isn't defined for the class 'C'.
+ }
+
+ print(a.a);
+ print(a.b);
+ print(a.c);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'c' isn't defined for the class 'B'.
+ print(a.d);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'd' isn't defined for the class 'B'.
+ }
+ if (a is C) {
+ print(a.a);
+ print(a.b);
+ print(a.c);
+ print(a.d);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'd' isn't defined for the class 'C'.
+
+ if (a is B) {
+ print(a.a);
+ print(a.b);
+ print(a.c);
+ print(a.d);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'd' isn't defined for the class 'C'.
+ }
+ if (a is D) {
+ print(a.a);
+ print(a.b);
+ print(a.c);
+ print(a.d);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'd' isn't defined for the class 'C'.
+ }
+
+ print(a.a);
+ print(a.b);
+ print(a.c);
+ print(a.d);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'd' isn't defined for the class 'C'.
+ }
+
+ print(a.a);
+ print(a.b);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'b' isn't defined for the class 'A'.
+ print(a.c);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'c' isn't defined for the class 'A'.
+ print(a.d);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'd' isn't defined for the class 'A'.
+
+ if (a is D) {
+ print(a.a);
+ print(a.b);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'b' isn't defined for the class 'D'.
+ print(a.c);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'c' isn't defined for the class 'D'.
+ print(a.d);
+ }
+
+ print(a.a);
+ print(a.b);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'b' isn't defined for the class 'A'.
+ print(a.c);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'c' isn't defined for the class 'A'.
+ print(a.d);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'd' isn't defined for the class 'A'.
+
+ var o1 = a is B
+ ? '${a.a}'
+ '${a.b}'
+ '${a.c}'
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'c' isn't defined for the class 'B'.
+ '${a.d}'
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'd' isn't defined for the class 'B'.
+ : '${a.a}'
+ '${a.b}'
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'b' isn't defined for the class 'A'.
+ '${a.c}'
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'c' isn't defined for the class 'A'.
+ '${a.d}'
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'd' isn't defined for the class 'A'.
+ ;
+
+ var o2 = a is C
+ ? '${a.a}'
+ '${a.b}'
+ '${a.c}'
+ '${a.d}'
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'd' isn't defined for the class 'C'.
+ : '${a.a}'
+ '${a.b}'
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'b' isn't defined for the class 'A'.
+ '${a.c}'
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'c' isn't defined for the class 'A'.
+ '${a.d}'
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'd' isn't defined for the class 'A'.
+ ;
+
+ var o3 = a is D
+ ? '${a.a}'
+ '${a.b}'
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'b' isn't defined for the class 'D'.
+ '${a.c}'
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'c' isn't defined for the class 'D'.
+ '${a.d}'
+ : '${a.a}'
+ '${a.b}'
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'b' isn't defined for the class 'A'.
+ '${a.c}'
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'c' isn't defined for the class 'A'.
+ '${a.d}'
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'd' isn't defined for the class 'A'.
+ ;
+
+ if (a is B && a is B) {
+ print(a.a);
+ print(a.b);
+ print(a.c);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'c' isn't defined for the class 'B'.
+ print(a.d);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'd' isn't defined for the class 'B'.
+ }
+ if (a is B && a is C) {
+ print(a.a);
+ print(a.b);
+ print(a.c);
+ print(a.d);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'd' isn't defined for the class 'C'.
+ }
+ if (a is C && a is B) {
+ print(a.a);
+ print(a.b);
+ print(a.c);
+ print(a.d);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'd' isn't defined for the class 'C'.
+ }
+ if (a is C && a is D) {
+ print(a.a);
+ print(a.b);
+ print(a.c);
+ print(a.d);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'd' isn't defined for the class 'C'.
+ }
+ if (a is D && a is C) {
+ print(a.a);
+ print(a.b);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'b' isn't defined for the class 'D'.
+ print(a.c);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'c' isn't defined for the class 'D'.
+ print(a.d);
+ }
+}
diff --git a/tests/language/type_promotion/logical_and_runtime_test.dart b/tests/language/type_promotion/logical_and_runtime_test.dart
new file mode 100644
index 0000000..ce27686
--- /dev/null
+++ b/tests/language/type_promotion/logical_and_runtime_test.dart
@@ -0,0 +1,52 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2013, 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.
+
+// Test type promotion of locals potentially mutated.
+
+class A {
+ var a = true;
+}
+
+class B extends A {
+ var b = true;
+}
+
+class C extends B {
+ var c = true;
+}
+
+class D extends A {
+ var d = true;
+}
+
+class E implements C, D {
+ var a = true;
+ var b = true;
+ var c = true;
+ var d = true;
+}
+
+void main() {
+ A a = new E();
+ var b;
+ if (a is D && ((a = new D()) != null)) {
+
+ }
+ if (a is D && (b = a.d)) {
+
+ a = A();
+ }
+ if ((((a) is D) && (b = (a).d))) {
+
+ a = A();
+ }
+ if (f(a = A()) && a is D) {
+ b = a.d;
+ }
+}
+
+bool f(x) => true;
diff --git a/tests/language/type_promotion/logical_and_test.dart b/tests/language/type_promotion/logical_and_test.dart
new file mode 100644
index 0000000..ec4b756
--- /dev/null
+++ b/tests/language/type_promotion/logical_and_test.dart
@@ -0,0 +1,49 @@
+// Copyright (c) 2013, 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.
+
+// Test type promotion of locals potentially mutated.
+
+class A {
+ var a = true;
+}
+
+class B extends A {
+ var b = true;
+}
+
+class C extends B {
+ var c = true;
+}
+
+class D extends A {
+ var d = true;
+}
+
+class E implements C, D {
+ var a = true;
+ var b = true;
+ var c = true;
+ var d = true;
+}
+
+void main() {
+ A a = new E();
+ var b;
+ if (a is D && ((a = new D()) != null)) {
+ b = a.d;
+ }
+ if (a is D && (b = a.d)) {
+ b = a.d;
+ a = A();
+ }
+ if ((((a) is D) && (b = (a).d))) {
+ b = a.d;
+ a = A();
+ }
+ if (f(a = A()) && a is D) {
+ b = a.d;
+ }
+}
+
+bool f(x) => true;
diff --git a/tests/language/type_promotion/more_specific_runtime_1_test.dart b/tests/language/type_promotion/more_specific_runtime_1_test.dart
new file mode 100644
index 0000000..b6b1f54
--- /dev/null
+++ b/tests/language/type_promotion/more_specific_runtime_1_test.dart
@@ -0,0 +1,93 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2013, 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.
+
+// Test use of more specific in type promotion of interface types.
+
+class A {
+ var a;
+}
+
+class B extends A {
+ var b;
+}
+
+class C {
+ var c;
+}
+
+class D<T> {
+ T? d;
+
+ D(this.d);
+}
+
+class E<T> extends D<T> {
+ T? e;
+
+ E(e)
+ : this.e = e,
+ super(e);
+}
+
+void main() {
+ testInterface();
+ testGeneric();
+}
+
+void testInterface() {
+ var x;
+ var y;
+
+ A a = new B();
+ if (a is B) {
+ // Promotion B << A.
+ x = a.b;
+ }
+ if (a is C) {
+ // No promotion C !<< A.
+
+ }
+ B b = new B();
+ if (b is A) {
+ // No promotion B !<< A.
+
+ }
+ if (x is A) {
+ // Promotion A << dynamic.
+
+ }
+}
+
+testGeneric() {
+ var x;
+ var y;
+
+ D d1 = new E<B>(null);
+ if (d1 is E) {
+ // Promotion: E << D.
+
+ }
+ if (d1 is E<A>) {
+ // Promotion: E<A> << D.
+
+
+
+ }
+
+ D<A> d2 = new E<B>(null);
+ if (d2 is E) {
+ // No promotion: E !<< D<A>
+
+ }
+
+ D<A> d3 = new E<B>(new B());
+ if (d3 is E<B>) {
+ // Promotion: E<B> << D<A>
+
+
+ }
+}
diff --git a/tests/language/type_promotion/more_specific_runtime_2_test.dart b/tests/language/type_promotion/more_specific_runtime_2_test.dart
new file mode 100644
index 0000000..1ffb593
--- /dev/null
+++ b/tests/language/type_promotion/more_specific_runtime_2_test.dart
@@ -0,0 +1,93 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2013, 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.
+
+// Test use of more specific in type promotion of interface types.
+
+class A {
+ var a;
+}
+
+class B extends A {
+ var b;
+}
+
+class C {
+ var c;
+}
+
+class D<T> {
+ T? d;
+
+ D(this.d);
+}
+
+class E<T> extends D<T> {
+ T? e;
+
+ E(e)
+ : this.e = e,
+ super(e);
+}
+
+void main() {
+ testInterface();
+ testGeneric();
+}
+
+void testInterface() {
+ var x;
+ var y;
+
+ A a = new B();
+ if (a is B) {
+ // Promotion B << A.
+
+ }
+ if (a is C) {
+ // No promotion C !<< A.
+
+ }
+ B b = new B();
+ if (b is A) {
+ // No promotion B !<< A.
+ x = b.b;
+ }
+ if (x is A) {
+ // Promotion A << dynamic.
+
+ }
+}
+
+testGeneric() {
+ var x;
+ var y;
+
+ D d1 = new E<B>(null);
+ if (d1 is E) {
+ // Promotion: E << D.
+
+ }
+ if (d1 is E<A>) {
+ // Promotion: E<A> << D.
+
+
+
+ }
+
+ D<A> d2 = new E<B>(null);
+ if (d2 is E) {
+ // No promotion: E !<< D<A>
+
+ }
+
+ D<A> d3 = new E<B>(new B());
+ if (d3 is E<B>) {
+ // Promotion: E<B> << D<A>
+
+
+ }
+}
diff --git a/tests/language/type_promotion/more_specific_runtime_3_test.dart b/tests/language/type_promotion/more_specific_runtime_3_test.dart
new file mode 100644
index 0000000..de25d35
--- /dev/null
+++ b/tests/language/type_promotion/more_specific_runtime_3_test.dart
@@ -0,0 +1,93 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2013, 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.
+
+// Test use of more specific in type promotion of interface types.
+
+class A {
+ var a;
+}
+
+class B extends A {
+ var b;
+}
+
+class C {
+ var c;
+}
+
+class D<T> {
+ T? d;
+
+ D(this.d);
+}
+
+class E<T> extends D<T> {
+ T? e;
+
+ E(e)
+ : this.e = e,
+ super(e);
+}
+
+void main() {
+ testInterface();
+ testGeneric();
+}
+
+void testInterface() {
+ var x;
+ var y;
+
+ A a = new B();
+ if (a is B) {
+ // Promotion B << A.
+
+ }
+ if (a is C) {
+ // No promotion C !<< A.
+
+ }
+ B b = new B();
+ if (b is A) {
+ // No promotion B !<< A.
+
+ }
+ if (x is A) {
+ // Promotion A << dynamic.
+
+ }
+}
+
+testGeneric() {
+ var x;
+ var y;
+
+ D d1 = new E<B>(null);
+ if (d1 is E) {
+ // Promotion: E << D.
+ x = d1.e;
+ }
+ if (d1 is E<A>) {
+ // Promotion: E<A> << D.
+
+
+
+ }
+
+ D<A> d2 = new E<B>(null);
+ if (d2 is E) {
+ // No promotion: E !<< D<A>
+
+ }
+
+ D<A> d3 = new E<B>(new B());
+ if (d3 is E<B>) {
+ // Promotion: E<B> << D<A>
+
+
+ }
+}
diff --git a/tests/language/type_promotion/more_specific_runtime_4_test.dart b/tests/language/type_promotion/more_specific_runtime_4_test.dart
new file mode 100644
index 0000000..355e24c
--- /dev/null
+++ b/tests/language/type_promotion/more_specific_runtime_4_test.dart
@@ -0,0 +1,93 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2013, 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.
+
+// Test use of more specific in type promotion of interface types.
+
+class A {
+ var a;
+}
+
+class B extends A {
+ var b;
+}
+
+class C {
+ var c;
+}
+
+class D<T> {
+ T? d;
+
+ D(this.d);
+}
+
+class E<T> extends D<T> {
+ T? e;
+
+ E(e)
+ : this.e = e,
+ super(e);
+}
+
+void main() {
+ testInterface();
+ testGeneric();
+}
+
+void testInterface() {
+ var x;
+ var y;
+
+ A a = new B();
+ if (a is B) {
+ // Promotion B << A.
+
+ }
+ if (a is C) {
+ // No promotion C !<< A.
+
+ }
+ B b = new B();
+ if (b is A) {
+ // No promotion B !<< A.
+
+ }
+ if (x is A) {
+ // Promotion A << dynamic.
+
+ }
+}
+
+testGeneric() {
+ var x;
+ var y;
+
+ D d1 = new E<B>(null);
+ if (d1 is E) {
+ // Promotion: E << D.
+
+ }
+ if (d1 is E<A>) {
+ // Promotion: E<A> << D.
+
+
+ x = d1.e;
+ }
+
+ D<A> d2 = new E<B>(null);
+ if (d2 is E) {
+ // No promotion: E !<< D<A>
+
+ }
+
+ D<A> d3 = new E<B>(new B());
+ if (d3 is E<B>) {
+ // Promotion: E<B> << D<A>
+
+
+ }
+}
diff --git a/tests/language/type_promotion/more_specific_runtime_5_test.dart b/tests/language/type_promotion/more_specific_runtime_5_test.dart
new file mode 100644
index 0000000..74539dd
--- /dev/null
+++ b/tests/language/type_promotion/more_specific_runtime_5_test.dart
@@ -0,0 +1,93 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2013, 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.
+
+// Test use of more specific in type promotion of interface types.
+
+class A {
+ var a;
+}
+
+class B extends A {
+ var b;
+}
+
+class C {
+ var c;
+}
+
+class D<T> {
+ T? d;
+
+ D(this.d);
+}
+
+class E<T> extends D<T> {
+ T? e;
+
+ E(e)
+ : this.e = e,
+ super(e);
+}
+
+void main() {
+ testInterface();
+ testGeneric();
+}
+
+void testInterface() {
+ var x;
+ var y;
+
+ A a = new B();
+ if (a is B) {
+ // Promotion B << A.
+
+ }
+ if (a is C) {
+ // No promotion C !<< A.
+
+ }
+ B b = new B();
+ if (b is A) {
+ // No promotion B !<< A.
+
+ }
+ if (x is A) {
+ // Promotion A << dynamic.
+
+ }
+}
+
+testGeneric() {
+ var x;
+ var y;
+
+ D d1 = new E<B>(null);
+ if (d1 is E) {
+ // Promotion: E << D.
+
+ }
+ if (d1 is E<A>) {
+ // Promotion: E<A> << D.
+
+
+
+ }
+
+ D<A> d2 = new E<B>(null);
+ if (d2 is E) {
+ // No promotion: E !<< D<A>
+
+ }
+
+ D<A> d3 = new E<B>(new B());
+ if (d3 is E<B>) {
+ // Promotion: E<B> << D<A>
+ x = d3.d!.b;
+
+ }
+}
diff --git a/tests/language/type_promotion/more_specific_runtime_6_test.dart b/tests/language/type_promotion/more_specific_runtime_6_test.dart
new file mode 100644
index 0000000..e9ad755
--- /dev/null
+++ b/tests/language/type_promotion/more_specific_runtime_6_test.dart
@@ -0,0 +1,93 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2013, 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.
+
+// Test use of more specific in type promotion of interface types.
+
+class A {
+ var a;
+}
+
+class B extends A {
+ var b;
+}
+
+class C {
+ var c;
+}
+
+class D<T> {
+ T? d;
+
+ D(this.d);
+}
+
+class E<T> extends D<T> {
+ T? e;
+
+ E(e)
+ : this.e = e,
+ super(e);
+}
+
+void main() {
+ testInterface();
+ testGeneric();
+}
+
+void testInterface() {
+ var x;
+ var y;
+
+ A a = new B();
+ if (a is B) {
+ // Promotion B << A.
+
+ }
+ if (a is C) {
+ // No promotion C !<< A.
+
+ }
+ B b = new B();
+ if (b is A) {
+ // No promotion B !<< A.
+
+ }
+ if (x is A) {
+ // Promotion A << dynamic.
+
+ }
+}
+
+testGeneric() {
+ var x;
+ var y;
+
+ D d1 = new E<B>(null);
+ if (d1 is E) {
+ // Promotion: E << D.
+
+ }
+ if (d1 is E<A>) {
+ // Promotion: E<A> << D.
+
+
+
+ }
+
+ D<A> d2 = new E<B>(null);
+ if (d2 is E) {
+ // No promotion: E !<< D<A>
+
+ }
+
+ D<A> d3 = new E<B>(new B());
+ if (d3 is E<B>) {
+ // Promotion: E<B> << D<A>
+
+ x = d3.e!.b;
+ }
+}
diff --git a/tests/language/type_promotion/more_specific_runtime_test.dart b/tests/language/type_promotion/more_specific_runtime_test.dart
new file mode 100644
index 0000000..3a3e7bf
--- /dev/null
+++ b/tests/language/type_promotion/more_specific_runtime_test.dart
@@ -0,0 +1,93 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2013, 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.
+
+// Test use of more specific in type promotion of interface types.
+
+class A {
+ var a;
+}
+
+class B extends A {
+ var b;
+}
+
+class C {
+ var c;
+}
+
+class D<T> {
+ T? d;
+
+ D(this.d);
+}
+
+class E<T> extends D<T> {
+ T? e;
+
+ E(e)
+ : this.e = e,
+ super(e);
+}
+
+void main() {
+ testInterface();
+ testGeneric();
+}
+
+void testInterface() {
+ var x;
+ var y;
+
+ A a = new B();
+ if (a is B) {
+ // Promotion B << A.
+
+ }
+ if (a is C) {
+ // No promotion C !<< A.
+
+ }
+ B b = new B();
+ if (b is A) {
+ // No promotion B !<< A.
+
+ }
+ if (x is A) {
+ // Promotion A << dynamic.
+
+ }
+}
+
+testGeneric() {
+ var x;
+ var y;
+
+ D d1 = new E<B>(null);
+ if (d1 is E) {
+ // Promotion: E << D.
+
+ }
+ if (d1 is E<A>) {
+ // Promotion: E<A> << D.
+
+
+
+ }
+
+ D<A> d2 = new E<B>(null);
+ if (d2 is E) {
+ // No promotion: E !<< D<A>
+
+ }
+
+ D<A> d3 = new E<B>(new B());
+ if (d3 is E<B>) {
+ // Promotion: E<B> << D<A>
+
+
+ }
+}
diff --git a/tests/language/type_promotion/more_specific_test.dart b/tests/language/type_promotion/more_specific_test.dart
new file mode 100644
index 0000000..21392a3
--- /dev/null
+++ b/tests/language/type_promotion/more_specific_test.dart
@@ -0,0 +1,107 @@
+// Copyright (c) 2013, 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.
+
+// Test use of more specific in type promotion of interface types.
+
+class A {
+ var a;
+}
+
+class B extends A {
+ var b;
+}
+
+class C {
+ var c;
+}
+
+class D<T> {
+ T d;
+
+ D(this.d);
+}
+
+class E<T> extends D<T> {
+ T? e;
+
+ E(e)
+ : this.e = e,
+ super(e);
+}
+
+void main() {
+ testInterface();
+ testGeneric();
+}
+
+void testInterface() {
+ var x;
+ var y;
+
+ A a = new B();
+ if (a is B) {
+ // Promotion B << A.
+ x = a.b;
+ }
+ if (a is C) {
+ // No promotion C !<< A.
+ x = a.c;
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'c' isn't defined for the class 'A'.
+ }
+ B b = new B();
+ if (b is A) {
+ // No promotion B !<< A.
+ x = b.b;
+ }
+ if (x is A) {
+ // Promotion A << dynamic.
+ y = x.b;
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'b' isn't defined for the class 'A'.
+ }
+}
+
+testGeneric() {
+ var x;
+ var y;
+
+ D d1 = new E<B>(null);
+ if (d1 is E) {
+ // Promotion: E << D.
+ x = d1.e;
+ }
+ if (d1 is E<A>) {
+ // Promotion: E<A> << D.
+ int a = d1.d;
+ // ^^^^
+ // [analyzer] STATIC_TYPE_WARNING.INVALID_ASSIGNMENT
+ // ^
+ // [cfe] A value of type 'A' can't be assigned to a variable of type 'int'.
+ String b = d1.d;
+ // ^^^^
+ // [analyzer] STATIC_TYPE_WARNING.INVALID_ASSIGNMENT
+ // ^
+ // [cfe] A value of type 'A' can't be assigned to a variable of type 'String'.
+ x = d1.e;
+ }
+
+ D<A> d2 = new E<B>(null);
+ if (d2 is E) {
+ // No promotion: E !<< D<A>
+ x = d2.e;
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'e' isn't defined for the class 'D<A>'.
+ }
+
+ D<A> d3 = new E<B>(new B());
+ if (d3 is E<B>) {
+ // Promotion: E<B> << D<A>
+ x = d3.d.b;
+ x = d3.e!.b;
+ }
+}
diff --git a/tests/language/type_promotion/multiple_runtime_test.dart b/tests/language/type_promotion/multiple_runtime_test.dart
new file mode 100644
index 0000000..b465d9b
--- /dev/null
+++ b/tests/language/type_promotion/multiple_runtime_test.dart
@@ -0,0 +1,103 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2013, 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.
+
+// Test type promotion of locals.
+
+class A {
+ var a = "a";
+}
+
+class B extends A {
+ var b = "b";
+}
+
+class C extends B {
+ var c = "c";
+}
+
+class D extends A {
+ var d = "d";
+}
+
+class E implements C, D {
+ var a = "";
+ var b = "";
+ var c = "";
+ var d = "";
+}
+
+void main() {
+ test(new E());
+}
+
+void test(A a1) {
+ A a2 = new E();
+ print(a1.a);
+
+
+
+
+ print(a2.a);
+
+
+
+
+ if (a1 is B && a2 is C) {
+ print(a1.a);
+ print(a1.b);
+
+
+
+ print(a2.a);
+ print(a2.b);
+ print(a2.c);
+
+
+ if (a1 is C && a2 is D) {
+ print(a1.a);
+ print(a1.b);
+ print(a1.c);
+
+
+ print(a2.a);
+ print(a2.b);
+ print(a2.c);
+
+ }
+ }
+
+ var o1 = a1 is B && a2 is C
+ ? '${a1.a}'
+ '${a1.b}'
+
+
+ '${a2.a}'
+ '${a2.b}'
+ '${a2.c}'
+
+ : '${a1.a}'
+
+
+
+ '${a2.a}'
+
+
+
+ ;
+
+ if (a2 is C && a1 is B && a1 is C && a2 is B && a2 is D) {
+ print(a1.a);
+ print(a1.b);
+ print(a1.c);
+
+
+ print(a2.a);
+ print(a2.b);
+ print(a2.c);
+
+ }
+}
diff --git a/tests/language/type_promotion/multiple_test.dart b/tests/language/type_promotion/multiple_test.dart
new file mode 100644
index 0000000..8410597
--- /dev/null
+++ b/tests/language/type_promotion/multiple_test.dart
@@ -0,0 +1,166 @@
+// Copyright (c) 2013, 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.
+
+// Test type promotion of locals.
+
+class A {
+ var a = "a";
+}
+
+class B extends A {
+ var b = "b";
+}
+
+class C extends B {
+ var c = "c";
+}
+
+class D extends A {
+ var d = "d";
+}
+
+class E implements C, D {
+ var a = "";
+ var b = "";
+ var c = "";
+ var d = "";
+}
+
+void main() {
+ test(new E());
+}
+
+void test(A a1) {
+ A a2 = new E();
+ print(a1.a);
+ print(a1.b);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'b' isn't defined for the class 'A'.
+ print(a1.c);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'c' isn't defined for the class 'A'.
+ print(a1.d);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'd' isn't defined for the class 'A'.
+
+ print(a2.a);
+ print(a2.b);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'b' isn't defined for the class 'A'.
+ print(a2.c);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'c' isn't defined for the class 'A'.
+ print(a2.d);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'd' isn't defined for the class 'A'.
+
+ if (a1 is B && a2 is C) {
+ print(a1.a);
+ print(a1.b);
+ print(a1.c);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'c' isn't defined for the class 'B'.
+ print(a1.d);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'd' isn't defined for the class 'B'.
+
+ print(a2.a);
+ print(a2.b);
+ print(a2.c);
+ print(a2.d);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'd' isn't defined for the class 'C'.
+
+ if (a1 is C && a2 is D) {
+ print(a1.a);
+ print(a1.b);
+ print(a1.c);
+ print(a1.d);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'd' isn't defined for the class 'C'.
+
+ print(a2.a);
+ print(a2.b);
+ print(a2.c);
+ print(a2.d);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'd' isn't defined for the class 'C'.
+ }
+ }
+
+ var o1 = a1 is B && a2 is C
+ ? '${a1.a}'
+ '${a1.b}'
+ '${a1.c}'
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'c' isn't defined for the class 'B'.
+ '${a1.d}'
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'd' isn't defined for the class 'B'.
+ '${a2.a}'
+ '${a2.b}'
+ '${a2.c}'
+ '${a2.d}'
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'd' isn't defined for the class 'C'.
+ : '${a1.a}'
+ '${a1.b}'
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'b' isn't defined for the class 'A'.
+ '${a1.c}'
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'c' isn't defined for the class 'A'.
+ '${a1.d}'
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'd' isn't defined for the class 'A'.
+ '${a2.a}'
+ '${a2.b}'
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'b' isn't defined for the class 'A'.
+ '${a2.c}'
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'c' isn't defined for the class 'A'.
+ '${a2.d}'
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'd' isn't defined for the class 'A'.
+ ;
+
+ if (a2 is C && a1 is B && a1 is C && a2 is B && a2 is D) {
+ print(a1.a);
+ print(a1.b);
+ print(a1.c);
+ print(a1.d);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'd' isn't defined for the class 'C'.
+
+ print(a2.a);
+ print(a2.b);
+ print(a2.c);
+ print(a2.d);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'd' isn't defined for the class 'C'.
+ }
+}
diff --git a/tests/language/type_promotion/parameter_runtime_test.dart b/tests/language/type_promotion/parameter_runtime_test.dart
new file mode 100644
index 0000000..935dd06
--- /dev/null
+++ b/tests/language/type_promotion/parameter_runtime_test.dart
@@ -0,0 +1,211 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2013, 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.
+
+// Test type promotion of parameters.
+
+class A {
+ var a = "a";
+}
+
+class B extends A {
+ var b = "b";
+}
+
+class C extends B {
+ var c = "c";
+}
+
+class D extends A {
+ var d = "d";
+}
+
+class E implements C, D {
+ var a = "";
+ var b = "";
+ var c = "";
+ var d = "";
+}
+
+void main() {
+ test(new E());
+}
+
+void test(A a) {
+ print(a.a);
+
+
+
+
+ if (a is B) {
+ print(a.a);
+ print(a.b);
+
+
+
+ if (a is C) {
+ print(a.a);
+ print(a.b);
+ print(a.c);
+
+ }
+
+ print(a.a);
+ print(a.b);
+
+
+ }
+ if (a is C) {
+ print(a.a);
+ print(a.b);
+ print(a.c);
+
+
+ if (a is B) {
+ print(a.a);
+ print(a.b);
+ print(a.c);
+
+ }
+ if (a is D) {
+ print(a.a);
+ print(a.b);
+ print(a.c);
+
+ }
+
+ print(a.a);
+ print(a.b);
+ print(a.c);
+
+ }
+
+ print(a.a);
+
+
+
+
+ if (a is D) {
+ print(a.a);
+
+
+ print(a.d);
+ }
+
+ print(a.a);
+
+
+
+
+ var o1 = a is B
+ ? '${a.a}'
+ '${a.b}'
+
+
+ : '${a.a}'
+
+
+
+ ;
+
+ var o2 = a is C
+ ? '${a.a}'
+ '${a.b}'
+ '${a.c}'
+
+ : '${a.a}'
+
+
+
+ ;
+
+ var o3 = a is D
+ ? '${a.a}'
+
+
+ '${a.d}'
+ : '${a.a}'
+
+
+
+ ;
+
+ if (a is B && a is B) {
+ print(a.a);
+ print(a.b);
+
+
+ }
+ if (a is B && a is C) {
+ print(a.a);
+ print(a.b);
+ print(a.c);
+
+ }
+ if (a is C && a is B) {
+ print(a.a);
+ print(a.b);
+ print(a.c);
+
+ }
+ if (a is C && a is D) {
+ print(a.a);
+ print(a.b);
+ print(a.c);
+
+ }
+ if (a is D && a is C) {
+ print(a.a);
+
+
+ print(a.d);
+ }
+ if (a is D &&
+ a.a == ""
+
+
+ &&
+ a.d == "") {
+ print(a.a);
+
+
+ print(a.d);
+ }
+ if (a.a == ""
+
+
+
+ &&
+ a is B &&
+ a.a == "" &&
+ a.b == ""
+
+
+ &&
+ a is C &&
+ a.a == "" &&
+ a.b == "" &&
+ a.c == ""
+
+ ) {
+ print(a.a);
+ print(a.b);
+ print(a.c);
+
+ }
+ if ((a is B)) {
+ print(a.a);
+ print(a.b);
+
+
+ }
+ if ((a is B && (a) is C) && a is B) {
+ print(a.a);
+ print(a.b);
+ print(a.c);
+
+ }
+}
diff --git a/tests/language/type_promotion/parameter_test.dart b/tests/language/type_promotion/parameter_test.dart
new file mode 100644
index 0000000..b93d764
--- /dev/null
+++ b/tests/language/type_promotion/parameter_test.dart
@@ -0,0 +1,373 @@
+// Copyright (c) 2013, 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.
+
+// Test type promotion of parameters.
+
+class A {
+ var a = "a";
+}
+
+class B extends A {
+ var b = "b";
+}
+
+class C extends B {
+ var c = "c";
+}
+
+class D extends A {
+ var d = "d";
+}
+
+class E implements C, D {
+ var a = "";
+ var b = "";
+ var c = "";
+ var d = "";
+}
+
+void main() {
+ test(new E());
+}
+
+void test(A a) {
+ print(a.a);
+ print(a.b);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'b' isn't defined for the class 'A'.
+ print(a.c);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'c' isn't defined for the class 'A'.
+ print(a.d);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'd' isn't defined for the class 'A'.
+
+ if (a is B) {
+ print(a.a);
+ print(a.b);
+ print(a.c);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'c' isn't defined for the class 'B'.
+ print(a.d);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'd' isn't defined for the class 'B'.
+
+ if (a is C) {
+ print(a.a);
+ print(a.b);
+ print(a.c);
+ print(a.d);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'd' isn't defined for the class 'C'.
+ }
+
+ print(a.a);
+ print(a.b);
+ print(a.c);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'c' isn't defined for the class 'B'.
+ print(a.d);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'd' isn't defined for the class 'B'.
+ }
+ if (a is C) {
+ print(a.a);
+ print(a.b);
+ print(a.c);
+ print(a.d);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'd' isn't defined for the class 'C'.
+
+ if (a is B) {
+ print(a.a);
+ print(a.b);
+ print(a.c);
+ print(a.d);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'd' isn't defined for the class 'C'.
+ }
+ if (a is D) {
+ print(a.a);
+ print(a.b);
+ print(a.c);
+ print(a.d);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'd' isn't defined for the class 'C'.
+ }
+
+ print(a.a);
+ print(a.b);
+ print(a.c);
+ print(a.d);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'd' isn't defined for the class 'C'.
+ }
+
+ print(a.a);
+ print(a.b);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'b' isn't defined for the class 'A'.
+ print(a.c);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'c' isn't defined for the class 'A'.
+ print(a.d);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'd' isn't defined for the class 'A'.
+
+ if (a is D) {
+ print(a.a);
+ print(a.b);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'b' isn't defined for the class 'D'.
+ print(a.c);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'c' isn't defined for the class 'D'.
+ print(a.d);
+ }
+
+ print(a.a);
+ print(a.b);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'b' isn't defined for the class 'A'.
+ print(a.c);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'c' isn't defined for the class 'A'.
+ print(a.d);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'd' isn't defined for the class 'A'.
+
+ var o1 = a is B
+ ? '${a.a}'
+ '${a.b}'
+ '${a.c}'
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'c' isn't defined for the class 'B'.
+ '${a.d}'
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'd' isn't defined for the class 'B'.
+ : '${a.a}'
+ '${a.b}'
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'b' isn't defined for the class 'A'.
+ '${a.c}'
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'c' isn't defined for the class 'A'.
+ '${a.d}'
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'd' isn't defined for the class 'A'.
+ ;
+
+ var o2 = a is C
+ ? '${a.a}'
+ '${a.b}'
+ '${a.c}'
+ '${a.d}'
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'd' isn't defined for the class 'C'.
+ : '${a.a}'
+ '${a.b}'
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'b' isn't defined for the class 'A'.
+ '${a.c}'
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'c' isn't defined for the class 'A'.
+ '${a.d}'
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'd' isn't defined for the class 'A'.
+ ;
+
+ var o3 = a is D
+ ? '${a.a}'
+ '${a.b}'
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'b' isn't defined for the class 'D'.
+ '${a.c}'
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'c' isn't defined for the class 'D'.
+ '${a.d}'
+ : '${a.a}'
+ '${a.b}'
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'b' isn't defined for the class 'A'.
+ '${a.c}'
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'c' isn't defined for the class 'A'.
+ '${a.d}'
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'd' isn't defined for the class 'A'.
+ ;
+
+ if (a is B && a is B) {
+ print(a.a);
+ print(a.b);
+ print(a.c);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'c' isn't defined for the class 'B'.
+ print(a.d);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'd' isn't defined for the class 'B'.
+ }
+ if (a is B && a is C) {
+ print(a.a);
+ print(a.b);
+ print(a.c);
+ print(a.d);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'd' isn't defined for the class 'C'.
+ }
+ if (a is C && a is B) {
+ print(a.a);
+ print(a.b);
+ print(a.c);
+ print(a.d);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'd' isn't defined for the class 'C'.
+ }
+ if (a is C && a is D) {
+ print(a.a);
+ print(a.b);
+ print(a.c);
+ print(a.d);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'd' isn't defined for the class 'C'.
+ }
+ if (a is D && a is C) {
+ print(a.a);
+ print(a.b);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'b' isn't defined for the class 'D'.
+ print(a.c);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'c' isn't defined for the class 'D'.
+ print(a.d);
+ }
+ if (a is D &&
+ a.a == ""
+ && a.b == ""
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'b' isn't defined for the class 'D'.
+ && a.c == ""
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'c' isn't defined for the class 'D'.
+ &&
+ a.d == "") {
+ print(a.a);
+ print(a.b);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'b' isn't defined for the class 'D'.
+ print(a.c);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'c' isn't defined for the class 'D'.
+ print(a.d);
+ }
+ if (a.a == ""
+ && a.b == ""
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'b' isn't defined for the class 'A'.
+ && a.c == ""
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'c' isn't defined for the class 'A'.
+ && a.d == ""
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'd' isn't defined for the class 'A'.
+ &&
+ a is B &&
+ a.a == "" &&
+ a.b == ""
+ && a.c == ""
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'c' isn't defined for the class 'B'.
+ && a.d == ""
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'd' isn't defined for the class 'B'.
+ &&
+ a is C &&
+ a.a == "" &&
+ a.b == "" &&
+ a.c == ""
+ && a.d == ""
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'd' isn't defined for the class 'C'.
+ ) {
+ print(a.a);
+ print(a.b);
+ print(a.c);
+ print(a.d);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'd' isn't defined for the class 'C'.
+ }
+ if ((a is B)) {
+ print(a.a);
+ print(a.b);
+ print(a.c);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'c' isn't defined for the class 'B'.
+ print(a.d);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'd' isn't defined for the class 'B'.
+ }
+ if ((a is B && (a) is C) && a is B) {
+ print(a.a);
+ print(a.b);
+ print(a.c);
+ print(a.d);
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'd' isn't defined for the class 'C'.
+ }
+}
diff --git a/tests/language/type_variable/bound_access_runtime_test.dart b/tests/language/type_variable/bound_access_runtime_test.dart
new file mode 100644
index 0000000..0c7a123
--- /dev/null
+++ b/tests/language/type_variable/bound_access_runtime_test.dart
@@ -0,0 +1,32 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// 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.
+
+class DynamicClass<T extends dynamic, S extends T> {
+ T field1;
+ T field2;
+
+ DynamicClass(this.field1, this.field2);
+
+ method() => field1 * field2;
+}
+
+class NumClass<T extends num, S extends T> {
+ T field1;
+ S field2;
+
+ NumClass(this.field1, this.field2);
+
+ num method1() => field1 * field2;
+
+
+}
+
+main() {
+ new DynamicClass<num, int>(0.5, 2).method();
+ new NumClass<num, double>(2, 0.5).method1();
+
+}
diff --git a/tests/language/type_variable/bound_access_test.dart b/tests/language/type_variable/bound_access_test.dart
new file mode 100644
index 0000000..77f8594
--- /dev/null
+++ b/tests/language/type_variable/bound_access_test.dart
@@ -0,0 +1,32 @@
+// 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.
+
+class DynamicClass<T extends dynamic, S extends T> {
+ T field1;
+ T field2;
+
+ DynamicClass(this.field1, this.field2);
+
+ method() => field1 * field2;
+}
+
+class NumClass<T extends num, S extends T> {
+ T field1;
+ S field2;
+
+ NumClass(this.field1, this.field2);
+
+ num method1() => field1 * field2;
+
+ num method2() => field1 + field2.length;
+ // ^^^^^^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'length' isn't defined for the class 'num'.
+}
+
+main() {
+ new DynamicClass<num, int>(0.5, 2).method();
+ new NumClass<num, double>(2, 0.5).method1();
+ new NumClass<num, double>(2, 0.5).method2();
+}
diff --git a/tests/language/type_variable/bounds2_test.dart b/tests/language/type_variable/bounds2_test.dart
new file mode 100644
index 0000000..ecb6c9f
--- /dev/null
+++ b/tests/language/type_variable/bounds2_test.dart
@@ -0,0 +1,19 @@
+// Copyright (c) 2012, 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:expect/expect.dart";
+
+// Test of parameterized types with invalid bounds.
+
+abstract class J<T> {}
+
+abstract class I<T extends num> {}
+
+class A</*@compile-error=unspecified*/T> implements I<T>, J<T> {}
+
+main() {
+ // We are only interested in the instance creation, hence
+ // the result is assigned to `dynamic`.
+ dynamic a = /*@compile-error=unspecified*/ new A<String>();
+}
diff --git a/tests/language/type_variable/bounds3_runtime_test.dart b/tests/language/type_variable/bounds3_runtime_test.dart
new file mode 100644
index 0000000..4dd5843
--- /dev/null
+++ b/tests/language/type_variable/bounds3_runtime_test.dart
@@ -0,0 +1,21 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2013, 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.
+
+// Test of parameterized types with invalid bounds.
+
+class A<K extends int> {}
+
+class B<X, Y> {
+ foo(x) {
+
+ }
+}
+
+main() {
+ var b = new B<double, double>();
+ b.foo(new A());
+}
diff --git a/tests/language/type_variable/bounds3_test.dart b/tests/language/type_variable/bounds3_test.dart
new file mode 100644
index 0000000..471112b
--- /dev/null
+++ b/tests/language/type_variable/bounds3_test.dart
@@ -0,0 +1,22 @@
+// Copyright (c) 2013, 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.
+
+// Test of parameterized types with invalid bounds.
+
+class A<K extends int> {}
+
+class B<X, Y> {
+ foo(x) {
+ return x is A<X>;
+ // ^
+ // [cfe] Type argument 'X' doesn't conform to the bound 'int' of the type variable 'K' on 'A'.
+ // ^
+ // [analyzer] COMPILE_TIME_ERROR.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS
+ }
+}
+
+main() {
+ var b = new B<double, double>();
+ b.foo(new A());
+}
diff --git a/tests/language/type_variable/bounds4_runtime_test.dart b/tests/language/type_variable/bounds4_runtime_test.dart
new file mode 100644
index 0000000..eef3b1d
--- /dev/null
+++ b/tests/language/type_variable/bounds4_runtime_test.dart
@@ -0,0 +1,58 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2013, 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.
+
+// Test instantiation of object with malbounded types.
+
+class A<
+ T
+
+ > {}
+
+class B<T> implements A<T> {}
+
+class C<
+ T
+
+ > implements B<T> {}
+
+class Class<T> {
+ newA() {
+ new A<T>();
+ }
+ newB() {
+ new B<T>();
+ }
+ newC() {
+ new C<T>();
+ }
+}
+
+void test(f()) {
+ var v = f();
+}
+
+void main() {
+ test(() => new A<int>());
+ // TODO(eernst): Should it be a compile-time error to create an instance
+ // of this class in #01?
+ test(() => new B<int>());
+ test(() => new C<int>());
+
+ test(() => new A<String>());
+ test(() => new B<String>());
+ test(() => new C<String>());
+
+ dynamic c = new Class<int>();
+ test(() => c.newA());
+ test(() => c.newB());
+ test(() => c.newC());
+
+ c = new Class<String>();
+ test(() => c.newA());
+ test(() => c.newB());
+ test(() => c.newC());
+}
diff --git a/tests/language/type_variable/bounds4_test.dart b/tests/language/type_variable/bounds4_test.dart
new file mode 100644
index 0000000..9908b9c
--- /dev/null
+++ b/tests/language/type_variable/bounds4_test.dart
@@ -0,0 +1,75 @@
+// Copyright (c) 2013, 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.
+
+// Test instantiation of object with malbounded types.
+
+class A<
+ T
+ extends num
+ > {}
+
+class B<T> implements A<T> {}
+// ^
+// [cfe] Type argument 'T' doesn't conform to the bound 'num' of the type variable 'T' on 'A' in the supertype 'A' of class 'B'.
+// ^
+// [analyzer] COMPILE_TIME_ERROR.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS
+
+class C<
+ T
+ extends num
+ > implements B<T> {}
+
+class Class<T> {
+ newA() {
+ new A<T>();
+ // ^
+ // [cfe] Type argument 'T' doesn't conform to the bound 'num' of the type variable 'T' on 'A'.
+ // ^
+ // [analyzer] COMPILE_TIME_ERROR.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS
+ }
+ newB() {
+ new B<T>();
+ }
+ newC() {
+ new C<T>();
+ // ^
+ // [cfe] Type argument 'T' doesn't conform to the bound 'num' of the type variable 'T' on 'C'.
+ // ^
+ // [analyzer] COMPILE_TIME_ERROR.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS
+ }
+}
+
+void test(f()) {
+ var v = f();
+}
+
+void main() {
+ test(() => new A<int>());
+ // TODO(eernst): Should it be a compile-time error to create an instance
+ // of this class in #01?
+ test(() => new B<int>());
+ test(() => new C<int>());
+
+ test(() => new A<String>());
+ // ^
+ // [cfe] Type argument 'String' doesn't conform to the bound 'num' of the type variable 'T' on 'A'.
+ // ^^^^^^
+ // [analyzer] COMPILE_TIME_ERROR.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS
+ test(() => new B<String>());
+ test(() => new C<String>());
+ // ^
+ // [cfe] Type argument 'String' doesn't conform to the bound 'num' of the type variable 'T' on 'C'.
+ // ^^^^^^
+ // [analyzer] COMPILE_TIME_ERROR.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS
+
+ dynamic c = new Class<int>();
+ test(() => c.newA());
+ test(() => c.newB());
+ test(() => c.newC());
+
+ c = new Class<String>();
+ test(() => c.newA());
+ test(() => c.newB());
+ test(() => c.newC());
+}
diff --git a/tests/language/type_variable/bounds_test.dart b/tests/language/type_variable/bounds_test.dart
new file mode 100644
index 0000000..c23942d
--- /dev/null
+++ b/tests/language/type_variable/bounds_test.dart
@@ -0,0 +1,75 @@
+// Copyright (c) 2012, 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.
+
+// Test of parameterized factory methods.
+
+class Foo<T extends num> {
+ Foo();
+
+ factory Foo.bad() = XFoo; // //# 00: compile-time error
+
+ factory Foo.good() = Foo<T>;
+
+ factory Foo.IFoo() {
+ throw "uncalled";
+ }
+}
+
+abstract class IFoo<T extends num> {
+ factory IFoo() = Foo<T>; //# 11: compile-time error
+}
+
+// String is not a subtype of num.
+class Baz
+ extends Foo<String> //# 01: compile-time error
+{}
+
+class Biz extends Foo<int> {}
+
+late Foo<int> fi;
+
+// String is not a subtype of num.
+late Foo
+ <String> //# 02: compile-time error
+ fs;
+
+class Box<T> {
+ // Box.T is not guaranteed to be a subtype of num.
+ Foo<T> t; //# 03: compile-time error
+
+ makeFoo() {
+ // Box.T is not guaranteed to be a subtype of num.
+ return new Foo<T>(); //# 04: compile-time error
+ }
+}
+
+main() {
+ // String is not a subtype of num.
+ var v1 = new Foo<String>(); //# 05: compile-time error
+
+ // String is not a subtype of num.
+ Foo<String> v2 = null; //# 06: compile-time error
+
+ new Baz();
+ new Biz();
+
+ fi = new Foo();
+ fs = new Foo();
+
+ new Box().makeFoo();
+ new Box<int>().makeFoo();
+ new Box<String>().makeFoo();
+
+ // Fisk does not exist.
+ new Box<Fisk>(); //# 07: compile-time error
+
+ // Too many type arguments.
+ new Box<Object, Object>(); //# 08: compile-time error
+
+ // Fisk does not exist.
+ Box<Fisk> box = null; //# 09: compile-time error
+
+ // Too many type arguments.
+ Box<Object, Object> box = null; //# 10: compile-time error
+}
diff --git a/tests/language/type_variable/closure2_test.dart b/tests/language/type_variable/closure2_test.dart
new file mode 100644
index 0000000..1b55ff9
--- /dev/null
+++ b/tests/language/type_variable/closure2_test.dart
@@ -0,0 +1,31 @@
+// Copyright (c) 2013, 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:expect/expect.dart";
+
+class A<T> {}
+
+class C<T> {
+ a() {
+ return () => new A<T>();
+ }
+
+ list() {
+ return () => <T>[];
+ }
+
+ map() {
+ return () => <T, T>{};
+ }
+}
+
+main() {
+ Expect.isTrue(new C<int>().a()() is A<int>);
+ Expect.isFalse(new C<int>().a()() is A<String>);
+ Expect.isTrue(new C<int>().list()() is List<int>);
+ Expect.isFalse(new C<int>().list()() is List<String>);
+ Expect.isTrue(new C<int>().map()() is Map<int, int>);
+ Expect.isFalse(new C<int>().map()() is Map<String, int>);
+ Expect.isFalse(new C<int>().map()() is Map<int, String>);
+}
diff --git a/tests/language/type_variable/closure3_test.dart b/tests/language/type_variable/closure3_test.dart
new file mode 100644
index 0000000..ecb1c39
--- /dev/null
+++ b/tests/language/type_variable/closure3_test.dart
@@ -0,0 +1,18 @@
+// Copyright (c) 2016, 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:expect/expect.dart";
+
+class A<T> {}
+
+class C<T> {
+ a() {
+ return () => new A<T>();
+ }
+}
+
+main() {
+ Expect.isTrue(new C<int>().a()() is A<int>);
+ Expect.isFalse(new C<int>().a()() is A<String>);
+}
diff --git a/tests/language/type_variable/closure4_test.dart b/tests/language/type_variable/closure4_test.dart
new file mode 100644
index 0000000..e75bc08
--- /dev/null
+++ b/tests/language/type_variable/closure4_test.dart
@@ -0,0 +1,19 @@
+// Copyright (c) 2016, 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:expect/expect.dart";
+
+class A<T> {}
+
+class C<T> {
+ map() {
+ return () => <T, T>{};
+ }
+}
+
+main() {
+ Expect.isTrue(new C<int>().map()() is Map<int, int>);
+ Expect.isFalse(new C<int>().map()() is Map<String, int>);
+ Expect.isFalse(new C<int>().map()() is Map<int, String>);
+}
diff --git a/tests/language/type_variable/closure_test.dart b/tests/language/type_variable/closure_test.dart
new file mode 100644
index 0000000..7fd7381
--- /dev/null
+++ b/tests/language/type_variable/closure_test.dart
@@ -0,0 +1,27 @@
+// Copyright (c) 2013, 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:expect/expect.dart";
+
+class C<T> {
+ C.foo() {
+ x = (a) => a is T;
+ }
+ C.bar() {
+ x = (a) => a is! T;
+ }
+ C.baz() {
+ x = (a) => a as T;
+ }
+ var x;
+}
+
+main() {
+ Expect.isTrue(new C<int>.foo().x(1));
+ Expect.isFalse(new C<int>.foo().x('1'));
+ Expect.isFalse(new C<int>.bar().x(1));
+ Expect.isTrue(new C<int>.bar().x('1'));
+ Expect.equals(new C<int>.baz().x(1), 1);
+ Expect.throws(() => new C<int>.baz().x('1'));
+}
diff --git a/tests/language/type_variable/conflict2_runtime_test.dart b/tests/language/type_variable/conflict2_runtime_test.dart
new file mode 100644
index 0000000..275f8dc
--- /dev/null
+++ b/tests/language/type_variable/conflict2_runtime_test.dart
@@ -0,0 +1,59 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// 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.
+
+// Regression test for issue 13134. Invocation of a type parameter.
+
+import "package:expect/expect.dart";
+
+class C<T> {
+ noSuchMethod(Invocation im) {
+ throw "noSuchMethod shouldn't be called in this test.";
+ }
+
+ // This is equivalent to (T).call(). See issue 19725
+
+
+ // T is in scope, even in static context. Compile-time error to call this.T().
+
+
+ // X is not in scope. NoSuchMethodError.
+
+
+ // Class 'C' has no static method 'T': NoSuchMethodError.
+
+
+ // Class '_Type' has no instance method 'call': NoSuchMethodError.
+
+
+ // Runtime type T not accessible from static context. Compile-time error.
+
+
+ // Class '_Type' has no [] operator: NoSuchMethodError.
+
+
+ // Runtime type T not accessible from static context. Compile-time error.
+
+
+ // Class '_Type' has no member m: NoSuchMethodError.
+
+
+ // Runtime type T not accessible from static context. Compile-time error.
+
+}
+
+main() {
+
+
+
+
+
+
+
+
+
+
+}
diff --git a/tests/language/type_variable/conflict2_test.dart b/tests/language/type_variable/conflict2_test.dart
new file mode 100644
index 0000000..4a03e01
--- /dev/null
+++ b/tests/language/type_variable/conflict2_test.dart
@@ -0,0 +1,99 @@
+// 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.
+
+// Regression test for issue 13134. Invocation of a type parameter.
+
+import "package:expect/expect.dart";
+
+class C<T> {
+ noSuchMethod(Invocation im) {
+ throw "noSuchMethod shouldn't be called in this test.";
+ }
+
+ // This is equivalent to (T).call(). See issue 19725
+ foo() => T();
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.INVOCATION_OF_NON_FUNCTION
+ // [cfe] Method not found: 'T'.
+
+ // T is in scope, even in static context. Compile-time error to call this.T().
+ static bar() => T();
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.INVOCATION_OF_NON_FUNCTION
+ // [cfe] Method not found: 'T'.
+ // ^
+ // [analyzer] STATIC_WARNING.TYPE_PARAMETER_REFERENCED_BY_STATIC
+
+ // X is not in scope. NoSuchMethodError.
+ static baz() => X();
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_METHOD
+ // [cfe] Method not found: 'X'.
+
+ // Class 'C' has no static method 'T': NoSuchMethodError.
+ static qux() => C.T();
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_METHOD
+ // [cfe] Method not found: 'C.T'.
+
+ // Class '_Type' has no instance method 'call': NoSuchMethodError.
+ quux() => (T)();
+ // ^^^
+ // [analyzer] STATIC_TYPE_WARNING.INVOCATION_OF_NON_FUNCTION_EXPRESSION
+ // ^
+ // [cfe] The method 'call' isn't defined for the class 'Type'.
+
+ // Runtime type T not accessible from static context. Compile-time error.
+ static corge() => (T)();
+ // ^^^
+ // [analyzer] STATIC_TYPE_WARNING.INVOCATION_OF_NON_FUNCTION_EXPRESSION
+ // ^
+ // [analyzer] STATIC_WARNING.TYPE_PARAMETER_REFERENCED_BY_STATIC
+ // [cfe] Type variables can't be used in static members.
+ // ^
+ // [cfe] The method 'call' isn't defined for the class 'Type'.
+
+ // Class '_Type' has no [] operator: NoSuchMethodError.
+ grault() => T[0];
+ // ^^^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_OPERATOR
+ // [cfe] The operator '[]' isn't defined for the class 'Type'.
+
+ // Runtime type T not accessible from static context. Compile-time error.
+ static garply() => T[0];
+ // ^
+ // [analyzer] STATIC_WARNING.TYPE_PARAMETER_REFERENCED_BY_STATIC
+ // [cfe] Type variables can't be used in static members.
+ // ^^^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_OPERATOR
+ // [cfe] The operator '[]' isn't defined for the class 'Type'.
+
+ // Class '_Type' has no member m: NoSuchMethodError.
+ waldo() => T.m;
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'm' isn't defined for the class 'Type'.
+
+ // Runtime type T not accessible from static context. Compile-time error.
+ static fred() => T.m;
+ // ^
+ // [analyzer] STATIC_WARNING.TYPE_PARAMETER_REFERENCED_BY_STATIC
+ // [cfe] Type variables can't be used in static members.
+ // ^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
+ // [cfe] The getter 'm' isn't defined for the class 'Type'.
+}
+
+main() {
+ Expect.throwsNoSuchMethodError(() => new C().foo());
+ C.bar();
+ Expect.throwsNoSuchMethodError(() => C.baz());
+ Expect.throwsNoSuchMethodError(() => C.qux());
+ Expect.throwsNoSuchMethodError(() => new C().quux());
+ C.corge();
+ Expect.throwsNoSuchMethodError(() => new C().grault());
+ C.garply();
+ Expect.throwsNoSuchMethodError(() => new C().waldo());
+ C.fred();
+}
diff --git a/tests/language/type_variable/conflict_runtime_test.dart b/tests/language/type_variable/conflict_runtime_test.dart
new file mode 100644
index 0000000..04623cb
--- /dev/null
+++ b/tests/language/type_variable/conflict_runtime_test.dart
@@ -0,0 +1,54 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2013, 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.
+
+// Test that we report a compile-time error when a type parameter conflicts
+// with an instance or static member with the same name.
+
+import "package:expect/expect.dart";
+
+class G1<T> {
+
+}
+
+class G2<T> {
+
+}
+
+class G3<T> {
+
+}
+
+class G4<T> {
+
+}
+
+class G5<T> {
+
+}
+
+class G6<T> {
+
+}
+
+class G7<T> {
+
+}
+
+class G8<T> {
+
+}
+
+main() {
+ new G1<int>();
+ new G2<int>();
+ new G3<int>();
+ new G4<int>();
+ new G5<int>();
+ new G6<int>();
+ new G7<int>();
+ new G8<int>();
+}
diff --git a/tests/language/type_variable/conflict_test.dart b/tests/language/type_variable/conflict_test.dart
new file mode 100644
index 0000000..cc8e4da
--- /dev/null
+++ b/tests/language/type_variable/conflict_test.dart
@@ -0,0 +1,83 @@
+// Copyright (c) 2013, 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.
+
+// Test that we report a compile-time error when a type parameter conflicts
+// with an instance or static member with the same name.
+
+import "package:expect/expect.dart";
+
+class G1<T> {
+// ^
+// [analyzer] COMPILE_TIME_ERROR.CONFLICTING_TYPE_VARIABLE_AND_MEMBER
+ var T;
+ // ^
+ // [cfe] Conflicts with type variable 'T'.
+}
+
+class G2<T> {
+// ^
+// [analyzer] COMPILE_TIME_ERROR.CONFLICTING_TYPE_VARIABLE_AND_MEMBER
+ get T {}
+ // ^
+ // [cfe] Conflicts with type variable 'T'.
+}
+
+class G3<T> {
+// ^
+// [analyzer] COMPILE_TIME_ERROR.CONFLICTING_TYPE_VARIABLE_AND_MEMBER
+ T() {}
+//^
+// [cfe] Conflicts with type variable 'T'.
+}
+
+class G4<T> {
+// ^
+// [analyzer] COMPILE_TIME_ERROR.CONFLICTING_TYPE_VARIABLE_AND_MEMBER
+ static var T;
+ // ^
+ // [cfe] Conflicts with type variable 'T'.
+}
+
+class G5<T> {
+// ^
+// [analyzer] COMPILE_TIME_ERROR.CONFLICTING_TYPE_VARIABLE_AND_MEMBER
+ static get T {}
+ // ^
+ // [cfe] Conflicts with type variable 'T'.
+}
+
+class G6<T> {
+// ^
+// [analyzer] COMPILE_TIME_ERROR.CONFLICTING_TYPE_VARIABLE_AND_MEMBER
+ static T() {}
+ // ^
+ // [cfe] Conflicts with type variable 'T'.
+}
+
+class G7<T> {
+// ^
+// [analyzer] COMPILE_TIME_ERROR.CONFLICTING_TYPE_VARIABLE_AND_MEMBER
+ set T(_) {}
+ // ^
+ // [cfe] Conflicts with type variable 'T'.
+}
+
+class G8<T> {
+// ^
+// [analyzer] COMPILE_TIME_ERROR.CONFLICTING_TYPE_VARIABLE_AND_MEMBER
+ static set T(x) {}
+ // ^
+ // [cfe] Conflicts with type variable 'T'.
+}
+
+main() {
+ new G1<int>();
+ new G2<int>();
+ new G3<int>();
+ new G4<int>();
+ new G5<int>();
+ new G6<int>();
+ new G7<int>();
+ new G8<int>();
+}
diff --git a/tests/language/type_variable/field_initializer2_test.dart b/tests/language/type_variable/field_initializer2_test.dart
new file mode 100644
index 0000000..5249bf2
--- /dev/null
+++ b/tests/language/type_variable/field_initializer2_test.dart
@@ -0,0 +1,21 @@
+// Copyright (c) 2016, 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.
+
+// Check that an inlined field initializer has access to the enclosing
+// type variables.
+
+import "package:expect/expect.dart";
+
+class A<T> {
+ var c = new X<T>();
+}
+
+class B<T> extends A<T> {}
+
+class X<T> {}
+
+main() {
+ Expect.isTrue(new B<int>().c is X<int>);
+ Expect.isFalse(new B<String>().c is X<int>);
+}
diff --git a/tests/language/type_variable/field_initializer_closure2_test.dart b/tests/language/type_variable/field_initializer_closure2_test.dart
new file mode 100644
index 0000000..f3536ca
--- /dev/null
+++ b/tests/language/type_variable/field_initializer_closure2_test.dart
@@ -0,0 +1,21 @@
+// Copyright (c) 2016, 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.
+
+// Check that an inlined field closure has access to the enclosing
+// type variables.
+
+import "package:expect/expect.dart";
+
+class A<T> {
+ var c = (() => new X<T>())();
+}
+
+class B<T> extends A<T> {}
+
+class X<T> {}
+
+main() {
+ Expect.isTrue(new B<int>().c is X<int>);
+ Expect.isFalse(new B<String>().c is X<int>);
+}
diff --git a/tests/language/type_variable/field_initializer_closure_test.dart b/tests/language/type_variable/field_initializer_closure_test.dart
new file mode 100644
index 0000000..3b27750
--- /dev/null
+++ b/tests/language/type_variable/field_initializer_closure_test.dart
@@ -0,0 +1,19 @@
+// Copyright (c) 2013, 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.
+
+// Check that an inlined field closure has access to the enclosing
+// type variables.
+
+import "package:expect/expect.dart";
+
+class A<T> {
+ var c = (() => <T>[])();
+}
+
+class B<T> extends A<T> {}
+
+main() {
+ Expect.isTrue(new B<int>().c is List<int>);
+ Expect.isFalse(new B<String>().c is List<int>);
+}
diff --git a/tests/language/type_variable/field_initializer_test.dart b/tests/language/type_variable/field_initializer_test.dart
new file mode 100644
index 0000000..09331c9
--- /dev/null
+++ b/tests/language/type_variable/field_initializer_test.dart
@@ -0,0 +1,19 @@
+// Copyright (c) 2013, 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.
+
+// Check that an inlined field initializer has access to the enclosing
+// type variables.
+
+import "package:expect/expect.dart";
+
+class A<T> {
+ var c = <T>[];
+}
+
+class B<T> extends A<T> {}
+
+main() {
+ Expect.isTrue(new B<int>().c is List<int>);
+ Expect.isFalse(new B<String>().c is List<int>);
+}
diff --git a/tests/language/type_variable/function_type_test.dart b/tests/language/type_variable/function_type_test.dart
new file mode 100644
index 0000000..f2b3602
--- /dev/null
+++ b/tests/language/type_variable/function_type_test.dart
@@ -0,0 +1,27 @@
+// Copyright (c) 2013, 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:expect/expect.dart';
+
+typedef T Func<T>();
+
+class Foo<S> {
+ m(x) => x is Func<S>;
+}
+
+class Bar<T> {
+ f() {
+ T local() {
+ throw "uncalled";
+ }
+ return local;
+ }
+}
+
+void main() {
+ dynamic x = new Foo<List<String>>();
+ if (new DateTime.now().millisecondsSinceEpoch == 42) x = new Foo<int>();
+ Expect.isFalse(x.m(new Bar<String>().f()));
+ Expect.isTrue(x.m(new Bar<List<String>>().f()));
+}
diff --git a/tests/language/type_variable/identifier_expression_test.dart b/tests/language/type_variable/identifier_expression_test.dart
new file mode 100644
index 0000000..1827c56
--- /dev/null
+++ b/tests/language/type_variable/identifier_expression_test.dart
@@ -0,0 +1,20 @@
+// Copyright (c) 2012, 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.
+// VMOptions=--enable_type_checks
+
+class A {
+ static func() {
+ return "class A";
+ }
+}
+
+class B<T> {
+ doFunc() {
+ T.func(); /*@compile-error=unspecified*/
+ }
+}
+
+main() {
+ new B<A>().doFunc();
+}
diff --git a/tests/language/type_variable/initializer_test.dart b/tests/language/type_variable/initializer_test.dart
new file mode 100644
index 0000000..3d5a85d
--- /dev/null
+++ b/tests/language/type_variable/initializer_test.dart
@@ -0,0 +1,24 @@
+// Copyright (c) 2013, 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:expect/expect.dart";
+
+// Regression test for dart2js where the reference to [:this:] in a
+// constructor was not propagated to the super initializers.
+
+class A<T> {
+ var map;
+ // Usage of type variables in the initializer makes the SSA builder
+ // want to access [:this:]. And because the initializers of A are
+ // inlined in the constructor of B, we have to make sure the
+ // [:this:] in the A constructor has a corresponding
+ // SSA instruction.
+ A() : map = new Map<T, T>();
+}
+
+class B<T> extends A<T> {}
+
+main() {
+ Expect.isTrue(new B<int>().map is Map<int, int>);
+}
diff --git a/tests/language/type_variable/nested_test.dart b/tests/language/type_variable/nested_test.dart
new file mode 100644
index 0000000..423ce6d
--- /dev/null
+++ b/tests/language/type_variable/nested_test.dart
@@ -0,0 +1,32 @@
+// Copyright (c) 2013, 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.
+
+// Regression test for
+// http://code.google.com/p/dart/issues/detail?id=9050.
+
+import 'package:expect/expect.dart';
+
+class A<T> {}
+
+class B<T> {
+ var _copy;
+ B() {
+ // We used to not register the dependency between List and B.
+ _copy = <A<T>>[];
+ }
+}
+
+main() {
+ var a = new B();
+ Expect.isFalse(a._copy is List<int>);
+ Expect.isTrue(a._copy is List<A>);
+ Expect.isFalse(a._copy is List<A<int>>); //# 01: ok
+
+ a = new B<String>();
+ Expect.isFalse(a._copy is List<String>);
+ Expect.isTrue(a._copy is List<A>);
+ Expect.isTrue(a._copy is List<A<String>>);
+ Expect.isTrue(a._copy is List<A<Object>>);
+ Expect.isFalse(a._copy is List<A<int>>);
+}
diff --git a/tests/language/type_variable/promotion_issue39752_test.dart b/tests/language/type_variable/promotion_issue39752_test.dart
new file mode 100644
index 0000000..731557d
--- /dev/null
+++ b/tests/language/type_variable/promotion_issue39752_test.dart
@@ -0,0 +1,13 @@
+// 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.
+
+main() {}
+
+void f<T>(T a) {
+ if (a is num) {
+ if (a is int) {
+ a.isEven;
+ }
+ }
+}
diff --git a/tests/language/type_variable/promotion_test.dart b/tests/language/type_variable/promotion_test.dart
new file mode 100644
index 0000000..c0a507c
--- /dev/null
+++ b/tests/language/type_variable/promotion_test.dart
@@ -0,0 +1,40 @@
+// 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 'package:expect/expect.dart';
+
+class A {}
+
+class B extends A {}
+
+class Foo<T extends A> {
+ String foo(T x) {
+ if (x is B) {
+ var list = [x];
+ return list.runtimeType.toString();
+ }
+ return '';
+ }
+
+ List<T> bar(T x) {
+ var tlist = <T>[];
+ if (x is B) {
+ var list = [x];
+ tlist = list;
+ }
+ return tlist;
+ }
+}
+
+main() {
+ var foo = new Foo<B>();
+ var b = new B();
+
+ // List class has many different platform specific implementations
+ // so we can't rely on the fact that all of them have the same
+ // user visible name. Instead we build a name for List<B> from List<A>.
+ final expected = (<A>[]).runtimeType.toString().replaceAll('<$A>', '<$B>');
+ Expect.equals(expected, foo.foo(b));
+ Expect.listEquals([b], foo.bar(b));
+}
diff --git a/tests/language/type_variable/scope2_test.dart b/tests/language/type_variable/scope2_test.dart
new file mode 100644
index 0000000..6893e2d
--- /dev/null
+++ b/tests/language/type_variable/scope2_test.dart
@@ -0,0 +1,16 @@
+// Copyright (c) 2012, 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:expect/expect.dart";
+
+// Test that malformed type arguments are treated as an error.
+
+class Foo<T> {
+ // T is not in scope for a static method.
+ static Foo<T> m() { /*@compile-error=unspecified*/
+ return new Foo();
+ }
+}
+
+main() {}
diff --git a/tests/language/type_variable/scope3_runtime_test.dart b/tests/language/type_variable/scope3_runtime_test.dart
new file mode 100644
index 0000000..e75b4a0
--- /dev/null
+++ b/tests/language/type_variable/scope3_runtime_test.dart
@@ -0,0 +1,20 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2012, 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.
+
+// Test that a type parameter cannot be repeated.
+
+class Foo<
+ T
+
+ > {}
+
+main() {
+ new Foo<
+ String
+
+ >();
+}
diff --git a/tests/language/type_variable/scope3_test.dart b/tests/language/type_variable/scope3_test.dart
new file mode 100644
index 0000000..9a38200
--- /dev/null
+++ b/tests/language/type_variable/scope3_test.dart
@@ -0,0 +1,20 @@
+// Copyright (c) 2012, 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.
+
+// Test that a type parameter cannot be repeated.
+
+class Foo<
+ T
+ , T
+ //^
+ // [analyzer] COMPILE_TIME_ERROR.DUPLICATE_DEFINITION
+ // [cfe] A type variable can't have the same name as another.
+ > {}
+
+main() {
+ new Foo<
+ String
+ , String
+ >();
+}
diff --git a/tests/language/type_variable/scope_runtime_test.dart b/tests/language/type_variable/scope_runtime_test.dart
new file mode 100644
index 0000000..7212278
--- /dev/null
+++ b/tests/language/type_variable/scope_runtime_test.dart
@@ -0,0 +1,52 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2012, 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.
+
+// Test that type variables referenced from within static members are malformed.
+
+class Foo<T> implements I<T> {
+ Foo() {}
+
+ static
+
+ m(
+
+ f) {
+
+ return new Foo<String>();
+ }
+
+ // T is in scope for a factory method.
+ factory Foo.I(Foo<T> f) {
+ Foo<T> x = f;
+ return f;
+ }
+
+ // T is not in scope for a static field.
+
+
+ static
+
+ get f {
+ return new Foo<String>();
+ }
+
+ static void set f(
+
+ value) {}
+}
+
+abstract class I<T> {
+ factory I(Foo<T> f) = Foo<T>.I;
+}
+
+main() {
+ Foo.m(new Foo<String>());
+ new I(new Foo<String>());
+
+ var x = Foo.f;
+ Foo.f = x;
+}
diff --git a/tests/language/type_variable/scope_test.dart b/tests/language/type_variable/scope_test.dart
new file mode 100644
index 0000000..9930d68
--- /dev/null
+++ b/tests/language/type_variable/scope_test.dart
@@ -0,0 +1,85 @@
+// Copyright (c) 2012, 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.
+
+// Test that type variables referenced from within static members are malformed.
+
+class Foo<T> implements I<T> {
+ Foo() {}
+
+ static
+ Foo<T>
+ // ^
+ // [analyzer] STATIC_WARNING.TYPE_PARAMETER_REFERENCED_BY_STATIC
+ m(
+// ^
+// [cfe] Can only use type variables in instance methods.
+ Foo<T>
+ // ^
+ // [analyzer] STATIC_WARNING.TYPE_PARAMETER_REFERENCED_BY_STATIC
+ // [cfe] Type variables can't be used in static members.
+ f) {
+ Foo<T> x = new Foo<String>();
+ // ^
+ // [analyzer] STATIC_WARNING.TYPE_PARAMETER_REFERENCED_BY_STATIC
+ // [cfe] Type variables can't be used in static members.
+ // ^^^^^^^^^^^^^^^^^
+ // [analyzer] STATIC_TYPE_WARNING.INVALID_ASSIGNMENT
+ return new Foo<String>();
+ // ^^^^^^^^^^^^^^^^^
+ // [analyzer] STATIC_TYPE_WARNING.RETURN_OF_INVALID_TYPE
+ }
+
+ // T is in scope for a factory method.
+ factory Foo.I(Foo<T> f) {
+ Foo<T> x = f;
+ return f;
+ }
+
+ // T is not in scope for a static field.
+ static late Foo<T> f1;
+ // ^
+ // [analyzer] STATIC_WARNING.TYPE_PARAMETER_REFERENCED_BY_STATIC
+ // [cfe] Type variables can't be used in static members.
+ // ^
+ // [cfe] Verification of the generated program failed:
+
+ static
+ Foo<T>
+ // ^
+ // [analyzer] STATIC_WARNING.TYPE_PARAMETER_REFERENCED_BY_STATIC
+ get f {
+ // ^
+ // [cfe] Can only use type variables in instance methods.
+ return new Foo<String>();
+ // ^^^^^^^^^^^^^^^^^
+ // [analyzer] STATIC_TYPE_WARNING.RETURN_OF_INVALID_TYPE
+ }
+
+ static void set f(
+ // ^
+ // [cfe] Can only use type variables in instance methods.
+ Foo<T>
+ // ^
+ // [analyzer] STATIC_WARNING.TYPE_PARAMETER_REFERENCED_BY_STATIC
+ // [cfe] Type variables can't be used in static members.
+ value) {}
+}
+
+abstract class I<T> {
+ factory I(Foo<T> f) = Foo<T>.I;
+}
+
+main() {
+ Foo.m(new Foo<String>());
+ // ^^^^^^^^^^^^^^^^^
+ // [analyzer] STATIC_WARNING.ARGUMENT_TYPE_NOT_ASSIGNABLE
+ new I(new Foo<String>());
+ Foo.f1 = new Foo<String>();
+ // ^^^^^^^^^^^^^^^^^
+ // [analyzer] STATIC_TYPE_WARNING.INVALID_ASSIGNMENT
+ // ^
+ // [cfe] A value of type 'Foo<String>' can't be assigned to a variable of type 'Foo<T>'.
+ var x = Foo.f;
+ Foo.f = x;
+}
diff --git a/tests/language/type_variable/static_context_test.dart b/tests/language/type_variable/static_context_test.dart
new file mode 100644
index 0000000..e0cd2bc
--- /dev/null
+++ b/tests/language/type_variable/static_context_test.dart
@@ -0,0 +1,17 @@
+// Copyright (c) 2012, 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.
+
+// A type variable can't be referenced in a static class
+
+class A<T> {
+ static int method() {
+ // error, can't reference a type variable in a static context
+ var foo =
+ new T(); /*@compile-error=unspecified*/
+ }
+}
+
+main() {
+ A.method();
+}
diff --git a/tests/language/type_variable/typedef_test.dart b/tests/language/type_variable/typedef_test.dart
new file mode 100644
index 0000000..caa1649
--- /dev/null
+++ b/tests/language/type_variable/typedef_test.dart
@@ -0,0 +1,30 @@
+// Copyright (c) 2013, 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.
+
+// Test that rti dependency registration takes type variables within typedefs
+// into account.
+
+import 'package:expect/expect.dart';
+
+typedef Foo<T>(T t);
+
+class A<T> {
+ m() => new B<Foo<T>>();
+}
+
+class B<T> {
+ m(o) => o is T;
+}
+
+foo(int i) {}
+bar(String s) {}
+
+void main() {
+ Expect.isTrue(new A<int>().m().m(foo));
+ Expect.isFalse(new A<int>().m().m(bar));
+ Expect.isFalse(new A<String>().m().m(foo));
+ Expect.isTrue(new A<String>().m().m(bar));
+ Expect.isFalse(new A<double>().m().m(foo));
+ Expect.isFalse(new A<double>().m().m(bar));
+}
diff --git a/tests/language/typedef/bad_typedef_runtime_test.dart b/tests/language/typedef/bad_typedef_runtime_test.dart
new file mode 100644
index 0000000..274f7e7
--- /dev/null
+++ b/tests/language/typedef/bad_typedef_runtime_test.dart
@@ -0,0 +1,19 @@
+// TODO(multitest): This was automatically migrated from a multitest and may
+// contain strange or dead code.
+
+// Copyright (c) 2016, 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.
+// Dart test for a function type test that cannot be eliminated at compile time.
+
+import "package:expect/expect.dart";
+
+typedef int H(
+ Function
+
+ x);
+
+main() {
+ bool b = true;
+ Expect.isFalse(b is H);
+}
diff --git a/tests/language/typedef/bad_typedef_test.dart b/tests/language/typedef/bad_typedef_test.dart
new file mode 100644
index 0000000..c49eea8
--- /dev/null
+++ b/tests/language/typedef/bad_typedef_test.dart
@@ -0,0 +1,19 @@
+// Copyright (c) 2016, 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.
+// Dart test for a function type test that cannot be eliminated at compile time.
+
+import "package:expect/expect.dart";
+
+typedef int H(
+ Function
+ Function
+ x);
+// ^
+// [analyzer] SYNTACTIC_ERROR.EXPECTED_TOKEN
+// [cfe] Expected ')' before this.
+
+main() {
+ bool b = true;
+ Expect.isFalse(b is H);
+}
diff --git a/tests/language/typedef/check_bounds_generic_test.dart b/tests/language/typedef/check_bounds_generic_test.dart
new file mode 100644
index 0000000..a96fd1f
--- /dev/null
+++ b/tests/language/typedef/check_bounds_generic_test.dart
@@ -0,0 +1,13 @@
+// Copyright (c) 2018, 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.
+
+// Check that typedef type parameters are verified to satisfy their bounds.
+
+typedef F<T extends num> = T Function<U>(T x);
+
+void g(/*@compile-error=unspecified*/ F<String>? f) {}
+
+main() {
+ g(null);
+}
diff --git a/tests/language/typedef/check_bounds_test.dart b/tests/language/typedef/check_bounds_test.dart
new file mode 100644
index 0000000..45018ec
--- /dev/null
+++ b/tests/language/typedef/check_bounds_test.dart
@@ -0,0 +1,13 @@
+// Copyright (c) 2018, 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.
+
+// Check that typedef type parameters are verified to satisfy their bounds.
+
+typedef T F<T extends num>(T x);
+
+void g(/*@compile-error=unspecified*/ F<String>? f) {}
+
+main() {
+ g(null);
+}
diff --git a/tests/language/typedef/check_bounds_unused_test.dart b/tests/language/typedef/check_bounds_unused_test.dart
new file mode 100644
index 0000000..8f9bed5
--- /dev/null
+++ b/tests/language/typedef/check_bounds_unused_test.dart
@@ -0,0 +1,14 @@
+// Copyright (c) 2018, 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.
+
+// Check that typedef type parameters are verified to satisfy their bounds, even
+// if the type parameter in question isn't used by the typedef.
+
+typedef void F<T extends num>();
+
+void g(/*@compile-error=unspecified*/ F<String>? f) {}
+
+main() {
+ g(null);
+}
diff --git a/tests/language/typedef/class_in_other_file_helper.dart b/tests/language/typedef/class_in_other_file_helper.dart
new file mode 100644
index 0000000..3b66850
--- /dev/null
+++ b/tests/language/typedef/class_in_other_file_helper.dart
@@ -0,0 +1,5 @@
+// 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.
+
+class Bar {}
diff --git a/tests/language/typedef/class_in_other_file_test.dart b/tests/language/typedef/class_in_other_file_test.dart
new file mode 100644
index 0000000..c4a5b1a
--- /dev/null
+++ b/tests/language/typedef/class_in_other_file_test.dart
@@ -0,0 +1,13 @@
+// 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.
+
+// This has crashed DDC with Kernel because of a
+// "Concurrent modification during iteration" exception.
+
+import 'class_in_other_file_helper.dart';
+
+typedef bool Foo1(bool baz);
+typedef bool Foo2(Bar baz);
+
+main() {}
diff --git a/tests/language/typedef/cyclic_test.dart b/tests/language/typedef/cyclic_test.dart
new file mode 100644
index 0000000..6da9ad6
--- /dev/null
+++ b/tests/language/typedef/cyclic_test.dart
@@ -0,0 +1,69 @@
+// Copyright (c) 2013, 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.
+
+// Check that cyclic reference of a typedef is a compile-time error.
+
+// To test various cyclic references the definition of the [:typedef A():] is
+// split over several lines:
+typedef
+
+// Cyclic through return type.
+A //# 01: compile-time error
+
+ A // The name of the typedef
+
+// Cyclic through type variable bound.
+<T extends A> //# 10: compile-time error
+
+// Cyclic through generic type variable bound.
+<T extends List<A>> //# 11: compile-time error
+
+ (// The left parenthesis of the typedef arguments.
+
+// Cyclic through parameter type.
+A a //# 02: compile-time error
+
+// Cyclic through optional parameter type.
+[A a] //# 03: compile-time error
+
+// Cyclic through named parameter type.
+{A a} //# 04: compile-time error
+
+// Cyclic through generic parameter type.
+List<A> a //# 05: compile-time error
+
+// Cyclic through return type of function typed parameter.
+A f() //# 06: compile-time error
+
+// Cyclic through parameter type of function typed parameter.
+f(A a) //# 07: compile-time error
+
+// Cyclic through another typedef.
+B b //# 08: compile-time error
+
+// Cyclic through another more typedefs.
+C c //# 09: compile-time error
+
+// Reference through a class is not a cyclic self-reference.
+Class c //# 12: ok
+
+// Reference through a class type bound is not a cyclic self-reference.
+Class c //# 13: compile-time error
+
+ ); // The right parenthesis of the typedef arguments.
+
+typedef B(A a);
+typedef C(B b);
+
+class Class
+<T extends A> //# 13: continued
+{
+ A? a; //# 12: continued
+}
+
+void testA(A? a) {}
+
+void main() {
+ testA(null);
+}
diff --git a/tests/language/typedef/is_test.dart b/tests/language/typedef/is_test.dart
new file mode 100644
index 0000000..e8c4d3b
--- /dev/null
+++ b/tests/language/typedef/is_test.dart
@@ -0,0 +1,125 @@
+// Copyright (c) 2012, 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:expect/expect.dart";
+
+// Test is-test of typedefs with optional and named parameters.
+
+typedef int Func1(int a);
+typedef int Func2(int a, [int b]);
+typedef int Func3(int a, [int b, int c]);
+typedef int Func4([int a, int b, int c]);
+typedef int Func5(int a, {int b});
+typedef int Func6(int a, {int b, int c});
+typedef int Func7({int a, int b, int c});
+
+void main() {
+ int func1(int i) => -1;
+ Expect.isTrue(func1 is Func1);
+ Expect.isFalse(func1 is Func2);
+ Expect.isFalse(func1 is Func3);
+ Expect.isFalse(func1 is Func4);
+ Expect.isFalse(func1 is Func5);
+ Expect.isFalse(func1 is Func6);
+ Expect.isFalse(func1 is Func7);
+
+ int func2(int i, int j) => -1;
+ Expect.isFalse(func2 is Func1);
+ Expect.isFalse(func2 is Func2);
+ Expect.isFalse(func2 is Func3);
+ Expect.isFalse(func2 is Func4);
+ Expect.isFalse(func2 is Func5);
+ Expect.isFalse(func2 is Func6);
+ Expect.isFalse(func2 is Func7);
+
+ int func3(int i, int j, int k) => -1;
+ Expect.isFalse(func3 is Func1);
+ Expect.isFalse(func3 is Func2);
+ Expect.isFalse(func3 is Func3);
+ Expect.isFalse(func3 is Func4);
+ Expect.isFalse(func3 is Func5);
+ Expect.isFalse(func3 is Func6);
+ Expect.isFalse(func3 is Func7);
+
+ int func4(int i, [int j = -1]) => -1;
+ Expect.isTrue(func4 is Func1);
+ Expect.isTrue(func4 is Func2);
+ Expect.isFalse(func4 is Func3);
+ Expect.isFalse(func4 is Func4);
+ Expect.isFalse(func4 is Func5);
+ Expect.isFalse(func4 is Func6);
+ Expect.isFalse(func4 is Func7);
+
+ int func5(int i, [int j = -1, int k = -1]) => -1;
+ Expect.isTrue(func5 is Func1);
+ Expect.isTrue(func5 is Func2);
+ Expect.isTrue(func5 is Func3);
+ Expect.isFalse(func5 is Func4);
+ Expect.isFalse(func5 is Func5);
+ Expect.isFalse(func5 is Func6);
+ Expect.isFalse(func5 is Func7);
+
+ int func6([int i = -1, int j = -1, int k = -1]) => -1;
+ Expect.isTrue(func6 is Func1);
+ Expect.isTrue(func6 is Func2);
+ Expect.isTrue(func6 is Func3);
+ Expect.isTrue(func6 is Func4);
+ Expect.isFalse(func6 is Func5);
+ Expect.isFalse(func6 is Func6);
+ Expect.isFalse(func6 is Func7);
+
+ int func7(int i, {int j = -1}) => -1;
+ Expect.isTrue(func7 is Func1);
+ Expect.isFalse(func7 is Func2);
+ Expect.isFalse(func7 is Func3);
+ Expect.isFalse(func7 is Func4);
+ Expect.isFalse(func7 is Func5);
+ Expect.isFalse(func7 is Func6);
+ Expect.isFalse(func7 is Func7);
+
+ int func8(int i, {int b = -1}) => -1;
+ Expect.isTrue(func8 is Func1);
+ Expect.isFalse(func8 is Func2);
+ Expect.isFalse(func8 is Func3);
+ Expect.isFalse(func8 is Func4);
+ Expect.isTrue(func8 is Func5);
+ Expect.isFalse(func8 is Func6);
+ Expect.isFalse(func8 is Func7);
+
+ int func9(int i, {int b = -1, int c = -1}) => -1;
+ Expect.isTrue(func9 is Func1);
+ Expect.isFalse(func9 is Func2);
+ Expect.isFalse(func9 is Func3);
+ Expect.isFalse(func9 is Func4);
+ Expect.isTrue(func9 is Func5);
+ Expect.isTrue(func9 is Func6);
+ Expect.isFalse(func9 is Func7);
+
+ int func10(int i, {int c = -1, int b = -1}) => -1;
+ Expect.isTrue(func10 is Func1);
+ Expect.isFalse(func10 is Func2);
+ Expect.isFalse(func10 is Func3);
+ Expect.isFalse(func10 is Func4);
+ Expect.isTrue(func10 is Func5);
+ Expect.isTrue(func10 is Func6);
+ Expect.isFalse(func10 is Func7);
+
+ int func11({int a = -1, int b = -1, int c = -1}) => -1;
+ Expect.isFalse(func11 is Func1);
+ Expect.isFalse(func11 is Func2);
+ Expect.isFalse(func11 is Func3);
+ Expect.isFalse(func11 is Func4);
+ Expect.isFalse(func11 is Func5);
+ Expect.isFalse(func11 is Func6);
+ Expect.isTrue(func11 is Func7);
+
+ int func12({int c = -1, int a = -1, int b = -1}) => -1;
+ Expect.isFalse(func12 is Func1);
+ Expect.isFalse(func12 is Func2);
+ Expect.isFalse(func12 is Func3);
+ Expect.isFalse(func12 is Func4);
+ Expect.isFalse(func12 is Func5);
+ Expect.isFalse(func12 is Func6);
+ Expect.isTrue(func12 is Func7);
+}
diff --git a/tests/language_2/covariant/subtyping_test.dart b/tests/language_2/covariant/subtyping_test.dart
index b083c3d..255c360 100644
--- a/tests/language_2/covariant/subtyping_test.dart
+++ b/tests/language_2/covariant/subtyping_test.dart
@@ -217,20 +217,25 @@
method7(T Function() Function() returnsReturnsT) => null; // needs check
}
+typedef F1 = dynamic Function(Object);
+typedef F2 = dynamic Function(dynamic Function(int));
+typedef F3 = dynamic Function(dynamic Function(int Function()));
+typedef F4 = dynamic Function(dynamic Function(int) Function());
+
testTearOffRuntimeType() {
- expectRTTI(tearoff, type) => Expect.equals('${tearoff.runtimeType}', type,
+ expectRTTI(tearoff, type) => Expect.equals(tearoff.runtimeType, type,
'covariant params should reify with Object as their type');
TearOff<num> t = new TearOff<int>();
- expectRTTI(t.method1, '(Object) => dynamic');
+ expectRTTI(t.method1, F1);
- expectRTTI(t.method2, '((int) => dynamic) => dynamic');
- expectRTTI(t.method3, '(Object) => dynamic');
+ expectRTTI(t.method2, F2);
+ expectRTTI(t.method3, F1);
- expectRTTI(t.method4, '(Object) => dynamic');
- expectRTTI(t.method5, '((() => int) => dynamic) => dynamic');
- expectRTTI(t.method6, '(() => (int) => dynamic) => dynamic');
- expectRTTI(t.method7, '(Object) => dynamic');
+ expectRTTI(t.method4, F1);
+ expectRTTI(t.method5, F3);
+ expectRTTI(t.method6, F4);
+ expectRTTI(t.method7, F1);
}
main() {
diff --git a/tests/language_2/interface/implements_futureor_test.dart b/tests/language_2/interface/implements_futureor_test.dart
index 1ec6779..abe2e64 100644
--- a/tests/language_2/interface/implements_futureor_test.dart
+++ b/tests/language_2/interface/implements_futureor_test.dart
@@ -6,7 +6,7 @@
class A<T> implements FutureOr<T> {}
// ^^^^^^^^^^^
-// [analyzer] COMPILE_TIME_ERROR.IMPLEMENTS_DISALLOWED_CLASS
+// [analyzer] COMPILE_TIME_ERROR.SUBTYPE_OF_DISALLOWED_TYPE
// ^
// [cfe] The type 'FutureOr' can't be used in an 'implements' clause.
diff --git a/tests/language_2/nonfunction_type_aliases/infer_aliased_instance_creation_01_test.dart b/tests/language_2/nonfunction_type_aliases/infer_aliased_instance_creation_01_test.dart
new file mode 100644
index 0000000..99f6365c
--- /dev/null
+++ b/tests/language_2/nonfunction_type_aliases/infer_aliased_instance_creation_01_test.dart
@@ -0,0 +1,20 @@
+// 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.
+
+// SharedOptions=--enable-experiment=nonfunction-type-aliases
+
+import 'package:expect/expect.dart';
+
+class C<X> {
+ C(Type t) {
+ Expect.equals(t, X);
+ }
+}
+
+typedef T<X> = C<X>;
+
+void main() {
+ T<num> x1 = T(num);
+ C<num> x2 = T(num);
+}
diff --git a/tests/language_2/nonfunction_type_aliases/infer_aliased_instance_creation_02_test.dart b/tests/language_2/nonfunction_type_aliases/infer_aliased_instance_creation_02_test.dart
new file mode 100644
index 0000000..081f4d8
--- /dev/null
+++ b/tests/language_2/nonfunction_type_aliases/infer_aliased_instance_creation_02_test.dart
@@ -0,0 +1,16 @@
+// 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.
+
+// SharedOptions=--enable-experiment=nonfunction-type-aliases
+
+import 'package:expect/expect.dart';
+
+class C {}
+
+typedef T<X> = C;
+
+void main() {
+ T<num> x1 = T();
+ C x2 = T();
+}
diff --git a/tests/language_2/nonfunction_type_aliases/infer_aliased_instance_creation_03_test.dart b/tests/language_2/nonfunction_type_aliases/infer_aliased_instance_creation_03_test.dart
new file mode 100644
index 0000000..b11314a
--- /dev/null
+++ b/tests/language_2/nonfunction_type_aliases/infer_aliased_instance_creation_03_test.dart
@@ -0,0 +1,20 @@
+// 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.
+
+// SharedOptions=--enable-experiment=nonfunction-type-aliases
+
+import 'package:expect/expect.dart';
+
+class C<X> {
+ C(Type t) {
+ Expect.equals(t, X);
+ }
+}
+
+typedef T = C<num>;
+
+void main() {
+ T x1 = T(num);
+ C<num> x2 = T(num);
+}
diff --git a/tests/language_2/nonfunction_type_aliases/infer_aliased_instance_creation_04_test.dart b/tests/language_2/nonfunction_type_aliases/infer_aliased_instance_creation_04_test.dart
new file mode 100644
index 0000000..97d6600
--- /dev/null
+++ b/tests/language_2/nonfunction_type_aliases/infer_aliased_instance_creation_04_test.dart
@@ -0,0 +1,22 @@
+// 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.
+
+// SharedOptions=--enable-experiment=nonfunction-type-aliases
+
+import 'package:expect/expect.dart';
+
+class C<X> {
+ C(Type t) {
+ Expect.equals(t, X);
+ }
+}
+
+typedef T<X> = C<List<X>>;
+
+Type typeOf<X>() => X;
+
+void main() {
+ T<num> x1 = T(typeOf<List<num>>());
+ C<Iterable<num>> x2 = T(typeOf<List<num>>());
+}
diff --git a/tests/language_2/nonfunction_type_aliases/infer_aliased_instance_creation_05_test.dart b/tests/language_2/nonfunction_type_aliases/infer_aliased_instance_creation_05_test.dart
new file mode 100644
index 0000000..7736a0d
--- /dev/null
+++ b/tests/language_2/nonfunction_type_aliases/infer_aliased_instance_creation_05_test.dart
@@ -0,0 +1,21 @@
+// 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.
+
+// SharedOptions=--enable-experiment=nonfunction-type-aliases
+
+import 'package:expect/expect.dart';
+
+class C<X, Y> {
+ C(Type tx, Type ty) {
+ Expect.equals(tx, X);
+ Expect.equals(ty, Y);
+ }
+}
+
+typedef T<Y, X> = C<X, Y>;
+
+void main() {
+ T<int, String> x1 = T(String, int);
+ C<String, int> x2 = T(String, int);
+}
diff --git a/tests/language_2/nonfunction_type_aliases/infer_aliased_instance_creation_06_test.dart b/tests/language_2/nonfunction_type_aliases/infer_aliased_instance_creation_06_test.dart
new file mode 100644
index 0000000..543deb9
--- /dev/null
+++ b/tests/language_2/nonfunction_type_aliases/infer_aliased_instance_creation_06_test.dart
@@ -0,0 +1,19 @@
+// 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.
+
+// SharedOptions=--enable-experiment=nonfunction-type-aliases
+
+import 'package:expect/expect.dart';
+
+class C<X> {
+ C(X x, Type t) {
+ Expect.equals(t, X);
+ }
+}
+
+typedef T<X> = C<X>;
+
+void main() {
+ T(1, int);
+}
diff --git a/tests/language_2/nonfunction_type_aliases/infer_aliased_instance_creation_07_test.dart b/tests/language_2/nonfunction_type_aliases/infer_aliased_instance_creation_07_test.dart
new file mode 100644
index 0000000..0d61966
--- /dev/null
+++ b/tests/language_2/nonfunction_type_aliases/infer_aliased_instance_creation_07_test.dart
@@ -0,0 +1,21 @@
+// 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.
+
+// SharedOptions=--enable-experiment=nonfunction-type-aliases
+
+import 'package:expect/expect.dart';
+
+class C<X> {
+ C(X x, Type t) {
+ Expect.equals(t, X);
+ }
+}
+
+typedef T<X> = C<List<X>>;
+
+Type typeOf<X>() => X;
+
+void main() {
+ T(<num>[1], typeOf<List<num>>());
+}
diff --git a/tests/language_2/nonfunction_type_aliases/infer_aliased_instance_creation_08_test.dart b/tests/language_2/nonfunction_type_aliases/infer_aliased_instance_creation_08_test.dart
new file mode 100644
index 0000000..fe92b57
--- /dev/null
+++ b/tests/language_2/nonfunction_type_aliases/infer_aliased_instance_creation_08_test.dart
@@ -0,0 +1,20 @@
+// 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.
+
+// SharedOptions=--enable-experiment=nonfunction-type-aliases
+
+import 'package:expect/expect.dart';
+
+class C<X extends num> {
+ C(Type t) {
+ Expect.equals(t, X);
+ }
+}
+
+typedef T<X extends int> = C<X>;
+
+void main() {
+ T<int> x1 = T(int);
+ C<int> x2 = T(int);
+}
diff --git a/tests/language_2/nonfunction_type_aliases/infer_aliased_instance_creation_09_test.dart b/tests/language_2/nonfunction_type_aliases/infer_aliased_instance_creation_09_test.dart
new file mode 100644
index 0000000..7a07602
--- /dev/null
+++ b/tests/language_2/nonfunction_type_aliases/infer_aliased_instance_creation_09_test.dart
@@ -0,0 +1,16 @@
+// 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.
+
+// SharedOptions=--enable-experiment=nonfunction-type-aliases
+
+import 'package:expect/expect.dart';
+
+class C {}
+
+typedef T<X extends num> = C;
+
+void main() {
+ T<num> x1 = T();
+ C x2 = T();
+}
diff --git a/tests/language_2/nonfunction_type_aliases/infer_aliased_instance_creation_10_test.dart b/tests/language_2/nonfunction_type_aliases/infer_aliased_instance_creation_10_test.dart
new file mode 100644
index 0000000..8ce475d
--- /dev/null
+++ b/tests/language_2/nonfunction_type_aliases/infer_aliased_instance_creation_10_test.dart
@@ -0,0 +1,20 @@
+// 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.
+
+// SharedOptions=--enable-experiment=nonfunction-type-aliases
+
+import 'package:expect/expect.dart';
+
+class C<X extends num> {
+ C(Type t) {
+ Expect.equals(t, X);
+ }
+}
+
+typedef T = C<num>;
+
+void main() {
+ T x1 = T(num);
+ C<num> x2 = T(num);
+}
diff --git a/tests/language_2/nonfunction_type_aliases/infer_aliased_instance_creation_11_test.dart b/tests/language_2/nonfunction_type_aliases/infer_aliased_instance_creation_11_test.dart
new file mode 100644
index 0000000..b669f00
--- /dev/null
+++ b/tests/language_2/nonfunction_type_aliases/infer_aliased_instance_creation_11_test.dart
@@ -0,0 +1,22 @@
+// 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.
+
+// SharedOptions=--enable-experiment=nonfunction-type-aliases
+
+import 'package:expect/expect.dart';
+
+class C<X extends Iterable<num>> {
+ C(Type t) {
+ Expect.equals(t, X);
+ }
+}
+
+typedef T<X extends int> = C<List<X>>;
+
+Type typeOf<X>() => X;
+
+void main() {
+ T<int> x1 = T(typeOf<List<int>>());
+ C<Iterable<num>> x2 = T(typeOf<List<int>>());
+}
diff --git a/tests/language_2/nonfunction_type_aliases/infer_aliased_instance_creation_12_test.dart b/tests/language_2/nonfunction_type_aliases/infer_aliased_instance_creation_12_test.dart
new file mode 100644
index 0000000..4322e2d
--- /dev/null
+++ b/tests/language_2/nonfunction_type_aliases/infer_aliased_instance_creation_12_test.dart
@@ -0,0 +1,21 @@
+// 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.
+
+// SharedOptions=--enable-experiment=nonfunction-type-aliases
+
+import 'package:expect/expect.dart';
+
+class C<X extends String, Y extends num> {
+ C(Type tx, Type ty) {
+ Expect.equals(tx, X);
+ Expect.equals(ty, Y);
+ }
+}
+
+typedef T<Y extends int, X extends String> = C<X, Y>;
+
+void main() {
+ T<int, String> x1 = T(String, int);
+ C<String, int> x2 = T(String, int);
+}
diff --git a/tests/language_2/type_object/runtime_type_function_test.dart b/tests/language_2/type_object/runtime_type_function_test.dart
index b1c30c6..1e56ee1 100644
--- a/tests/language_2/type_object/runtime_type_function_test.dart
+++ b/tests/language_2/type_object/runtime_type_function_test.dart
@@ -5,10 +5,9 @@
import "package:expect/expect.dart";
typedef String F(String returns, String arguments, [Map<String, String> named]);
-F fn;
-/// Formats types the VM way: `(String, [int], {bool name}) => double`.
-String form1(String returns, String positional,
+/// Formats a type like `(String, [int], {bool name}) => double`.
+String fn(String returns, String positional,
[Map<String, String> named = const {}]) {
var result = new StringBuffer();
result.write("($positional");
@@ -30,39 +29,7 @@
return result.toString();
}
-/// Formats types the dart2js way: `double Function(String, [int], {bool name})`.
-String form2(String returns, String positional,
- [Map<String, String> named = const {}]) {
- var result = new StringBuffer();
- result.write("$returns Function");
- result.write("($positional");
- if (positional != "" && named.isNotEmpty) result.write(", ");
- if (named.isNotEmpty) {
- result.write("{");
- bool first = true;
- named.forEach((name, type) {
- if (first) {
- first = false;
- } else {
- result.write(", ");
- }
- result.write("$type $name");
- });
- result.write("}");
- }
- result.write(")");
-}
-
-F detectForm() {
- var s = main.runtimeType.toString();
- if (s.contains('=>')) return form1;
- if (s.contains('Function')) return form2;
- Expect.fail('"$s" contains neither "=>", nor "Function"');
-}
-
main() {
- fn = detectForm();
-
// Types that do not use class names - these can be checked on dart2js in
// minified mode.
diff --git a/tests/standalone/dwarf_stack_trace_obfuscate_test.dart b/tests/standalone/dwarf_stack_trace_obfuscate_test.dart
new file mode 100644
index 0000000..5750ab5
--- /dev/null
+++ b/tests/standalone/dwarf_stack_trace_obfuscate_test.dart
@@ -0,0 +1,76 @@
+// 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.
+
+/// VMOptions=--dwarf-stack-traces --save-debugging-info=dwarf_obfuscate.so --obfuscate
+
+import 'dart:io';
+
+import 'package:native_stack_traces/native_stack_traces.dart';
+import 'package:path/path.dart' as path;
+
+import 'dwarf_stack_trace_test.dart' as base;
+
+@pragma("vm:prefer-inline")
+bar() {
+ // Keep the 'throw' and its argument on separate lines.
+ throw // force linebreak with dartfmt
+ "Hello, Dwarf!";
+}
+
+@pragma("vm:never-inline")
+foo() {
+ bar();
+}
+
+Future<void> main() async {
+ String rawStack = "";
+ try {
+ foo();
+ } catch (e, st) {
+ rawStack = st.toString();
+ }
+
+ if (path.basenameWithoutExtension(Platform.executable) !=
+ "dart_precompiled_runtime") {
+ return; // Not running from an AOT compiled snapshot.
+ }
+
+ if (Platform.isAndroid) {
+ return; // Generated dwarf.so not available on the test device.
+ }
+
+ final dwarf = Dwarf.fromFile("dwarf_obfuscate.so");
+
+ await base.checkStackTrace(rawStack, dwarf, expectedCallsInfo);
+}
+
+final expectedCallsInfo = <List<DartCallInfo>>[
+ // The first frame should correspond to the throw in bar, which was inlined
+ // into foo (so we'll get information for two calls for that PC address).
+ [
+ DartCallInfo(
+ function: "bar",
+ filename: "dwarf_stack_trace_obfuscate_test.dart",
+ line: 17,
+ column: 3,
+ inlined: true),
+ DartCallInfo(
+ function: "foo",
+ filename: "dwarf_stack_trace_obfuscate_test.dart",
+ line: 23,
+ column: 3,
+ inlined: false)
+ ],
+ // The second frame corresponds to call to foo in main.
+ [
+ DartCallInfo(
+ function: "main",
+ filename: "dwarf_stack_trace_obfuscate_test.dart",
+ line: 29,
+ column: 5,
+ inlined: false)
+ ],
+ // Don't assume anything about any of the frames below the call to foo
+ // in main, as this makes the test too brittle.
+];
diff --git a/tests/standalone/dwarf_stack_trace_test.dart b/tests/standalone/dwarf_stack_trace_test.dart
new file mode 100644
index 0000000..9cf2cb0
--- /dev/null
+++ b/tests/standalone/dwarf_stack_trace_test.dart
@@ -0,0 +1,228 @@
+// 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.
+
+/// VMOptions=--dwarf-stack-traces --save-debugging-info=dwarf.so
+
+import 'dart:convert';
+import 'dart:io';
+
+import 'package:native_stack_traces/native_stack_traces.dart';
+import 'package:path/path.dart' as path;
+import 'package:expect/expect.dart';
+
+@pragma("vm:prefer-inline")
+bar() {
+ // Keep the 'throw' and its argument on separate lines.
+ throw // force linebreak with dartfmt
+ "Hello, Dwarf!";
+}
+
+@pragma("vm:never-inline")
+foo() {
+ bar();
+}
+
+Future<void> main() async {
+ String rawStack = "";
+ try {
+ foo();
+ } catch (e, st) {
+ rawStack = st.toString();
+ }
+
+ if (path.basenameWithoutExtension(Platform.executable) !=
+ "dart_precompiled_runtime") {
+ return; // Not running from an AOT compiled snapshot.
+ }
+
+ if (Platform.isAndroid) {
+ return; // Generated dwarf.so not available on the test device.
+ }
+
+ final dwarf = Dwarf.fromFile("dwarf.so");
+
+ await checkStackTrace(rawStack, dwarf, expectedCallsInfo);
+}
+
+Future<void> checkStackTrace(String rawStack, Dwarf dwarf,
+ List<List<DartCallInfo>> expectedCallsInfo) async {
+ print("");
+ print("Raw stack trace:");
+ print(rawStack);
+
+ final rawLines =
+ await Stream.value(rawStack).transform(const LineSplitter()).toList();
+
+ final pcOffsets = collectPCOffsets(rawLines).toList();
+
+ // We should have at least enough PC addresses to cover the frames we'll be
+ // checking.
+ Expect.isTrue(pcOffsets.length >= expectedCallsInfo.length);
+
+ final virtualAddresses =
+ pcOffsets.map((o) => dwarf.virtualAddressOf(o)).toList();
+
+ // Some double-checks using other information in the non-symbolic stack trace.
+ final dsoBase = dsoBaseAddresses(rawLines).single;
+ final absolutes = absoluteAddresses(rawLines);
+ final relocatedAddresses = absolutes.map((a) => a - dsoBase);
+ final explicits = explicitVirtualAddresses(rawLines);
+
+ // Explicits will be empty if not generating ELF snapshots directly, which
+ // means we can't depend on virtual addresses in the snapshot lining up with
+ // those in the separate debugging information.
+ if (explicits.isNotEmpty) {
+ // Direct-to-ELF snapshots should have a build ID.
+ Expect.isNotNull(dwarf.buildId);
+ Expect.deepEquals(relocatedAddresses, virtualAddresses);
+ Expect.deepEquals(explicits, virtualAddresses);
+ }
+
+ final gotCallsInfo = <List<DartCallInfo>>[];
+
+ for (final addr in virtualAddresses) {
+ final externalCallInfo = dwarf.callInfoFor(addr);
+ Expect.isNotNull(externalCallInfo);
+ final allCallInfo = dwarf.callInfoFor(addr, includeInternalFrames: true);
+ Expect.isNotNull(allCallInfo);
+ for (final call in allCallInfo) {
+ Expect.isTrue(call is DartCallInfo, "got non-Dart call info ${call}");
+ }
+ Expect.deepEquals(externalCallInfo, allCallInfo);
+ gotCallsInfo.add(allCallInfo.cast<DartCallInfo>().toList());
+ }
+
+ print("");
+ print("Call information for PC addresses:");
+ for (var i = 0; i < virtualAddresses.length; i++) {
+ print("For PC 0x${virtualAddresses[i].toRadixString(16)}:");
+ print(" Calls corresponding to user or library code:");
+ gotCallsInfo[i].forEach((frame) => print(" ${frame}"));
+ }
+
+ checkFrames(gotCallsInfo, expectedCallsInfo);
+
+ final gotSymbolizedLines = await Stream.fromIterable(rawLines)
+ .transform(DwarfStackTraceDecoder(dwarf, includeInternalFrames: true))
+ .toList();
+
+ final gotSymbolizedCalls =
+ gotSymbolizedLines.where((s) => s.startsWith('#')).toList();
+
+ print("");
+ print("Symbolized stack trace:");
+ gotSymbolizedLines.forEach(print);
+ print("");
+ print("Extracted calls:");
+ gotSymbolizedCalls.forEach(print);
+
+ final expectedStrings = extractCallStrings(expectedCallsInfo);
+ // There are two strings in the list for each line in the output.
+ final expectedCallCount = expectedStrings.length ~/ 2;
+
+ Expect.isTrue(gotSymbolizedCalls.length >= expectedCallCount);
+
+ // Strip off any unexpected lines, so we can also make sure we didn't get
+ // unexpected calls prior to those calls we expect.
+ final gotCallsTrace =
+ gotSymbolizedCalls.sublist(0, expectedCallCount).join('\n');
+
+ Expect.stringContainsInOrder(gotCallsTrace, expectedStrings);
+}
+
+final expectedCallsInfo = <List<DartCallInfo>>[
+ // The first frame should correspond to the throw in bar, which was inlined
+ // into foo (so we'll get information for two calls for that PC address).
+ [
+ DartCallInfo(
+ function: "bar",
+ filename: "dwarf_stack_trace_test.dart",
+ line: 17,
+ column: 3,
+ inlined: true),
+ DartCallInfo(
+ function: "foo",
+ filename: "dwarf_stack_trace_test.dart",
+ line: 23,
+ column: 3,
+ inlined: false)
+ ],
+ // The second frame corresponds to call to foo in main.
+ [
+ DartCallInfo(
+ function: "main",
+ filename: "dwarf_stack_trace_test.dart",
+ line: 29,
+ column: 5,
+ inlined: false)
+ ],
+ // Don't assume anything about any of the frames below the call to foo
+ // in main, as this makes the test too brittle.
+];
+
+void checkFrames(
+ List<List<DartCallInfo>> gotInfo, List<List<DartCallInfo>> expectedInfo) {
+ // There may be frames below those we check.
+ Expect.isTrue(gotInfo.length >= expectedInfo.length);
+
+ // We can't just use deep equality, since we only have the filenames in the
+ // expected version, not the whole path, and we don't really care if
+ // non-positive line numbers match, as long as they're both non-positive.
+ for (var i = 0; i < expectedInfo.length; i++) {
+ for (var j = 0; j < expectedInfo[i].length; j++) {
+ final got = gotInfo[i][j];
+ final expected = expectedInfo[i][j];
+ Expect.equals(expected.function, got.function);
+ Expect.equals(expected.inlined, got.inlined);
+ Expect.equals(expected.filename, path.basename(got.filename));
+ if (expected.isInternal) {
+ Expect.isTrue(got.isInternal);
+ } else {
+ Expect.equals(expected.line, got.line);
+ }
+ }
+ }
+}
+
+List<String> extractCallStrings(List<List<CallInfo>> expectedCalls) {
+ var ret = <String>[];
+ for (final frame in expectedCalls) {
+ for (final call in frame) {
+ if (call is DartCallInfo) {
+ ret.add(call.function);
+ if (call.isInternal) {
+ ret.add("${call.filename}:??");
+ } else {
+ ret.add("${call.filename}:${call.line}");
+ }
+ }
+ }
+ }
+ return ret;
+}
+
+Iterable<int> parseUsingAddressRegExp(RegExp re, Iterable<String> lines) sync* {
+ for (final line in lines) {
+ final match = re.firstMatch(line);
+ if (match == null) continue;
+ final s = match.group(1);
+ if (s == null) continue;
+ yield int.parse(s, radix: 16);
+ }
+}
+
+final _absRE = RegExp(r'abs ([a-f\d]+)');
+
+Iterable<int> absoluteAddresses(Iterable<String> lines) =>
+ parseUsingAddressRegExp(_absRE, lines);
+
+final _virtRE = RegExp(r'virt ([a-f\d]+)');
+
+Iterable<int> explicitVirtualAddresses(Iterable<String> lines) =>
+ parseUsingAddressRegExp(_virtRE, lines);
+
+final _dsoBaseRE = RegExp(r'isolate_dso_base: ([a-f\d]+)');
+
+Iterable<int> dsoBaseAddresses(Iterable<String> lines) =>
+ parseUsingAddressRegExp(_dsoBaseRE, lines);
diff --git a/tests/standalone/io/http_headers_content_length_test.dart b/tests/standalone/io/http_headers_content_length_test.dart
new file mode 100644
index 0000000..9997ffe
--- /dev/null
+++ b/tests/standalone/io/http_headers_content_length_test.dart
@@ -0,0 +1,21 @@
+// 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:expect/expect.dart";
+
+Future<void> main() async {
+ final server = await HttpServer.bind("localhost", 0);
+ final request = await HttpClient().get("localhost", server.port, "/");
+ final headers = request.headers;
+ headers.contentLength = 100;
+ headers.set('Content-Length', 100);
+ Expect.equals('100', headers['Content-Length']?[0]);
+ try {
+ await request.close();
+ } catch (e) {
+ server.close();
+ }
+}
diff --git a/tests/standalone/io/http_proxy_close_test.dart b/tests/standalone/io/http_proxy_close_test.dart
new file mode 100644
index 0000000..2fd39f2
--- /dev/null
+++ b/tests/standalone/io/http_proxy_close_test.dart
@@ -0,0 +1,50 @@
+// 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:expect/expect.dart';
+
+// Test that a HTTP "CONNECT" request with 200 status code won't close the
+// underlying socket.
+// issue: https://github.com/dart-lang/sdk/issues/37808
+Future<void> testConnect(int statusCode, int port) async {
+ final url = "https://domain.invalid";
+ var client = HttpClient();
+ try {
+ client.findProxy = (uri) => "PROXY 127.0.0.1:$port";
+ try {
+ final request = await client.getUrl(Uri.parse(url));
+ await request.close();
+ Expect.fail('request should have thrown an exception');
+ } catch (e) {
+ if (statusCode == HttpStatus.ok) {
+ // Underlying sockets won't be closed and then handshake will fail.
+ Expect.type<HandshakeException>(e);
+ } else {
+ Expect.type<HttpException>(e);
+ }
+ }
+ } finally {
+ client.close();
+ }
+}
+
+Future<void> main() async {
+ final server = await HttpServer.bind('127.0.0.1', 0);
+ try {
+ final statusCodes = <int>[200, 299, 199, 300];
+ int index = 0;
+ server.listen((request) {
+ request.response.statusCode = statusCodes[index++];
+ request.response.headers.contentLength = 0;
+ request.response.close();
+ });
+ for (final statusCode in statusCodes) {
+ await testConnect(statusCode, server.port);
+ }
+ } finally {
+ server.close();
+ }
+}
diff --git a/tests/standalone_2/dwarf_stack_trace_obfuscate_test.dart b/tests/standalone_2/dwarf_stack_trace_obfuscate_test.dart
index 37fd305..5750ab5 100644
--- a/tests/standalone_2/dwarf_stack_trace_obfuscate_test.dart
+++ b/tests/standalone_2/dwarf_stack_trace_obfuscate_test.dart
@@ -24,7 +24,7 @@
}
Future<void> main() async {
- String rawStack;
+ String rawStack = "";
try {
foo();
} catch (e, st) {
@@ -45,7 +45,7 @@
await base.checkStackTrace(rawStack, dwarf, expectedCallsInfo);
}
-final expectedCallsInfo = <List<CallInfo>>[
+final expectedCallsInfo = <List<DartCallInfo>>[
// The first frame should correspond to the throw in bar, which was inlined
// into foo (so we'll get information for two calls for that PC address).
[
@@ -53,11 +53,13 @@
function: "bar",
filename: "dwarf_stack_trace_obfuscate_test.dart",
line: 17,
+ column: 3,
inlined: true),
DartCallInfo(
function: "foo",
filename: "dwarf_stack_trace_obfuscate_test.dart",
line: 23,
+ column: 3,
inlined: false)
],
// The second frame corresponds to call to foo in main.
@@ -66,6 +68,7 @@
function: "main",
filename: "dwarf_stack_trace_obfuscate_test.dart",
line: 29,
+ column: 5,
inlined: false)
],
// Don't assume anything about any of the frames below the call to foo
diff --git a/tests/standalone_2/dwarf_stack_trace_test.dart b/tests/standalone_2/dwarf_stack_trace_test.dart
index dac3e58..9cf2cb0 100644
--- a/tests/standalone_2/dwarf_stack_trace_test.dart
+++ b/tests/standalone_2/dwarf_stack_trace_test.dart
@@ -24,7 +24,7 @@
}
Future<void> main() async {
- String rawStack;
+ String rawStack = "";
try {
foo();
} catch (e, st) {
@@ -46,10 +46,7 @@
}
Future<void> checkStackTrace(String rawStack, Dwarf dwarf,
- List<List<CallInfo>> expectedCallsInfo) async {
- final expectedAllCallsInfo = expectedCallsInfo;
- final expectedExternalCallInfo = removeInternalCalls(expectedCallsInfo);
-
+ List<List<DartCallInfo>> expectedCallsInfo) async {
print("");
print("Raw stack trace:");
print(rawStack);
@@ -61,7 +58,7 @@
// We should have at least enough PC addresses to cover the frames we'll be
// checking.
- Expect.isTrue(pcOffsets.length >= expectedAllCallsInfo.length);
+ Expect.isTrue(pcOffsets.length >= expectedCallsInfo.length);
final virtualAddresses =
pcOffsets.map((o) => dwarf.virtualAddressOf(o)).toList();
@@ -82,13 +79,18 @@
Expect.deepEquals(explicits, virtualAddresses);
}
- final externalFramesInfo = <List<CallInfo>>[];
- final allFramesInfo = <List<CallInfo>>[];
+ final gotCallsInfo = <List<DartCallInfo>>[];
for (final addr in virtualAddresses) {
- externalFramesInfo.add(dwarf.callInfoFor(addr)?.toList());
- allFramesInfo
- .add(dwarf.callInfoFor(addr, includeInternalFrames: true)?.toList());
+ final externalCallInfo = dwarf.callInfoFor(addr);
+ Expect.isNotNull(externalCallInfo);
+ final allCallInfo = dwarf.callInfoFor(addr, includeInternalFrames: true);
+ Expect.isNotNull(allCallInfo);
+ for (final call in allCallInfo) {
+ Expect.isTrue(call is DartCallInfo, "got non-Dart call info ${call}");
+ }
+ Expect.deepEquals(externalCallInfo, allCallInfo);
+ gotCallsInfo.add(allCallInfo.cast<DartCallInfo>().toList());
}
print("");
@@ -96,66 +98,40 @@
for (var i = 0; i < virtualAddresses.length; i++) {
print("For PC 0x${virtualAddresses[i].toRadixString(16)}:");
print(" Calls corresponding to user or library code:");
- externalFramesInfo[i]?.forEach((frame) => print(" ${frame}"));
- print(" All calls:");
- allFramesInfo[i]?.forEach((frame) => print(" ${frame}"));
+ gotCallsInfo[i].forEach((frame) => print(" ${frame}"));
}
- // Check that our results are also consistent.
- checkConsistency(externalFramesInfo, allFramesInfo);
+ checkFrames(gotCallsInfo, expectedCallsInfo);
- checkFrames(externalFramesInfo, expectedExternalCallInfo);
- checkFrames(allFramesInfo, expectedAllCallsInfo);
-
- final externalSymbolizedLines = await Stream.fromIterable(rawLines)
- .transform(DwarfStackTraceDecoder(dwarf))
- .toList();
-
- final externalSymbolizedCalls =
- externalSymbolizedLines.where((s) => s.startsWith('#')).toList();
-
- print("");
- print("Symbolized external-only stack trace:");
- externalSymbolizedLines.forEach(print);
- print("");
- print("Extracted calls:");
- externalSymbolizedCalls.forEach(print);
-
- final allSymbolizedLines = await Stream.fromIterable(rawLines)
+ final gotSymbolizedLines = await Stream.fromIterable(rawLines)
.transform(DwarfStackTraceDecoder(dwarf, includeInternalFrames: true))
.toList();
- final allSymbolizedCalls =
- allSymbolizedLines.where((s) => s.startsWith('#')).toList();
+ final gotSymbolizedCalls =
+ gotSymbolizedLines.where((s) => s.startsWith('#')).toList();
print("");
- print("Symbolized full stack trace:");
- allSymbolizedLines.forEach(print);
+ print("Symbolized stack trace:");
+ gotSymbolizedLines.forEach(print);
print("");
print("Extracted calls:");
- allSymbolizedCalls.forEach(print);
+ gotSymbolizedCalls.forEach(print);
- final expectedExternalStrings = extractCallStrings(expectedExternalCallInfo);
+ final expectedStrings = extractCallStrings(expectedCallsInfo);
// There are two strings in the list for each line in the output.
- final expectedExternalCallCount = expectedExternalStrings.length ~/ 2;
- final expectedStrings = extractCallStrings(expectedAllCallsInfo);
final expectedCallCount = expectedStrings.length ~/ 2;
- Expect.isTrue(externalSymbolizedCalls.length >= expectedExternalCallCount);
- Expect.isTrue(allSymbolizedCalls.length >= expectedCallCount);
+ Expect.isTrue(gotSymbolizedCalls.length >= expectedCallCount);
// Strip off any unexpected lines, so we can also make sure we didn't get
// unexpected calls prior to those calls we expect.
- final externalCallsTrace =
- externalSymbolizedCalls.sublist(0, expectedExternalCallCount).join('\n');
- final allCallsTrace =
- allSymbolizedCalls.sublist(0, expectedCallCount).join('\n');
+ final gotCallsTrace =
+ gotSymbolizedCalls.sublist(0, expectedCallCount).join('\n');
- Expect.stringContainsInOrder(externalCallsTrace, expectedExternalStrings);
- Expect.stringContainsInOrder(allCallsTrace, expectedStrings);
+ Expect.stringContainsInOrder(gotCallsTrace, expectedStrings);
}
-final expectedCallsInfo = <List<CallInfo>>[
+final expectedCallsInfo = <List<DartCallInfo>>[
// The first frame should correspond to the throw in bar, which was inlined
// into foo (so we'll get information for two calls for that PC address).
[
@@ -163,11 +139,13 @@
function: "bar",
filename: "dwarf_stack_trace_test.dart",
line: 17,
+ column: 3,
inlined: true),
DartCallInfo(
function: "foo",
filename: "dwarf_stack_trace_test.dart",
line: 23,
+ column: 3,
inlined: false)
],
// The second frame corresponds to call to foo in main.
@@ -176,65 +154,25 @@
function: "main",
filename: "dwarf_stack_trace_test.dart",
line: 29,
+ column: 5,
inlined: false)
],
// Don't assume anything about any of the frames below the call to foo
// in main, as this makes the test too brittle.
];
-List<List<CallInfo>> removeInternalCalls(List<List<CallInfo>> original) =>
- original
- .map((frame) => frame.where((call) => !call.isInternal).toList())
- .toList();
-
-void checkConsistency(
- List<List<CallInfo>> externalFrames, List<List<CallInfo>> allFrames) {
- // We should have the same number of frames for both external-only
- // and combined call information.
- Expect.equals(allFrames.length, externalFrames.length);
-
- for (var frame in externalFrames) {
- // There should be no frames in either version where we failed to look up
- // call information.
- Expect.isNotNull(frame);
-
- // External-only call information should only include call information with
- // positive line numbers.
- for (var call in frame) {
- Expect.isTrue(!call.isInternal);
- }
- }
-
- for (var frame in allFrames) {
- // There should be no frames in either version where we failed to look up
- // call information.
- Expect.isNotNull(frame);
-
- // All frames in the internal-including version should have at least one
- // piece of call information.
- Expect.isTrue(frame.isNotEmpty);
- }
-
- // The information in the external-only and combined call information should
- // be consistent for externally visible calls.
- final allFramesStripped = removeInternalCalls(allFrames);
- for (var i = 0; i < allFramesStripped.length; i++) {
- Expect.listEquals(allFramesStripped[i], externalFrames[i]);
- }
-}
-
void checkFrames(
- List<List<CallInfo>> framesInfo, List<List<CallInfo>> expectedInfo) {
+ List<List<DartCallInfo>> gotInfo, List<List<DartCallInfo>> expectedInfo) {
// There may be frames below those we check.
- Expect.isTrue(framesInfo.length >= expectedInfo.length);
+ Expect.isTrue(gotInfo.length >= expectedInfo.length);
// We can't just use deep equality, since we only have the filenames in the
// expected version, not the whole path, and we don't really care if
// non-positive line numbers match, as long as they're both non-positive.
for (var i = 0; i < expectedInfo.length; i++) {
for (var j = 0; j < expectedInfo[i].length; j++) {
- final DartCallInfo got = framesInfo[i][j];
- final DartCallInfo expected = expectedInfo[i][j];
+ final got = gotInfo[i][j];
+ final expected = expectedInfo[i][j];
Expect.equals(expected.function, got.function);
Expect.equals(expected.inlined, got.inlined);
Expect.equals(expected.filename, path.basename(got.filename));
@@ -267,9 +205,10 @@
Iterable<int> parseUsingAddressRegExp(RegExp re, Iterable<String> lines) sync* {
for (final line in lines) {
final match = re.firstMatch(line);
- if (match != null) {
- yield int.parse(match.group(1), radix: 16);
- }
+ if (match == null) continue;
+ final s = match.group(1);
+ if (s == null) continue;
+ yield int.parse(s, radix: 16);
}
}
diff --git a/tests/standalone_2/io/http_headers_content_length_test.dart b/tests/standalone_2/io/http_headers_content_length_test.dart
new file mode 100644
index 0000000..523ac69
--- /dev/null
+++ b/tests/standalone_2/io/http_headers_content_length_test.dart
@@ -0,0 +1,21 @@
+// 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:expect/expect.dart";
+
+Future<void> main() async {
+ final server = await HttpServer.bind("localhost", 0);
+ final request = await HttpClient().get("localhost", server.port, "/");
+ final headers = request.headers;
+ headers.contentLength = 100;
+ headers.set('Content-Length', 100);
+ Expect.equals('100', headers['Content-Length'][0]);
+ try {
+ await request.close();
+ } catch (e) {
+ server.close();
+ }
+}
diff --git a/tests/standalone_2/io/http_proxy_close_test.dart b/tests/standalone_2/io/http_proxy_close_test.dart
new file mode 100644
index 0000000..2fd39f2
--- /dev/null
+++ b/tests/standalone_2/io/http_proxy_close_test.dart
@@ -0,0 +1,50 @@
+// 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:expect/expect.dart';
+
+// Test that a HTTP "CONNECT" request with 200 status code won't close the
+// underlying socket.
+// issue: https://github.com/dart-lang/sdk/issues/37808
+Future<void> testConnect(int statusCode, int port) async {
+ final url = "https://domain.invalid";
+ var client = HttpClient();
+ try {
+ client.findProxy = (uri) => "PROXY 127.0.0.1:$port";
+ try {
+ final request = await client.getUrl(Uri.parse(url));
+ await request.close();
+ Expect.fail('request should have thrown an exception');
+ } catch (e) {
+ if (statusCode == HttpStatus.ok) {
+ // Underlying sockets won't be closed and then handshake will fail.
+ Expect.type<HandshakeException>(e);
+ } else {
+ Expect.type<HttpException>(e);
+ }
+ }
+ } finally {
+ client.close();
+ }
+}
+
+Future<void> main() async {
+ final server = await HttpServer.bind('127.0.0.1', 0);
+ try {
+ final statusCodes = <int>[200, 299, 199, 300];
+ int index = 0;
+ server.listen((request) {
+ request.response.statusCode = statusCodes[index++];
+ request.response.headers.contentLength = 0;
+ request.response.close();
+ });
+ for (final statusCode in statusCodes) {
+ await testConnect(statusCode, server.port);
+ }
+ } finally {
+ server.close();
+ }
+}
diff --git a/tools/FAKE_COMMITS b/tools/FAKE_COMMITS
index 5ce0701..286b4b7 100644
--- a/tools/FAKE_COMMITS
+++ b/tools/FAKE_COMMITS
@@ -24,6 +24,7 @@
Trigger mirroring of github repository
Force build after DEPS format revert
Force build while trybots are broken, to check builders for brokenness.
+Force build to test recipe changes
Analyzer branch commits:
Force build on new analyzer-branch linux build with new workflow
diff --git a/tools/VERSION b/tools/VERSION
index 60fa9bb..fd6266b 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -33,7 +33,7 @@
MAJOR 2
MINOR 9
PATCH 0
-PRERELEASE 18
+PRERELEASE 19
PRERELEASE_PATCH 0
ABI_VERSION 37
OLDEST_SUPPORTED_ABI_VERSION 37
diff --git a/tools/bots/test_matrix.json b/tools/bots/test_matrix.json
index 828c326..be160e9 100644
--- a/tools/bots/test_matrix.json
+++ b/tools/bots/test_matrix.json
@@ -2630,7 +2630,6 @@
"name": "dart2js nnbd unit tests",
"arguments": [
"-nunittest-weak-asserts-no-sdk-linux",
- "dart2js_2",
"pkg//compiler/"
]
}
diff --git a/tools/dom/nnbd_src/AttributeMap.dart b/tools/dom/nnbd_src/AttributeMap.dart
deleted file mode 100644
index 88f3bd4..0000000
--- a/tools/dom/nnbd_src/AttributeMap.dart
+++ /dev/null
@@ -1,299 +0,0 @@
-// Copyright (c) 2012, 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.
-
-part of html;
-
-abstract class _AttributeMap extends MapBase<String, String> {
- final Element _element;
-
- _AttributeMap(this._element);
-
- void addAll(Map<String, String> other) {
- other.forEach((k, v) {
- this[k] = v;
- });
- }
-
- Map<K, V> cast<K, V>() => Map.castFrom<String, String, K, V>(this);
- bool containsValue(Object? value) {
- for (var v in this.values) {
- if (value == v) {
- return true;
- }
- }
- return false;
- }
-
- String putIfAbsent(String key, String ifAbsent()) {
- if (!containsKey(key)) {
- this[key] = ifAbsent();
- }
- return this[key] as String;
- }
-
- void clear() {
- for (var key in keys) {
- remove(key);
- }
- }
-
- void forEach(void f(String key, String value)) {
- for (var key in keys) {
- var value = this[key];
- f(key, value as String);
- }
- }
-
- Iterable<String> get keys {
- // TODO: generate a lazy collection instead.
- var attributes = _element._attributes;
- var keys = <String>[];
- for (int i = 0, len = attributes.length; i < len; i++) {
- _Attr attr = attributes[i] as _Attr;
- if (_matches(attr)) {
- keys.add(attr.name);
- }
- }
- return keys;
- }
-
- Iterable<String> get values {
- // TODO: generate a lazy collection instead.
- var attributes = _element._attributes;
- var values = <String>[];
- for (int i = 0, len = attributes.length; i < len; i++) {
- _Attr attr = attributes[i] as _Attr;
- if (_matches(attr)) {
- values.add(attr.value);
- }
- }
- return values;
- }
-
- /**
- * Returns true if there is no {key, value} pair in the map.
- */
- bool get isEmpty {
- return length == 0;
- }
-
- /**
- * Returns true if there is at least one {key, value} pair in the map.
- */
- bool get isNotEmpty => !isEmpty;
-
- /**
- * Checks to see if the node should be included in this map.
- */
- bool _matches(_Attr node);
-}
-
-/**
- * Wrapper to expose [Element.attributes] as a typed map.
- */
-class _ElementAttributeMap extends _AttributeMap {
- _ElementAttributeMap(Element element) : super(element);
-
- bool containsKey(Object? key) {
- return key is String && _element._hasAttribute(key);
- }
-
- String? operator [](Object? key) {
- return _element.getAttribute(key as String);
- }
-
- void operator []=(String key, String value) {
- _element.setAttribute(key, value);
- }
-
- @pragma('dart2js:tryInline')
- String? remove(Object? key) => key is String ? _remove(_element, key) : null;
-
- /**
- * The number of {key, value} pairs in the map.
- */
- int get length {
- return keys.length;
- }
-
- bool _matches(_Attr node) => node._namespaceUri == null;
-
- // Inline this because almost all call sites of [remove] do not use [value],
- // and the annotations on the `getAttribute` call allow it to be removed.
- @pragma('dart2js:tryInline')
- static String? _remove(Element element, String key) {
- String? value = JS(
- // throws:null(1) is not accurate since [key] could be malformed, but
- // [key] is checked again by `removeAttributeNS`.
- 'returns:String|Null;depends:all;effects:none;throws:null(1)',
- '#.getAttribute(#)',
- element,
- key);
- JS('', '#.removeAttribute(#)', element, key);
- return value;
- }
-}
-
-/**
- * Wrapper to expose namespaced attributes as a typed map.
- */
-class _NamespacedAttributeMap extends _AttributeMap {
- final String? _namespace;
-
- _NamespacedAttributeMap(Element element, this._namespace) : super(element);
-
- bool containsKey(Object? key) {
- return key is String && _element._hasAttributeNS(_namespace, key);
- }
-
- String? operator [](Object? key) {
- return _element.getAttributeNS(_namespace, key as String);
- }
-
- void operator []=(String key, String value) {
- _element.setAttributeNS(_namespace, key, value);
- }
-
- @pragma('dart2js:tryInline')
- String? remove(Object? key) =>
- key is String ? _remove(_namespace, _element, key) : null;
-
- /**
- * The number of {key, value} pairs in the map.
- */
- int get length {
- return keys.length;
- }
-
- bool _matches(_Attr node) => node._namespaceUri == _namespace;
-
- // Inline this because almost all call sites of [remove] do not use the
- // returned [value], and the annotations on the `getAttributeNS` call allow it
- // to be removed.
- @pragma('dart2js:tryInline')
- static String? _remove(String? namespace, Element element, String key) {
- String? value = JS(
- // throws:null(1) is not accurate since [key] could be malformed, but
- // [key] is checked again by `removeAttributeNS`.
- 'returns:String|Null;depends:all;effects:none;throws:null(1)',
- '#.getAttributeNS(#, #)',
- element,
- namespace,
- key);
- JS('', '#.removeAttributeNS(#, #)', element, namespace, key);
- return value;
- }
-}
-
-/**
- * Provides a Map abstraction on top of data-* attributes, similar to the
- * dataSet in the old DOM.
- */
-class _DataAttributeMap extends MapBase<String, String> {
- final Map<String, String> _attributes;
-
- _DataAttributeMap(this._attributes);
-
- // interface Map
-
- void addAll(Map<String, String> other) {
- other.forEach((k, v) {
- this[k] = v;
- });
- }
-
- Map<K, V> cast<K, V>() => Map.castFrom<String, String, K, V>(this);
- // TODO: Use lazy iterator when it is available on Map.
- bool containsValue(Object? value) => values.any((v) => v == value);
-
- bool containsKey(Object? key) =>
- _attributes.containsKey(_attr(key as String));
-
- String? operator [](Object? key) => _attributes[_attr(key as String)];
-
- void operator []=(String key, String value) {
- _attributes[_attr(key)] = value;
- }
-
- String putIfAbsent(String key, String ifAbsent()) =>
- _attributes.putIfAbsent(_attr(key), ifAbsent);
-
- String? remove(Object? key) => _attributes.remove(_attr(key as String));
-
- void clear() {
- // Needs to operate on a snapshot since we are mutating the collection.
- for (String key in keys) {
- remove(key);
- }
- }
-
- void forEach(void f(String key, String value)) {
- _attributes.forEach((String key, String value) {
- if (_matches(key)) {
- f(_strip(key), value);
- }
- });
- }
-
- Iterable<String> get keys {
- final keys = <String>[];
- _attributes.forEach((String key, String value) {
- if (_matches(key)) {
- keys.add(_strip(key));
- }
- });
- return keys;
- }
-
- Iterable<String> get values {
- final values = <String>[];
- _attributes.forEach((String key, String value) {
- if (_matches(key)) {
- values.add(value);
- }
- });
- return values;
- }
-
- int get length => keys.length;
-
- // TODO: Use lazy iterator when it is available on Map.
- bool get isEmpty => length == 0;
-
- bool get isNotEmpty => !isEmpty;
-
- // Helpers.
- String _attr(String key) => 'data-${_toHyphenedName(key)}';
- bool _matches(String key) => key.startsWith('data-');
- String _strip(String key) => _toCamelCase(key.substring(5));
-
- /**
- * Converts a string name with hyphens into an identifier, by removing hyphens
- * and capitalizing the following letter. Optionally [startUppercase] to
- * capitalize the first letter.
- */
- String _toCamelCase(String hyphenedName, {bool startUppercase: false}) {
- var segments = hyphenedName.split('-');
- int start = startUppercase ? 0 : 1;
- for (int i = start; i < segments.length; i++) {
- var segment = segments[i];
- if (segment.length > 0) {
- // Character between 'a'..'z' mapped to 'A'..'Z'
- segments[i] = '${segment[0].toUpperCase()}${segment.substring(1)}';
- }
- }
- return segments.join('');
- }
-
- /** Reverse of [toCamelCase]. */
- String _toHyphenedName(String word) {
- var sb = new StringBuffer();
- for (int i = 0; i < word.length; i++) {
- var lower = word[i].toLowerCase();
- if (word[i] != lower && i > 0) sb.write('-');
- sb.write(lower);
- }
- return sb.toString();
- }
-}
diff --git a/tools/dom/nnbd_src/CanvasImageSource.dart b/tools/dom/nnbd_src/CanvasImageSource.dart
deleted file mode 100644
index ffcd34a..0000000
--- a/tools/dom/nnbd_src/CanvasImageSource.dart
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright (c) 2012, 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.
-
-part of html;
-
-/**
- * An object that can be drawn to a 2D canvas rendering context.
- *
- * The image drawn to the canvas depends on the type of this object:
- *
- * * If this object is an [ImageElement], then this element's image is
- * drawn to the canvas. If this element is an animated image, then this
- * element's poster frame is drawn. If this element has no poster frame, then
- * the first frame of animation is drawn.
- *
- * * If this object is a [VideoElement], then the frame at this element's current
- * playback position is drawn to the canvas.
- *
- * * If this object is a [CanvasElement], then this element's bitmap is drawn to
- * the canvas.
- *
- * **Note:** Currently all versions of Internet Explorer do not support
- * drawing a video element to a canvas. You may also encounter problems drawing
- * a video to a canvas in Firefox if the source of the video is a data URL.
- *
- * ## See also
- *
- * * [CanvasRenderingContext2D.drawImage]
- * * [CanvasRenderingContext2D.drawImageToRect]
- * * [CanvasRenderingContext2D.drawImageScaled]
- * * [CanvasRenderingContext2D.drawImageScaledFromSource]
- *
- * ## Other resources
- *
- * * [Image sources for 2D rendering
- * contexts](https://html.spec.whatwg.org/multipage/scripting.html#image-sources-for-2d-rendering-contexts)
- * from WHATWG.
- * * [Drawing images](https://html.spec.whatwg.org/multipage/scripting.html#dom-context-2d-drawimage)
- * from WHATWG.
- */
-abstract class CanvasImageSource {}
diff --git a/tools/dom/nnbd_src/CrossFrameTypes.dart b/tools/dom/nnbd_src/CrossFrameTypes.dart
deleted file mode 100644
index 7d210a3..0000000
--- a/tools/dom/nnbd_src/CrossFrameTypes.dart
+++ /dev/null
@@ -1,156 +0,0 @@
-// Copyright (c) 2012, 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.
-
-part of html;
-
-/**
- * Top-level container for a browser tab or window.
- *
- * In a web browser, a [WindowBase] object represents any browser window. This
- * object contains the window's state and its relation to other
- * windows, such as which window opened this window.
- *
- * **Note:** This class represents any window, while [Window] is
- * used to access the properties and content of the current window or tab.
- *
- * ## See also
- *
- * * [Window]
- *
- * ## Other resources
- *
- * * [DOM Window](https://developer.mozilla.org/en-US/docs/DOM/window) from MDN.
- * * [Window](http://www.w3.org/TR/Window/) from the W3C.
- */
-abstract class WindowBase implements EventTarget {
- // Fields.
-
- /**
- * The current location of this window.
- *
- * Location currentLocation = window.location;
- * print(currentLocation.href); // 'http://www.example.com:80/'
- */
- LocationBase get location;
-
- /**
- * The current session history for this window.
- *
- * ## Other resources
- *
- * * [Session history and navigation
- * specification](https://html.spec.whatwg.org/multipage/browsers.html#history)
- * from WHATWG.
- */
- HistoryBase get history;
-
- /**
- * Indicates whether this window has been closed.
- *
- * print(window.closed); // 'false'
- * window.close();
- * print(window.closed); // 'true'
- */
- bool get closed;
-
- /**
- * A reference to the window that opened this one.
- *
- * Window thisWindow = window;
- * WindowBase otherWindow = thisWindow.open('http://www.example.com/', 'foo');
- * print(otherWindow.opener == thisWindow); // 'true'
- */
- WindowBase? get opener;
-
- /**
- * A reference to the parent of this window.
- *
- * If this [WindowBase] has no parent, [parent] will return a reference to
- * the [WindowBase] itself.
- *
- * IFrameElement myIFrame = new IFrameElement();
- * window.document.body.elements.add(myIFrame);
- * print(myIframe.contentWindow.parent == window) // 'true'
- *
- * print(window.parent == window) // 'true'
- */
- WindowBase? get parent;
-
- /**
- * A reference to the topmost window in the window hierarchy.
- *
- * If this [WindowBase] is the topmost [WindowBase], [top] will return a
- * reference to the [WindowBase] itself.
- *
- * // Add an IFrame to the current window.
- * IFrameElement myIFrame = new IFrameElement();
- * window.document.body.elements.add(myIFrame);
- *
- * // Add an IFrame inside of the other IFrame.
- * IFrameElement innerIFrame = new IFrameElement();
- * myIFrame.elements.add(innerIFrame);
- *
- * print(myIframe.contentWindow.top == window) // 'true'
- * print(innerIFrame.contentWindow.top == window) // 'true'
- *
- * print(window.top == window) // 'true'
- */
- WindowBase? get top;
-
- // Methods.
- /**
- * Closes the window.
- *
- * This method should only succeed if the [WindowBase] object is
- * **script-closeable** and the window calling [close] is allowed to navigate
- * the window.
- *
- * A window is script-closeable if it is either a window
- * that was opened by another window, or if it is a window with only one
- * document in its history.
- *
- * A window might not be allowed to navigate, and therefore close, another
- * window due to browser security features.
- *
- * var other = window.open('http://www.example.com', 'foo');
- * // Closes other window, as it is script-closeable.
- * other.close();
- * print(other.closed); // 'true'
- *
- * var newLocation = window.location
- * ..href = 'http://www.mysite.com';
- * window.location = newLocation;
- * // Does not close this window, as the history has changed.
- * window.close();
- * print(window.closed); // 'false'
- *
- * See also:
- *
- * * [Window close discussion](http://www.w3.org/TR/html5/browsers.html#dom-window-close) from the W3C
- */
- void close();
-
- /**
- * Sends a cross-origin message.
- *
- * ## Other resources
- *
- * * [window.postMessage](https://developer.mozilla.org/en-US/docs/Web/API/Window.postMessage)
- * from MDN.
- * * [Cross-document messaging](https://html.spec.whatwg.org/multipage/comms.html#web-messaging)
- * from WHATWG.
- */
- void postMessage(var message, String targetOrigin,
- [List<MessagePort>? messagePorts]);
-}
-
-abstract class LocationBase {
- void set href(String val);
-}
-
-abstract class HistoryBase {
- void back();
- void forward();
- void go(int distance);
-}
diff --git a/tools/dom/nnbd_src/CssClassSet.dart b/tools/dom/nnbd_src/CssClassSet.dart
deleted file mode 100644
index 6a76202..0000000
--- a/tools/dom/nnbd_src/CssClassSet.dart
+++ /dev/null
@@ -1,113 +0,0 @@
-// Copyright (c) 2012, 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.
-
-part of html;
-
-/** A Set that stores the CSS class names for an element. */
-abstract class CssClassSet implements Set<String> {
- /**
- * Adds the class [value] to the element if it is not on it, removes it if it
- * is.
- *
- * If [shouldAdd] is true, then we always add that [value] to the element. If
- * [shouldAdd] is false then we always remove [value] from the element.
- *
- * If this corresponds to one element, returns `true` if [value] is present
- * after the operation, and returns `false` if [value] is absent after the
- * operation.
- *
- * If this CssClassSet corresponds to many elements, `false` is always
- * returned.
- *
- * [value] must be a valid 'token' representing a single class, i.e. a
- * non-empty string containing no whitespace. To toggle multiple classes, use
- * [toggleAll].
- */
- bool toggle(String value, [bool? shouldAdd]);
-
- /**
- * Returns [:true:] if classes cannot be added or removed from this
- * [:CssClassSet:].
- */
- bool get frozen;
-
- /**
- * Determine if this element contains the class [value].
- *
- * This is the Dart equivalent of jQuery's
- * [hasClass](http://api.jquery.com/hasClass/).
- *
- * [value] must be a valid 'token' representing a single class, i.e. a
- * non-empty string containing no whitespace.
- */
- bool contains(Object? value);
-
- /**
- * Add the class [value] to element.
- *
- * [add] and [addAll] are the Dart equivalent of jQuery's
- * [addClass](http://api.jquery.com/addClass/).
- *
- * If this CssClassSet corresponds to one element. Returns true if [value] was
- * added to the set, otherwise false.
- *
- * If this CssClassSet corresponds to many elements, `false` is always
- * returned.
- *
- * [value] must be a valid 'token' representing a single class, i.e. a
- * non-empty string containing no whitespace. To add multiple classes use
- * [addAll].
- */
- bool add(String value);
-
- /**
- * Remove the class [value] from element, and return true on successful
- * removal.
- *
- * [remove] and [removeAll] are the Dart equivalent of jQuery's
- * [removeClass](http://api.jquery.com/removeClass/).
- *
- * [value] must be a valid 'token' representing a single class, i.e. a
- * non-empty string containing no whitespace. To remove multiple classes, use
- * [removeAll].
- */
- bool remove(Object? value);
-
- /**
- * Add all classes specified in [iterable] to element.
- *
- * [add] and [addAll] are the Dart equivalent of jQuery's
- * [addClass](http://api.jquery.com/addClass/).
- *
- * Each element of [iterable] must be a valid 'token' representing a single
- * class, i.e. a non-empty string containing no whitespace.
- */
- void addAll(Iterable<String> iterable);
-
- /**
- * Remove all classes specified in [iterable] from element.
- *
- * [remove] and [removeAll] are the Dart equivalent of jQuery's
- * [removeClass](http://api.jquery.com/removeClass/).
- *
- * Each element of [iterable] must be a valid 'token' representing a single
- * class, i.e. a non-empty string containing no whitespace.
- */
- void removeAll(Iterable<Object?> iterable);
-
- /**
- * Toggles all classes specified in [iterable] on element.
- *
- * Iterate through [iterable]'s items, and add it if it is not on it, or
- * remove it if it is. This is the Dart equivalent of jQuery's
- * [toggleClass](http://api.jquery.com/toggleClass/).
- * If [shouldAdd] is true, then we always add all the classes in [iterable]
- * element. If [shouldAdd] is false then we always remove all the classes in
- * [iterable] from the element.
- *
- * Each element of [iterable] must be a valid 'token' representing a single
- * class, i.e. a non-empty string containing no whitespace.
- */
- void toggleAll(Iterable<String> iterable, [bool? shouldAdd]);
-}
diff --git a/tools/dom/nnbd_src/CssRectangle.dart b/tools/dom/nnbd_src/CssRectangle.dart
deleted file mode 100644
index d26aac7..0000000
--- a/tools/dom/nnbd_src/CssRectangle.dart
+++ /dev/null
@@ -1,372 +0,0 @@
-// Copyright (c) 2013, 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.
-
-part of html;
-
-/**
- * A rectangle representing all the content of the element in the
- * [box model](http://www.w3.org/TR/CSS2/box.html).
- */
-class _ContentCssRect extends CssRect {
- _ContentCssRect(Element element) : super(element);
-
- num get height =>
- _element.offsetHeight + _addOrSubtractToBoxModel(_HEIGHT, _CONTENT);
-
- num get width =>
- _element.offsetWidth + _addOrSubtractToBoxModel(_WIDTH, _CONTENT);
-
- /**
- * Set the height to `newHeight`.
- *
- * newHeight can be either a [num] representing the height in pixels or a
- * [Dimension] object. Values of newHeight that are less than zero are
- * converted to effectively setting the height to 0. This is equivalent to the
- * `height` function in jQuery and the calculated `height` CSS value,
- * converted to a num in pixels.
- */
- set height(dynamic newHeight) {
- if (newHeight is Dimension) {
- Dimension newHeightAsDimension = newHeight;
- if (newHeightAsDimension.value < 0) newHeight = new Dimension.px(0);
- _element.style.height = newHeight.toString();
- } else if (newHeight is num) {
- if (newHeight < 0) newHeight = 0;
- _element.style.height = '${newHeight}px';
- } else {
- throw new ArgumentError("newHeight is not a Dimension or num");
- }
- }
-
- /**
- * Set the current computed width in pixels of this element.
- *
- * newWidth can be either a [num] representing the width in pixels or a
- * [Dimension] object. This is equivalent to the `width` function in jQuery
- * and the calculated
- * `width` CSS value, converted to a dimensionless num in pixels.
- */
- set width(dynamic newWidth) {
- if (newWidth is Dimension) {
- Dimension newWidthAsDimension = newWidth;
- if (newWidthAsDimension.value < 0) newWidth = new Dimension.px(0);
- _element.style.width = newWidth.toString();
- } else if (newWidth is num) {
- if (newWidth < 0) newWidth = 0;
- _element.style.width = '${newWidth}px';
- } else {
- throw new ArgumentError("newWidth is not a Dimension or num");
- }
- }
-
- num get left =>
- _element.getBoundingClientRect().left -
- _addOrSubtractToBoxModel(['left'], _CONTENT);
- num get top =>
- _element.getBoundingClientRect().top -
- _addOrSubtractToBoxModel(['top'], _CONTENT);
-}
-
-/**
- * A list of element content rectangles in the
- * [box model](http://www.w3.org/TR/CSS2/box.html).
- */
-class _ContentCssListRect extends _ContentCssRect {
- List<Element> _elementList;
-
- _ContentCssListRect(List<Element> elementList)
- : _elementList = elementList,
- super(elementList.first);
-
- /**
- * Set the height to `newHeight`.
- *
- * Values of newHeight that are less than zero are converted to effectively
- * setting the height to 0. This is equivalent to the `height`
- * function in jQuery and the calculated `height` CSS value, converted to a
- * num in pixels.
- */
- set height(newHeight) {
- _elementList.forEach((e) => e.contentEdge.height = newHeight);
- }
-
- /**
- * Set the current computed width in pixels of this element.
- *
- * This is equivalent to the `width` function in jQuery and the calculated
- * `width` CSS value, converted to a dimensionless num in pixels.
- */
- set width(newWidth) {
- _elementList.forEach((e) => e.contentEdge.width = newWidth);
- }
-}
-
-/**
- * A rectangle representing the dimensions of the space occupied by the
- * element's content + padding in the
- * [box model](http://www.w3.org/TR/CSS2/box.html).
- */
-class _PaddingCssRect extends CssRect {
- _PaddingCssRect(element) : super(element);
- num get height =>
- _element.offsetHeight + _addOrSubtractToBoxModel(_HEIGHT, _PADDING);
- num get width =>
- _element.offsetWidth + _addOrSubtractToBoxModel(_WIDTH, _PADDING);
-
- num get left =>
- _element.getBoundingClientRect().left -
- _addOrSubtractToBoxModel(['left'], _PADDING);
- num get top =>
- _element.getBoundingClientRect().top -
- _addOrSubtractToBoxModel(['top'], _PADDING);
-}
-
-/**
- * A rectangle representing the dimensions of the space occupied by the
- * element's content + padding + border in the
- * [box model](http://www.w3.org/TR/CSS2/box.html).
- */
-class _BorderCssRect extends CssRect {
- _BorderCssRect(element) : super(element);
- num get height => _element.offsetHeight;
- num get width => _element.offsetWidth;
-
- num get left => _element.getBoundingClientRect().left;
- num get top => _element.getBoundingClientRect().top;
-}
-
-/**
- * A rectangle representing the dimensions of the space occupied by the
- * element's content + padding + border + margin in the
- * [box model](http://www.w3.org/TR/CSS2/box.html).
- */
-class _MarginCssRect extends CssRect {
- _MarginCssRect(element) : super(element);
- num get height =>
- _element.offsetHeight + _addOrSubtractToBoxModel(_HEIGHT, _MARGIN);
- num get width =>
- _element.offsetWidth + _addOrSubtractToBoxModel(_WIDTH, _MARGIN);
-
- num get left =>
- _element.getBoundingClientRect().left -
- _addOrSubtractToBoxModel(['left'], _MARGIN);
- num get top =>
- _element.getBoundingClientRect().top -
- _addOrSubtractToBoxModel(['top'], _MARGIN);
-}
-
-/**
- * A class for representing CSS dimensions.
- *
- * In contrast to the more general purpose [Rectangle] class, this class's
- * values are mutable, so one can change the height of an element
- * programmatically.
- *
- * _Important_ _note_: use of these methods will perform CSS calculations that
- * can trigger a browser reflow. Therefore, use of these properties _during_ an
- * animation frame is discouraged. See also:
- * [Browser Reflow](https://developers.google.com/speed/articles/reflow)
- */
-abstract class CssRect implements Rectangle<num> {
- Element _element;
-
- CssRect(this._element);
-
- num get left;
-
- num get top;
-
- /**
- * The height of this rectangle.
- *
- * This is equivalent to the `height` function in jQuery and the calculated
- * `height` CSS value, converted to a dimensionless num in pixels. Unlike
- * [getBoundingClientRect], `height` will return the same numerical width if
- * the element is hidden or not.
- */
- num get height;
-
- /**
- * The width of this rectangle.
- *
- * This is equivalent to the `width` function in jQuery and the calculated
- * `width` CSS value, converted to a dimensionless num in pixels. Unlike
- * [getBoundingClientRect], `width` will return the same numerical width if
- * the element is hidden or not.
- */
- num get width;
-
- /**
- * Set the height to `newHeight`.
- *
- * newHeight can be either a [num] representing the height in pixels or a
- * [Dimension] object. Values of newHeight that are less than zero are
- * converted to effectively setting the height to 0. This is equivalent to the
- * `height` function in jQuery and the calculated `height` CSS value,
- * converted to a num in pixels.
- *
- * Note that only the content height can actually be set via this method.
- */
- set height(dynamic newHeight) {
- throw new UnsupportedError("Can only set height for content rect.");
- }
-
- /**
- * Set the current computed width in pixels of this element.
- *
- * newWidth can be either a [num] representing the width in pixels or a
- * [Dimension] object. This is equivalent to the `width` function in jQuery
- * and the calculated
- * `width` CSS value, converted to a dimensionless num in pixels.
- *
- * Note that only the content width can be set via this method.
- */
- set width(dynamic newWidth) {
- throw new UnsupportedError("Can only set width for content rect.");
- }
-
- /**
- * Return a value that is used to modify the initial height or width
- * measurement of an element. Depending on the value (ideally an enum) passed
- * to augmentingMeasurement, we may need to add or subtract margin, padding,
- * or border values, depending on the measurement we're trying to obtain.
- */
- num _addOrSubtractToBoxModel(
- List<String> dimensions, String augmentingMeasurement) {
- // getComputedStyle always returns pixel values (hence, computed), so we're
- // always dealing with pixels in this method.
- var styles = _element.getComputedStyle();
-
- num val = 0;
-
- for (String measurement in dimensions) {
- // The border-box and default box model both exclude margin in the regular
- // height/width calculation, so add it if we want it for this measurement.
- if (augmentingMeasurement == _MARGIN) {
- val += new Dimension.css(
- styles.getPropertyValue('$augmentingMeasurement-$measurement'))
- .value;
- }
-
- // The border-box includes padding and border, so remove it if we want
- // just the content itself.
- if (augmentingMeasurement == _CONTENT) {
- val -= new Dimension.css(
- styles.getPropertyValue('${_PADDING}-$measurement'))
- .value;
- }
-
- // At this point, we don't wan't to augment with border or margin,
- // so remove border.
- if (augmentingMeasurement != _MARGIN) {
- val -= new Dimension.css(
- styles.getPropertyValue('border-${measurement}-width'))
- .value;
- }
- }
- return val;
- }
-
- // TODO(jacobr): these methods are duplicated from _RectangleBase in dart:math
- // Ideally we would provide a RectangleMixin class that provides this
- // implementation. In an ideal world we would exp
- /** The x-coordinate of the right edge. */
- num get right => left + width;
- /** The y-coordinate of the bottom edge. */
- num get bottom => top + height;
-
- String toString() {
- return 'Rectangle ($left, $top) $width x $height';
- }
-
- bool operator ==(other) =>
- other is Rectangle &&
- left == other.left &&
- top == other.top &&
- right == other.right &&
- bottom == other.bottom;
-
- int get hashCode => _JenkinsSmiHash.hash4(
- left.hashCode, top.hashCode, right.hashCode, bottom.hashCode);
-
- /**
- * Computes the intersection of `this` and [other].
- *
- * The intersection of two axis-aligned rectangles, if any, is always another
- * axis-aligned rectangle.
- *
- * Returns the intersection of this and `other`, or `null` if they don't
- * intersect.
- */
- Rectangle<num>? intersection(Rectangle<num> other) {
- var x0 = max(left, other.left);
- var x1 = min(left + width, other.left + other.width);
-
- if (x0 <= x1) {
- var y0 = max(top, other.top);
- var y1 = min(top + height, other.top + other.height);
-
- if (y0 <= y1) {
- return new Rectangle<num>(x0, y0, x1 - x0, y1 - y0);
- }
- }
- return null;
- }
-
- /**
- * Returns true if `this` intersects [other].
- */
- bool intersects(Rectangle<num> other) {
- return (left <= other.left + other.width &&
- other.left <= left + width &&
- top <= other.top + other.height &&
- other.top <= top + height);
- }
-
- /**
- * Returns a new rectangle which completely contains `this` and [other].
- */
- Rectangle<num> boundingBox(Rectangle<num> other) {
- var right = max(this.left + this.width, other.left + other.width);
- var bottom = max(this.top + this.height, other.top + other.height);
-
- var left = min(this.left, other.left);
- var top = min(this.top, other.top);
-
- return new Rectangle<num>(left, top, right - left, bottom - top);
- }
-
- /**
- * Tests whether `this` entirely contains [another].
- */
- bool containsRectangle(Rectangle<num> another) {
- return left <= another.left &&
- left + width >= another.left + another.width &&
- top <= another.top &&
- top + height >= another.top + another.height;
- }
-
- /**
- * Tests whether [another] is inside or along the edges of `this`.
- */
- bool containsPoint(Point<num> another) {
- return another.x >= left &&
- another.x <= left + width &&
- another.y >= top &&
- another.y <= top + height;
- }
-
- Point<num> get topLeft => new Point<num>(this.left, this.top);
- Point<num> get topRight => new Point<num>(this.left + this.width, this.top);
- Point<num> get bottomRight =>
- new Point<num>(this.left + this.width, this.top + this.height);
- Point<num> get bottomLeft =>
- new Point<num>(this.left, this.top + this.height);
-}
-
-final _HEIGHT = ['top', 'bottom'];
-final _WIDTH = ['right', 'left'];
-final _CONTENT = 'content';
-final _PADDING = 'padding';
-final _MARGIN = 'margin';
diff --git a/tools/dom/nnbd_src/Dimension.dart b/tools/dom/nnbd_src/Dimension.dart
deleted file mode 100644
index 7ed02192..0000000
--- a/tools/dom/nnbd_src/Dimension.dart
+++ /dev/null
@@ -1,84 +0,0 @@
-part of html;
-
-/**
- * Class representing a
- * [length measurement](https://developer.mozilla.org/en-US/docs/Web/CSS/length)
- * in CSS.
- */
-class Dimension {
- num _value;
- String _unit;
-
- /** Set this CSS Dimension to a percentage `value`. */
- Dimension.percent(this._value) : _unit = '%';
-
- /** Set this CSS Dimension to a pixel `value`. */
- Dimension.px(this._value) : _unit = 'px';
-
- /** Set this CSS Dimension to a pica `value`. */
- Dimension.pc(this._value) : _unit = 'pc';
-
- /** Set this CSS Dimension to a point `value`. */
- Dimension.pt(this._value) : _unit = 'pt';
-
- /** Set this CSS Dimension to an inch `value`. */
- Dimension.inch(this._value) : _unit = 'in';
-
- /** Set this CSS Dimension to a centimeter `value`. */
- Dimension.cm(this._value) : _unit = 'cm';
-
- /** Set this CSS Dimension to a millimeter `value`. */
- Dimension.mm(this._value) : _unit = 'mm';
-
- /**
- * Set this CSS Dimension to the specified number of ems.
- *
- * 1em is equal to the current font size. (So 2ems is equal to double the font
- * size). This is useful for producing website layouts that scale nicely with
- * the user's desired font size.
- */
- Dimension.em(this._value) : _unit = 'em';
-
- /**
- * Set this CSS Dimension to the specified number of x-heights.
- *
- * One ex is equal to the x-height of a font's baseline to its mean line,
- * generally the height of the letter "x" in the font, which is usually about
- * half the font-size.
- */
- Dimension.ex(this._value) : _unit = 'ex';
-
- /**
- * Construct a Dimension object from the valid, simple CSS string `cssValue`
- * that represents a distance measurement.
- *
- * This constructor is intended as a convenience method for working with
- * simplistic CSS length measurements. Non-numeric values such as `auto` or
- * `inherit` or invalid CSS will cause this constructor to throw a
- * FormatError.
- */
- Dimension.css(String cssValue)
- : _unit = '',
- _value = 0 {
- if (cssValue == '') cssValue = '0px';
- if (cssValue.endsWith('%')) {
- _unit = '%';
- } else {
- _unit = cssValue.substring(cssValue.length - 2);
- }
- if (cssValue.contains('.')) {
- _value =
- double.parse(cssValue.substring(0, cssValue.length - _unit.length));
- } else {
- _value = int.parse(cssValue.substring(0, cssValue.length - _unit.length));
- }
- }
-
- /** Print out the CSS String representation of this value. */
- String toString() {
- return '${_value}${_unit}';
- }
-
- /** Return a unitless, numerical value of this CSS value. */
- num get value => this._value;
-}
diff --git a/tools/dom/nnbd_src/EventListener.dart b/tools/dom/nnbd_src/EventListener.dart
deleted file mode 100644
index 60b29f1..0000000
--- a/tools/dom/nnbd_src/EventListener.dart
+++ /dev/null
@@ -1,7 +0,0 @@
-// Copyright (c) 2011, 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.
-
-part of html;
-
-typedef EventListener(Event event);
diff --git a/tools/dom/nnbd_src/EventStreamProvider.dart b/tools/dom/nnbd_src/EventStreamProvider.dart
deleted file mode 100644
index fe01be9..0000000
--- a/tools/dom/nnbd_src/EventStreamProvider.dart
+++ /dev/null
@@ -1,461 +0,0 @@
-// Copyright (c) 2013, 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.
-
-part of html;
-
-/**
- * A factory to expose DOM events as Streams.
- */
-class EventStreamProvider<T extends Event> {
- final String _eventType;
-
- const EventStreamProvider(this._eventType);
-
- /**
- * Gets a [Stream] for this event type, on the specified target.
- *
- * This will always return a broadcast stream so multiple listeners can be
- * used simultaneously.
- *
- * This may be used to capture DOM events:
- *
- * Element.keyDownEvent.forTarget(element, useCapture: true).listen(...);
- *
- * // Alternate method:
- * Element.keyDownEvent.forTarget(element).capture(...);
- *
- * Or for listening to an event which will bubble through the DOM tree:
- *
- * MediaElement.pauseEvent.forTarget(document.body).listen(...);
- *
- * See also:
- *
- * * [EventTarget.addEventListener](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener)
- * from MDN.
- */
- Stream<T> forTarget(EventTarget? e, {bool useCapture: false}) =>
- new _EventStream<T>(e, _eventType, useCapture);
-
- /**
- * Gets an [ElementEventStream] for this event type, on the specified element.
- *
- * This will always return a broadcast stream so multiple listeners can be
- * used simultaneously.
- *
- * This may be used to capture DOM events:
- *
- * Element.keyDownEvent.forElement(element, useCapture: true).listen(...);
- *
- * // Alternate method:
- * Element.keyDownEvent.forElement(element).capture(...);
- *
- * Or for listening to an event which will bubble through the DOM tree:
- *
- * MediaElement.pauseEvent.forElement(document.body).listen(...);
- *
- * See also:
- *
- * * [EventTarget.addEventListener](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener)
- * from MDN.
- */
- ElementStream<T> forElement(Element e, {bool useCapture: false}) {
- return new _ElementEventStreamImpl<T>(e, _eventType, useCapture);
- }
-
- /**
- * Gets an [ElementEventStream] for this event type, on the list of elements.
- *
- * This will always return a broadcast stream so multiple listeners can be
- * used simultaneously.
- *
- * This may be used to capture DOM events:
- *
- * Element.keyDownEvent._forElementList(element, useCapture: true).listen(...);
- *
- * See also:
- *
- * * [EventTarget.addEventListener](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener)
- * from MDN.
- */
- ElementStream<T> _forElementList(ElementList<Element> e,
- {bool useCapture: false}) {
- return new _ElementListEventStreamImpl<T>(e, _eventType, useCapture);
- }
-
- /**
- * Gets the type of the event which this would listen for on the specified
- * event target.
- *
- * The target is necessary because some browsers may use different event names
- * for the same purpose and the target allows differentiating browser support.
- */
- String getEventType(EventTarget target) {
- return _eventType;
- }
-}
-
-/** A specialized Stream available to [Element]s to enable event delegation. */
-abstract class ElementStream<T extends Event> implements Stream<T> {
- /**
- * Return a stream that only fires when the particular event fires for
- * elements matching the specified CSS selector.
- *
- * This is the Dart equivalent to jQuery's
- * [delegate](http://api.jquery.com/delegate/).
- */
- Stream<T> matches(String selector);
-
- /**
- * Adds a capturing subscription to this stream.
- *
- * If the target of the event is a descendant of the element from which this
- * stream derives then [onData] is called before the event propagates down to
- * the target. This is the opposite of bubbling behavior, where the event
- * is first processed for the event target and then bubbles upward.
- *
- * ## Other resources
- *
- * * [Event Capture](http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-flow-capture)
- * from the W3C DOM Events specification.
- */
- StreamSubscription<T> capture(void onData(T event));
-}
-
-/**
- * Adapter for exposing DOM events as Dart streams.
- */
-class _EventStream<T extends Event> extends Stream<T> {
- final EventTarget? _target;
- final String _eventType;
- final bool _useCapture;
-
- _EventStream(this._target, this._eventType, this._useCapture);
-
- // DOM events are inherently multi-subscribers.
- Stream<T> asBroadcastStream(
- {void onListen(StreamSubscription<T> subscription)?,
- void onCancel(StreamSubscription<T> subscription)?}) =>
- this;
- bool get isBroadcast => true;
-
- // TODO(9757): Inlining should be smart and inline only when inlining would
- // enable scalar replacement of an immediately allocated receiver.
- @pragma('dart2js:tryInline')
- StreamSubscription<T> listen(void onData(T event)?,
- {Function? onError, void onDone()?, bool? cancelOnError}) {
- return new _EventStreamSubscription<T>(
- this._target, this._eventType, onData, this._useCapture);
- }
-}
-
-bool _matchesWithAncestors(Event event, String selector) {
- var target = event.target;
- return target is Element ? target.matchesWithAncestors(selector) : false;
-}
-
-/**
- * Adapter for exposing DOM Element events as streams, while also allowing
- * event delegation.
- */
-class _ElementEventStreamImpl<T extends Event> extends _EventStream<T>
- implements ElementStream<T> {
- _ElementEventStreamImpl(target, eventType, useCapture)
- : super(target, eventType, useCapture);
-
- Stream<T> matches(String selector) =>
- this.where((event) => _matchesWithAncestors(event, selector)).map((e) {
- e._selector = selector;
- return e;
- });
-
- StreamSubscription<T> capture(void onData(T event)) =>
- new _EventStreamSubscription<T>(
- this._target, this._eventType, onData, true);
-}
-
-/**
- * Adapter for exposing events on a collection of DOM Elements as streams,
- * while also allowing event delegation.
- */
-class _ElementListEventStreamImpl<T extends Event> extends Stream<T>
- implements ElementStream<T> {
- final Iterable<Element> _targetList;
- final bool _useCapture;
- final String _eventType;
-
- _ElementListEventStreamImpl(
- this._targetList, this._eventType, this._useCapture);
-
- Stream<T> matches(String selector) =>
- this.where((event) => _matchesWithAncestors(event, selector)).map((e) {
- e._selector = selector;
- return e;
- });
-
- // Delegate all regular Stream behavior to a wrapped Stream.
- StreamSubscription<T> listen(void onData(T event)?,
- {Function? onError, void onDone()?, bool? cancelOnError}) {
- var pool = new _StreamPool<T>.broadcast();
- for (var target in _targetList) {
- pool.add(new _EventStream<T>(target, _eventType, _useCapture));
- }
- return pool.stream.listen(onData,
- onError: onError, onDone: onDone, cancelOnError: cancelOnError);
- }
-
- StreamSubscription<T> capture(void onData(T event)) {
- var pool = new _StreamPool<T>.broadcast();
- for (var target in _targetList) {
- pool.add(new _EventStream<T>(target, _eventType, true));
- }
- return pool.stream.listen(onData);
- }
-
- Stream<T> asBroadcastStream(
- {void onListen(StreamSubscription<T> subscription)?,
- void onCancel(StreamSubscription<T> subscription)?}) =>
- this;
- bool get isBroadcast => true;
-}
-
-// We would like this to just be EventListener<T> but that typdef cannot
-// use generics until dartbug/26276 is fixed.
-typedef _EventListener<T extends Event>(T event);
-
-class _EventStreamSubscription<T extends Event> extends StreamSubscription<T> {
- int _pauseCount = 0;
- EventTarget? _target;
- final String _eventType;
- EventListener? _onData;
- final bool _useCapture;
-
- // TODO(leafp): It would be better to write this as
- // _onData = onData == null ? null :
- // onData is void Function(Event)
- // ? _wrapZone<Event>(onData)
- // : _wrapZone<Event>((e) => onData(e as T))
- // In order to support existing tests which pass the wrong type of events but
- // use a more general listener, without causing as much slowdown for things
- // which are typed correctly. But this currently runs afoul of restrictions
- // on is checks for compatibility with the VM.
- _EventStreamSubscription(
- this._target, this._eventType, void onData(T event)?, this._useCapture)
- : _onData = onData == null
- ? null
- : _wrapZone<Event>((e) => (onData as dynamic)(e)) {
- _tryResume();
- }
-
- Future cancel() {
- // Check for strong mode. This function can no longer return null in strong
- // mode, so only return null in weak mode to preserve synchronous timing.
- // See issue 41653 for more details.
- dynamic emptyFuture =
- typeAcceptsNull<Event>() ? null : Future<void>.value();
- if (_canceled) return emptyFuture as Future;
-
- _unlisten();
- // Clear out the target to indicate this is complete.
- _target = null;
- _onData = null;
- return emptyFuture as Future;
- }
-
- bool get _canceled => _target == null;
-
- void onData(void handleData(T event)?) {
- if (_canceled) {
- throw new StateError("Subscription has been canceled.");
- }
- // Remove current event listener.
- _unlisten();
- _onData = handleData == null
- ? null
- : _wrapZone<Event>((e) => (handleData as dynamic)(e));
- _tryResume();
- }
-
- /// Has no effect.
- void onError(Function? handleError) {}
-
- /// Has no effect.
- void onDone(void handleDone()?) {}
-
- void pause([Future? resumeSignal]) {
- if (_canceled) return;
- ++_pauseCount;
- _unlisten();
-
- if (resumeSignal != null) {
- resumeSignal.whenComplete(resume);
- }
- }
-
- bool get isPaused => _pauseCount > 0;
-
- void resume() {
- if (_canceled || !isPaused) return;
- --_pauseCount;
- _tryResume();
- }
-
- void _tryResume() {
- if (_onData != null && !isPaused) {
- _target!.addEventListener(_eventType, _onData, _useCapture);
- }
- }
-
- void _unlisten() {
- if (_onData != null) {
- _target!.removeEventListener(_eventType, _onData, _useCapture);
- }
- }
-
- Future<E> asFuture<E>([E? futureValue]) {
- // We just need a future that will never succeed or fail.
- var completer = new Completer<E>();
- return completer.future;
- }
-}
-
-/**
- * A stream of custom events, which enables the user to "fire" (add) their own
- * custom events to a stream.
- */
-abstract class CustomStream<T extends Event> implements Stream<T> {
- /**
- * Add the following custom event to the stream for dispatching to interested
- * listeners.
- */
- void add(T event);
-}
-
-class _CustomEventStreamImpl<T extends Event> extends Stream<T>
- implements CustomStream<T> {
- StreamController<T> _streamController;
- /** The type of event this stream is providing (e.g. "keydown"). */
- String _type;
-
- _CustomEventStreamImpl(String type)
- : _type = type,
- _streamController = new StreamController.broadcast(sync: true);
-
- // Delegate all regular Stream behavior to our wrapped Stream.
- StreamSubscription<T> listen(void onData(T event)?,
- {Function? onError, void onDone()?, bool? cancelOnError}) {
- return _streamController.stream.listen(onData,
- onError: onError, onDone: onDone, cancelOnError: cancelOnError);
- }
-
- Stream<T> asBroadcastStream(
- {void onListen(StreamSubscription<T> subscription)?,
- void onCancel(StreamSubscription<T> subscription)?}) =>
- _streamController.stream;
-
- bool get isBroadcast => true;
-
- void add(T event) {
- if (event.type == _type) _streamController.add(event);
- }
-}
-
-class _CustomKeyEventStreamImpl extends _CustomEventStreamImpl<KeyEvent>
- implements CustomStream<KeyEvent> {
- _CustomKeyEventStreamImpl(String type) : super(type);
-
- void add(KeyEvent event) {
- if (event.type == _type) {
- event.currentTarget!.dispatchEvent(event._parent);
- _streamController.add(event);
- }
- }
-}
-
-/**
- * A pool of streams whose events are unified and emitted through a central
- * stream.
- */
-// TODO (efortuna): Remove this when Issue 12218 is addressed.
-class _StreamPool<T> {
- StreamController<T>? _controller;
-
- /// Subscriptions to the streams that make up the pool.
- var _subscriptions = new Map<Stream<T>, StreamSubscription<T>>();
-
- /**
- * Creates a new stream pool where [stream] can be listened to more than
- * once.
- *
- * Any events from buffered streams in the pool will be emitted immediately,
- * regardless of whether [stream] has any subscribers.
- */
- _StreamPool.broadcast() {
- _controller =
- new StreamController<T>.broadcast(sync: true, onCancel: close);
- }
-
- /**
- * The stream through which all events from streams in the pool are emitted.
- */
- Stream<T> get stream => _controller!.stream;
-
- /**
- * Adds [stream] as a member of this pool.
- *
- * Any events from [stream] will be emitted through [this.stream]. If
- * [stream] is sync, they'll be emitted synchronously; if [stream] is async,
- * they'll be emitted asynchronously.
- */
- void add(Stream<T> stream) {
- if (_subscriptions.containsKey(stream)) return;
- _subscriptions[stream] = stream.listen(_controller!.add,
- onError: _controller!.addError, onDone: () => remove(stream));
- }
-
- /** Removes [stream] as a member of this pool. */
- void remove(Stream<T> stream) {
- var subscription = _subscriptions.remove(stream);
- if (subscription != null) subscription.cancel();
- }
-
- /** Removes all streams from this pool and closes [stream]. */
- void close() {
- for (var subscription in _subscriptions.values) {
- subscription.cancel();
- }
- _subscriptions.clear();
- _controller!.close();
- }
-}
-
-/**
- * A factory to expose DOM events as streams, where the DOM event name has to
- * be determined on the fly (for example, mouse wheel events).
- */
-class _CustomEventStreamProvider<T extends Event>
- implements EventStreamProvider<T> {
- final _eventTypeGetter;
- const _CustomEventStreamProvider(this._eventTypeGetter);
-
- Stream<T> forTarget(EventTarget? e, {bool useCapture: false}) {
- return new _EventStream<T>(e, _eventTypeGetter(e), useCapture);
- }
-
- ElementStream<T> forElement(Element e, {bool useCapture: false}) {
- return new _ElementEventStreamImpl<T>(e, _eventTypeGetter(e), useCapture);
- }
-
- ElementStream<T> _forElementList(ElementList<Element> e,
- {bool useCapture: false}) {
- return new _ElementListEventStreamImpl<T>(
- e, _eventTypeGetter(e), useCapture);
- }
-
- String getEventType(EventTarget target) {
- return _eventTypeGetter(target);
- }
-
- String get _eventType =>
- throw new UnsupportedError('Access type through getEventType method.');
-}
diff --git a/tools/dom/nnbd_src/Html5NodeValidator.dart b/tools/dom/nnbd_src/Html5NodeValidator.dart
deleted file mode 100644
index e6bdede..0000000
--- a/tools/dom/nnbd_src/Html5NodeValidator.dart
+++ /dev/null
@@ -1,449 +0,0 @@
-// DO NOT EDIT- this file is generated from running tool/generator.sh.
-
-// Copyright (c) 2013, 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.
-
-part of dart.dom.html;
-
-/**
- * A Dart DOM validator generated from Caja whitelists.
- *
- * This contains a whitelist of known HTML tagNames and attributes and will only
- * accept known good values.
- *
- * See also:
- *
- * * <https://code.google.com/p/google-caja/wiki/CajaWhitelists>
- */
-class _Html5NodeValidator implements NodeValidator {
- static final Set<String> _allowedElements = new Set.from([
- 'A',
- 'ABBR',
- 'ACRONYM',
- 'ADDRESS',
- 'AREA',
- 'ARTICLE',
- 'ASIDE',
- 'AUDIO',
- 'B',
- 'BDI',
- 'BDO',
- 'BIG',
- 'BLOCKQUOTE',
- 'BR',
- 'BUTTON',
- 'CANVAS',
- 'CAPTION',
- 'CENTER',
- 'CITE',
- 'CODE',
- 'COL',
- 'COLGROUP',
- 'COMMAND',
- 'DATA',
- 'DATALIST',
- 'DD',
- 'DEL',
- 'DETAILS',
- 'DFN',
- 'DIR',
- 'DIV',
- 'DL',
- 'DT',
- 'EM',
- 'FIELDSET',
- 'FIGCAPTION',
- 'FIGURE',
- 'FONT',
- 'FOOTER',
- 'FORM',
- 'H1',
- 'H2',
- 'H3',
- 'H4',
- 'H5',
- 'H6',
- 'HEADER',
- 'HGROUP',
- 'HR',
- 'I',
- 'IFRAME',
- 'IMG',
- 'INPUT',
- 'INS',
- 'KBD',
- 'LABEL',
- 'LEGEND',
- 'LI',
- 'MAP',
- 'MARK',
- 'MENU',
- 'METER',
- 'NAV',
- 'NOBR',
- 'OL',
- 'OPTGROUP',
- 'OPTION',
- 'OUTPUT',
- 'P',
- 'PRE',
- 'PROGRESS',
- 'Q',
- 'S',
- 'SAMP',
- 'SECTION',
- 'SELECT',
- 'SMALL',
- 'SOURCE',
- 'SPAN',
- 'STRIKE',
- 'STRONG',
- 'SUB',
- 'SUMMARY',
- 'SUP',
- 'TABLE',
- 'TBODY',
- 'TD',
- 'TEXTAREA',
- 'TFOOT',
- 'TH',
- 'THEAD',
- 'TIME',
- 'TR',
- 'TRACK',
- 'TT',
- 'U',
- 'UL',
- 'VAR',
- 'VIDEO',
- 'WBR',
- ]);
-
- static const _standardAttributes = const <String>[
- '*::class',
- '*::dir',
- '*::draggable',
- '*::hidden',
- '*::id',
- '*::inert',
- '*::itemprop',
- '*::itemref',
- '*::itemscope',
- '*::lang',
- '*::spellcheck',
- '*::title',
- '*::translate',
- 'A::accesskey',
- 'A::coords',
- 'A::hreflang',
- 'A::name',
- 'A::shape',
- 'A::tabindex',
- 'A::target',
- 'A::type',
- 'AREA::accesskey',
- 'AREA::alt',
- 'AREA::coords',
- 'AREA::nohref',
- 'AREA::shape',
- 'AREA::tabindex',
- 'AREA::target',
- 'AUDIO::controls',
- 'AUDIO::loop',
- 'AUDIO::mediagroup',
- 'AUDIO::muted',
- 'AUDIO::preload',
- 'BDO::dir',
- 'BODY::alink',
- 'BODY::bgcolor',
- 'BODY::link',
- 'BODY::text',
- 'BODY::vlink',
- 'BR::clear',
- 'BUTTON::accesskey',
- 'BUTTON::disabled',
- 'BUTTON::name',
- 'BUTTON::tabindex',
- 'BUTTON::type',
- 'BUTTON::value',
- 'CANVAS::height',
- 'CANVAS::width',
- 'CAPTION::align',
- 'COL::align',
- 'COL::char',
- 'COL::charoff',
- 'COL::span',
- 'COL::valign',
- 'COL::width',
- 'COLGROUP::align',
- 'COLGROUP::char',
- 'COLGROUP::charoff',
- 'COLGROUP::span',
- 'COLGROUP::valign',
- 'COLGROUP::width',
- 'COMMAND::checked',
- 'COMMAND::command',
- 'COMMAND::disabled',
- 'COMMAND::label',
- 'COMMAND::radiogroup',
- 'COMMAND::type',
- 'DATA::value',
- 'DEL::datetime',
- 'DETAILS::open',
- 'DIR::compact',
- 'DIV::align',
- 'DL::compact',
- 'FIELDSET::disabled',
- 'FONT::color',
- 'FONT::face',
- 'FONT::size',
- 'FORM::accept',
- 'FORM::autocomplete',
- 'FORM::enctype',
- 'FORM::method',
- 'FORM::name',
- 'FORM::novalidate',
- 'FORM::target',
- 'FRAME::name',
- 'H1::align',
- 'H2::align',
- 'H3::align',
- 'H4::align',
- 'H5::align',
- 'H6::align',
- 'HR::align',
- 'HR::noshade',
- 'HR::size',
- 'HR::width',
- 'HTML::version',
- 'IFRAME::align',
- 'IFRAME::frameborder',
- 'IFRAME::height',
- 'IFRAME::marginheight',
- 'IFRAME::marginwidth',
- 'IFRAME::width',
- 'IMG::align',
- 'IMG::alt',
- 'IMG::border',
- 'IMG::height',
- 'IMG::hspace',
- 'IMG::ismap',
- 'IMG::name',
- 'IMG::usemap',
- 'IMG::vspace',
- 'IMG::width',
- 'INPUT::accept',
- 'INPUT::accesskey',
- 'INPUT::align',
- 'INPUT::alt',
- 'INPUT::autocomplete',
- 'INPUT::autofocus',
- 'INPUT::checked',
- 'INPUT::disabled',
- 'INPUT::inputmode',
- 'INPUT::ismap',
- 'INPUT::list',
- 'INPUT::max',
- 'INPUT::maxlength',
- 'INPUT::min',
- 'INPUT::multiple',
- 'INPUT::name',
- 'INPUT::placeholder',
- 'INPUT::readonly',
- 'INPUT::required',
- 'INPUT::size',
- 'INPUT::step',
- 'INPUT::tabindex',
- 'INPUT::type',
- 'INPUT::usemap',
- 'INPUT::value',
- 'INS::datetime',
- 'KEYGEN::disabled',
- 'KEYGEN::keytype',
- 'KEYGEN::name',
- 'LABEL::accesskey',
- 'LABEL::for',
- 'LEGEND::accesskey',
- 'LEGEND::align',
- 'LI::type',
- 'LI::value',
- 'LINK::sizes',
- 'MAP::name',
- 'MENU::compact',
- 'MENU::label',
- 'MENU::type',
- 'METER::high',
- 'METER::low',
- 'METER::max',
- 'METER::min',
- 'METER::value',
- 'OBJECT::typemustmatch',
- 'OL::compact',
- 'OL::reversed',
- 'OL::start',
- 'OL::type',
- 'OPTGROUP::disabled',
- 'OPTGROUP::label',
- 'OPTION::disabled',
- 'OPTION::label',
- 'OPTION::selected',
- 'OPTION::value',
- 'OUTPUT::for',
- 'OUTPUT::name',
- 'P::align',
- 'PRE::width',
- 'PROGRESS::max',
- 'PROGRESS::min',
- 'PROGRESS::value',
- 'SELECT::autocomplete',
- 'SELECT::disabled',
- 'SELECT::multiple',
- 'SELECT::name',
- 'SELECT::required',
- 'SELECT::size',
- 'SELECT::tabindex',
- 'SOURCE::type',
- 'TABLE::align',
- 'TABLE::bgcolor',
- 'TABLE::border',
- 'TABLE::cellpadding',
- 'TABLE::cellspacing',
- 'TABLE::frame',
- 'TABLE::rules',
- 'TABLE::summary',
- 'TABLE::width',
- 'TBODY::align',
- 'TBODY::char',
- 'TBODY::charoff',
- 'TBODY::valign',
- 'TD::abbr',
- 'TD::align',
- 'TD::axis',
- 'TD::bgcolor',
- 'TD::char',
- 'TD::charoff',
- 'TD::colspan',
- 'TD::headers',
- 'TD::height',
- 'TD::nowrap',
- 'TD::rowspan',
- 'TD::scope',
- 'TD::valign',
- 'TD::width',
- 'TEXTAREA::accesskey',
- 'TEXTAREA::autocomplete',
- 'TEXTAREA::cols',
- 'TEXTAREA::disabled',
- 'TEXTAREA::inputmode',
- 'TEXTAREA::name',
- 'TEXTAREA::placeholder',
- 'TEXTAREA::readonly',
- 'TEXTAREA::required',
- 'TEXTAREA::rows',
- 'TEXTAREA::tabindex',
- 'TEXTAREA::wrap',
- 'TFOOT::align',
- 'TFOOT::char',
- 'TFOOT::charoff',
- 'TFOOT::valign',
- 'TH::abbr',
- 'TH::align',
- 'TH::axis',
- 'TH::bgcolor',
- 'TH::char',
- 'TH::charoff',
- 'TH::colspan',
- 'TH::headers',
- 'TH::height',
- 'TH::nowrap',
- 'TH::rowspan',
- 'TH::scope',
- 'TH::valign',
- 'TH::width',
- 'THEAD::align',
- 'THEAD::char',
- 'THEAD::charoff',
- 'THEAD::valign',
- 'TR::align',
- 'TR::bgcolor',
- 'TR::char',
- 'TR::charoff',
- 'TR::valign',
- 'TRACK::default',
- 'TRACK::kind',
- 'TRACK::label',
- 'TRACK::srclang',
- 'UL::compact',
- 'UL::type',
- 'VIDEO::controls',
- 'VIDEO::height',
- 'VIDEO::loop',
- 'VIDEO::mediagroup',
- 'VIDEO::muted',
- 'VIDEO::preload',
- 'VIDEO::width',
- ];
-
- static const _uriAttributes = const <String>[
- 'A::href',
- 'AREA::href',
- 'BLOCKQUOTE::cite',
- 'BODY::background',
- 'COMMAND::icon',
- 'DEL::cite',
- 'FORM::action',
- 'IMG::src',
- 'INPUT::src',
- 'INS::cite',
- 'Q::cite',
- 'VIDEO::poster',
- ];
-
- final UriPolicy uriPolicy;
-
- static final Map<String, Function> _attributeValidators = {};
-
- /**
- * All known URI attributes will be validated against the UriPolicy, if
- * [uriPolicy] is null then a default UriPolicy will be used.
- */
- _Html5NodeValidator({UriPolicy? uriPolicy})
- : uriPolicy = uriPolicy ?? UriPolicy() {
- if (_attributeValidators.isEmpty) {
- for (var attr in _standardAttributes) {
- _attributeValidators[attr] = _standardAttributeValidator;
- }
-
- for (var attr in _uriAttributes) {
- _attributeValidators[attr] = _uriAttributeValidator;
- }
- }
- }
-
- bool allowsElement(Element element) {
- return _allowedElements.contains(Element._safeTagName(element));
- }
-
- bool allowsAttribute(Element element, String attributeName, String value) {
- var tagName = Element._safeTagName(element);
- var validator = _attributeValidators['$tagName::$attributeName'];
- if (validator == null) {
- validator = _attributeValidators['*::$attributeName'];
- }
- if (validator == null) {
- return false;
- }
- return validator(element, attributeName, value, this);
- }
-
- static bool _standardAttributeValidator(Element element, String attributeName,
- String value, _Html5NodeValidator context) {
- return true;
- }
-
- static bool _uriAttributeValidator(Element element, String attributeName,
- String value, _Html5NodeValidator context) {
- return context.uriPolicy.allowsUri(value);
- }
-}
diff --git a/tools/dom/nnbd_src/ImmutableListMixin.dart b/tools/dom/nnbd_src/ImmutableListMixin.dart
deleted file mode 100644
index 9910ebe..0000000
--- a/tools/dom/nnbd_src/ImmutableListMixin.dart
+++ /dev/null
@@ -1,80 +0,0 @@
-// Copyright (c) 2012, 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.
-
-part of dart.dom.html;
-
-abstract class ImmutableListMixin<E> implements List<E> {
- // From Iterable<$E>:
- Iterator<E> get iterator {
- // Note: NodeLists are not fixed size. And most probably length shouldn't
- // be cached in both iterator _and_ forEach method. For now caching it
- // for consistency.
- return new FixedSizeListIterator<E>(this);
- }
-
- // From List<E>:
- void add(E value) {
- throw new UnsupportedError("Cannot add to immutable List.");
- }
-
- void addAll(Iterable<E> iterable) {
- throw new UnsupportedError("Cannot add to immutable List.");
- }
-
- void sort([int compare(E a, E b)?]) {
- throw new UnsupportedError("Cannot sort immutable List.");
- }
-
- void shuffle([Random? random]) {
- throw new UnsupportedError("Cannot shuffle immutable List.");
- }
-
- void insert(int index, E element) {
- throw new UnsupportedError("Cannot add to immutable List.");
- }
-
- void insertAll(int index, Iterable<E> iterable) {
- throw new UnsupportedError("Cannot add to immutable List.");
- }
-
- void setAll(int index, Iterable<E> iterable) {
- throw new UnsupportedError("Cannot modify an immutable List.");
- }
-
- E removeAt(int pos) {
- throw new UnsupportedError("Cannot remove from immutable List.");
- }
-
- E removeLast() {
- throw new UnsupportedError("Cannot remove from immutable List.");
- }
-
- bool remove(Object? object) {
- throw new UnsupportedError("Cannot remove from immutable List.");
- }
-
- void removeWhere(bool test(E element)) {
- throw new UnsupportedError("Cannot remove from immutable List.");
- }
-
- void retainWhere(bool test(E element)) {
- throw new UnsupportedError("Cannot remove from immutable List.");
- }
-
- void setRange(int start, int end, Iterable<E> iterable, [int skipCount = 0]) {
- throw new UnsupportedError("Cannot setRange on immutable List.");
- }
-
- void removeRange(int start, int end) {
- throw new UnsupportedError("Cannot removeRange on immutable List.");
- }
-
- void replaceRange(int start, int end, Iterable<E> iterable) {
- throw new UnsupportedError("Cannot modify an immutable List.");
- }
-
- void fillRange(int start, int end, [E? fillValue]) {
- throw new UnsupportedError("Cannot modify an immutable List.");
- }
-}
diff --git a/tools/dom/nnbd_src/KeyCode.dart b/tools/dom/nnbd_src/KeyCode.dart
deleted file mode 100644
index a51d062..0000000
--- a/tools/dom/nnbd_src/KeyCode.dart
+++ /dev/null
@@ -1,331 +0,0 @@
-// Copyright (c) 2012, 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.
-
-part of html;
-
-/**
- * Defines the keycode values for keys that are returned by
- * KeyboardEvent.keyCode.
- *
- * Important note: There is substantial divergence in how different browsers
- * handle keycodes and their variants in different locales/keyboard layouts. We
- * provide these constants to help make code processing keys more readable.
- */
-abstract class KeyCode {
- // These constant names were borrowed from Closure's Keycode enumeration
- // class.
- // https://github.com/google/closure-library/blob/master/closure/goog/events/keycodes.js
- static const int WIN_KEY_FF_LINUX = 0;
- static const int MAC_ENTER = 3;
- static const int BACKSPACE = 8;
- static const int TAB = 9;
- /** NUM_CENTER is also NUMLOCK for FF and Safari on Mac. */
- static const int NUM_CENTER = 12;
- static const int ENTER = 13;
- static const int SHIFT = 16;
- static const int CTRL = 17;
- static const int ALT = 18;
- static const int PAUSE = 19;
- static const int CAPS_LOCK = 20;
- static const int ESC = 27;
- static const int SPACE = 32;
- static const int PAGE_UP = 33;
- static const int PAGE_DOWN = 34;
- static const int END = 35;
- static const int HOME = 36;
- static const int LEFT = 37;
- static const int UP = 38;
- static const int RIGHT = 39;
- static const int DOWN = 40;
- static const int NUM_NORTH_EAST = 33;
- static const int NUM_SOUTH_EAST = 34;
- static const int NUM_SOUTH_WEST = 35;
- static const int NUM_NORTH_WEST = 36;
- static const int NUM_WEST = 37;
- static const int NUM_NORTH = 38;
- static const int NUM_EAST = 39;
- static const int NUM_SOUTH = 40;
- static const int PRINT_SCREEN = 44;
- static const int INSERT = 45;
- static const int NUM_INSERT = 45;
- static const int DELETE = 46;
- static const int NUM_DELETE = 46;
- static const int ZERO = 48;
- static const int ONE = 49;
- static const int TWO = 50;
- static const int THREE = 51;
- static const int FOUR = 52;
- static const int FIVE = 53;
- static const int SIX = 54;
- static const int SEVEN = 55;
- static const int EIGHT = 56;
- static const int NINE = 57;
- static const int FF_SEMICOLON = 59;
- static const int FF_EQUALS = 61;
- /**
- * CAUTION: The question mark is for US-keyboard layouts. It varies
- * for other locales and keyboard layouts.
- */
- static const int QUESTION_MARK = 63;
- static const int A = 65;
- static const int B = 66;
- static const int C = 67;
- static const int D = 68;
- static const int E = 69;
- static const int F = 70;
- static const int G = 71;
- static const int H = 72;
- static const int I = 73;
- static const int J = 74;
- static const int K = 75;
- static const int L = 76;
- static const int M = 77;
- static const int N = 78;
- static const int O = 79;
- static const int P = 80;
- static const int Q = 81;
- static const int R = 82;
- static const int S = 83;
- static const int T = 84;
- static const int U = 85;
- static const int V = 86;
- static const int W = 87;
- static const int X = 88;
- static const int Y = 89;
- static const int Z = 90;
- static const int META = 91;
- static const int WIN_KEY_LEFT = 91;
- static const int WIN_KEY_RIGHT = 92;
- static const int CONTEXT_MENU = 93;
- static const int NUM_ZERO = 96;
- static const int NUM_ONE = 97;
- static const int NUM_TWO = 98;
- static const int NUM_THREE = 99;
- static const int NUM_FOUR = 100;
- static const int NUM_FIVE = 101;
- static const int NUM_SIX = 102;
- static const int NUM_SEVEN = 103;
- static const int NUM_EIGHT = 104;
- static const int NUM_NINE = 105;
- static const int NUM_MULTIPLY = 106;
- static const int NUM_PLUS = 107;
- static const int NUM_MINUS = 109;
- static const int NUM_PERIOD = 110;
- static const int NUM_DIVISION = 111;
- static const int F1 = 112;
- static const int F2 = 113;
- static const int F3 = 114;
- static const int F4 = 115;
- static const int F5 = 116;
- static const int F6 = 117;
- static const int F7 = 118;
- static const int F8 = 119;
- static const int F9 = 120;
- static const int F10 = 121;
- static const int F11 = 122;
- static const int F12 = 123;
- static const int NUMLOCK = 144;
- static const int SCROLL_LOCK = 145;
-
- // OS-specific media keys like volume controls and browser controls.
- static const int FIRST_MEDIA_KEY = 166;
- static const int LAST_MEDIA_KEY = 183;
-
- /**
- * CAUTION: This constant requires localization for other locales and keyboard
- * layouts.
- */
- static const int SEMICOLON = 186;
- /**
- * CAUTION: This constant requires localization for other locales and keyboard
- * layouts.
- */
- static const int DASH = 189;
- /**
- * CAUTION: This constant requires localization for other locales and keyboard
- * layouts.
- */
- static const int EQUALS = 187;
- /**
- * CAUTION: This constant requires localization for other locales and keyboard
- * layouts.
- */
- static const int COMMA = 188;
- /**
- * CAUTION: This constant requires localization for other locales and keyboard
- * layouts.
- */
- static const int PERIOD = 190;
- /**
- * CAUTION: This constant requires localization for other locales and keyboard
- * layouts.
- */
- static const int SLASH = 191;
- /**
- * CAUTION: This constant requires localization for other locales and keyboard
- * layouts.
- */
- static const int APOSTROPHE = 192;
- /**
- * CAUTION: This constant requires localization for other locales and keyboard
- * layouts.
- */
- static const int TILDE = 192;
- /**
- * CAUTION: This constant requires localization for other locales and keyboard
- * layouts.
- */
- static const int SINGLE_QUOTE = 222;
- /**
- * CAUTION: This constant requires localization for other locales and keyboard
- * layouts.
- */
- static const int OPEN_SQUARE_BRACKET = 219;
- /**
- * CAUTION: This constant requires localization for other locales and keyboard
- * layouts.
- */
- static const int BACKSLASH = 220;
- /**
- * CAUTION: This constant requires localization for other locales and keyboard
- * layouts.
- */
- static const int CLOSE_SQUARE_BRACKET = 221;
- static const int WIN_KEY = 224;
- static const int MAC_FF_META = 224;
- static const int WIN_IME = 229;
-
- /** A sentinel value if the keycode could not be determined. */
- static const int UNKNOWN = -1;
-
- /**
- * Returns true if the keyCode produces a (US keyboard) character.
- * Note: This does not (yet) cover characters on non-US keyboards (Russian,
- * Hebrew, etc.).
- */
- static bool isCharacterKey(int keyCode) {
- if ((keyCode >= ZERO && keyCode <= NINE) ||
- (keyCode >= NUM_ZERO && keyCode <= NUM_MULTIPLY) ||
- (keyCode >= A && keyCode <= Z)) {
- return true;
- }
-
- // Safari sends zero key code for non-latin characters.
- if (Device.isWebKit && keyCode == 0) {
- return true;
- }
-
- return (keyCode == SPACE ||
- keyCode == QUESTION_MARK ||
- keyCode == NUM_PLUS ||
- keyCode == NUM_MINUS ||
- keyCode == NUM_PERIOD ||
- keyCode == NUM_DIVISION ||
- keyCode == SEMICOLON ||
- keyCode == FF_SEMICOLON ||
- keyCode == DASH ||
- keyCode == EQUALS ||
- keyCode == FF_EQUALS ||
- keyCode == COMMA ||
- keyCode == PERIOD ||
- keyCode == SLASH ||
- keyCode == APOSTROPHE ||
- keyCode == SINGLE_QUOTE ||
- keyCode == OPEN_SQUARE_BRACKET ||
- keyCode == BACKSLASH ||
- keyCode == CLOSE_SQUARE_BRACKET);
- }
-
- /**
- * Experimental helper function for converting keyCodes to keyNames for the
- * keyIdentifier attribute still used in browsers not updated with current
- * spec. This is an imperfect conversion! It will need to be refined, but
- * hopefully it can just completely go away once all the browsers update to
- * follow the DOM3 spec.
- */
- static String _convertKeyCodeToKeyName(int keyCode) {
- switch (keyCode) {
- case KeyCode.ALT:
- return _KeyName.ALT;
- case KeyCode.BACKSPACE:
- return _KeyName.BACKSPACE;
- case KeyCode.CAPS_LOCK:
- return _KeyName.CAPS_LOCK;
- case KeyCode.CTRL:
- return _KeyName.CONTROL;
- case KeyCode.DELETE:
- return _KeyName.DEL;
- case KeyCode.DOWN:
- return _KeyName.DOWN;
- case KeyCode.END:
- return _KeyName.END;
- case KeyCode.ENTER:
- return _KeyName.ENTER;
- case KeyCode.ESC:
- return _KeyName.ESC;
- case KeyCode.F1:
- return _KeyName.F1;
- case KeyCode.F2:
- return _KeyName.F2;
- case KeyCode.F3:
- return _KeyName.F3;
- case KeyCode.F4:
- return _KeyName.F4;
- case KeyCode.F5:
- return _KeyName.F5;
- case KeyCode.F6:
- return _KeyName.F6;
- case KeyCode.F7:
- return _KeyName.F7;
- case KeyCode.F8:
- return _KeyName.F8;
- case KeyCode.F9:
- return _KeyName.F9;
- case KeyCode.F10:
- return _KeyName.F10;
- case KeyCode.F11:
- return _KeyName.F11;
- case KeyCode.F12:
- return _KeyName.F12;
- case KeyCode.HOME:
- return _KeyName.HOME;
- case KeyCode.INSERT:
- return _KeyName.INSERT;
- case KeyCode.LEFT:
- return _KeyName.LEFT;
- case KeyCode.META:
- return _KeyName.META;
- case KeyCode.NUMLOCK:
- return _KeyName.NUM_LOCK;
- case KeyCode.PAGE_DOWN:
- return _KeyName.PAGE_DOWN;
- case KeyCode.PAGE_UP:
- return _KeyName.PAGE_UP;
- case KeyCode.PAUSE:
- return _KeyName.PAUSE;
- case KeyCode.PRINT_SCREEN:
- return _KeyName.PRINT_SCREEN;
- case KeyCode.RIGHT:
- return _KeyName.RIGHT;
- case KeyCode.SCROLL_LOCK:
- return _KeyName.SCROLL;
- case KeyCode.SHIFT:
- return _KeyName.SHIFT;
- case KeyCode.SPACE:
- return _KeyName.SPACEBAR;
- case KeyCode.TAB:
- return _KeyName.TAB;
- case KeyCode.UP:
- return _KeyName.UP;
- case KeyCode.WIN_IME:
- case KeyCode.WIN_KEY:
- case KeyCode.WIN_KEY_LEFT:
- case KeyCode.WIN_KEY_RIGHT:
- return _KeyName.WIN;
- default:
- return _KeyName.UNIDENTIFIED;
- }
- return _KeyName.UNIDENTIFIED;
- }
-}
diff --git a/tools/dom/nnbd_src/KeyLocation.dart b/tools/dom/nnbd_src/KeyLocation.dart
deleted file mode 100644
index db4c423..0000000
--- a/tools/dom/nnbd_src/KeyLocation.dart
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright (c) 2011, 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.
-
-part of html;
-
-/**
- * Defines the standard key locations returned by
- * KeyboardEvent.getKeyLocation.
- */
-abstract class KeyLocation {
- /**
- * The event key is not distinguished as the left or right version
- * of the key, and did not originate from the numeric keypad (or did not
- * originate with a virtual key corresponding to the numeric keypad).
- */
- static const int STANDARD = 0;
-
- /**
- * The event key is in the left key location.
- */
- static const int LEFT = 1;
-
- /**
- * The event key is in the right key location.
- */
- static const int RIGHT = 2;
-
- /**
- * The event key originated on the numeric keypad or with a virtual key
- * corresponding to the numeric keypad.
- */
- static const int NUMPAD = 3;
-
- /**
- * The event key originated on a mobile device, either on a physical
- * keypad or a virtual keyboard.
- */
- static const int MOBILE = 4;
-
- /**
- * The event key originated on a game controller or a joystick on a mobile
- * device.
- */
- static const int JOYSTICK = 5;
-}
diff --git a/tools/dom/nnbd_src/KeyName.dart b/tools/dom/nnbd_src/KeyName.dart
deleted file mode 100644
index 094d57e..0000000
--- a/tools/dom/nnbd_src/KeyName.dart
+++ /dev/null
@@ -1,494 +0,0 @@
-// Copyright (c) 2012, 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.
-
-part of html;
-
-/**
- * Defines the standard keyboard identifier names for keys that are returned
- * by KeyboardEvent.getKeyboardIdentifier when the key does not have a direct
- * unicode mapping.
- */
-abstract class _KeyName {
- /** The Accept (Commit, OK) key */
- static const String ACCEPT = "Accept";
-
- /** The Add key */
- static const String ADD = "Add";
-
- /** The Again key */
- static const String AGAIN = "Again";
-
- /** The All Candidates key */
- static const String ALL_CANDIDATES = "AllCandidates";
-
- /** The Alphanumeric key */
- static const String ALPHANUMERIC = "Alphanumeric";
-
- /** The Alt (Menu) key */
- static const String ALT = "Alt";
-
- /** The Alt-Graph key */
- static const String ALT_GRAPH = "AltGraph";
-
- /** The Application key */
- static const String APPS = "Apps";
-
- /** The ATTN key */
- static const String ATTN = "Attn";
-
- /** The Browser Back key */
- static const String BROWSER_BACK = "BrowserBack";
-
- /** The Browser Favorites key */
- static const String BROWSER_FAVORTIES = "BrowserFavorites";
-
- /** The Browser Forward key */
- static const String BROWSER_FORWARD = "BrowserForward";
-
- /** The Browser Home key */
- static const String BROWSER_NAME = "BrowserHome";
-
- /** The Browser Refresh key */
- static const String BROWSER_REFRESH = "BrowserRefresh";
-
- /** The Browser Search key */
- static const String BROWSER_SEARCH = "BrowserSearch";
-
- /** The Browser Stop key */
- static const String BROWSER_STOP = "BrowserStop";
-
- /** The Camera key */
- static const String CAMERA = "Camera";
-
- /** The Caps Lock (Capital) key */
- static const String CAPS_LOCK = "CapsLock";
-
- /** The Clear key */
- static const String CLEAR = "Clear";
-
- /** The Code Input key */
- static const String CODE_INPUT = "CodeInput";
-
- /** The Compose key */
- static const String COMPOSE = "Compose";
-
- /** The Control (Ctrl) key */
- static const String CONTROL = "Control";
-
- /** The Crsel key */
- static const String CRSEL = "Crsel";
-
- /** The Convert key */
- static const String CONVERT = "Convert";
-
- /** The Copy key */
- static const String COPY = "Copy";
-
- /** The Cut key */
- static const String CUT = "Cut";
-
- /** The Decimal key */
- static const String DECIMAL = "Decimal";
-
- /** The Divide key */
- static const String DIVIDE = "Divide";
-
- /** The Down Arrow key */
- static const String DOWN = "Down";
-
- /** The diagonal Down-Left Arrow key */
- static const String DOWN_LEFT = "DownLeft";
-
- /** The diagonal Down-Right Arrow key */
- static const String DOWN_RIGHT = "DownRight";
-
- /** The Eject key */
- static const String EJECT = "Eject";
-
- /** The End key */
- static const String END = "End";
-
- /**
- * The Enter key. Note: This key value must also be used for the Return
- * (Macintosh numpad) key
- */
- static const String ENTER = "Enter";
-
- /** The Erase EOF key */
- static const String ERASE_EOF = "EraseEof";
-
- /** The Execute key */
- static const String EXECUTE = "Execute";
-
- /** The Exsel key */
- static const String EXSEL = "Exsel";
-
- /** The Function switch key */
- static const String FN = "Fn";
-
- /** The F1 key */
- static const String F1 = "F1";
-
- /** The F2 key */
- static const String F2 = "F2";
-
- /** The F3 key */
- static const String F3 = "F3";
-
- /** The F4 key */
- static const String F4 = "F4";
-
- /** The F5 key */
- static const String F5 = "F5";
-
- /** The F6 key */
- static const String F6 = "F6";
-
- /** The F7 key */
- static const String F7 = "F7";
-
- /** The F8 key */
- static const String F8 = "F8";
-
- /** The F9 key */
- static const String F9 = "F9";
-
- /** The F10 key */
- static const String F10 = "F10";
-
- /** The F11 key */
- static const String F11 = "F11";
-
- /** The F12 key */
- static const String F12 = "F12";
-
- /** The F13 key */
- static const String F13 = "F13";
-
- /** The F14 key */
- static const String F14 = "F14";
-
- /** The F15 key */
- static const String F15 = "F15";
-
- /** The F16 key */
- static const String F16 = "F16";
-
- /** The F17 key */
- static const String F17 = "F17";
-
- /** The F18 key */
- static const String F18 = "F18";
-
- /** The F19 key */
- static const String F19 = "F19";
-
- /** The F20 key */
- static const String F20 = "F20";
-
- /** The F21 key */
- static const String F21 = "F21";
-
- /** The F22 key */
- static const String F22 = "F22";
-
- /** The F23 key */
- static const String F23 = "F23";
-
- /** The F24 key */
- static const String F24 = "F24";
-
- /** The Final Mode (Final) key used on some asian keyboards */
- static const String FINAL_MODE = "FinalMode";
-
- /** The Find key */
- static const String FIND = "Find";
-
- /** The Full-Width Characters key */
- static const String FULL_WIDTH = "FullWidth";
-
- /** The Half-Width Characters key */
- static const String HALF_WIDTH = "HalfWidth";
-
- /** The Hangul (Korean characters) Mode key */
- static const String HANGUL_MODE = "HangulMode";
-
- /** The Hanja (Korean characters) Mode key */
- static const String HANJA_MODE = "HanjaMode";
-
- /** The Help key */
- static const String HELP = "Help";
-
- /** The Hiragana (Japanese Kana characters) key */
- static const String HIRAGANA = "Hiragana";
-
- /** The Home key */
- static const String HOME = "Home";
-
- /** The Insert (Ins) key */
- static const String INSERT = "Insert";
-
- /** The Japanese-Hiragana key */
- static const String JAPANESE_HIRAGANA = "JapaneseHiragana";
-
- /** The Japanese-Katakana key */
- static const String JAPANESE_KATAKANA = "JapaneseKatakana";
-
- /** The Japanese-Romaji key */
- static const String JAPANESE_ROMAJI = "JapaneseRomaji";
-
- /** The Junja Mode key */
- static const String JUNJA_MODE = "JunjaMode";
-
- /** The Kana Mode (Kana Lock) key */
- static const String KANA_MODE = "KanaMode";
-
- /**
- * The Kanji (Japanese name for ideographic characters of Chinese origin)
- * Mode key
- */
- static const String KANJI_MODE = "KanjiMode";
-
- /** The Katakana (Japanese Kana characters) key */
- static const String KATAKANA = "Katakana";
-
- /** The Start Application One key */
- static const String LAUNCH_APPLICATION_1 = "LaunchApplication1";
-
- /** The Start Application Two key */
- static const String LAUNCH_APPLICATION_2 = "LaunchApplication2";
-
- /** The Start Mail key */
- static const String LAUNCH_MAIL = "LaunchMail";
-
- /** The Left Arrow key */
- static const String LEFT = "Left";
-
- /** The Menu key */
- static const String MENU = "Menu";
-
- /**
- * The Meta key. Note: This key value shall be also used for the Apple
- * Command key
- */
- static const String META = "Meta";
-
- /** The Media Next Track key */
- static const String MEDIA_NEXT_TRACK = "MediaNextTrack";
-
- /** The Media Play Pause key */
- static const String MEDIA_PAUSE_PLAY = "MediaPlayPause";
-
- /** The Media Previous Track key */
- static const String MEDIA_PREVIOUS_TRACK = "MediaPreviousTrack";
-
- /** The Media Stop key */
- static const String MEDIA_STOP = "MediaStop";
-
- /** The Mode Change key */
- static const String MODE_CHANGE = "ModeChange";
-
- /** The Next Candidate function key */
- static const String NEXT_CANDIDATE = "NextCandidate";
-
- /** The Nonconvert (Don't Convert) key */
- static const String NON_CONVERT = "Nonconvert";
-
- /** The Number Lock key */
- static const String NUM_LOCK = "NumLock";
-
- /** The Page Down (Next) key */
- static const String PAGE_DOWN = "PageDown";
-
- /** The Page Up key */
- static const String PAGE_UP = "PageUp";
-
- /** The Paste key */
- static const String PASTE = "Paste";
-
- /** The Pause key */
- static const String PAUSE = "Pause";
-
- /** The Play key */
- static const String PLAY = "Play";
-
- /**
- * The Power key. Note: Some devices may not expose this key to the
- * operating environment
- */
- static const String POWER = "Power";
-
- /** The Previous Candidate function key */
- static const String PREVIOUS_CANDIDATE = "PreviousCandidate";
-
- /** The Print Screen (PrintScrn, SnapShot) key */
- static const String PRINT_SCREEN = "PrintScreen";
-
- /** The Process key */
- static const String PROCESS = "Process";
-
- /** The Props key */
- static const String PROPS = "Props";
-
- /** The Right Arrow key */
- static const String RIGHT = "Right";
-
- /** The Roman Characters function key */
- static const String ROMAN_CHARACTERS = "RomanCharacters";
-
- /** The Scroll Lock key */
- static const String SCROLL = "Scroll";
-
- /** The Select key */
- static const String SELECT = "Select";
-
- /** The Select Media key */
- static const String SELECT_MEDIA = "SelectMedia";
-
- /** The Separator key */
- static const String SEPARATOR = "Separator";
-
- /** The Shift key */
- static const String SHIFT = "Shift";
-
- /** The Soft1 key */
- static const String SOFT_1 = "Soft1";
-
- /** The Soft2 key */
- static const String SOFT_2 = "Soft2";
-
- /** The Soft3 key */
- static const String SOFT_3 = "Soft3";
-
- /** The Soft4 key */
- static const String SOFT_4 = "Soft4";
-
- /** The Stop key */
- static const String STOP = "Stop";
-
- /** The Subtract key */
- static const String SUBTRACT = "Subtract";
-
- /** The Symbol Lock key */
- static const String SYMBOL_LOCK = "SymbolLock";
-
- /** The Up Arrow key */
- static const String UP = "Up";
-
- /** The diagonal Up-Left Arrow key */
- static const String UP_LEFT = "UpLeft";
-
- /** The diagonal Up-Right Arrow key */
- static const String UP_RIGHT = "UpRight";
-
- /** The Undo key */
- static const String UNDO = "Undo";
-
- /** The Volume Down key */
- static const String VOLUME_DOWN = "VolumeDown";
-
- /** The Volume Mute key */
- static const String VOLUMN_MUTE = "VolumeMute";
-
- /** The Volume Up key */
- static const String VOLUMN_UP = "VolumeUp";
-
- /** The Windows Logo key */
- static const String WIN = "Win";
-
- /** The Zoom key */
- static const String ZOOM = "Zoom";
-
- /**
- * The Backspace (Back) key. Note: This key value shall be also used for the
- * key labeled 'delete' MacOS keyboards when not modified by the 'Fn' key
- */
- static const String BACKSPACE = "Backspace";
-
- /** The Horizontal Tabulation (Tab) key */
- static const String TAB = "Tab";
-
- /** The Cancel key */
- static const String CANCEL = "Cancel";
-
- /** The Escape (Esc) key */
- static const String ESC = "Esc";
-
- /** The Space (Spacebar) key: */
- static const String SPACEBAR = "Spacebar";
-
- /**
- * The Delete (Del) Key. Note: This key value shall be also used for the key
- * labeled 'delete' MacOS keyboards when modified by the 'Fn' key
- */
- static const String DEL = "Del";
-
- /** The Combining Grave Accent (Greek Varia, Dead Grave) key */
- static const String DEAD_GRAVE = "DeadGrave";
-
- /**
- * The Combining Acute Accent (Stress Mark, Greek Oxia, Tonos, Dead Eacute)
- * key
- */
- static const String DEAD_EACUTE = "DeadEacute";
-
- /** The Combining Circumflex Accent (Hat, Dead Circumflex) key */
- static const String DEAD_CIRCUMFLEX = "DeadCircumflex";
-
- /** The Combining Tilde (Dead Tilde) key */
- static const String DEAD_TILDE = "DeadTilde";
-
- /** The Combining Macron (Long, Dead Macron) key */
- static const String DEAD_MACRON = "DeadMacron";
-
- /** The Combining Breve (Short, Dead Breve) key */
- static const String DEAD_BREVE = "DeadBreve";
-
- /** The Combining Dot Above (Derivative, Dead Above Dot) key */
- static const String DEAD_ABOVE_DOT = "DeadAboveDot";
-
- /**
- * The Combining Diaeresis (Double Dot Abode, Umlaut, Greek Dialytika,
- * Double Derivative, Dead Diaeresis) key
- */
- static const String DEAD_UMLAUT = "DeadUmlaut";
-
- /** The Combining Ring Above (Dead Above Ring) key */
- static const String DEAD_ABOVE_RING = "DeadAboveRing";
-
- /** The Combining Double Acute Accent (Dead Doubleacute) key */
- static const String DEAD_DOUBLEACUTE = "DeadDoubleacute";
-
- /** The Combining Caron (Hacek, V Above, Dead Caron) key */
- static const String DEAD_CARON = "DeadCaron";
-
- /** The Combining Cedilla (Dead Cedilla) key */
- static const String DEAD_CEDILLA = "DeadCedilla";
-
- /** The Combining Ogonek (Nasal Hook, Dead Ogonek) key */
- static const String DEAD_OGONEK = "DeadOgonek";
-
- /**
- * The Combining Greek Ypogegrammeni (Greek Non-Spacing Iota Below, Iota
- * Subscript, Dead Iota) key
- */
- static const String DEAD_IOTA = "DeadIota";
-
- /**
- * The Combining Katakana-Hiragana Voiced Sound Mark (Dead Voiced Sound) key
- */
- static const String DEAD_VOICED_SOUND = "DeadVoicedSound";
-
- /**
- * The Combining Katakana-Hiragana Semi-Voiced Sound Mark (Dead Semivoiced
- * Sound) key
- */
- static const String DEC_SEMIVOICED_SOUND = "DeadSemivoicedSound";
-
- /**
- * Key value used when an implementation is unable to identify another key
- * value, due to either hardware, platform, or software constraints
- */
- static const String UNIDENTIFIED = "Unidentified";
-}
diff --git a/tools/dom/nnbd_src/KeyboardEventStream.dart b/tools/dom/nnbd_src/KeyboardEventStream.dart
deleted file mode 100644
index 399c037..0000000
--- a/tools/dom/nnbd_src/KeyboardEventStream.dart
+++ /dev/null
@@ -1,394 +0,0 @@
-// Copyright (c) 2012, 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.
-
-part of html;
-
-/**
- * Internal class that does the actual calculations to determine keyCode and
- * charCode for keydown, keypress, and keyup events for all browsers.
- */
-class _KeyboardEventHandler extends EventStreamProvider<KeyEvent> {
- // This code inspired by Closure's KeyHandling library.
- // https://github.com/google/closure-library/blob/master/closure/goog/events/keyhandler.js
-
- /**
- * The set of keys that have been pressed down without seeing their
- * corresponding keyup event.
- */
- final List<KeyEvent> _keyDownList = <KeyEvent>[];
-
- /** The type of KeyEvent we are tracking (keyup, keydown, keypress). */
- final String _type;
-
- /** The element we are watching for events to happen on. */
- final EventTarget? _target;
-
- // The distance to shift from upper case alphabet Roman letters to lower case.
- static final int _ROMAN_ALPHABET_OFFSET = "a".codeUnits[0] - "A".codeUnits[0];
-
- /** Custom Stream (Controller) to produce KeyEvents for the stream. */
- _CustomKeyEventStreamImpl _stream;
-
- static const _EVENT_TYPE = 'KeyEvent';
-
- /**
- * An enumeration of key identifiers currently part of the W3C draft for DOM3
- * and their mappings to keyCodes.
- * https://www.w3.org/TR/2003/NOTE-DOM-Level-3-Events-20031107/keyset.html#KeySet-Set
- */
- static const Map<String, int> _keyIdentifier = const {
- 'Up': KeyCode.UP,
- 'Down': KeyCode.DOWN,
- 'Left': KeyCode.LEFT,
- 'Right': KeyCode.RIGHT,
- 'Enter': KeyCode.ENTER,
- 'F1': KeyCode.F1,
- 'F2': KeyCode.F2,
- 'F3': KeyCode.F3,
- 'F4': KeyCode.F4,
- 'F5': KeyCode.F5,
- 'F6': KeyCode.F6,
- 'F7': KeyCode.F7,
- 'F8': KeyCode.F8,
- 'F9': KeyCode.F9,
- 'F10': KeyCode.F10,
- 'F11': KeyCode.F11,
- 'F12': KeyCode.F12,
- 'U+007F': KeyCode.DELETE,
- 'Home': KeyCode.HOME,
- 'End': KeyCode.END,
- 'PageUp': KeyCode.PAGE_UP,
- 'PageDown': KeyCode.PAGE_DOWN,
- 'Insert': KeyCode.INSERT
- };
-
- /** Return a stream for KeyEvents for the specified target. */
- // Note: this actually functions like a factory constructor.
- CustomStream<KeyEvent> forTarget(EventTarget? e, {bool useCapture: false}) {
- var handler =
- new _KeyboardEventHandler.initializeAllEventListeners(_type, e);
- return handler._stream;
- }
-
- /**
- * General constructor, performs basic initialization for our improved
- * KeyboardEvent controller.
- */
- _KeyboardEventHandler(this._type)
- : _stream = new _CustomKeyEventStreamImpl('event'),
- _target = null,
- super(_EVENT_TYPE);
-
- /**
- * Hook up all event listeners under the covers so we can estimate keycodes
- * and charcodes when they are not provided.
- */
- _KeyboardEventHandler.initializeAllEventListeners(this._type, this._target)
- : _stream = new _CustomKeyEventStreamImpl(_type),
- super(_EVENT_TYPE) {
- Element.keyDownEvent
- .forTarget(_target, useCapture: true)
- .listen(processKeyDown);
- Element.keyPressEvent
- .forTarget(_target, useCapture: true)
- .listen(processKeyPress);
- Element.keyUpEvent
- .forTarget(_target, useCapture: true)
- .listen(processKeyUp);
- }
-
- /** Determine if caps lock is one of the currently depressed keys. */
- bool get _capsLockOn =>
- _keyDownList.any((var element) => element.keyCode == KeyCode.CAPS_LOCK);
-
- /**
- * Given the previously recorded keydown key codes, see if we can determine
- * the keycode of this keypress [event]. (Generally browsers only provide
- * charCode information for keypress events, but with a little
- * reverse-engineering, we can also determine the keyCode.) Returns
- * KeyCode.UNKNOWN if the keycode could not be determined.
- */
- int _determineKeyCodeForKeypress(KeyboardEvent event) {
- // Note: This function is a work in progress. We'll expand this function
- // once we get more information about other keyboards.
- for (var prevEvent in _keyDownList) {
- if (prevEvent._shadowCharCode == event.charCode) {
- return prevEvent.keyCode;
- }
- if ((event.shiftKey || _capsLockOn) &&
- event.charCode >= "A".codeUnits[0] &&
- event.charCode <= "Z".codeUnits[0] &&
- event.charCode + _ROMAN_ALPHABET_OFFSET ==
- prevEvent._shadowCharCode) {
- return prevEvent.keyCode;
- }
- }
- return KeyCode.UNKNOWN;
- }
-
- /**
- * Given the character code returned from a keyDown [event], try to ascertain
- * and return the corresponding charCode for the character that was pressed.
- * This information is not shown to the user, but used to help polyfill
- * keypress events.
- */
- int _findCharCodeKeyDown(KeyboardEvent event) {
- if (event.location == 3) {
- // Numpad keys.
- switch (event.keyCode) {
- case KeyCode.NUM_ZERO:
- // Even though this function returns _charCodes_, for some cases the
- // KeyCode == the charCode we want, in which case we use the keycode
- // constant for readability.
- return KeyCode.ZERO;
- case KeyCode.NUM_ONE:
- return KeyCode.ONE;
- case KeyCode.NUM_TWO:
- return KeyCode.TWO;
- case KeyCode.NUM_THREE:
- return KeyCode.THREE;
- case KeyCode.NUM_FOUR:
- return KeyCode.FOUR;
- case KeyCode.NUM_FIVE:
- return KeyCode.FIVE;
- case KeyCode.NUM_SIX:
- return KeyCode.SIX;
- case KeyCode.NUM_SEVEN:
- return KeyCode.SEVEN;
- case KeyCode.NUM_EIGHT:
- return KeyCode.EIGHT;
- case KeyCode.NUM_NINE:
- return KeyCode.NINE;
- case KeyCode.NUM_MULTIPLY:
- return 42; // Char code for *
- case KeyCode.NUM_PLUS:
- return 43; // +
- case KeyCode.NUM_MINUS:
- return 45; // -
- case KeyCode.NUM_PERIOD:
- return 46; // .
- case KeyCode.NUM_DIVISION:
- return 47; // /
- }
- } else if (event.keyCode >= 65 && event.keyCode <= 90) {
- // Set the "char code" for key down as the lower case letter. Again, this
- // will not show up for the user, but will be helpful in estimating
- // keyCode locations and other information during the keyPress event.
- return event.keyCode + _ROMAN_ALPHABET_OFFSET;
- }
- switch (event.keyCode) {
- case KeyCode.SEMICOLON:
- return KeyCode.FF_SEMICOLON;
- case KeyCode.EQUALS:
- return KeyCode.FF_EQUALS;
- case KeyCode.COMMA:
- return 44; // Ascii value for ,
- case KeyCode.DASH:
- return 45; // -
- case KeyCode.PERIOD:
- return 46; // .
- case KeyCode.SLASH:
- return 47; // /
- case KeyCode.APOSTROPHE:
- return 96; // `
- case KeyCode.OPEN_SQUARE_BRACKET:
- return 91; // [
- case KeyCode.BACKSLASH:
- return 92; // \
- case KeyCode.CLOSE_SQUARE_BRACKET:
- return 93; // ]
- case KeyCode.SINGLE_QUOTE:
- return 39; // '
- }
- return event.keyCode;
- }
-
- /**
- * Returns true if the key fires a keypress event in the current browser.
- */
- bool _firesKeyPressEvent(KeyEvent event) {
- if (!Device.isIE && !Device.isWebKit) {
- return true;
- }
-
- if (Device.userAgent.contains('Mac') && event.altKey) {
- return KeyCode.isCharacterKey(event.keyCode);
- }
-
- // Alt but not AltGr which is represented as Alt+Ctrl.
- if (event.altKey && !event.ctrlKey) {
- return false;
- }
-
- // Saves Ctrl or Alt + key for IE and WebKit, which won't fire keypress.
- if (!event.shiftKey &&
- (_keyDownList.last.keyCode == KeyCode.CTRL ||
- _keyDownList.last.keyCode == KeyCode.ALT ||
- Device.userAgent.contains('Mac') &&
- _keyDownList.last.keyCode == KeyCode.META)) {
- return false;
- }
-
- // Some keys with Ctrl/Shift do not issue keypress in WebKit.
- if (Device.isWebKit &&
- event.ctrlKey &&
- event.shiftKey &&
- (event.keyCode == KeyCode.BACKSLASH ||
- event.keyCode == KeyCode.OPEN_SQUARE_BRACKET ||
- event.keyCode == KeyCode.CLOSE_SQUARE_BRACKET ||
- event.keyCode == KeyCode.TILDE ||
- event.keyCode == KeyCode.SEMICOLON ||
- event.keyCode == KeyCode.DASH ||
- event.keyCode == KeyCode.EQUALS ||
- event.keyCode == KeyCode.COMMA ||
- event.keyCode == KeyCode.PERIOD ||
- event.keyCode == KeyCode.SLASH ||
- event.keyCode == KeyCode.APOSTROPHE ||
- event.keyCode == KeyCode.SINGLE_QUOTE)) {
- return false;
- }
-
- switch (event.keyCode) {
- case KeyCode.ENTER:
- // IE9 does not fire keypress on ENTER.
- return !Device.isIE;
- case KeyCode.ESC:
- return !Device.isWebKit;
- }
-
- return KeyCode.isCharacterKey(event.keyCode);
- }
-
- /**
- * Normalize the keycodes to the IE KeyCodes (this is what Chrome, IE, and
- * Opera all use).
- */
- int _normalizeKeyCodes(KeyboardEvent event) {
- // Note: This may change once we get input about non-US keyboards.
- if (Device.isFirefox) {
- switch (event.keyCode) {
- case KeyCode.FF_EQUALS:
- return KeyCode.EQUALS;
- case KeyCode.FF_SEMICOLON:
- return KeyCode.SEMICOLON;
- case KeyCode.MAC_FF_META:
- return KeyCode.META;
- case KeyCode.WIN_KEY_FF_LINUX:
- return KeyCode.WIN_KEY;
- }
- }
- return event.keyCode;
- }
-
- /** Handle keydown events. */
- void processKeyDown(KeyboardEvent e) {
- // Ctrl-Tab and Alt-Tab can cause the focus to be moved to another window
- // before we've caught a key-up event. If the last-key was one of these
- // we reset the state.
- if (_keyDownList.length > 0 &&
- (_keyDownList.last.keyCode == KeyCode.CTRL && !e.ctrlKey ||
- _keyDownList.last.keyCode == KeyCode.ALT && !e.altKey ||
- Device.userAgent.contains('Mac') &&
- _keyDownList.last.keyCode == KeyCode.META &&
- !e.metaKey)) {
- _keyDownList.clear();
- }
-
- var event = new KeyEvent.wrap(e);
- event._shadowKeyCode = _normalizeKeyCodes(event);
- // Technically a "keydown" event doesn't have a charCode. This is
- // calculated nonetheless to provide us with more information in giving
- // as much information as possible on keypress about keycode and also
- // charCode.
- event._shadowCharCode = _findCharCodeKeyDown(event);
- if (_keyDownList.length > 0 &&
- event.keyCode != _keyDownList.last.keyCode &&
- !_firesKeyPressEvent(event)) {
- // Some browsers have quirks not firing keypress events where all other
- // browsers do. This makes them more consistent.
- processKeyPress(e);
- }
- _keyDownList.add(event);
- _stream.add(event);
- }
-
- /** Handle keypress events. */
- void processKeyPress(KeyboardEvent event) {
- var e = new KeyEvent.wrap(event);
- // IE reports the character code in the keyCode field for keypress events.
- // There are two exceptions however, Enter and Escape.
- if (Device.isIE) {
- if (e.keyCode == KeyCode.ENTER || e.keyCode == KeyCode.ESC) {
- e._shadowCharCode = 0;
- } else {
- e._shadowCharCode = e.keyCode;
- }
- } else if (Device.isOpera) {
- // Opera reports the character code in the keyCode field.
- e._shadowCharCode = KeyCode.isCharacterKey(e.keyCode) ? e.keyCode : 0;
- }
- // Now we guesstimate about what the keycode is that was actually
- // pressed, given previous keydown information.
- e._shadowKeyCode = _determineKeyCodeForKeypress(e);
-
- // Correct the key value for certain browser-specific quirks.
- if (e._shadowKeyIdentifier != null &&
- _keyIdentifier.containsKey(e._shadowKeyIdentifier)) {
- // This is needed for Safari Windows because it currently doesn't give a
- // keyCode/which for non printable keys.
- e._shadowKeyCode = _keyIdentifier[e._shadowKeyIdentifier]!;
- }
- e._shadowAltKey = _keyDownList.any((var element) => element.altKey);
- _stream.add(e);
- }
-
- /** Handle keyup events. */
- void processKeyUp(KeyboardEvent event) {
- var e = new KeyEvent.wrap(event);
- KeyboardEvent? toRemove = null;
- for (var key in _keyDownList) {
- if (key.keyCode == e.keyCode) {
- toRemove = key;
- }
- }
- if (toRemove != null) {
- _keyDownList.removeWhere((element) => element == toRemove);
- } else if (_keyDownList.length > 0) {
- // This happens when we've reached some international keyboard case we
- // haven't accounted for or we haven't correctly eliminated all browser
- // inconsistencies. Filing bugs on when this is reached is welcome!
- _keyDownList.removeLast();
- }
- _stream.add(e);
- }
-}
-
-/**
- * Records KeyboardEvents that occur on a particular element, and provides a
- * stream of outgoing KeyEvents with cross-browser consistent keyCode and
- * charCode values despite the fact that a multitude of browsers that have
- * varying keyboard default behavior.
- *
- * Example usage:
- *
- * KeyboardEventStream.onKeyDown(document.body).listen(
- * keydownHandlerTest);
- *
- * This class is very much a work in progress, and we'd love to get information
- * on how we can make this class work with as many international keyboards as
- * possible. Bugs welcome!
- */
-class KeyboardEventStream {
- /** Named constructor to produce a stream for onKeyPress events. */
- static CustomStream<KeyEvent> onKeyPress(EventTarget target) =>
- new _KeyboardEventHandler('keypress').forTarget(target);
-
- /** Named constructor to produce a stream for onKeyUp events. */
- static CustomStream<KeyEvent> onKeyUp(EventTarget target) =>
- new _KeyboardEventHandler('keyup').forTarget(target);
-
- /** Named constructor to produce a stream for onKeyDown events. */
- static CustomStream<KeyEvent> onKeyDown(EventTarget target) =>
- new _KeyboardEventHandler('keydown').forTarget(target);
-}
diff --git a/tools/dom/nnbd_src/NodeValidatorBuilder.dart b/tools/dom/nnbd_src/NodeValidatorBuilder.dart
deleted file mode 100644
index 59f2b27..0000000
--- a/tools/dom/nnbd_src/NodeValidatorBuilder.dart
+++ /dev/null
@@ -1,454 +0,0 @@
-// Copyright (c) 2013, 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.
-
-part of dart.dom.html;
-
-/**
- * Class which helps construct standard node validation policies.
- *
- * By default this will not accept anything, but the 'allow*' functions can be
- * used to expand what types of elements or attributes are allowed.
- *
- * All allow functions are additive- elements will be accepted if they are
- * accepted by any specific rule.
- *
- * It is important to remember that sanitization is not just intended to prevent
- * cross-site scripting attacks, but also to prevent information from being
- * displayed in unexpected ways. For example something displaying basic
- * formatted text may not expect `<video>` tags to appear. In this case an
- * empty NodeValidatorBuilder with just [allowTextElements] might be
- * appropriate.
- */
-class NodeValidatorBuilder implements NodeValidator {
- final List<NodeValidator> _validators = <NodeValidator>[];
-
- NodeValidatorBuilder() {}
-
- /**
- * Creates a new NodeValidatorBuilder which accepts common constructs.
- *
- * By default this will accept HTML5 elements and attributes with the default
- * [UriPolicy] and templating elements.
- *
- * Notable syntax which is filtered:
- *
- * * Only known-good HTML5 elements and attributes are allowed.
- * * All URLs must be same-origin, use [allowNavigation] and [allowImages] to
- * specify additional URI policies.
- * * Inline-styles are not allowed.
- * * Custom element tags are disallowed, use [allowCustomElement].
- * * Custom tags extensions are disallowed, use [allowTagExtension].
- * * SVG Elements are not allowed, use [allowSvg].
- *
- * For scenarios where the HTML should only contain formatted text
- * [allowTextElements] is more appropriate.
- *
- * Use [allowSvg] to allow SVG elements.
- */
- NodeValidatorBuilder.common() {
- allowHtml5();
- allowTemplating();
- }
-
- /**
- * Allows navigation elements- Form and Anchor tags, along with common
- * attributes.
- *
- * The UriPolicy can be used to restrict the locations the navigation elements
- * are allowed to direct to. By default this will use the default [UriPolicy].
- */
- void allowNavigation([UriPolicy? uriPolicy]) {
- if (uriPolicy == null) {
- uriPolicy = new UriPolicy();
- }
- add(new _SimpleNodeValidator.allowNavigation(uriPolicy));
- }
-
- /**
- * Allows image elements.
- *
- * The UriPolicy can be used to restrict the locations the images may be
- * loaded from. By default this will use the default [UriPolicy].
- */
- void allowImages([UriPolicy? uriPolicy]) {
- if (uriPolicy == null) {
- uriPolicy = new UriPolicy();
- }
- add(new _SimpleNodeValidator.allowImages(uriPolicy));
- }
-
- /**
- * Allow basic text elements.
- *
- * This allows a subset of HTML5 elements, specifically just these tags and
- * no attributes.
- *
- * * B
- * * BLOCKQUOTE
- * * BR
- * * EM
- * * H1
- * * H2
- * * H3
- * * H4
- * * H5
- * * H6
- * * HR
- * * I
- * * LI
- * * OL
- * * P
- * * SPAN
- * * UL
- */
- void allowTextElements() {
- add(new _SimpleNodeValidator.allowTextElements());
- }
-
- /**
- * Allow inline styles on elements.
- *
- * If [tagName] is not specified then this allows inline styles on all
- * elements. Otherwise tagName limits the styles to the specified elements.
- */
- void allowInlineStyles({String? tagName}) {
- if (tagName == null) {
- tagName = '*';
- } else {
- tagName = tagName.toUpperCase();
- }
- add(new _SimpleNodeValidator(null, allowedAttributes: ['$tagName::style']));
- }
-
- /**
- * Allow common safe HTML5 elements and attributes.
- *
- * This list is based off of the Caja whitelists at:
- * https://code.google.com/p/google-caja/wiki/CajaWhitelists.
- *
- * Common things which are not allowed are script elements, style attributes
- * and any script handlers.
- */
- void allowHtml5({UriPolicy? uriPolicy}) {
- add(new _Html5NodeValidator(uriPolicy: uriPolicy));
- }
-
- /**
- * Allow SVG elements and attributes except for known bad ones.
- */
- void allowSvg() {
- add(new _SvgNodeValidator());
- }
-
- /**
- * Allow custom elements with the specified tag name and specified attributes.
- *
- * This will allow the elements as custom tags (such as <x-foo></x-foo>),
- * but will not allow tag extensions. Use [allowTagExtension] to allow
- * tag extensions.
- */
- void allowCustomElement(String tagName,
- {UriPolicy? uriPolicy,
- Iterable<String>? attributes,
- Iterable<String>? uriAttributes}) {
- var tagNameUpper = tagName.toUpperCase();
- var attrs = attributes
- ?.map<String>((name) => '$tagNameUpper::${name.toLowerCase()}');
- var uriAttrs = uriAttributes
- ?.map<String>((name) => '$tagNameUpper::${name.toLowerCase()}');
- if (uriPolicy == null) {
- uriPolicy = new UriPolicy();
- }
-
- add(new _CustomElementNodeValidator(
- uriPolicy, [tagNameUpper], attrs, uriAttrs, false, true));
- }
-
- /**
- * Allow custom tag extensions with the specified type name and specified
- * attributes.
- *
- * This will allow tag extensions (such as <div is="x-foo"></div>),
- * but will not allow custom tags. Use [allowCustomElement] to allow
- * custom tags.
- */
- void allowTagExtension(String tagName, String baseName,
- {UriPolicy? uriPolicy,
- Iterable<String>? attributes,
- Iterable<String>? uriAttributes}) {
- var baseNameUpper = baseName.toUpperCase();
- var tagNameUpper = tagName.toUpperCase();
- var attrs = attributes
- ?.map<String>((name) => '$baseNameUpper::${name.toLowerCase()}');
- var uriAttrs = uriAttributes
- ?.map<String>((name) => '$baseNameUpper::${name.toLowerCase()}');
- if (uriPolicy == null) {
- uriPolicy = new UriPolicy();
- }
-
- add(new _CustomElementNodeValidator(uriPolicy,
- [tagNameUpper, baseNameUpper], attrs, uriAttrs, true, false));
- }
-
- void allowElement(String tagName,
- {UriPolicy? uriPolicy,
- Iterable<String>? attributes,
- Iterable<String>? uriAttributes}) {
- allowCustomElement(tagName,
- uriPolicy: uriPolicy,
- attributes: attributes,
- uriAttributes: uriAttributes);
- }
-
- /**
- * Allow templating elements (such as <template> and template-related
- * attributes.
- *
- * This still requires other validators to allow regular attributes to be
- * bound (such as [allowHtml5]).
- */
- void allowTemplating() {
- add(new _TemplatingNodeValidator());
- }
-
- /**
- * Add an additional validator to the current list of validators.
- *
- * Elements and attributes will be accepted if they are accepted by any
- * validators.
- */
- void add(NodeValidator validator) {
- _validators.add(validator);
- }
-
- bool allowsElement(Element element) {
- return _validators.any((v) => v.allowsElement(element));
- }
-
- bool allowsAttribute(Element element, String attributeName, String value) {
- return _validators
- .any((v) => v.allowsAttribute(element, attributeName, value));
- }
-}
-
-class _SimpleNodeValidator implements NodeValidator {
- final Set<String> allowedElements = new Set<String>();
- final Set<String> allowedAttributes = new Set<String>();
- final Set<String> allowedUriAttributes = new Set<String>();
- final UriPolicy? uriPolicy;
-
- factory _SimpleNodeValidator.allowNavigation(UriPolicy uriPolicy) {
- return new _SimpleNodeValidator(uriPolicy, allowedElements: const [
- 'A',
- 'FORM'
- ], allowedAttributes: const [
- 'A::accesskey',
- 'A::coords',
- 'A::hreflang',
- 'A::name',
- 'A::shape',
- 'A::tabindex',
- 'A::target',
- 'A::type',
- 'FORM::accept',
- 'FORM::autocomplete',
- 'FORM::enctype',
- 'FORM::method',
- 'FORM::name',
- 'FORM::novalidate',
- 'FORM::target',
- ], allowedUriAttributes: const [
- 'A::href',
- 'FORM::action',
- ]);
- }
-
- factory _SimpleNodeValidator.allowImages(UriPolicy uriPolicy) {
- return new _SimpleNodeValidator(uriPolicy, allowedElements: const [
- 'IMG'
- ], allowedAttributes: const [
- 'IMG::align',
- 'IMG::alt',
- 'IMG::border',
- 'IMG::height',
- 'IMG::hspace',
- 'IMG::ismap',
- 'IMG::name',
- 'IMG::usemap',
- 'IMG::vspace',
- 'IMG::width',
- ], allowedUriAttributes: const [
- 'IMG::src',
- ]);
- }
-
- factory _SimpleNodeValidator.allowTextElements() {
- return new _SimpleNodeValidator(null, allowedElements: const [
- 'B',
- 'BLOCKQUOTE',
- 'BR',
- 'EM',
- 'H1',
- 'H2',
- 'H3',
- 'H4',
- 'H5',
- 'H6',
- 'HR',
- 'I',
- 'LI',
- 'OL',
- 'P',
- 'SPAN',
- 'UL',
- ]);
- }
-
- /**
- * Elements must be uppercased tag names. For example `'IMG'`.
- * Attributes must be uppercased tag name followed by :: followed by
- * lowercase attribute name. For example `'IMG:src'`.
- */
- _SimpleNodeValidator(this.uriPolicy,
- {Iterable<String>? allowedElements,
- Iterable<String>? allowedAttributes,
- Iterable<String>? allowedUriAttributes}) {
- this.allowedElements.addAll(allowedElements ?? const []);
- allowedAttributes = allowedAttributes ?? const [];
- allowedUriAttributes = allowedUriAttributes ?? const [];
- var legalAttributes = allowedAttributes
- .where((x) => !_Html5NodeValidator._uriAttributes.contains(x));
- var extraUriAttributes = allowedAttributes
- .where((x) => _Html5NodeValidator._uriAttributes.contains(x));
- this.allowedAttributes.addAll(legalAttributes);
- this.allowedUriAttributes.addAll(allowedUriAttributes);
- this.allowedUriAttributes.addAll(extraUriAttributes);
- }
-
- bool allowsElement(Element element) {
- return allowedElements.contains(Element._safeTagName(element));
- }
-
- bool allowsAttribute(Element element, String attributeName, String value) {
- var tagName = Element._safeTagName(element);
- if (allowedUriAttributes.contains('$tagName::$attributeName')) {
- return uriPolicy!.allowsUri(value);
- } else if (allowedUriAttributes.contains('*::$attributeName')) {
- return uriPolicy!.allowsUri(value);
- } else if (allowedAttributes.contains('$tagName::$attributeName')) {
- return true;
- } else if (allowedAttributes.contains('*::$attributeName')) {
- return true;
- } else if (allowedAttributes.contains('$tagName::*')) {
- return true;
- } else if (allowedAttributes.contains('*::*')) {
- return true;
- }
- return false;
- }
-}
-
-class _CustomElementNodeValidator extends _SimpleNodeValidator {
- final bool allowTypeExtension;
- final bool allowCustomTag;
-
- _CustomElementNodeValidator(
- UriPolicy uriPolicy,
- Iterable<String> allowedElements,
- Iterable<String>? allowedAttributes,
- Iterable<String>? allowedUriAttributes,
- bool allowTypeExtension,
- bool allowCustomTag)
- : this.allowTypeExtension = allowTypeExtension == true,
- this.allowCustomTag = allowCustomTag == true,
- super(uriPolicy,
- allowedElements: allowedElements,
- allowedAttributes: allowedAttributes,
- allowedUriAttributes: allowedUriAttributes);
-
- bool allowsElement(Element element) {
- if (allowTypeExtension) {
- var isAttr = element.attributes['is'];
- if (isAttr != null) {
- return allowedElements.contains(isAttr.toUpperCase()) &&
- allowedElements.contains(Element._safeTagName(element));
- }
- }
- return allowCustomTag &&
- allowedElements.contains(Element._safeTagName(element));
- }
-
- bool allowsAttribute(Element element, String attributeName, String value) {
- if (allowsElement(element)) {
- if (allowTypeExtension &&
- attributeName == 'is' &&
- allowedElements.contains(value.toUpperCase())) {
- return true;
- }
- return super.allowsAttribute(element, attributeName, value);
- }
- return false;
- }
-}
-
-class _TemplatingNodeValidator extends _SimpleNodeValidator {
- static const _TEMPLATE_ATTRS = const <String>[
- 'bind',
- 'if',
- 'ref',
- 'repeat',
- 'syntax'
- ];
-
- final Set<String> _templateAttrs;
-
- _TemplatingNodeValidator()
- : _templateAttrs = new Set<String>.from(_TEMPLATE_ATTRS),
- super(null,
- allowedElements: ['TEMPLATE'],
- allowedAttributes:
- _TEMPLATE_ATTRS.map((attr) => 'TEMPLATE::$attr')) {}
-
- bool allowsAttribute(Element element, String attributeName, String value) {
- if (super.allowsAttribute(element, attributeName, value)) {
- return true;
- }
-
- if (attributeName == 'template' && value == "") {
- return true;
- }
-
- if (element.attributes['template'] == "") {
- return _templateAttrs.contains(attributeName);
- }
- return false;
- }
-}
-
-class _SvgNodeValidator implements NodeValidator {
- bool allowsElement(Element element) {
- if (element is svg.ScriptElement) {
- return false;
- }
- // Firefox 37 has issues with creating foreign elements inside a
- // foreignobject tag as SvgElement. We don't want foreignobject contents
- // anyway, so just remove the whole tree outright. And we can't rely
- // on IE recognizing the SvgForeignObject type, so go by tagName. Bug 23144
- if (element is svg.SvgElement &&
- Element._safeTagName(element) == 'foreignObject') {
- return false;
- }
- if (element is svg.SvgElement) {
- return true;
- }
- return false;
- }
-
- bool allowsAttribute(Element element, String attributeName, String value) {
- if (attributeName == 'is' || attributeName.startsWith('on')) {
- return false;
- }
- return allowsElement(element);
- }
-}
diff --git a/tools/dom/nnbd_src/ReadyState.dart b/tools/dom/nnbd_src/ReadyState.dart
deleted file mode 100644
index 37c3fdb..0000000
--- a/tools/dom/nnbd_src/ReadyState.dart
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright (c) 2011, 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.
-
-part of html;
-
-/**
- * Contains the set of standard values returned by HTMLDocument.getReadyState.
- */
-abstract class ReadyState {
- /**
- * Indicates the document is still loading and parsing.
- */
- static const String LOADING = "loading";
-
- /**
- * Indicates the document is finished parsing but is still loading
- * subresources.
- */
- static const String INTERACTIVE = "interactive";
-
- /**
- * Indicates the document and all subresources have been loaded.
- */
- static const String COMPLETE = "complete";
-}
diff --git a/tools/dom/nnbd_src/Validators.dart b/tools/dom/nnbd_src/Validators.dart
deleted file mode 100644
index a498da9..0000000
--- a/tools/dom/nnbd_src/Validators.dart
+++ /dev/null
@@ -1,321 +0,0 @@
-// Copyright (c) 2013, 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.
-
-part of dart.dom.html;
-
-/**
- * Interface used to validate that only accepted elements and attributes are
- * allowed while parsing HTML strings into DOM nodes.
- *
- * In general, customization of validation behavior should be done via the
- * [NodeValidatorBuilder] class to mitigate the chances of incorrectly
- * implementing validation rules.
- */
-abstract class NodeValidator {
- /**
- * Construct a default NodeValidator which only accepts whitelisted HTML5
- * elements and attributes.
- *
- * If a uriPolicy is not specified then the default uriPolicy will be used.
- */
- factory NodeValidator({UriPolicy? uriPolicy}) =>
- new _Html5NodeValidator(uriPolicy: uriPolicy);
-
- factory NodeValidator.throws(NodeValidator base) =>
- new _ThrowsNodeValidator(base);
-
- /**
- * Returns true if the tagName is an accepted type.
- */
- bool allowsElement(Element element);
-
- /**
- * Returns true if the attribute is allowed.
- *
- * The attributeName parameter will always be in lowercase.
- *
- * See [allowsElement] for format of tagName.
- */
- bool allowsAttribute(Element element, String attributeName, String value);
-}
-
-/**
- * Performs sanitization of a node tree after construction to ensure that it
- * does not contain any disallowed elements or attributes.
- *
- * In general custom implementations of this class should not be necessary and
- * all validation customization should be done in custom NodeValidators, but
- * custom implementations of this class can be created to perform more complex
- * tree sanitization.
- */
-abstract class NodeTreeSanitizer {
- /**
- * Constructs a default tree sanitizer which will remove all elements and
- * attributes which are not allowed by the provided validator.
- */
- factory NodeTreeSanitizer(NodeValidator validator) =>
- new _ValidatingTreeSanitizer(validator);
-
- /**
- * Called with the root of the tree which is to be sanitized.
- *
- * This method needs to walk the entire tree and either remove elements and
- * attributes which are not recognized as safe or throw an exception which
- * will mark the entire tree as unsafe.
- */
- void sanitizeTree(Node node);
-
- /**
- * A sanitizer for trees that we trust. It does no validation and allows
- * any elements. It is also more efficient, since it can pass the text
- * directly through to the underlying APIs without creating a document
- * fragment to be sanitized.
- */
- static const trusted = const _TrustedHtmlTreeSanitizer();
-}
-
-/**
- * A sanitizer for trees that we trust. It does no validation and allows
- * any elements.
- */
-class _TrustedHtmlTreeSanitizer implements NodeTreeSanitizer {
- const _TrustedHtmlTreeSanitizer();
-
- sanitizeTree(Node node) {}
-}
-
-/**
- * Defines the policy for what types of uris are allowed for particular
- * attribute values.
- *
- * This can be used to provide custom rules such as allowing all http:// URIs
- * for image attributes but only same-origin URIs for anchor tags.
- */
-abstract class UriPolicy {
- /**
- * Constructs the default UriPolicy which is to only allow Uris to the same
- * origin as the application was launched from.
- *
- * This will block all ftp: mailto: URIs. It will also block accessing
- * https://example.com if the app is running from http://example.com.
- */
- factory UriPolicy() => new _SameOriginUriPolicy();
-
- /**
- * Checks if the uri is allowed on the specified attribute.
- *
- * The uri provided may or may not be a relative path.
- */
- bool allowsUri(String uri);
-}
-
-/**
- * Allows URIs to the same origin as the current application was loaded from
- * (such as https://example.com:80).
- */
-class _SameOriginUriPolicy implements UriPolicy {
- final AnchorElement _hiddenAnchor = new AnchorElement();
- final Location _loc = window.location;
-
- bool allowsUri(String uri) {
- _hiddenAnchor.href = uri;
- // IE leaves an empty hostname for same-origin URIs.
- return (_hiddenAnchor.hostname == _loc.hostname &&
- _hiddenAnchor.port == _loc.port &&
- _hiddenAnchor.protocol == _loc.protocol) ||
- (_hiddenAnchor.hostname == '' &&
- _hiddenAnchor.port == '' &&
- (_hiddenAnchor.protocol == ':' || _hiddenAnchor.protocol == ''));
- }
-}
-
-class _ThrowsNodeValidator implements NodeValidator {
- final NodeValidator validator;
-
- _ThrowsNodeValidator(this.validator) {}
-
- bool allowsElement(Element element) {
- if (!validator.allowsElement(element)) {
- throw new ArgumentError(Element._safeTagName(element));
- }
- return true;
- }
-
- bool allowsAttribute(Element element, String attributeName, String value) {
- if (!validator.allowsAttribute(element, attributeName, value)) {
- throw new ArgumentError(
- '${Element._safeTagName(element)}[$attributeName="$value"]');
- }
- return true;
- }
-}
-
-/**
- * Standard tree sanitizer which validates a node tree against the provided
- * validator and removes any nodes or attributes which are not allowed.
- */
-class _ValidatingTreeSanitizer implements NodeTreeSanitizer {
- NodeValidator validator;
-
- /// Did we modify the tree by removing anything.
- bool modifiedTree = false;
- _ValidatingTreeSanitizer(this.validator) {}
-
- void sanitizeTree(Node node) {
- void walk(Node node, Node? parent) {
- sanitizeNode(node, parent);
-
- var child = node.lastChild;
- while (null != child) {
- Node? nextChild;
- try {
- // Child may be removed during the walk, and we may not even be able
- // to get its previousNode. But it's also possible that previousNode
- // (i.e. previousSibling) is being spoofed, so double-check it.
- nextChild = child.previousNode;
- if (nextChild != null && nextChild.nextNode != child) {
- throw StateError("Corrupt HTML");
- }
- } catch (e) {
- // Child appears bad, remove it. We want to check the rest of the
- // children of node and, but we have no way of getting to the next
- // child, so start again from the last child.
- _removeNode(child, node);
- child = null;
- nextChild = node.lastChild;
- }
- if (child != null) walk(child, node);
- child = nextChild;
- }
- }
-
- modifiedTree = false;
- walk(node, null);
- while (modifiedTree) {
- modifiedTree = false;
- walk(node, null);
- }
- }
-
- /// Aggressively try to remove node.
- void _removeNode(Node node, Node? parent) {
- // If we have the parent, it's presumably already passed more sanitization
- // or is the fragment, so ask it to remove the child. And if that fails
- // try to set the outer html.
- modifiedTree = true;
- if (parent == null || parent != node.parentNode) {
- node.remove();
- } else {
- parent._removeChild(node);
- }
- }
-
- /// Sanitize the element, assuming we can't trust anything about it.
- void _sanitizeUntrustedElement(/* Element */ element, Node? parent) {
- // If the _hasCorruptedAttributes does not successfully return false,
- // then we consider it corrupted and remove.
- // TODO(alanknight): This is a workaround because on Firefox
- // embed/object
- // tags typeof is "function", not "object". We don't recognize them, and
- // can't call methods. This does mean that you can't explicitly allow an
- // embed tag. The only thing that will let it through is a null
- // sanitizer that doesn't traverse the tree at all. But sanitizing while
- // allowing embeds seems quite unlikely. This is also the reason that we
- // can't declare the type of element, as an embed won't pass any type
- // check in dart2js.
- var corrupted = true;
- var attrs;
- var isAttr;
- try {
- // If getting/indexing attributes throws, count that as corrupt.
- attrs = element.attributes;
- isAttr = attrs['is'];
- var corruptedTest1 = Element._hasCorruptedAttributes(element);
-
- // On IE, erratically, the hasCorruptedAttributes test can return false,
- // even though it clearly is corrupted. A separate copy of the test
- // inlining just the basic check seems to help.
- corrupted = corruptedTest1
- ? true
- : Element._hasCorruptedAttributesAdditionalCheck(element);
- } catch (e) {}
- var elementText = 'element unprintable';
- try {
- elementText = element.toString();
- } catch (e) {}
- try {
- var elementTagName = Element._safeTagName(element);
- _sanitizeElement(element, parent, corrupted, elementText, elementTagName,
- attrs, isAttr);
- } on ArgumentError {
- // Thrown by _ThrowsNodeValidator
- rethrow;
- } catch (e) {
- // Unexpected exception sanitizing -> remove
- _removeNode(element, parent);
- window.console.warn('Removing corrupted element $elementText');
- }
- }
-
- /// Having done basic sanity checking on the element, and computed the
- /// important attributes we want to check, remove it if it's not valid
- /// or not allowed, either as a whole or particular attributes.
- void _sanitizeElement(Element element, Node? parent, bool corrupted,
- String text, String tag, Map attrs, String? isAttr) {
- if (false != corrupted) {
- _removeNode(element, parent);
- window.console
- .warn('Removing element due to corrupted attributes on <$text>');
- return;
- }
- if (!validator.allowsElement(element)) {
- _removeNode(element, parent);
- window.console.warn('Removing disallowed element <$tag> from $parent');
- return;
- }
-
- if (isAttr != null) {
- if (!validator.allowsAttribute(element, 'is', isAttr)) {
- _removeNode(element, parent);
- window.console.warn('Removing disallowed type extension '
- '<$tag is="$isAttr">');
- return;
- }
- }
-
- // TODO(blois): Need to be able to get all attributes, irrespective of
- // XMLNS.
- var keys = attrs.keys.toList();
- for (var i = attrs.length - 1; i >= 0; --i) {
- var name = keys[i];
- if (!validator.allowsAttribute(
- element, name.toLowerCase(), attrs[name])) {
- window.console.warn('Removing disallowed attribute '
- '<$tag $name="${attrs[name]}">');
- attrs.remove(name);
- }
- }
-
- if (element is TemplateElement) {
- TemplateElement template = element;
- sanitizeTree(template.content);
- }
- }
-
- /// Sanitize the node and its children recursively.
- void sanitizeNode(Node node, Node? parent) {
- switch (node.nodeType) {
- case Node.ELEMENT_NODE:
- _sanitizeUntrustedElement(node, parent);
- break;
- case Node.COMMENT_NODE:
- case Node.DOCUMENT_FRAGMENT_NODE:
- case Node.TEXT_NODE:
- case Node.CDATA_SECTION_NODE:
- break;
- default:
- _removeNode(node, parent);
- }
- }
-}
diff --git a/tools/dom/nnbd_src/WrappedList.dart b/tools/dom/nnbd_src/WrappedList.dart
deleted file mode 100644
index 65d0d94..0000000
--- a/tools/dom/nnbd_src/WrappedList.dart
+++ /dev/null
@@ -1,97 +0,0 @@
-// Copyright (c) 2013, 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.
-
-part of dart.dom.html;
-
-/**
- * A list which just wraps another list, for either intercepting list calls or
- * retyping the list (for example, from List<A> to List<B> where B extends A).
- */
-class _WrappedList<E extends Node> extends ListBase<E>
- implements NodeListWrapper {
- final List<Node> _list;
-
- _WrappedList(this._list);
-
- // Iterable APIs
-
- Iterator<E> get iterator => new _WrappedIterator<E>(_list.iterator);
-
- int get length => _list.length;
-
- // Collection APIs
-
- void add(E element) {
- _list.add(element);
- }
-
- bool remove(Object? element) => _list.remove(element);
-
- void clear() {
- _list.clear();
- }
-
- // List APIs
-
- E operator [](int index) => _list[index] as E;
-
- void operator []=(int index, E value) {
- _list[index] = value;
- }
-
- set length(int newLength) {
- _list.length = newLength;
- }
-
- void sort([int compare(E a, E b)?]) {
- if (compare == null) {
- _list.sort();
- } else {
- _list.sort((Node a, Node b) => compare(a as E, b as E));
- }
- }
-
- int indexOf(Object? element, [int start = 0]) =>
- _list.indexOf(element as Node, start);
-
- int lastIndexOf(Object? element, [int? start]) =>
- _list.lastIndexOf(element as Node, start);
-
- void insert(int index, E element) => _list.insert(index, element);
-
- E removeAt(int index) => _list.removeAt(index) as E;
-
- void setRange(int start, int end, Iterable<E> iterable, [int skipCount = 0]) {
- _list.setRange(start, end, iterable, skipCount);
- }
-
- void removeRange(int start, int end) {
- _list.removeRange(start, end);
- }
-
- void replaceRange(int start, int end, Iterable<E> iterable) {
- _list.replaceRange(start, end, iterable);
- }
-
- void fillRange(int start, int end, [E? fillValue]) {
- _list.fillRange(start, end, fillValue);
- }
-
- List<Node> get rawList => _list;
-}
-
-/**
- * Iterator wrapper for _WrappedList.
- */
-class _WrappedIterator<E extends Node> implements Iterator<E> {
- Iterator<Node> _iterator;
-
- _WrappedIterator(this._iterator);
-
- bool moveNext() {
- return _iterator.moveNext();
- }
-
- E get current => _iterator.current as E;
-}
diff --git a/tools/dom/nnbd_src/_HttpRequestUtils.dart b/tools/dom/nnbd_src/_HttpRequestUtils.dart
deleted file mode 100644
index d6e73d7..0000000
--- a/tools/dom/nnbd_src/_HttpRequestUtils.dart
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright (c) 2012, 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.
-
-part of html;
-
-class _HttpRequestUtils {
- // Helper for factory HttpRequest.get
- static HttpRequest get(
- String url, onComplete(HttpRequest request), bool withCredentials) {
- final request = new HttpRequest();
- request.open('GET', url, async: true);
-
- request.withCredentials = withCredentials;
-
- request.onReadyStateChange.listen((e) {
- if (request.readyState == HttpRequest.DONE) {
- onComplete(request);
- }
- });
-
- request.send();
-
- return request;
- }
-}
diff --git a/tools/dom/nnbd_src/_ListIterators.dart b/tools/dom/nnbd_src/_ListIterators.dart
deleted file mode 100644
index 74aaddc..0000000
--- a/tools/dom/nnbd_src/_ListIterators.dart
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright (c) 2011, 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.
-
-part of html;
-
-// Iterator for arrays with fixed size.
-class FixedSizeListIterator<T> implements Iterator<T> {
- final List<T> _array;
- final int _length; // Cache array length for faster access.
- int _position;
- T? _current;
-
- FixedSizeListIterator(List<T> array)
- : _array = array,
- _position = -1,
- _length = array.length;
-
- bool moveNext() {
- int nextPosition = _position + 1;
- if (nextPosition < _length) {
- _current = _array[nextPosition];
- _position = nextPosition;
- return true;
- }
- _current = null;
- _position = _length;
- return false;
- }
-
- T get current => _current as T;
-}
-
-// Iterator for arrays with variable size.
-class _VariableSizeListIterator<T> implements Iterator<T> {
- final List<T> _array;
- int _position;
- T? _current;
-
- _VariableSizeListIterator(List<T> array)
- : _array = array,
- _position = -1;
-
- bool moveNext() {
- int nextPosition = _position + 1;
- if (nextPosition < _array.length) {
- _current = _array[nextPosition];
- _position = nextPosition;
- return true;
- }
- _current = null;
- _position = _array.length;
- return false;
- }
-
- T get current => _current as T;
-}
diff --git a/tools/dom/nnbd_src/dart2js_Console.dart b/tools/dom/nnbd_src/dart2js_Console.dart
deleted file mode 100644
index b3297d2..0000000
--- a/tools/dom/nnbd_src/dart2js_Console.dart
+++ /dev/null
@@ -1,82 +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.
-
-part of html;
-
-class Console {
- const Console._safe();
- static const Console _safeConsole = const Console._safe();
-
- bool get _isConsoleDefined => JS('bool', 'typeof console != "undefined"');
-
- MemoryInfo? get memory =>
- _isConsoleDefined ? JS('MemoryInfo', 'window.console.memory') : null;
-
- void assertCondition(bool condition, Object arg) => _isConsoleDefined
- ? JS('void', 'window.console.assertCondition(#, #)', condition, arg)
- : null;
-
- void clear(Object arg) =>
- _isConsoleDefined ? JS('void', 'window.console.clear(#)', arg) : null;
-
- void count(Object arg) =>
- _isConsoleDefined ? JS('void', 'window.console.count(#)', arg) : null;
-
- void debug(Object arg) =>
- _isConsoleDefined ? JS('void', 'window.console.debug(#)', arg) : null;
-
- void dir(Object arg) =>
- _isConsoleDefined ? JS('void', 'window.console.dir(#)', arg) : null;
-
- void dirxml(Object arg) =>
- _isConsoleDefined ? JS('void', 'window.console.dirxml(#)', arg) : null;
-
- void error(Object arg) =>
- _isConsoleDefined ? JS('void', 'window.console.error(#)', arg) : null;
-
- void group(Object arg) =>
- _isConsoleDefined ? JS('void', 'window.console.group(#)', arg) : null;
-
- void groupCollapsed(Object arg) => _isConsoleDefined
- ? JS('void', 'window.console.groupCollapsed(#)', arg)
- : null;
-
- void groupEnd() =>
- _isConsoleDefined ? JS('void', 'window.console.groupEnd()') : null;
-
- void info(Object arg) =>
- _isConsoleDefined ? JS('void', 'window.console.info(#)', arg) : null;
-
- void log(Object arg) =>
- _isConsoleDefined ? JS('void', 'window.console.log(#)', arg) : null;
-
- void markTimeline(Object arg) => _isConsoleDefined
- ? JS('void', 'window.console.markTimeline(#)', arg)
- : null;
-
- void profile(String title) =>
- _isConsoleDefined ? JS('void', 'window.console.profile(#)', title) : null;
-
- void profileEnd(String title) => _isConsoleDefined
- ? JS('void', 'window.console.profileEnd(#)', title)
- : null;
-
- void table(Object arg) =>
- _isConsoleDefined ? JS('void', 'window.console.table(#)', arg) : null;
-
- void time(String title) =>
- _isConsoleDefined ? JS('void', 'window.console.time(#)', title) : null;
-
- void timeEnd(String title) =>
- _isConsoleDefined ? JS('void', 'window.console.timeEnd(#)', title) : null;
-
- void timeStamp(Object arg) =>
- _isConsoleDefined ? JS('void', 'window.console.timeStamp(#)', arg) : null;
-
- void trace(Object arg) =>
- _isConsoleDefined ? JS('void', 'window.console.trace(#)', arg) : null;
-
- void warn(Object arg) =>
- _isConsoleDefined ? JS('void', 'window.console.warn(#)', arg) : null;
-}
diff --git a/tools/dom/nnbd_src/dart2js_Conversions.dart b/tools/dom/nnbd_src/dart2js_Conversions.dart
deleted file mode 100644
index cb943b4..0000000
--- a/tools/dom/nnbd_src/dart2js_Conversions.dart
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright (c) 2012, 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.
-
-// Conversions for Window. These check if the window is the local
-// window, and if it's not, wraps or unwraps it with a secure wrapper.
-// We need to test for EventTarget here as well as it's a base type.
-// We omit an unwrapper for Window as no methods take a non-local
-// window as a parameter.
-
-part of html;
-
-WindowBase? _convertNativeToDart_Window(win) {
- if (win == null) return null;
- return _DOMWindowCrossFrame._createSafe(win);
-}
-
-EventTarget? _convertNativeToDart_EventTarget(e) {
- if (e == null) {
- return null;
- }
- // Assume it's a Window if it contains the postMessage property. It may be
- // from a different frame - without a patched prototype - so we cannot
- // rely on Dart type checking.
- if (JS('bool', r'"postMessage" in #', e)) {
- var window = _DOMWindowCrossFrame._createSafe(e);
- // If it's a native window.
- if (window is EventTarget) {
- return window;
- }
- return null;
- } else
- return e;
-}
-
-EventTarget? _convertDartToNative_EventTarget(e) {
- if (e is _DOMWindowCrossFrame) {
- return e._window;
- } else {
- return e;
- }
-}
-
-_convertNativeToDart_XHR_Response(o) {
- if (o is Document) {
- return o;
- }
- return convertNativeToDart_SerializedScriptValue(o);
-}
diff --git a/tools/dom/nnbd_src/dart2js_CssClassSet.dart b/tools/dom/nnbd_src/dart2js_CssClassSet.dart
deleted file mode 100644
index 00d80bc..0000000
--- a/tools/dom/nnbd_src/dart2js_CssClassSet.dart
+++ /dev/null
@@ -1,260 +0,0 @@
-// Copyright (c) 2015, 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.
-
-part of html;
-
-/**
- * A set (union) of the CSS classes that are present in a set of elements.
- * Implemented separately from _ElementCssClassSet for performance.
- */
-class _MultiElementCssClassSet extends CssClassSetImpl {
- final Iterable<Element> _elementIterable;
-
- // TODO(sra): Perhaps we should store the DomTokenList instead.
- final List<CssClassSetImpl> _sets;
-
- factory _MultiElementCssClassSet(Iterable<Element> elements) {
- return new _MultiElementCssClassSet._(elements,
- new List<CssClassSetImpl>.from(elements.map((Element e) => e.classes)));
- }
-
- _MultiElementCssClassSet._(this._elementIterable, this._sets);
-
- Set<String> readClasses() {
- var s = new LinkedHashSet<String>();
- _sets.forEach((CssClassSetImpl e) => s.addAll(e.readClasses()));
- return s;
- }
-
- void writeClasses(Set<String> s) {
- var classes = s.join(' ');
- for (Element e in _elementIterable) {
- e.className = classes;
- }
- }
-
- /**
- * Helper method used to modify the set of css classes on this element.
- *
- * f - callback with:
- * s - a Set of all the css class name currently on this element.
- *
- * After f returns, the modified set is written to the
- * className property of this element.
- */
- modify(f(Set<String> s)) {
- _sets.forEach((CssClassSetImpl e) => e.modify(f));
- }
-
- /**
- * Adds the class [value] to the element if it is not on it, removes it if it
- * is.
- *
- * TODO(sra): It seems wrong to collect a 'changed' flag like this when the
- * underlying toggle returns an 'is set' flag.
- */
- bool toggle(String value, [bool? shouldAdd]) => _sets.fold(
- false,
- (bool changed, CssClassSetImpl e) =>
- e.toggle(value, shouldAdd) || changed);
-
- /**
- * Remove the class [value] from element, and return true on successful
- * removal.
- *
- * This is the Dart equivalent of jQuery's
- * [removeClass](http://api.jquery.com/removeClass/).
- */
- bool remove(Object? value) => _sets.fold(
- false, (bool changed, CssClassSetImpl e) => e.remove(value) || changed);
-}
-
-class _ElementCssClassSet extends CssClassSetImpl {
- final Element _element;
-
- _ElementCssClassSet(this._element);
-
- Set<String> readClasses() {
- var s = new LinkedHashSet<String>();
- var classname = _element.className;
-
- for (String name in classname.split(' ')) {
- String trimmed = name.trim();
- if (!trimmed.isEmpty) {
- s.add(trimmed);
- }
- }
- return s;
- }
-
- void writeClasses(Set<String> s) {
- _element.className = s.join(' ');
- }
-
- int get length => _classListLength(_classListOf(_element));
- bool get isEmpty => length == 0;
- bool get isNotEmpty => length != 0;
-
- void clear() {
- _element.className = '';
- }
-
- bool contains(Object? value) {
- return _contains(_element, value);
- }
-
- bool add(String value) {
- return _add(_element, value);
- }
-
- bool remove(Object? value) {
- return value is String && _remove(_element, value);
- }
-
- bool toggle(String value, [bool? shouldAdd]) {
- return _toggle(_element, value, shouldAdd);
- }
-
- void addAll(Iterable<String> iterable) {
- _addAll(_element, iterable);
- }
-
- void removeAll(Iterable<Object?> iterable) {
- _removeAll(_element, iterable);
- }
-
- void retainAll(Iterable<Object?> iterable) {
- _removeWhere(_element, iterable.toSet().contains, false);
- }
-
- void removeWhere(bool test(String name)) {
- _removeWhere(_element, test, true);
- }
-
- void retainWhere(bool test(String name)) {
- _removeWhere(_element, test, false);
- }
-
- static bool _contains(Element _element, Object? value) {
- return value is String && _classListContains(_classListOf(_element), value);
- }
-
- @pragma('dart2js:tryInline')
- static bool _add(Element _element, String value) {
- DomTokenList list = _classListOf(_element);
- // Compute returned result independently of action upon the set.
- bool added = !_classListContainsBeforeAddOrRemove(list, value);
- _classListAdd(list, value);
- return added;
- }
-
- @pragma('dart2js:tryInline')
- static bool _remove(Element _element, String value) {
- DomTokenList list = _classListOf(_element);
- bool removed = _classListContainsBeforeAddOrRemove(list, value);
- _classListRemove(list, value);
- return removed;
- }
-
- static bool _toggle(Element _element, String value, bool? shouldAdd) {
- // There is no value that can be passed as the second argument of
- // DomTokenList.toggle that behaves the same as passing one argument.
- // `null` is seen as false, meaning 'remove'.
- return shouldAdd == null
- ? _toggleDefault(_element, value)
- : _toggleOnOff(_element, value, shouldAdd);
- }
-
- static bool _toggleDefault(Element _element, String value) {
- DomTokenList list = _classListOf(_element);
- return _classListToggle1(list, value);
- }
-
- static bool _toggleOnOff(Element _element, String value, bool? shouldAdd) {
- DomTokenList list = _classListOf(_element);
- // IE's toggle does not take a second parameter. We would prefer:
- //
- // return _classListToggle2(list, value, shouldAdd);
- //
- if (shouldAdd ?? false) {
- _classListAdd(list, value);
- return true;
- } else {
- _classListRemove(list, value);
- return false;
- }
- }
-
- static void _addAll(Element _element, Iterable<String> iterable) {
- DomTokenList list = _classListOf(_element);
- for (String value in iterable) {
- _classListAdd(list, value);
- }
- }
-
- static void _removeAll(Element _element, Iterable<Object?> iterable) {
- DomTokenList list = _classListOf(_element);
- for (Object? value in iterable) {
- _classListRemove(list, value as String);
- }
- }
-
- static void _removeWhere(
- Element _element, bool test(String name), bool doRemove) {
- DomTokenList list = _classListOf(_element);
- int i = 0;
- while (i < _classListLength(list)) {
- String item = list.item(i)!;
- if (doRemove == test(item)) {
- _classListRemove(list, item);
- } else {
- ++i;
- }
- }
- }
-
- // A collection of static methods for DomTokenList. These methods are a
- // work-around for the lack of annotations to express the full behaviour of
- // the DomTokenList methods.
-
- static DomTokenList _classListOf(Element e) => JS(
- 'returns:DomTokenList;creates:DomTokenList;effects:none;depends:all;',
- '#.classList',
- e);
-
- static int _classListLength(DomTokenList list) =>
- JS('returns:JSUInt31;effects:none;depends:all;', '#.length', list);
-
- static bool _classListContains(DomTokenList list, String value) =>
- JS('returns:bool;effects:none;depends:all', '#.contains(#)', list, value);
-
- static bool _classListContainsBeforeAddOrRemove(
- DomTokenList list, String value) =>
- // 'throws:never' is a lie, since 'contains' will throw on an illegal
- // token. However, we always call this function immediately prior to
- // add/remove/toggle with the same token. Often the result of 'contains'
- // is unused and the lie makes it possible for the 'contains' instruction
- // to be removed.
- JS('returns:bool;effects:none;depends:all;throws:null(1)',
- '#.contains(#)', list, value);
-
- static void _classListAdd(DomTokenList list, String value) {
- // list.add(value);
- JS('', '#.add(#)', list, value);
- }
-
- static void _classListRemove(DomTokenList list, String value) {
- // list.remove(value);
- JS('', '#.remove(#)', list, value);
- }
-
- static bool _classListToggle1(DomTokenList list, String value) {
- return JS('bool', '#.toggle(#)', list, value);
- }
-
- static bool _classListToggle2(
- DomTokenList list, String value, bool? shouldAdd) {
- return JS('bool', '#.toggle(#, #)', list, value, shouldAdd);
- }
-}
diff --git a/tools/dom/nnbd_src/dart2js_CustomElementSupport.dart b/tools/dom/nnbd_src/dart2js_CustomElementSupport.dart
deleted file mode 100644
index bbe6a37..0000000
--- a/tools/dom/nnbd_src/dart2js_CustomElementSupport.dart
+++ /dev/null
@@ -1,214 +0,0 @@
-// Copyright (c) 2013, 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.
-
-part of dart.dom.html;
-
-_callConstructor(constructor, interceptor) {
- return (receiver) {
- setNativeSubclassDispatchRecord(receiver, interceptor);
-
- // Mirrors uses the constructor property to cache lookups, so we need it to
- // be set correctly, including on IE where it is not automatically picked
- // up from the __proto__.
- JS('', '#.constructor = #.__proto__.constructor', receiver, receiver);
- return JS('', '#(#)', constructor, receiver);
- };
-}
-
-_callAttached(receiver) {
- return receiver.attached();
-}
-
-_callDetached(receiver) {
- return receiver.detached();
-}
-
-_callAttributeChanged(receiver, name, oldValue, newValue) {
- return receiver.attributeChanged(name, oldValue, newValue);
-}
-
-_makeCallbackMethod(callback) {
- return JS(
- '',
- '''((function(invokeCallback) {
- return function() {
- return invokeCallback(this);
- };
- })(#))''',
- convertDartClosureToJS(callback, 1));
-}
-
-_makeCallbackMethod3(callback) {
- return JS(
- '',
- '''((function(invokeCallback) {
- return function(arg1, arg2, arg3) {
- return invokeCallback(this, arg1, arg2, arg3);
- };
- })(#))''',
- convertDartClosureToJS(callback, 4));
-}
-
-/// Checks whether the given [element] correctly extends from the native class
-/// with the given [baseClassName]. This method will throw if the base class
-/// doesn't match, except when the element extends from `template` and it's base
-/// class is `HTMLUnknownElement`. This exclusion is needed to support extension
-/// of template elements (used heavily in Polymer 1.0) on IE11 when using the
-/// webcomponents-lite.js polyfill.
-void _checkExtendsNativeClassOrTemplate(
- Element element, String extendsTag, String baseClassName) {
- if (!JS('bool', '(# instanceof window[#])', element, baseClassName) &&
- !((extendsTag == 'template' &&
- JS('bool', '(# instanceof window["HTMLUnknownElement"])',
- element)))) {
- throw new UnsupportedError('extendsTag does not match base native class');
- }
-}
-
-Function _registerCustomElement(context, document, String tag, [Map? options]) {
- // Function follows the same pattern as the following JavaScript code for
- // registering a custom element.
- //
- // var proto = Object.create(HTMLElement.prototype, {
- // createdCallback: {
- // value: function() {
- // window.console.log('here');
- // }
- // }
- // });
- // document.registerElement('x-foo', { prototype: proto });
- // ...
- // var e = document.createElement('x-foo');
-
- var extendsTagName = '';
- Type? type;
- if (options != null) {
- extendsTagName = options['extends'];
- type = options['prototype'];
- }
-
- var interceptorClass = findInterceptorConstructorForType(type);
- if (interceptorClass == null) {
- throw new ArgumentError(type);
- }
-
- var interceptor = JS('=Object', '#.prototype', interceptorClass);
-
- var constructor = findConstructorForNativeSubclassType(type, 'created');
- if (constructor == null) {
- throw new ArgumentError("$type has no constructor called 'created'");
- }
-
- // Workaround for 13190- use an article element to ensure that HTMLElement's
- // interceptor is resolved correctly.
- getNativeInterceptor(new Element.tag('article'));
-
- String baseClassName = findDispatchTagForInterceptorClass(interceptorClass);
- if (baseClassName == null) {
- throw new ArgumentError(type);
- }
-
- if (extendsTagName == null) {
- if (baseClassName != 'HTMLElement') {
- throw new UnsupportedError('Class must provide extendsTag if base '
- 'native class is not HtmlElement');
- }
- } else {
- var element = document.createElement(extendsTagName);
- _checkExtendsNativeClassOrTemplate(element, extendsTagName, baseClassName);
- }
-
- var baseConstructor = JS('=Object', '#[#]', context, baseClassName);
-
- var properties = JS('=Object', '{}');
-
- JS(
- 'void',
- '#.createdCallback = #',
- properties,
- JS('=Object', '{value: #}',
- _makeCallbackMethod(_callConstructor(constructor, interceptor))));
- JS('void', '#.attachedCallback = #', properties,
- JS('=Object', '{value: #}', _makeCallbackMethod(_callAttached)));
- JS('void', '#.detachedCallback = #', properties,
- JS('=Object', '{value: #}', _makeCallbackMethod(_callDetached)));
- JS('void', '#.attributeChangedCallback = #', properties,
- JS('=Object', '{value: #}', _makeCallbackMethod3(_callAttributeChanged)));
-
- var baseProto = JS('=Object', '#.prototype', baseConstructor);
- var proto = JS('=Object', 'Object.create(#, #)', baseProto, properties);
-
- setNativeSubclassDispatchRecord(proto, interceptor);
-
- var opts = JS('=Object', '{prototype: #}', proto);
-
- if (extendsTagName != null) {
- JS('=Object', '#.extends = #', opts, extendsTagName);
- }
-
- return JS(
- 'JavaScriptFunction', '#.registerElement(#, #)', document, tag, opts);
-}
-
-//// Called by Element.created to do validation & initialization.
-void _initializeCustomElement(Element e) {
- // TODO(blois): Add validation that this is only in response to an upgrade.
-}
-
-/// Dart2JS implementation of ElementUpgrader
-class _JSElementUpgrader implements ElementUpgrader {
- var _interceptor;
- var _constructor;
- var _nativeType;
-
- _JSElementUpgrader(Document document, Type type, String? extendsTag) {
- var interceptorClass = findInterceptorConstructorForType(type);
- if (interceptorClass == null) {
- throw new ArgumentError(type);
- }
-
- _constructor = findConstructorForNativeSubclassType(type, 'created');
- if (_constructor == null) {
- throw new ArgumentError("$type has no constructor called 'created'");
- }
-
- // Workaround for 13190- use an article element to ensure that HTMLElement's
- // interceptor is resolved correctly.
- getNativeInterceptor(new Element.tag('article'));
-
- var baseClassName = findDispatchTagForInterceptorClass(interceptorClass);
- if (baseClassName == null) {
- throw new ArgumentError(type);
- }
-
- if (extendsTag == null) {
- if (baseClassName != 'HTMLElement') {
- throw new UnsupportedError('Class must provide extendsTag if base '
- 'native class is not HtmlElement');
- }
- _nativeType = HtmlElement;
- } else {
- var element = document.createElement(extendsTag);
- _checkExtendsNativeClassOrTemplate(element, extendsTag, baseClassName);
- _nativeType = element.runtimeType;
- }
-
- _interceptor = JS('=Object', '#.prototype', interceptorClass);
- }
-
- Element upgrade(Element element) {
- // Only exact type matches are supported- cannot be a subclass.
- if (element.runtimeType != _nativeType) {
- // Some browsers may represent non-upgraded elements <x-foo> as
- // UnknownElement and not a plain HtmlElement.
- if (_nativeType != HtmlElement || element.runtimeType != UnknownElement) {
- throw new ArgumentError('element is not subclass of $_nativeType');
- }
- }
-
- setNativeSubclassDispatchRecord(element, _interceptor);
- JS('', '#(#)', _constructor, element);
- return element;
- }
-}
diff --git a/tools/dom/nnbd_src/dart2js_DOMImplementation.dart b/tools/dom/nnbd_src/dart2js_DOMImplementation.dart
deleted file mode 100644
index 8016f6e..0000000
--- a/tools/dom/nnbd_src/dart2js_DOMImplementation.dart
+++ /dev/null
@@ -1,136 +0,0 @@
-// Copyright (c) 2012, 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.
-
-part of html;
-
-// TODO(vsm): Unify with Dartium version.
-class _DOMWindowCrossFrame implements WindowBase {
- // Private window. Note, this is a window in another frame, so it
- // cannot be typed as "Window" as its prototype is not patched
- // properly. Its fields and methods can only be accessed via JavaScript.
- final _window;
-
- // Fields.
- HistoryBase get history =>
- _HistoryCrossFrame._createSafe(JS('HistoryBase', '#.history', _window));
- LocationBase get location => _LocationCrossFrame._createSafe(
- JS('LocationBase', '#.location', _window));
-
- // TODO(vsm): Add frames to navigate subframes. See 2312.
-
- bool get closed => JS('bool', '#.closed', _window);
-
- WindowBase get opener => _createSafe(JS('WindowBase', '#.opener', _window));
-
- WindowBase get parent => _createSafe(JS('WindowBase', '#.parent', _window));
-
- WindowBase get top => _createSafe(JS('WindowBase', '#.top', _window));
-
- // Methods.
- void close() => JS('void', '#.close()', _window);
-
- void postMessage(var message, String targetOrigin, [List? messagePorts]) {
- if (messagePorts == null) {
- JS('void', '#.postMessage(#,#)', _window,
- convertDartToNative_SerializedScriptValue(message), targetOrigin);
- } else {
- JS(
- 'void',
- '#.postMessage(#,#,#)',
- _window,
- convertDartToNative_SerializedScriptValue(message),
- targetOrigin,
- messagePorts);
- }
- }
-
- // Implementation support.
- _DOMWindowCrossFrame(this._window);
-
- static WindowBase _createSafe(w) {
- if (identical(w, window)) {
- return w;
- } else {
- // TODO(vsm): Cache or implement equality.
- registerGlobalObject(w);
- return new _DOMWindowCrossFrame(w);
- }
- }
-
- // TODO(efortuna): Remove this method. dartbug.com/16814
- Events get on => throw new UnsupportedError(
- 'You can only attach EventListeners to your own window.');
- // TODO(efortuna): Remove this method. dartbug.com/16814
- void _addEventListener(String? type, EventListener? listener,
- [bool? useCapture]) =>
- throw new UnsupportedError(
- 'You can only attach EventListeners to your own window.');
- // TODO(efortuna): Remove this method. dartbug.com/16814
- void addEventListener(String type, EventListener? listener,
- [bool? useCapture]) =>
- throw new UnsupportedError(
- 'You can only attach EventListeners to your own window.');
- // TODO(efortuna): Remove this method. dartbug.com/16814
- bool dispatchEvent(Event event) => throw new UnsupportedError(
- 'You can only attach EventListeners to your own window.');
- // TODO(efortuna): Remove this method. dartbug.com/16814
- void _removeEventListener(String? type, EventListener? listener,
- [bool? useCapture]) =>
- throw new UnsupportedError(
- 'You can only attach EventListeners to your own window.');
- // TODO(efortuna): Remove this method. dartbug.com/16814
- void removeEventListener(String type, EventListener? listener,
- [bool? useCapture]) =>
- throw new UnsupportedError(
- 'You can only attach EventListeners to your own window.');
-}
-
-class _LocationCrossFrame implements LocationBase {
- // Private location. Note, this is a location object in another frame, so it
- // cannot be typed as "Location" as its prototype is not patched
- // properly. Its fields and methods can only be accessed via JavaScript.
- var _location;
-
- set href(String val) => _setHref(_location, val);
- static void _setHref(location, val) {
- JS('void', '#.href = #', location, val);
- }
-
- // Implementation support.
- _LocationCrossFrame(this._location);
-
- static LocationBase _createSafe(location) {
- if (identical(location, window.location)) {
- return location;
- } else {
- // TODO(vsm): Cache or implement equality.
- return new _LocationCrossFrame(location);
- }
- }
-}
-
-class _HistoryCrossFrame implements HistoryBase {
- // Private history. Note, this is a history object in another frame, so it
- // cannot be typed as "History" as its prototype is not patched
- // properly. Its fields and methods can only be accessed via JavaScript.
- var _history;
-
- void back() => JS('void', '#.back()', _history);
-
- void forward() => JS('void', '#.forward()', _history);
-
- void go(int distance) => JS('void', '#.go(#)', _history, distance);
-
- // Implementation support.
- _HistoryCrossFrame(this._history);
-
- static HistoryBase _createSafe(h) {
- if (identical(h, window.history)) {
- return h;
- } else {
- // TODO(vsm): Cache or implement equality.
- return new _HistoryCrossFrame(h);
- }
- }
-}
diff --git a/tools/dom/nnbd_src/dart2js_KeyEvent.dart b/tools/dom/nnbd_src/dart2js_KeyEvent.dart
deleted file mode 100644
index 422c7ad..0000000
--- a/tools/dom/nnbd_src/dart2js_KeyEvent.dart
+++ /dev/null
@@ -1,243 +0,0 @@
-/**
- * A custom KeyboardEvent that attempts to eliminate cross-browser
- * inconsistencies, and also provide both keyCode and charCode information
- * for all key events (when such information can be determined).
- *
- * KeyEvent tries to provide a higher level, more polished keyboard event
- * information on top of the "raw" [KeyboardEvent].
- *
- * The mechanics of using KeyEvents is a little different from the underlying
- * [KeyboardEvent]. To use KeyEvents, you need to create a stream and then add
- * KeyEvents to the stream, rather than using the [EventTarget.dispatchEvent].
- * Here's an example usage:
- *
- * // Initialize a stream for the KeyEvents:
- * var stream = KeyEvent.keyPressEvent.forTarget(document.body);
- * // Start listening to the stream of KeyEvents.
- * stream.listen((keyEvent) =>
- * window.console.log('KeyPress event detected ${keyEvent.charCode}'));
- * ...
- * // Add a new KeyEvent of someone pressing the 'A' key to the stream so
- * // listeners can know a KeyEvent happened.
- * stream.add(new KeyEvent('keypress', keyCode: 65, charCode: 97));
- *
- * This class is very much a work in progress, and we'd love to get information
- * on how we can make this class work with as many international keyboards as
- * possible. Bugs welcome!
- */
-class KeyEvent extends _WrappedEvent implements KeyboardEvent {
- /** The parent KeyboardEvent that this KeyEvent is wrapping and "fixing". */
- KeyboardEvent _parent;
-
- /** The "fixed" value of whether the alt key is being pressed. */
- bool _shadowAltKey;
-
- /** Calculated value of what the estimated charCode is for this event. */
- int _shadowCharCode;
-
- /** Calculated value of what the estimated keyCode is for this event. */
- int _shadowKeyCode;
-
- /** Calculated value of what the estimated keyCode is for this event. */
- int get keyCode => _shadowKeyCode;
-
- /** Calculated value of what the estimated charCode is for this event. */
- int get charCode => this.type == 'keypress' ? _shadowCharCode : 0;
-
- /** Calculated value of whether the alt key is pressed is for this event. */
- bool get altKey => _shadowAltKey;
-
- /** Calculated value of what the estimated keyCode is for this event. */
- int get which => keyCode;
-
- /** Accessor to the underlying keyCode value is the parent event. */
- int get _realKeyCode => JS('int', '#.keyCode', _parent);
-
- /** Accessor to the underlying charCode value is the parent event. */
- int get _realCharCode => JS('int', '#.charCode', _parent);
-
- /** Accessor to the underlying altKey value is the parent event. */
- bool get _realAltKey => JS('bool', '#.altKey', _parent);
-
- /** Shadows on top of the parent's currentTarget. */
- EventTarget? _currentTarget;
-
- InputDeviceCapabilities? get sourceCapabilities =>
- JS('InputDeviceCapabilities', '#.sourceCapabilities', this);
-
- /**
- * The value we want to use for this object's dispatch. Created here so it is
- * only invoked once.
- */
- static final _keyboardEventDispatchRecord = _makeRecord();
-
- /** Helper to statically create the dispatch record. */
- static _makeRecord() {
- var interceptor = JS_INTERCEPTOR_CONSTANT(KeyboardEvent);
- return makeLeafDispatchRecord(interceptor);
- }
-
- /** Construct a KeyEvent with [parent] as the event we're emulating. */
- KeyEvent.wrap(KeyboardEvent parent)
- : _parent = parent,
- _shadowAltKey = false,
- _shadowCharCode = 0,
- _shadowKeyCode = 0,
- super(parent) {
- _parent = parent;
- _shadowAltKey = _realAltKey;
- _shadowCharCode = _realCharCode;
- _shadowKeyCode = _realKeyCode;
- _currentTarget = _parent.currentTarget;
- }
-
- /** Programmatically create a new KeyEvent (and KeyboardEvent). */
- factory KeyEvent(String type,
- {Window? view,
- bool canBubble: true,
- bool cancelable: true,
- int keyCode: 0,
- int charCode: 0,
- int location: 1,
- bool ctrlKey: false,
- bool altKey: false,
- bool shiftKey: false,
- bool metaKey: false,
- EventTarget? currentTarget}) {
- if (view == null) {
- view = window;
- }
-
- dynamic eventObj;
-
- // Currently this works on everything but Safari. Safari throws an
- // "Attempting to change access mechanism for an unconfigurable property"
- // TypeError when trying to do the Object.defineProperty hack, so we avoid
- // this branch if possible.
- // Also, if we want this branch to work in FF, we also need to modify
- // _initKeyboardEvent to also take charCode and keyCode values to
- // initialize initKeyEvent.
-
- eventObj = new Event.eventType('KeyboardEvent', type,
- canBubble: canBubble, cancelable: cancelable);
-
- // Chromium Hack
- JS(
- 'void',
- "Object.defineProperty(#, 'keyCode', {"
- " get : function() { return this.keyCodeVal; } })",
- eventObj);
- JS(
- 'void',
- "Object.defineProperty(#, 'which', {"
- " get : function() { return this.keyCodeVal; } })",
- eventObj);
- JS(
- 'void',
- "Object.defineProperty(#, 'charCode', {"
- " get : function() { return this.charCodeVal; } })",
- eventObj);
-
- var keyIdentifier = _convertToHexString(charCode, keyCode);
- eventObj._initKeyboardEvent(type, canBubble, cancelable, view,
- keyIdentifier, location, ctrlKey, altKey, shiftKey, metaKey);
- JS('void', '#.keyCodeVal = #', eventObj, keyCode);
- JS('void', '#.charCodeVal = #', eventObj, charCode);
-
- // Tell dart2js that it smells like a KeyboardEvent!
- setDispatchProperty(eventObj, _keyboardEventDispatchRecord);
-
- var keyEvent = new KeyEvent.wrap(eventObj);
- if (keyEvent._currentTarget == null) {
- keyEvent._currentTarget = currentTarget == null ? window : currentTarget;
- }
- return keyEvent;
- }
-
- // Currently known to work on all browsers but IE.
- static bool get canUseDispatchEvent => JS(
- 'bool',
- '(typeof document.body.dispatchEvent == "function")'
- '&& document.body.dispatchEvent.length > 0');
-
- /** The currently registered target for this event. */
- EventTarget? get currentTarget => _currentTarget;
-
- // This is an experimental method to be sure.
- static String _convertToHexString(int charCode, int keyCode) {
- if (charCode != -1) {
- var hex = charCode.toRadixString(16); // Convert to hexadecimal.
- StringBuffer sb = new StringBuffer('U+');
- for (int i = 0; i < 4 - hex.length; i++) sb.write('0');
- sb.write(hex);
- return sb.toString();
- } else {
- return KeyCode._convertKeyCodeToKeyName(keyCode);
- }
- }
-
- // TODO(efortuna): If KeyEvent is sufficiently successful that we want to make
- // it the default keyboard event handling, move these methods over to Element.
- /** Accessor to provide a stream of KeyEvents on the desired target. */
- static EventStreamProvider<KeyEvent> keyDownEvent =
- new _KeyboardEventHandler('keydown');
- /** Accessor to provide a stream of KeyEvents on the desired target. */
- static EventStreamProvider<KeyEvent> keyUpEvent =
- new _KeyboardEventHandler('keyup');
- /** Accessor to provide a stream of KeyEvents on the desired target. */
- static EventStreamProvider<KeyEvent> keyPressEvent =
- new _KeyboardEventHandler('keypress');
-
- String get code => _parent.code;
- /** True if the ctrl key is pressed during this event. */
- bool get ctrlKey => _parent.ctrlKey;
- int get detail => _parent.detail;
- bool get isComposing => _parent.isComposing;
- String get key => _parent.key;
- /**
- * Accessor to the part of the keyboard that the key was pressed from (one of
- * KeyLocation.STANDARD, KeyLocation.RIGHT, KeyLocation.LEFT,
- * KeyLocation.NUMPAD, KeyLocation.MOBILE, KeyLocation.JOYSTICK).
- */
- int get location => _parent.location;
- /** True if the Meta (or Mac command) key is pressed during this event. */
- bool get metaKey => _parent.metaKey;
- /** True if the shift key was pressed during this event. */
- bool get shiftKey => _parent.shiftKey;
- WindowBase? get view => _parent.view;
- void _initUIEvent(
- String type, bool canBubble, bool cancelable, Window? view, int detail) {
- throw new UnsupportedError("Cannot initialize a UI Event from a KeyEvent.");
- }
-
- String get _shadowKeyIdentifier => JS('String', '#.keyIdentifier', _parent);
-
- int get _charCode => charCode;
- int get _keyCode => keyCode;
- int get _which => which;
-
- String get _keyIdentifier {
- throw new UnsupportedError("keyIdentifier is unsupported.");
- }
-
- void _initKeyboardEvent(
- String type,
- bool canBubble,
- bool cancelable,
- Window? view,
- String keyIdentifier,
- int? location,
- bool ctrlKey,
- bool altKey,
- bool shiftKey,
- bool metaKey) {
- throw new UnsupportedError(
- "Cannot initialize a KeyboardEvent from a KeyEvent.");
- }
-
- bool getModifierState(String keyArgument) => throw new UnimplementedError();
-
- bool get repeat => throw new UnimplementedError();
- bool get isComposed => throw new UnimplementedError();
- dynamic get _get_view => throw new UnimplementedError();
-}
diff --git a/tools/dom/nnbd_src/dart2js_Platform.dart b/tools/dom/nnbd_src/dart2js_Platform.dart
deleted file mode 100644
index 995f4f4..0000000
--- a/tools/dom/nnbd_src/dart2js_Platform.dart
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright (c) 2013, 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.
-
-part of html;
-
-class Platform {
- /**
- * Returns true if dart:typed_data types are supported on this
- * browser. If false, using these types will generate a runtime
- * error.
- */
- static final bool supportsTypedData = JS('bool', '!!(window.ArrayBuffer)');
-
- /**
- * Returns true if SIMD types in dart:typed_data types are supported
- * on this browser. If false, using these types will generate a runtime
- * error.
- */
- static final supportsSimd = false;
-}
diff --git a/tools/dom/nnbd_src/dart2js_TypedArrayFactoryProvider.dart b/tools/dom/nnbd_src/dart2js_TypedArrayFactoryProvider.dart
deleted file mode 100644
index 58112a7..0000000
--- a/tools/dom/nnbd_src/dart2js_TypedArrayFactoryProvider.dart
+++ /dev/null
@@ -1,154 +0,0 @@
-// Copyright (c) 2012, 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.
-
-part of html;
-
-class _TypedArrayFactoryProvider {
- static ByteData createByteData(int length) => _B8(length);
- static ByteData createByteData_fromBuffer(ByteBuffer buffer,
- [int byteOffset = 0, int length]) {
- if (length == null) return _B8_2(buffer, byteOffset);
- return _B8_3(buffer, byteOffset, length);
- }
-
- static Float32List createFloat32List(int length) => _F32(length);
- static Float32List createFloat32List_fromList(List<num> list) =>
- _F32(ensureNative(list));
- static Float32List createFloat32List_fromBuffer(ByteBuffer buffer,
- [int byteOffset = 0, int length]) {
- if (length == null) return _F32_2(buffer, byteOffset);
- return _F32_3(buffer, byteOffset, length);
- }
-
- static Float64List createFloat64List(int length) => _F64(length);
- static Float64List createFloat64List_fromList(List<num> list) =>
- _F64(ensureNative(list));
- static Float64List createFloat64List_fromBuffer(ByteBuffer buffer,
- [int byteOffset = 0, int length]) {
- if (length == null) return _F64_2(buffer, byteOffset);
- return _F64_3(buffer, byteOffset, length);
- }
-
- static Int8List createInt8List(int length) => _I8(length);
- static Int8List createInt8List_fromList(List<num> list) =>
- _I8(ensureNative(list));
- static Int8List createInt8List_fromBuffer(ByteBuffer buffer,
- [int byteOffset = 0, int length]) {
- if (length == null) return _I8_2(buffer, byteOffset);
- return _I8_3(buffer, byteOffset, length);
- }
-
- static Int16List createInt16List(int length) => _I16(length);
- static Int16List createInt16List_fromList(List<num> list) =>
- _I16(ensureNative(list));
- static Int16List createInt16List_fromBuffer(ByteBuffer buffer,
- [int byteOffset = 0, int length]) {
- if (length == null) return _I16_2(buffer, byteOffset);
- return _I16_3(buffer, byteOffset, length);
- }
-
- static Int32List createInt32List(int length) => _I32(length);
- static Int32List createInt32List_fromList(List<num> list) =>
- _I32(ensureNative(list));
- static Int32List createInt32List_fromBuffer(ByteBuffer buffer,
- [int byteOffset = 0, int length]) {
- if (length == null) return _I32_2(buffer, byteOffset);
- return _I32_3(buffer, byteOffset, length);
- }
-
- static Uint8List createUint8List(int length) => _U8(length);
- static Uint8List createUint8List_fromList(List<num> list) =>
- _U8(ensureNative(list));
- static Uint8List createUint8List_fromBuffer(ByteBuffer buffer,
- [int byteOffset = 0, int length]) {
- if (length == null) return _U8_2(buffer, byteOffset);
- return _U8_3(buffer, byteOffset, length);
- }
-
- static Uint16List createUint16List(int length) => _U16(length);
- static Uint16List createUint16List_fromList(List<num> list) =>
- _U16(ensureNative(list));
- static Uint16List createUint16List_fromBuffer(ByteBuffer buffer,
- [int byteOffset = 0, int length]) {
- if (length == null) return _U16_2(buffer, byteOffset);
- return _U16_3(buffer, byteOffset, length);
- }
-
- static Uint32List createUint32List(int length) => _U32(length);
- static Uint32List createUint32List_fromList(List<num> list) =>
- _U32(ensureNative(list));
- static Uint32List createUint32List_fromBuffer(ByteBuffer buffer,
- [int byteOffset = 0, int length]) {
- if (length == null) return _U32_2(buffer, byteOffset);
- return _U32_3(buffer, byteOffset, length);
- }
-
- static Uint8ClampedList createUint8ClampedList(int length) => _U8C(length);
- static Uint8ClampedList createUint8ClampedList_fromList(List<num> list) =>
- _U8C(ensureNative(list));
- static Uint8ClampedList createUint8ClampedList_fromBuffer(ByteBuffer buffer,
- [int byteOffset = 0, int length]) {
- if (length == null) return _U8C_2(buffer, byteOffset);
- return _U8C_3(buffer, byteOffset, length);
- }
-
- static ByteData _B8(arg) =>
- JS('ByteData', 'new DataView(new ArrayBuffer(#))', arg);
- static Float32List _F32(arg) => JS('Float32List', 'new Float32Array(#)', arg);
- static Float64List _F64(arg) => JS('Float64List', 'new Float64Array(#)', arg);
- static Int8List _I8(arg) => JS('Int8List', 'new Int8Array(#)', arg);
- static Int16List _I16(arg) => JS('Int16List', 'new Int16Array(#)', arg);
- static Int32List _I32(arg) => JS('Int32List', 'new Int32Array(#)', arg);
- static Uint8List _U8(arg) => JS('Uint8List', 'new Uint8Array(#)', arg);
- static Uint16List _U16(arg) => JS('Uint16List', 'new Uint16Array(#)', arg);
- static Uint32List _U32(arg) => JS('Uint32List', 'new Uint32Array(#)', arg);
- static Uint8ClampedList _U8C(arg) =>
- JS('Uint8ClampedList', 'new Uint8ClampedArray(#)', arg);
-
- static ByteData _B8_2(arg1, arg2) =>
- JS('ByteData', 'new DataView(#, #)', arg1, arg2);
- static Float32List _F32_2(arg1, arg2) =>
- JS('Float32List', 'new Float32Array(#, #)', arg1, arg2);
- static Float64List _F64_2(arg1, arg2) =>
- JS('Float64List', 'new Float64Array(#, #)', arg1, arg2);
- static Int8List _I8_2(arg1, arg2) =>
- JS('Int8List', 'new Int8Array(#, #)', arg1, arg2);
- static Int16List _I16_2(arg1, arg2) =>
- JS('Int16List', 'new Int16Array(#, #)', arg1, arg2);
- static Int32List _I32_2(arg1, arg2) =>
- JS('Int32List', 'new Int32Array(#, #)', arg1, arg2);
- static Uint8List _U8_2(arg1, arg2) =>
- JS('Uint8List', 'new Uint8Array(#, #)', arg1, arg2);
- static Uint16List _U16_2(arg1, arg2) =>
- JS('Uint16List', 'new Uint16Array(#, #)', arg1, arg2);
- static Uint32List _U32_2(arg1, arg2) =>
- JS('Uint32List', 'new Uint32Array(#, #)', arg1, arg2);
- static Uint8ClampedList _U8C_2(arg1, arg2) =>
- JS('Uint8ClampedList', 'new Uint8ClampedArray(#, #)', arg1, arg2);
-
- static ByteData _B8_3(arg1, arg2, arg3) =>
- JS('ByteData', 'new DataView(#, #, #)', arg1, arg2, arg3);
- static Float32List _F32_3(arg1, arg2, arg3) =>
- JS('Float32List', 'new Float32Array(#, #, #)', arg1, arg2, arg3);
- static Float64List _F64_3(arg1, arg2, arg3) =>
- JS('Float64List', 'new Float64Array(#, #, #)', arg1, arg2, arg3);
- static Int8List _I8_3(arg1, arg2, arg3) =>
- JS('Int8List', 'new Int8Array(#, #, #)', arg1, arg2, arg3);
- static Int16List _I16_3(arg1, arg2, arg3) =>
- JS('Int16List', 'new Int16Array(#, #, #)', arg1, arg2, arg3);
- static Int32List _I32_3(arg1, arg2, arg3) =>
- JS('Int32List', 'new Int32Array(#, #, #)', arg1, arg2, arg3);
- static Uint8List _U8_3(arg1, arg2, arg3) =>
- JS('Uint8List', 'new Uint8Array(#, #, #)', arg1, arg2, arg3);
- static Uint16List _U16_3(arg1, arg2, arg3) =>
- JS('Uint16List', 'new Uint16Array(#, #, #)', arg1, arg2, arg3);
- static Uint32List _U32_3(arg1, arg2, arg3) =>
- JS('Uint32List', 'new Uint32Array(#, #, #)', arg1, arg2, arg3);
- static Uint8ClampedList _U8C_3(arg1, arg2, arg3) => JS(
- 'Uint8ClampedList', 'new Uint8ClampedArray(#, #, #)', arg1, arg2, arg3);
-
- // Ensures that [list] is a JavaScript Array or a typed array. If necessary,
- // copies the list.
- static ensureNative(List list) => list; // TODO: make sure.
-}
diff --git a/tools/dom/nnbd_src/dart2js_WrappedEvent.dart b/tools/dom/nnbd_src/dart2js_WrappedEvent.dart
deleted file mode 100644
index dff9e38..0000000
--- a/tools/dom/nnbd_src/dart2js_WrappedEvent.dart
+++ /dev/null
@@ -1,90 +0,0 @@
-// Copyright (c) 2013, 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.
-
-part of dart.html;
-
-/**
- * Helper class to implement custom events which wrap DOM events.
- */
-class _WrappedEvent implements Event {
- final Event wrapped;
-
- /** The CSS selector involved with event delegation. */
- String? _selector;
-
- _WrappedEvent(this.wrapped);
-
- bool get bubbles => wrapped.bubbles;
-
- bool get cancelable => wrapped.cancelable;
-
- bool get composed => wrapped.composed;
-
- EventTarget? get currentTarget => wrapped.currentTarget;
-
- bool get defaultPrevented => wrapped.defaultPrevented;
-
- int get eventPhase => wrapped.eventPhase;
-
- bool get isTrusted => wrapped.isTrusted;
-
- EventTarget? get target => wrapped.target;
-
- double get timeStamp => wrapped.timeStamp as double;
-
- String get type => wrapped.type;
-
- void _initEvent(String type, [bool? bubbles, bool? cancelable]) {
- throw new UnsupportedError('Cannot initialize this Event.');
- }
-
- void preventDefault() {
- wrapped.preventDefault();
- }
-
- void stopImmediatePropagation() {
- wrapped.stopImmediatePropagation();
- }
-
- void stopPropagation() {
- wrapped.stopPropagation();
- }
-
- List<EventTarget> composedPath() => wrapped.composedPath();
-
- /**
- * A pointer to the element whose CSS selector matched within which an event
- * was fired. If this Event was not associated with any Event delegation,
- * accessing this value will throw an [UnsupportedError].
- */
- Element get matchingTarget {
- if (_selector == null) {
- throw new UnsupportedError('Cannot call matchingTarget if this Event did'
- ' not arise as a result of event delegation.');
- }
- Element? currentTarget = this.currentTarget as Element?;
- Element? target = this.target as Element?;
- do {
- if (target!.matches(_selector!)) return target;
- target = target.parent;
- } while (target != null && target != currentTarget!.parent);
- throw new StateError('No selector matched for populating matchedTarget.');
- }
-
- /**
- * This event's path, taking into account shadow DOM.
- *
- * ## Other resources
- *
- * * [Shadow DOM extensions to
- * Event](http://w3c.github.io/webcomponents/spec/shadow/#extensions-to-event)
- * from W3C.
- */
- // https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#extensions-to-event
- List<Node> get path => wrapped.path as List<Node>;
-
- dynamic get _get_currentTarget => wrapped._get_currentTarget;
-
- dynamic get _get_target => wrapped._get_target;
-}
diff --git a/tools/dom/nnbd_src/native_DOMImplementation.dart b/tools/dom/nnbd_src/native_DOMImplementation.dart
deleted file mode 100644
index 79f5d04..0000000
--- a/tools/dom/nnbd_src/native_DOMImplementation.dart
+++ /dev/null
@@ -1,1399 +0,0 @@
-// Copyright (c) 2012, 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.
-
-part of html;
-
-class _Property {
- _Property(this.name)
- : _hasValue = false,
- writable = false,
- isMethod = false,
- isOwn = true,
- wasThrown = false;
-
- bool get hasValue => _hasValue;
- get value => _value;
- set value(v) {
- _value = v;
- _hasValue = true;
- }
-
- final String name;
- Function setter;
- Function getter;
- var _value;
- bool _hasValue;
- bool writable;
- bool isMethod;
- bool isOwn;
- bool wasThrown;
-}
-
-/**
- * Manager for navigating between libraries from the devtools console.
- */
-class _LibraryManager {
- /**
- * Current active library
- */
- static var _currentLibrary;
- static var _validCache = false;
-
- static List<Uri> _libraryUris;
-
- // List of all maps to check to determine if there is an exact match.
- static Map<String, List<Uri>> _fastPaths;
-
- static void _addFastPath(String key, Uri uri) {
- _fastPaths.putIfAbsent(key, () => <Uri>[]).add(uri);
- }
-
- static cache() {
- if (_validCache) return;
- _validCache = true;
- _libraryUris = <Uri>[];
- _fastPaths = new Map<String, List<Uri>>();
- var system = currentMirrorSystem();
- system.libraries.forEach((uri, library) {
- _libraryUris.add(uri);
- _addFastPath(uri.toString(), uri);
- _addFastPath(MirrorSystem.getName(library.simpleName), uri);
- });
- }
-
- static String get currentLibrary {
- if (_currentLibrary == null) {
- _currentLibrary =
- currentMirrorSystem().isolate.rootLibrary.uri.toString();
- }
- return _currentLibrary;
- }
-
- /**
- * Find libraries matching a given name.
- *
- * Uses heuristics to only return a single match when the user intent is
- * generally unambiguous.
- */
- static List<Uri> findMatches(String name) {
- cache();
- var nameAsFile = name.endsWith('.dart') ? name : '${name}.dart';
- // Perfect match first.
- var fastPatchMatches = _fastPaths[name];
- if (fastPatchMatches != null) {
- return fastPatchMatches.toList();
- }
-
- // Exact match for file path.
- var matches = new LinkedHashSet<Uri>();
- for (var uri in _libraryUris) {
- if (uri.path == name || uri.path == nameAsFile) matches.add(uri);
- }
- if (matches.isNotEmpty) return matches.toList();
-
- // Exact match for file name.
- if (name != nameAsFile) {
- for (var uri in _libraryUris) {
- if (uri.pathSegments.isNotEmpty &&
- (uri.pathSegments.last == nameAsFile)) {
- matches.add(uri);
- }
- }
- if (matches.isNotEmpty) return matches.toList();
- }
-
- for (var uri in _libraryUris) {
- if (uri.pathSegments.isNotEmpty && (uri.pathSegments.last == name)) {
- matches.add(uri);
- }
- }
- if (matches.isNotEmpty) return matches.toList();
-
- // Partial match on path.
- for (var uri in _libraryUris) {
- if (uri.path.contains(name)) {
- matches.add(uri);
- }
- }
- if (matches.isNotEmpty) return matches.toList();
-
- // Partial match on entire uri.
- for (var uri in _libraryUris) {
- if (uri.toString().contains(name)) {
- matches.add(uri);
- }
- }
-
- if (matches.isNotEmpty) return matches.toList();
-
- // Partial match on entire uri ignoring case.
- name = name.toLowerCase();
- for (var uri in _libraryUris) {
- if (uri.toString().toLowerCase().contains(name)) {
- matches.add(uri);
- }
- }
- return matches.toList();
- }
-
- static setLibrary([String? name]) {
- // Bust cache in case library list has changed. Ideally we would listen for
- // when libraries are loaded and invalidate based on that.
- _validCache = false;
- cache();
- if (name == null) {
- window.console
- ..group("Current library: $_currentLibrary")
- ..groupCollapsed("All libraries:");
- _listLibraries();
- window.console..groupEnd()..groupEnd();
- return;
- }
- var matches = findMatches(name);
- if (matches.length != 1) {
- if (matches.length > 1) {
- window.console.warn("Ambiguous library name: $name");
- }
- showMatches(name, matches);
- return;
- }
- _currentLibrary = matches.first.toString();
- window.console.log("Set library to $_currentLibrary");
- }
-
- static getLibrary() {
- return currentLibrary;
- }
-
- static List<Uri> _sortUris(Iterable<Uri> uris) {
- return (uris.toList())
- ..sort((Uri a, Uri b) {
- if (a.scheme != b.scheme) {
- if (a.scheme == 'dart') return -1;
- if (b.scheme == 'dart') return 1;
- return a.scheme.compareTo(b.scheme);
- }
- return a.toString().compareTo(b.toString());
- });
- }
-
- static void listLibraries() {
- _validCache = false;
- cache();
- _listLibraries();
- }
-
- static void _listLibraries() {
- window.console.log(_sortUris(_libraryUris).join("\n"));
- }
-
- // Workaround to allow calling console.log with an arbitrary number of
- // arguments.
- static void _log(List<String> args) {
- js.JsNative.callMethod(window.console, 'log', args);
- }
-
- static showMatches(String key, Iterable<Uri> uris) {
- var boldPairs = [];
- var sb = new StringBuffer();
- if (uris.isEmpty) {
- window.console.group("All libraries:");
- _listLibraries();
- window.console
- ..groupEnd()
- ..error("No library names or URIs match '$key'");
- return;
- }
- sb.write("${uris.length} matches\n");
- var lowerCaseKey = key.toLowerCase();
- for (var uri in uris) {
- var txt = uri.toString();
- int index = txt.toLowerCase().indexOf(lowerCaseKey);
- if (index != -1) {
- // %c enables styling console log messages with css
- // specified at the end of the console.
- sb..write(txt.substring(0, index))..write('%c');
- var matchEnd = index + key.length;
- sb
- ..write(txt.substring(index, matchEnd))
- ..write('%c')
- ..write(txt.substring(matchEnd))
- ..write('\n');
- boldPairs..add('font-weight: bold')..add('font-weight: normal');
- }
- }
- _log([sb.toString()]..addAll(boldPairs));
- }
-}
-
-class _ConsoleVariables {
- Map<String, Object> _data = new Map<String, Object>();
-
- /**
- * Forward member accesses to the backing JavaScript object.
- */
- noSuchMethod(Invocation invocation) {
- String member = MirrorSystem.getName(invocation.memberName);
- if (invocation.isGetter) {
- return _data[member];
- } else if (invocation.isSetter) {
- assert(member.endsWith('='));
- member = member.substring(0, member.length - 1);
- _data[member] = invocation.positionalArguments[0];
- } else {
- return Function.apply(_data[member], invocation.positionalArguments,
- invocation.namedArguments);
- }
- }
-
- void clear() => _data.clear();
-
- /**
- * List all variables currently defined.
- */
- List variables() => _data.keys.toList();
-
- void setVariable(String name, value) {
- _data[name] = value;
- }
-}
-
-/**
- * Base class for invocation trampolines used to closurize methods, getters
- * and setters.
- */
-abstract class _Trampoline implements Function {
- final ObjectMirror _receiver;
- final MethodMirror _methodMirror;
- final Symbol _selector;
-
- _Trampoline(this._receiver, this._methodMirror, this._selector);
-}
-
-class _MethodTrampoline extends _Trampoline {
- _MethodTrampoline(
- ObjectMirror receiver, MethodMirror methodMirror, Symbol selector)
- : super(receiver, methodMirror, selector);
-
- noSuchMethod(Invocation msg) {
- if (msg.memberName != #call) return super.noSuchMethod(msg);
- return _receiver
- .invoke(_selector, msg.positionalArguments, msg.namedArguments)
- .reflectee;
- }
-}
-
-/**
- * Invocation trampoline class used to closurize getters.
- */
-class _GetterTrampoline extends _Trampoline {
- _GetterTrampoline(
- ObjectMirror receiver, MethodMirror methodMirror, Symbol selector)
- : super(receiver, methodMirror, selector);
-
- call() => _receiver.getField(_selector).reflectee;
-}
-
-/**
- * Invocation trampoline class used to closurize setters.
- */
-class _SetterTrampoline extends _Trampoline {
- _SetterTrampoline(
- ObjectMirror receiver, MethodMirror methodMirror, Symbol selector)
- : super(receiver, methodMirror, selector);
-
- call(value) {
- _receiver.setField(_selector, value);
- }
-}
-
-class _Utils {
- static double dateTimeToDouble(DateTime dateTime) =>
- dateTime.millisecondsSinceEpoch.toDouble();
- static DateTime doubleToDateTime(double dateTime) {
- try {
- return new DateTime.fromMillisecondsSinceEpoch(dateTime.toInt());
- } catch (_) {
- // TODO(antonnm): treat exceptions properly in bindings and
- // find out how to treat NaNs.
- return null;
- }
- }
-
- static List convertToList(List list) {
- // FIXME: [possible optimization]: do not copy the array if Dart_IsArray is fine w/ it.
- final length = list.length;
- List result = new List(length);
- result.setRange(0, length, list);
- return result;
- }
-
- static List convertMapToList(Map map) {
- List result = [];
- map.forEach((k, v) => result.addAll([k, v]));
- return result;
- }
-
- static int convertCanvasElementGetContextMap(Map map) {
- int result = 0;
- if (map['alpha'] == true) result |= 0x01;
- if (map['depth'] == true) result |= 0x02;
- if (map['stencil'] == true) result |= 0x4;
- if (map['antialias'] == true) result |= 0x08;
- if (map['premultipliedAlpha'] == true) result |= 0x10;
- if (map['preserveDrawingBuffer'] == true) result |= 0x20;
-
- return result;
- }
-
- static void populateMap(Map result, List list) {
- for (int i = 0; i < list.length; i += 2) {
- result[list[i]] = list[i + 1];
- }
- }
-
- static bool isMap(obj) => obj is Map;
-
- static List toListIfIterable(obj) => obj is Iterable ? obj.toList() : null;
-
- static Map createMap() => {};
-
- static parseJson(String jsonSource) =>
- const JsonDecoder().convert(jsonSource);
-
- static String getLibraryUrl() => _LibraryManager.currentLibrary;
-
- static makeUnimplementedError(String fileName, int lineNo) {
- return new UnsupportedError('[info: $fileName:$lineNo]');
- }
-
- static bool isTypeSubclassOf(Type type, Type other) {
- if (type == other) {
- return true;
- }
- var superclass = reflectClass(type).superclass;
- if (superclass != null) {
- return isTypeSubclassOf(superclass.reflectedType, other);
- }
- return false;
- }
-
- static Element getAndValidateNativeType(Type type, String tagName) {
- var element = new Element.tag(tagName);
- if (!isTypeSubclassOf(type, element.runtimeType)) {
- return null;
- }
- return element;
- }
-
- static forwardingPrint(String message) =>
- _blink.Blink_Utils.forwardingPrint(message);
- static void spawnDomHelper(Function f, int replyTo) =>
- _blink.Blink_Utils.spawnDomHelper(f, replyTo);
-
- // TODO(vsm): Make this API compatible with spawnUri. It should also
- // return a Future<Isolate>.
- // TODO(jacobr): IS THIS RIGHT? I worry we have broken conversion from Promise to Future.
- static spawnDomUri(String uri) => _blink.Blink_Utils.spawnDomUri(uri);
-
- // The following methods were added for debugger integration to make working
- // with the Dart C mirrors API simpler.
- // TODO(jacobr): consider moving them to a separate library.
- // If Dart supported dynamic code injection, we would only inject this code
- // when the debugger is invoked.
-
- /**
- * Strips the private secret prefix from member names of the form
- * someName@hash.
- */
- static String stripMemberName(String name) {
- int endIndex = name.indexOf('@');
- return endIndex > 0 ? name.substring(0, endIndex) : name;
- }
-
- /**
- * Takes a list containing variable names and corresponding values and
- * returns a map from normalized names to values. Variable names are assumed
- * to have list offsets 2*n values at offset 2*n+1. This method is required
- * because Dart_GetLocalVariables returns a list instead of an object that
- * can be queried to lookup names and values.
- */
- static Map<String, dynamic> createLocalVariablesMap(List localVariables) {
- var map = {};
- for (int i = 0; i < localVariables.length; i += 2) {
- map[stripMemberName(localVariables[i])] = localVariables[i + 1];
- }
- return map;
- }
-
- static _ConsoleVariables _consoleTempVariables = new _ConsoleVariables();
-
- /**
- * Takes an [expression] and a list of [local] variable and returns an
- * expression for a closure with a body matching the original expression
- * where locals are passed in as arguments. Returns a list containing the
- * String expression for the closure and the list of arguments that should
- * be passed to it. The expression should then be evaluated using
- * Dart_EvaluateExpr which will generate a closure that should be invoked
- * with the list of arguments passed to this method.
- *
- * For example:
- * <code>
- * _consoleTempVariables = {'a' : someValue, 'b': someOtherValue}
- * wrapExpressionAsClosure("foo + bar + a", ["bar", 40, "foo", 2], true)
- * </code>
- * will return:
- * <code>
- * ["""(final $consoleVariables, final bar, final foo, final a, final b) =>
- * (foo + bar + a
- * )""",
- * [_consoleTempVariables, 40, 2, someValue, someOtherValue]]
- * </code>
- */
- static List wrapExpressionAsClosure(
- String expression, List locals, bool includeCommandLineAPI) {
- var args = {};
- var sb = new StringBuffer("(");
- addArg(arg, value) {
- arg = stripMemberName(arg);
- if (args.containsKey(arg)) return;
- // We ignore arguments with the name 'this' rather than throwing an
- // exception because Dart_GetLocalVariables includes 'this' and it
- // is more convenient to filter it out here than from C++ code.
- // 'this' needs to be handled by calling Dart_EvaluateExpr with
- // 'this' as the target rather than by passing it as an argument.
- if (arg == 'this') return;
- // Avoid being broken by bogus ':async_op' local passed in when within
- // an async method.
- if (arg.startsWith(':')) return;
- if (args.isNotEmpty) {
- sb.write(", ");
- }
- sb.write("final $arg");
- args[arg] = value;
- }
-
- if (includeCommandLineAPI) {
- addArg("\$consoleVariables", _consoleTempVariables);
-
- // FIXME: use a real Dart tokenizer. The following regular expressions
- // only allow setting variables at the immediate start of the expression
- // to limit the number of edge cases we have to handle.
-
- // Match expressions that start with "var x"
- final _VARIABLE_DECLARATION = new RegExp("^(\\s*)var\\s+(\\w+)");
- // Match expressions that start with "someExistingConsoleVar ="
- final _SET_VARIABLE = new RegExp("^(\\s*)(\\w+)(\\s*=)");
- // Match trailing semicolons.
- final _ENDING_SEMICOLONS = new RegExp("(;\\s*)*\$");
- expression = expression.replaceAllMapped(_VARIABLE_DECLARATION, (match) {
- var variableName = match[2];
- // Set the console variable if it isn't already set.
- if (!_consoleTempVariables._data.containsKey(variableName)) {
- _consoleTempVariables._data[variableName] = null;
- }
- return "${match[1]}\$consoleVariables.${variableName}";
- });
-
- expression = expression.replaceAllMapped(_SET_VARIABLE, (match) {
- var variableName = match[2];
- // Only rewrite if the name matches an existing console variable.
- if (_consoleTempVariables._data.containsKey(variableName)) {
- return "${match[1]}\$consoleVariables.${variableName}${match[3]}";
- } else {
- return match[0];
- }
- });
-
- // We only allow dart expressions not Dart statements. Silently remove
- // trailing semicolons the user might have added by accident to reduce the
- // number of spurious compile errors.
- expression = expression.replaceFirst(_ENDING_SEMICOLONS, "");
- }
-
- if (locals != null) {
- for (int i = 0; i < locals.length; i += 2) {
- addArg(locals[i], locals[i + 1]);
- }
- }
- // Inject all the already defined console variables.
- _consoleTempVariables._data.forEach(addArg);
-
- // TODO(jacobr): remove the parentheses around the expression once
- // dartbug.com/13723 is fixed. Currently we wrap expression in parentheses
- // to ensure only valid Dart expressions are allowed. Otherwise the DartVM
- // quietly ignores trailing Dart statements resulting in user confusion
- // when part of an invalid expression they entered is ignored.
- sb..write(') => (\n$expression\n)');
- return [sb.toString(), args.values.toList(growable: false)];
- }
-
- static String _getShortSymbolName(
- Symbol symbol, DeclarationMirror declaration) {
- var name = MirrorSystem.getName(symbol);
- if (declaration is MethodMirror) {
- if (declaration.isSetter && name[name.length - 1] == "=") {
- return name.substring(0, name.length - 1);
- }
- if (declaration.isConstructor) {
- return name.substring(name.indexOf('.') + 1);
- }
- }
- return name;
- }
-
- /**
- * Handle special console commands such as $lib and $libs that should not be
- * evaluated as Dart expressions and instead should be interpreted directly.
- * Commands supported:
- * library <-- shows the current library and lists all libraries.
- * library "library_uri" <-- select a specific library
- * library "library_uri_fragment"
- */
- static bool maybeHandleSpecialConsoleCommand(String expression) {
- expression = expression.trim();
- var setLibraryCommand = r'library ';
- if (expression == r'library') {
- _LibraryManager.setLibrary();
- return true;
- }
- if (expression.startsWith(setLibraryCommand)) {
- expression = expression.substring(setLibraryCommand.length);
- if (expression.length >= 2) {
- String start = expression[0];
- String end = expression[expression.length - 1];
- // TODO(jacobr): maybe we should require quotes.
- if ((start == "'" && end == "'") || (start == '"' && end == '"')) {
- expression = expression.substring(1, expression.length - 1);
- }
- }
-
- _LibraryManager.setLibrary(expression);
- return true;
- }
- return false;
- }
-
- /**
- * Returns a list of completions to use if the receiver is o.
- */
- static List<String> getCompletions(o) {
- MirrorSystem system = currentMirrorSystem();
- var completions = new Set<String>();
- addAll(Map<Symbol, dynamic> map, bool isStatic) {
- map.forEach((symbol, mirror) {
- if (mirror.isStatic == isStatic && !mirror.isPrivate) {
- var name = MirrorSystem.getName(symbol);
- if (mirror is MethodMirror && mirror.isSetter)
- name = name.substring(0, name.length - 1);
- completions.add(name);
- }
- });
- }
-
- addForClass(ClassMirror mirror, bool isStatic) {
- if (mirror == null) return;
- addAll(mirror.declarations, isStatic);
- if (mirror.superclass != null) addForClass(mirror.superclass, isStatic);
- for (var interface in mirror.superinterfaces) {
- addForClass(interface, isStatic);
- }
- }
-
- if (o is Type) {
- addForClass(reflectClass(o), true);
- } else {
- addForClass(reflect(o).type, false);
- }
- return completions.toList(growable: false);
- }
-
- /**
- * Adds all candidate String completions from [declarations] to [output]
- * filtering based on [staticContext] and [includePrivate].
- */
- static void _getCompletionsHelper(ClassMirror classMirror, bool staticContext,
- LibraryMirror libraryMirror, Set<String> output) {
- bool includePrivate = libraryMirror == classMirror.owner;
- classMirror.declarations.forEach((symbol, declaration) {
- if (!includePrivate && declaration.isPrivate) return;
- if (declaration is VariableMirror) {
- if (staticContext != declaration.isStatic) return;
- } else if (declaration is MethodMirror) {
- if (declaration.isOperator) return;
- if (declaration.isConstructor) {
- if (!staticContext) return;
- var name = MirrorSystem.getName(declaration.constructorName);
- if (name.isNotEmpty) output.add(name);
- return;
- }
- if (staticContext != declaration.isStatic) return;
- } else if (declaration is TypeMirror) {
- return;
- }
- output.add(_getShortSymbolName(symbol, declaration));
- });
-
- if (!staticContext) {
- for (var interface in classMirror.superinterfaces) {
- _getCompletionsHelper(interface, staticContext, libraryMirror, output);
- }
- if (classMirror.superclass != null) {
- _getCompletionsHelper(
- classMirror.superclass, staticContext, libraryMirror, output);
- }
- }
- }
-
- static void _getLibraryCompletionsHelper(
- LibraryMirror library, bool includePrivate, Set<String> output) {
- library.declarations.forEach((symbol, declaration) {
- if (!includePrivate && declaration.isPrivate) return;
- output.add(_getShortSymbolName(symbol, declaration));
- });
- }
-
- static LibraryMirror getLibraryMirror(String url) =>
- currentMirrorSystem().libraries[Uri.parse(url)];
-
- /**
- * Get code completions for [o] only showing privates from [libraryUrl].
- */
- static List<String> getObjectCompletions(o, String libraryUrl) {
- var classMirror;
- bool staticContext;
- if (o is Type) {
- classMirror = reflectClass(o);
- staticContext = true;
- } else {
- classMirror = reflect(o).type;
- staticContext = false;
- }
- var names = new Set<String>();
- getClassCompletions(classMirror, names, staticContext, libraryUrl);
- return names.toList()..sort();
- }
-
- static void getClassCompletions(ClassMirror classMirror, Set<String> names,
- bool staticContext, String libraryUrl) {
- LibraryMirror libraryMirror = getLibraryMirror(libraryUrl);
- _getCompletionsHelper(classMirror, staticContext, libraryMirror, names);
- }
-
- static List<String> getLibraryCompletions(String url) {
- var names = new Set<String>();
- _getLibraryCompletionsHelper(getLibraryMirror(url), true, names);
- return names.toList();
- }
-
- /**
- * Get valid code completions from within a library and all libraries
- * imported by that library.
- */
- static List<String> getLibraryCompletionsIncludingImports(String url) {
- var names = new Set<String>();
- var libraryMirror = getLibraryMirror(url);
- _getLibraryCompletionsHelper(libraryMirror, true, names);
- for (var dependency in libraryMirror.libraryDependencies) {
- if (dependency.isImport) {
- if (dependency.prefix == null) {
- _getLibraryCompletionsHelper(dependency.targetLibrary, false, names);
- } else {
- names.add(MirrorSystem.getName(dependency.prefix));
- }
- }
- }
- return names.toList();
- }
-
- static final SIDE_EFFECT_FREE_LIBRARIES = new Set<String>()
- ..add('dart:html')
- ..add('dart:indexed_db')
- ..add('dart:svg')
- ..add('dart:typed_data')
- ..add('dart:web_audio')
- ..add('dart:web_gl')
- ..add('dart:web_sql');
-
- static LibraryMirror _getLibrary(MethodMirror methodMirror) {
- var owner = methodMirror.owner;
- if (owner is ClassMirror) {
- return owner;
- } else if (owner is LibraryMirror) {
- return owner;
- }
- return null;
- }
-
- /**
- * For parity with the JavaScript debugger, we treat some getters as if
- * they are fields so that users can see their values immediately.
- * This matches JavaScript's behavior for getters on DOM objects.
- * In the future we should consider adding an annotation to tag getters
- * in user libraries as side effect free.
- */
- static bool _isSideEffectFreeGetter(
- MethodMirror methodMirror, LibraryMirror libraryMirror) {
- // This matches JavaScript behavior. We should consider displaying
- // getters for all dart platform libraries rather than just the DOM
- // libraries.
- return libraryMirror.uri.scheme == 'dart' &&
- SIDE_EFFECT_FREE_LIBRARIES.contains(libraryMirror.uri.toString());
- }
-
- /**
- * Whether we should treat a property as a field for the purposes of the
- * debugger.
- */
- static bool treatPropertyAsField(
- MethodMirror methodMirror, LibraryMirror libraryMirror) {
- return (methodMirror.isGetter || methodMirror.isSetter) &&
- (methodMirror.isSynthetic ||
- _isSideEffectFreeGetter(methodMirror, libraryMirror));
- }
-
- // TODO(jacobr): generate more concise function descriptions instead of
- // dumping the entire function source.
- static String describeFunction(function) {
- if (function is _Trampoline) return function._methodMirror.source;
- try {
- var mirror = reflect(function);
- return mirror.function.source;
- } catch (e) {
- return function.toString();
- }
- }
-
- static List getInvocationTrampolineDetails(_Trampoline method) {
- var loc = method._methodMirror.location;
- return [
- loc.line,
- loc.column,
- loc.sourceUri.toString(),
- MirrorSystem.getName(method._selector)
- ];
- }
-
- static List getLibraryProperties(
- String libraryUrl, bool ownProperties, bool accessorPropertiesOnly) {
- var properties = new Map<String, _Property>();
- var libraryMirror = getLibraryMirror(libraryUrl);
- _addInstanceMirrors(
- libraryMirror,
- libraryMirror,
- libraryMirror.declarations,
- ownProperties,
- accessorPropertiesOnly,
- false,
- false,
- properties);
- if (!accessorPropertiesOnly) {
- // We need to add class properties for all classes in the library.
- libraryMirror.declarations.forEach((symbol, declarationMirror) {
- if (declarationMirror is ClassMirror) {
- var name = MirrorSystem.getName(symbol);
- if (declarationMirror.hasReflectedType &&
- !properties.containsKey(name)) {
- properties[name] = new _Property(name)
- ..value = declarationMirror.reflectedType;
- }
- }
- });
- }
- return packageProperties(properties);
- }
-
- static List getObjectProperties(
- o, bool ownProperties, bool accessorPropertiesOnly) {
- var properties = new Map<String, _Property>();
- var names = new Set<String>();
- var objectMirror = reflect(o);
- var classMirror = objectMirror.type;
- _addInstanceMirrors(
- objectMirror,
- classMirror.owner,
- classMirror.instanceMembers,
- ownProperties,
- accessorPropertiesOnly,
- false,
- true,
- properties);
- return packageProperties(properties);
- }
-
- static List getObjectClassProperties(
- o, bool ownProperties, bool accessorPropertiesOnly) {
- var properties = new Map<String, _Property>();
- var objectMirror = reflect(o);
- var classMirror = objectMirror.type;
- _addInstanceMirrors(
- objectMirror,
- classMirror.owner,
- classMirror.instanceMembers,
- ownProperties,
- accessorPropertiesOnly,
- true,
- false,
- properties);
- _addStatics(classMirror, properties, accessorPropertiesOnly);
- return packageProperties(properties);
- }
-
- static List getClassProperties(
- Type t, bool ownProperties, bool accessorPropertiesOnly) {
- var properties = new Map<String, _Property>();
- var classMirror = reflectClass(t);
- _addStatics(classMirror, properties, accessorPropertiesOnly);
- return packageProperties(properties);
- }
-
- static void _addStatics(ClassMirror classMirror,
- Map<String, _Property> properties, bool accessorPropertiesOnly) {
- var libraryMirror = classMirror.owner;
- classMirror.declarations.forEach((symbol, declaration) {
- var name = _getShortSymbolName(symbol, declaration);
- if (name.isEmpty) return;
- if (declaration is VariableMirror) {
- if (accessorPropertiesOnly) return;
- if (!declaration.isStatic) return;
- properties.putIfAbsent(name, () => new _Property(name))
- ..value = classMirror.getField(symbol).reflectee
- ..writable = !declaration.isFinal && !declaration.isConst;
- } else if (declaration is MethodMirror) {
- MethodMirror methodMirror = declaration;
- // FIXMEDART: should we display constructors?
- if (methodMirror.isConstructor) return;
- if (!methodMirror.isStatic) return;
- if (accessorPropertiesOnly) {
- if (methodMirror.isRegularMethod ||
- treatPropertyAsField(methodMirror, libraryMirror)) {
- return;
- }
- } else if (!methodMirror.isRegularMethod &&
- !treatPropertyAsField(methodMirror, libraryMirror)) {
- return;
- }
- var property = properties.putIfAbsent(name, () => new _Property(name));
- _fillMethodMirrorProperty(libraryMirror, classMirror, methodMirror,
- symbol, accessorPropertiesOnly, property);
- }
- });
- }
-
- static void _fillMethodMirrorProperty(
- LibraryMirror libraryMirror,
- methodOwner,
- MethodMirror methodMirror,
- Symbol symbol,
- bool accessorPropertiesOnly,
- _Property property) {
- if (methodMirror.isRegularMethod) {
- property
- ..value = new _MethodTrampoline(methodOwner, methodMirror, symbol)
- ..isMethod = true;
- } else if (methodMirror.isGetter) {
- if (treatPropertyAsField(methodMirror, libraryMirror)) {
- try {
- property.value = methodOwner.getField(symbol).reflectee;
- } catch (e) {
- property
- ..wasThrown = true
- ..value = e;
- }
- } else if (accessorPropertiesOnly) {
- property.getter =
- new _GetterTrampoline(methodOwner, methodMirror, symbol);
- }
- } else if (methodMirror.isSetter) {
- if (accessorPropertiesOnly &&
- !treatPropertyAsField(methodMirror, libraryMirror)) {
- property.setter = new _SetterTrampoline(methodOwner, methodMirror,
- MirrorSystem.getSymbol(property.name, libraryMirror));
- }
- property.writable = true;
- }
- }
-
- /**
- * Helper method that handles collecting up properties from classes
- * or libraries using the filters [ownProperties], [accessorPropertiesOnly],
- * [hideFields], and [hideMethods] to determine which properties are
- * collected. [accessorPropertiesOnly] specifies whether all properties
- * should be returned or just accessors. [hideFields] specifies whether
- * fields should be hidden. hideMethods specifies whether methods should be
- * shown or hidden. [ownProperties] is not currently used but is part of the
- * Blink devtools API for enumerating properties.
- */
- static void _addInstanceMirrors(
- ObjectMirror objectMirror,
- LibraryMirror libraryMirror,
- Map<Symbol, Mirror> declarations,
- bool ownProperties,
- bool accessorPropertiesOnly,
- bool hideFields,
- bool hideMethods,
- Map<String, _Property> properties) {
- declarations.forEach((symbol, declaration) {
- if (declaration is TypedefMirror || declaration is ClassMirror) return;
- var name = _getShortSymbolName(symbol, declaration);
- if (name.isEmpty) return;
- bool isField = declaration is VariableMirror ||
- (declaration is MethodMirror &&
- treatPropertyAsField(declaration, libraryMirror));
- if ((isField && hideFields) || (hideMethods && !isField)) return;
- if (accessorPropertiesOnly) {
- if (declaration is VariableMirror ||
- declaration.isRegularMethod ||
- isField) {
- return;
- }
- } else if (declaration is MethodMirror &&
- (declaration.isGetter || declaration.isSetter) &&
- !treatPropertyAsField(declaration, libraryMirror)) {
- return;
- }
- var property = properties.putIfAbsent(name, () => new _Property(name));
- if (declaration is VariableMirror) {
- property
- ..value = objectMirror.getField(symbol).reflectee
- ..writable = !declaration.isFinal && !declaration.isConst;
- return;
- }
- _fillMethodMirrorProperty(libraryMirror, objectMirror, declaration,
- symbol, accessorPropertiesOnly, property);
- });
- }
-
- /**
- * Flatten down the properties data structure into a List that is easy to
- * access from native code.
- */
- static List packageProperties(Map<String, _Property> properties) {
- var ret = [];
- for (var property in properties.values) {
- ret.addAll([
- property.name,
- property.setter,
- property.getter,
- property.value,
- property.hasValue,
- property.writable,
- property.isMethod,
- property.isOwn,
- property.wasThrown
- ]);
- }
- return ret;
- }
-
- /**
- * Get a property, returning null if the property does not exist.
- * For private property names, we attempt to resolve the property in the
- * context of each library that the property name could be associated with.
- */
- static getObjectPropertySafe(o, String propertyName) {
- var objectMirror = reflect(o);
- var classMirror = objectMirror.type;
- if (propertyName.startsWith("_")) {
- var attemptedLibraries = new Set<LibraryMirror>();
- while (classMirror != null) {
- LibraryMirror library = classMirror.owner;
- if (!attemptedLibraries.contains(library)) {
- try {
- return objectMirror
- .getField(MirrorSystem.getSymbol(propertyName, library))
- .reflectee;
- } catch (e) {}
- attemptedLibraries.add(library);
- }
- classMirror = classMirror.superclass;
- }
- return null;
- }
- try {
- return objectMirror
- .getField(MirrorSystem.getSymbol(propertyName))
- .reflectee;
- } catch (e) {
- return null;
- }
- }
-
- /**
- * Helper to wrap the inspect method on InjectedScriptHost to provide the
- * inspect method required for the
- */
- static List consoleApi(host) {
- return [
- "inspect",
- (o) {
- js.JsNative.callMethod(host, "_inspect", [o]);
- return o;
- },
- "dir",
- window.console.dir,
- "dirxml",
- window.console.dirxml
- // FIXME: add copy method.
- ];
- }
-
- static List getMapKeyList(Map map) => map.keys.toList();
-
- static bool isNoSuchMethodError(obj) => obj is NoSuchMethodError;
-
- static void register(
- Document document, String tag, Type type, String extendsTagName) {
- var nativeClass = _validateCustomType(type);
-
- if (extendsTagName == null) {
- if (nativeClass.reflectedType != HtmlElement) {
- throw new UnsupportedError('Class must provide extendsTag if base '
- 'native class is not HTMLElement');
- }
- }
-
- _register(document, tag, type, extendsTagName);
- }
-
- static void _register(Document document, String tag, Type customType,
- String extendsTagName) =>
- _blink.Blink_Utils.register(document, tag, customType, extendsTagName);
-
- static Element createElement(Document document, String tagName) =>
- _blink.Blink_Utils.createElement(document, tagName);
-}
-
-class _DOMWindowCrossFrame extends DartHtmlDomObject implements WindowBase {
- _DOMWindowCrossFrame.internal();
-
- static _createSafe(win) {
- if (identical(win, window)) {
- // The current Window object is the only window object that should not
- // use _DOMWindowCrossFrame.
- return window;
- }
- return win is _DOMWindowCrossFrame
- ? win
- : _blink.Blink_Utils.setInstanceInterceptor(win, _DOMWindowCrossFrame);
- }
-
- // Fields.
- HistoryBase get history {
- var history = _blink.BlinkWindow.instance.history_Getter_(this);
- return history is _HistoryCrossFrame
- ? history
- : _blink.Blink_Utils.setInstanceInterceptor(
- history, _HistoryCrossFrame);
- }
-
- LocationBase get location {
- var location = _blink.BlinkWindow.instance.location_Getter_(this);
- return location is _LocationCrossFrame
- ? location
- : _blink.Blink_Utils.setInstanceInterceptor(
- location, _LocationCrossFrame);
- }
-
- bool get closed => _blink.BlinkWindow.instance.closed_Getter_(this);
- WindowBase get opener => _convertNativeToDart_Window(
- _blink.BlinkWindow.instance.opener_Getter_(this));
- WindowBase get parent => _convertNativeToDart_Window(
- _blink.BlinkWindow.instance.parent_Getter_(this));
- WindowBase get top => _convertNativeToDart_Window(
- _blink.BlinkWindow.instance.top_Getter_(this));
-
- // Methods.
- void close() => _blink.BlinkWindow.instance.close_Callback_0_(this);
- void postMessage(Object message, String targetOrigin,
- [List<MessagePort>? transfer]) =>
- _blink.BlinkWindow.instance.postMessage_Callback_3_(
- this,
- convertDartToNative_SerializedScriptValue(message),
- targetOrigin,
- transfer);
-
- // Implementation support.
- String get typeName => "Window";
-
- // TODO(efortuna): Remove this method. dartbug.com/16814
- Events get on => throw new UnsupportedError(
- 'You can only attach EventListeners to your own window.');
- // TODO(efortuna): Remove this method. dartbug.com/16814
- void _addEventListener(
- [String? type, EventListener? listener, bool? useCapture]) =>
- throw new UnsupportedError(
- 'You can only attach EventListeners to your own window.');
- // TODO(efortuna): Remove this method. dartbug.com/16814
- void addEventListener(String type, EventListener? listener,
- [bool? useCapture]) =>
- throw new UnsupportedError(
- 'You can only attach EventListeners to your own window.');
- // TODO(efortuna): Remove this method. dartbug.com/16814
- bool dispatchEvent(Event event) => throw new UnsupportedError(
- 'You can only attach EventListeners to your own window.');
- // TODO(efortuna): Remove this method. dartbug.com/16814
- void _removeEventListener(
- [String? type, EventListener? listener, bool? useCapture]) =>
- throw new UnsupportedError(
- 'You can only attach EventListeners to your own window.');
- // TODO(efortuna): Remove this method. dartbug.com/16814
- void removeEventListener(String type, EventListener? listener,
- [bool? useCapture]) =>
- throw new UnsupportedError(
- 'You can only attach EventListeners to your own window.');
-}
-
-class _HistoryCrossFrame extends DartHtmlDomObject implements HistoryBase {
- _HistoryCrossFrame.internal();
-
- // Methods.
- void back() => _blink.BlinkHistory.instance.back_Callback_0_(this);
- void forward() => _blink.BlinkHistory.instance.forward_Callback_0_(this);
- void go([int? delta]) {
- if (delta != null) {
- _blink.BlinkHistory.instance.go_Callback_1_(this, delta);
- return;
- }
- _blink.BlinkHistory.instance.go_Callback_0_(this);
- return;
- }
-
- // Implementation support.
- String get typeName => "History";
-}
-
-class _LocationCrossFrame extends DartHtmlDomObject implements LocationBase {
- _LocationCrossFrame.internal();
-
- // Fields.
- set href(String value) =>
- _blink.BlinkLocation.instance.href_Setter_(this, value);
-
- // Implementation support.
- String get typeName => "Location";
-}
-
-// TODO(vsm): Remove DOM isolate code once we have Dartium isolates
-// as workers. This is only used to support
-// printing and timers in background isolates. As workers they should
-// be able to just do those things natively.
-
-_makeSendPortFuture(spawnRequest) {
- final completer = new Completer<SendPort>.sync();
- final port = new ReceivePort();
- port.listen((result) {
- completer.complete(result);
- port.close();
- });
- // TODO: SendPort.hashCode is ugly way to access port id.
- spawnRequest(port.sendPort.hashCode);
- return completer.future;
-}
-
-Future<SendPort> _spawnDomHelper(Function f) => _makeSendPortFuture((portId) {
- _Utils.spawnDomHelper(f, portId);
- });
-
-final Future<SendPort> __HELPER_ISOLATE_PORT =
- _spawnDomHelper(_helperIsolateMain);
-
-// Tricky part.
-// Once __HELPER_ISOLATE_PORT gets resolved, it will still delay in .then
-// and to delay Timer.run is used. However, Timer.run will try to register
-// another Timer and here we got stuck: event cannot be posted as then
-// callback is not executed because it's delayed with timer.
-// Therefore once future is resolved, it's unsafe to call .then on it
-// in Timer code.
-SendPort __SEND_PORT;
-
-_sendToHelperIsolate(msg, SendPort replyTo) {
- if (__SEND_PORT != null) {
- __SEND_PORT.send([msg, replyTo]);
- } else {
- __HELPER_ISOLATE_PORT.then((port) {
- __SEND_PORT = port;
- __SEND_PORT.send([msg, replyTo]);
- });
- }
-}
-
-final _TIMER_REGISTRY = new Map<SendPort, Timer>();
-
-const _NEW_TIMER = 'NEW_TIMER';
-const _CANCEL_TIMER = 'CANCEL_TIMER';
-const _TIMER_PING = 'TIMER_PING';
-const _PRINT = 'PRINT';
-
-_helperIsolateMain(originalSendPort) {
- var port = new ReceivePort();
- originalSendPort.send(port.sendPort);
- port.listen((args) {
- var msg = args.first;
- var replyTo = args.last;
- final cmd = msg[0];
- if (cmd == _NEW_TIMER) {
- final duration = new Duration(milliseconds: msg[1]);
- bool periodic = msg[2];
- ping() {
- replyTo.send(_TIMER_PING);
- }
-
- ;
- _TIMER_REGISTRY[replyTo] = periodic
- ? new Timer.periodic(duration, (_) {
- ping();
- })
- : new Timer(duration, ping);
- } else if (cmd == _CANCEL_TIMER) {
- _TIMER_REGISTRY.remove(replyTo).cancel();
- } else if (cmd == _PRINT) {
- final message = msg[1];
- // TODO(antonm): we need somehow identify those isolates.
- print('[From isolate] $message');
- }
- });
-}
-
-final _printClosure = (s) => window.console.log(s);
-final _pureIsolatePrintClosure = (s) {
- _sendToHelperIsolate([_PRINT, s], null);
-};
-
-final _forwardingPrintClosure = _Utils.forwardingPrint;
-
-final _uriBaseClosure = () => Uri.parse(window.location.href);
-
-final _pureIsolateUriBaseClosure = () {
- throw new UnimplementedError("Uri.base on a background isolate "
- "is not supported in the browser");
-};
-
-class _Timer implements Timer {
- static const int _STATE_TIMEOUT = 0;
- static const int _STATE_INTERVAL = 1;
- int _state;
-
- _Timer(int milliSeconds, void callback(Timer timer), bool repeating) {
- if (repeating) {
- _state = (window._setInterval(() {
- callback(this);
- }, milliSeconds) <<
- 1) |
- _STATE_INTERVAL;
- } else {
- _state = (window._setTimeout(() {
- _state = null;
- callback(this);
- }, milliSeconds) <<
- 1) |
- _STATE_TIMEOUT;
- }
- }
-
- void cancel() {
- if (_state == null) return;
- int id = _state >> 1;
- if ((_state & 1) == _STATE_TIMEOUT) {
- window._clearTimeout(id);
- } else {
- window._clearInterval(id);
- }
- _state = null;
- }
-
- bool get isActive => _state != null;
-}
-
-get _timerFactoryClosure =>
- (int milliSeconds, void callback(Timer timer), bool repeating) {
- return new _Timer(milliSeconds, callback, repeating);
- };
-
-class _PureIsolateTimer implements Timer {
- bool _isActive = true;
- final ReceivePort _port = new ReceivePort();
- SendPort _sendPort; // Effectively final.
-
- // static SendPort _SEND_PORT;
-
- _PureIsolateTimer(int milliSeconds, callback, repeating) {
- _sendPort = _port.sendPort;
- _port.listen((msg) {
- assert(msg == _TIMER_PING);
- _isActive = repeating;
- callback(this);
- if (!repeating) _cancel();
- });
-
- _send([_NEW_TIMER, milliSeconds, repeating]);
- }
-
- void cancel() {
- _cancel();
- _send([_CANCEL_TIMER]);
- }
-
- void _cancel() {
- _isActive = false;
- _port.close();
- }
-
- _send(msg) {
- _sendToHelperIsolate(msg, _sendPort);
- }
-
- bool get isActive => _isActive;
-}
-
-get _pureIsolateTimerFactoryClosure =>
- ((int milliSeconds, void callback(Timer time), bool repeating) =>
- new _PureIsolateTimer(milliSeconds, callback, repeating));
-
-class _ScheduleImmediateHelper {
- MutationObserver _observer;
- final DivElement _div = new DivElement();
- Function _callback;
-
- _ScheduleImmediateHelper() {
- // Run in the root-zone as the DOM callback would otherwise execute in the
- // current zone.
- Zone.root.run(() {
- // Mutation events get fired as soon as the current event stack is unwound
- // so we just make a dummy event and listen for that.
- _observer = new MutationObserver(_handleMutation);
- _observer.observe(_div, attributes: true);
- });
- }
-
- void _schedule(callback) {
- if (_callback != null) {
- throw new StateError(
- 'Only one immediate callback can be scheduled at once');
- }
- _callback = callback;
- // Toggle it to trigger the mutation event.
- _div.hidden = !_div.hidden;
- }
-
- _handleMutation(List<MutationRecord> mutations, MutationObserver observer) {
- var tmp = _callback;
- _callback = null;
- tmp();
- }
-}
-
-final _ScheduleImmediateHelper _scheduleImmediateHelper =
- new _ScheduleImmediateHelper();
-
-get _scheduleImmediateClosure => (void callback()) {
- _scheduleImmediateHelper._schedule(callback);
- };
-
-get _pureIsolateScheduleImmediateClosure => ((void callback()) =>
- throw new UnimplementedError("scheduleMicrotask in background isolates "
- "are not supported in the browser"));
-
-// Class for unsupported native browser 'DOM' objects.
-class _UnsupportedBrowserObject extends DartHtmlDomObject {}
diff --git a/tools/dom/nnbd_src/shared_SVGFactoryProviders.dart b/tools/dom/nnbd_src/shared_SVGFactoryProviders.dart
deleted file mode 100644
index 2c729f7..0000000
--- a/tools/dom/nnbd_src/shared_SVGFactoryProviders.dart
+++ /dev/null
@@ -1,13 +0,0 @@
-// Copyright (c) 2012, 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.
-
-part of dart.dom.svg;
-
-class _SvgElementFactoryProvider {
- static SvgElement createSvgElement_tag(String tag) {
- final Element temp =
- document.createElementNS("http://www.w3.org/2000/svg", tag);
- return temp as SvgElement;
- }
-}
diff --git a/tools/dom/nnbd_src/shared_html.dart b/tools/dom/nnbd_src/shared_html.dart
deleted file mode 100644
index f86f288..0000000
--- a/tools/dom/nnbd_src/shared_html.dart
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright (c) 2013, 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.
-
-part of dart.dom.html;
-
-void Function(T)? _wrapZone<T>(void Function(T)? callback) {
- // For performance reasons avoid wrapping if we are in the root zone.
- if (Zone.current == Zone.root) return callback;
- if (callback == null) return null;
- return Zone.current.bindUnaryCallbackGuarded(callback);
-}
-
-void Function(T1, T2)? _wrapBinaryZone<T1, T2>(
- void Function(T1, T2)? callback) {
- // For performance reasons avoid wrapping if we are in the root zone.
- if (Zone.current == Zone.root) return callback;
- if (callback == null) return null;
- return Zone.current.bindBinaryCallbackGuarded(callback);
-}
-
-/**
- * Finds the first descendant element of this document that matches the
- * specified group of selectors.
- *
- * Unless your webpage contains multiple documents, the top-level
- * [querySelector]
- * method behaves the same as this method, so you should use it instead to
- * save typing a few characters.
- *
- * [selectors] should be a string using CSS selector syntax.
- *
- * var element1 = document.querySelector('.className');
- * var element2 = document.querySelector('#id');
- *
- * For details about CSS selector syntax, see the
- * [CSS selector specification](http://www.w3.org/TR/css3-selectors/).
- */
-Element? querySelector(String selectors) => document.querySelector(selectors);
-
-/**
- * Finds all descendant elements of this document that match the specified
- * group of selectors.
- *
- * Unless your webpage contains multiple documents, the top-level
- * [querySelectorAll]
- * method behaves the same as this method, so you should use it instead to
- * save typing a few characters.
- *
- * [selectors] should be a string using CSS selector syntax.
- *
- * var items = document.querySelectorAll('.itemClassName');
- *
- * For details about CSS selector syntax, see the
- * [CSS selector specification](http://www.w3.org/TR/css3-selectors/).
- */
-ElementList<T> querySelectorAll<T extends Element>(String selectors) =>
- document.querySelectorAll(selectors);
-
-/// A utility for changing the Dart wrapper type for elements.
-abstract class ElementUpgrader {
- /// Upgrade the specified element to be of the Dart type this was created for.
- ///
- /// After upgrading the element passed in is invalid and the returned value
- /// should be used instead.
- Element upgrade(Element element);
-}
diff --git a/tools/dom/scripts/dartdomgenerator.py b/tools/dom/scripts/dartdomgenerator.py
index d4711a7..bf2662d 100755
--- a/tools/dom/scripts/dartdomgenerator.py
+++ b/tools/dom/scripts/dartdomgenerator.py
@@ -73,14 +73,13 @@
class GeneratorOptions(object):
def __init__(self, templates, database, type_registry, renamer, metadata,
- dart_js_interop, nnbd):
+ dart_js_interop):
self.templates = templates
self.database = database
self.type_registry = type_registry
self.renamer = renamer
self.metadata = metadata
self.dart_js_interop = dart_js_interop
- self.nnbd = nnbd
def LoadDatabase(database_dir, use_database_cache):
@@ -96,18 +95,14 @@
dart2js_output_dir,
update_dom_metadata=False,
logging_level=logging.WARNING,
- dart_js_interop=False,
- nnbd=False):
+ dart_js_interop=False):
print '\n ----- Accessing DOM using %s -----\n' % (
'dart:js' if dart_js_interop else 'C++')
start_time = time.time()
current_dir = os.path.dirname(__file__)
- if nnbd:
- auxiliary_dir = os.path.join(current_dir, '..', 'nnbd_src')
- else:
- auxiliary_dir = os.path.join(current_dir, '..', 'src')
+ auxiliary_dir = os.path.join(current_dir, '..', 'src')
template_dir = os.path.join(current_dir, '..', 'templates')
_logger.setLevel(logging_level)
@@ -142,7 +137,7 @@
backend_factory, dart_js_interop):
options = GeneratorOptions(template_loader, webkit_database,
type_registry, renamer, metadata,
- dart_js_interop, nnbd)
+ dart_js_interop)
dart_library_emitter = DartLibraryEmitter(emitters, dart_output_dir,
dart_libraries)
event_generator = HtmlEventGenerator(webkit_database, renamer, metadata,
@@ -167,11 +162,11 @@
'DARTIUM': False,
'DART2JS': True,
'JSINTEROP': False,
- 'NNBD': nnbd
+ 'NNBD': True,
})
backend_options = GeneratorOptions(template_loader, webkit_database,
type_registry, renamer, metadata,
- dart_js_interop, nnbd)
+ dart_js_interop)
backend_factory = lambda interface:\
Dart2JSBackend(interface, backend_options, logging_level)
@@ -268,12 +263,6 @@
default=None,
help='Directory to put the generated files')
parser.add_option(
- '--nnbd',
- dest='nnbd',
- action='store_true',
- default=False,
- help='Generate code with non-nullability annotations')
- parser.add_option(
'--use-database-cache',
dest='use_database_cache',
action='store_true',
@@ -348,7 +337,7 @@
GenerateFromDatabase(database, dart2js_output_dir,
options.update_dom_metadata, logging_level,
- options.dart_js_interop, options.nnbd)
+ options.dart_js_interop)
file_generation_start_time = time.time()
diff --git a/tools/dom/scripts/generator.py b/tools/dom/scripts/generator.py
index 44c721e..9f64192 100644
--- a/tools/dom/scripts/generator.py
+++ b/tools/dom/scripts/generator.py
@@ -13,13 +13,6 @@
from htmlrenamer import custom_html_constructors, html_interface_renames, \
typed_array_renames
-# TODO(srujzs): Pass options flag through to emitter functions.
-class GlobalOptionsHack(object):
- nnbd = False
-
-global_options_hack = GlobalOptionsHack()
-
-
_pure_interfaces = monitored.Set('generator._pure_interfaces', [
'AbstractWorker',
'CanvasPath',
@@ -688,7 +681,7 @@
where a type may be omitted.
The string is empty or has a trailing space.
"""
- nullability_operator = '?' if global_options_hack.nnbd and nullable else ''
+ nullability_operator = '?' if nullable else ''
if dart_type == 'dynamic':
if comment:
return '/*%s*/ ' % comment # Just a comment foo(/*T*/ x)
@@ -1274,8 +1267,7 @@
if self._data.dart_type:
return self._data.dart_type
if self.list_item_type() and not self.has_generated_interface():
- item_nullable = '?' if self._data.item_type_nullable and \
- global_options_hack.nnbd else ''
+ item_nullable = '?' if self._data.item_type_nullable else ''
return 'List<%s%s>' % (self._type_registry.TypeInfo(
self._data.item_type).dart_type(), item_nullable)
return self._dart_interface_name
diff --git a/tools/dom/scripts/htmldartgenerator.py b/tools/dom/scripts/htmldartgenerator.py
index ac223d9..f7797fd 100644
--- a/tools/dom/scripts/htmldartgenerator.py
+++ b/tools/dom/scripts/htmldartgenerator.py
@@ -114,7 +114,6 @@
for id in sorted(operationsByName.keys()):
operations = operationsByName[id]
info = AnalyzeOperation(interface, operations)
- info.nnbd = self._nnbd
self.AddOperation(info, declare_only, dart_js_interop)
if ('%s.%s' % (interface.id,
info.declared_name) in convert_to_future_members):
@@ -519,8 +518,7 @@
if self._interface_type_info.list_item_type():
item_type = self._type_registry.TypeInfo(
self._interface_type_info.list_item_type()).dart_type()
- if self._nnbd and \
- self._interface_type_info.list_item_type_nullable():
+ if self._interface_type_info.list_item_type_nullable():
item_type += '?'
implements.append('List<%s>' % item_type)
return implements
@@ -530,8 +528,7 @@
if self._interface_type_info.list_item_type():
item_type = self._type_registry.TypeInfo(
self._interface_type_info.list_item_type()).dart_type()
- if self._nnbd and \
- self._interface_type_info.list_item_type_nullable():
+ if self._interface_type_info.list_item_type_nullable():
item_type += '?'
mixins.append('ListMixin<%s>' % item_type)
mixins.append('ImmutableListMixin<%s>' % item_type)
@@ -907,8 +904,7 @@
})
if nullable:
element_js = element_name + "|Null"
- if self._nnbd:
- element_name += '?'
+ element_name += '?'
else:
element_js = element_name
self._members_emitter.Emit(
@@ -938,11 +934,10 @@
assert (dart_name != 'HistoryBase' and dart_name != 'LocationBase')
if dart_name == 'Window':
dart_name = _secure_base_types[dart_name]
- if self._nnbd:
- if type_name == 'any':
- dart_name = 'Object'
- if nullable and dart_name != 'dynamic':
- dart_name = dart_name + '?'
+ if type_name == 'any':
+ dart_name = 'Object'
+ if nullable and dart_name != 'dynamic':
+ dart_name = dart_name + '?'
return dart_name
def SecureBaseName(self, type_name):
@@ -1006,7 +1001,7 @@
NAME=temp_name,
CONVERT=conversion.function_name,
ARG=info.param_infos[position].name,
- NULLASSERT='!' if null_assert_needed and self._nnbd else '',
+ NULLASSERT='!' if null_assert_needed else '',
ARITY=callBackInfo)
converted_arguments.append(temp_name)
param_type = temp_type
diff --git a/tools/dom/scripts/systemhtml.py b/tools/dom/scripts/systemhtml.py
index 70248c6..2cf97d7 100644
--- a/tools/dom/scripts/systemhtml.py
+++ b/tools/dom/scripts/systemhtml.py
@@ -591,11 +591,6 @@
self._template_loader = options.templates
self._type_registry = options.type_registry
self._options = options
- # TODO(srujzs): This sets the nnbd option globally inside generator.py
- # since there is no options object there. This should should be cleaned
- # up and passed as an option instead.
- global_options_hack.nnbd = options.nnbd
- self._nnbd = options.nnbd
self._library_emitter = library_emitter
self._event_generator = event_generator
self._interface = interface
@@ -808,9 +803,9 @@
NATIVESPEC=native_spec,
KEYTYPE=maplikeKeyType,
VALUETYPE=maplikeValueType,
- NULLABLE='?' if self._options.nnbd else '',
- NULLSAFECAST=True if self._options.nnbd else False,
- NULLASSERT='!' if self._options.nnbd else '')
+ NULLABLE='?',
+ NULLSAFECAST=True,
+ NULLASSERT='!')
stream_getter_signatures_emitter = None
element_stream_getters_emitter = None
if type(implementation_members_emitter) == tuple:
@@ -1159,7 +1154,6 @@
self._type_registry = options.type_registry
self._renamer = options.renamer
self._metadata = options.metadata
- self._nnbd = options.nnbd
self._interface_type_info = self._type_registry.TypeInfo(
self._interface.id)
self._current_secondary_parent = None
@@ -1181,8 +1175,7 @@
):
item_type = self._type_registry.TypeInfo(
self._interface_type_info.list_item_type()).dart_type()
- if self._nnbd and \
- self._interface_type_info.list_item_type_nullable():
+ if self._interface_type_info.list_item_type_nullable():
item_type += '?'
implements.append('JavaScriptIndexingBehavior<%s>' % item_type)
return implements
@@ -1241,19 +1234,17 @@
"JS$CAST("
"'returns:$INTERFACE_NAME;creates:$INTERFACE_NAME;new:true',"
" '#.$METHOD(#)', $FACTORY, $ARGUMENTS)",
- CAST='<' + self._interface_type_info.interface_name() +
- '>' if self._nnbd else '',
+ CAST='<' + self._interface_type_info.interface_name() + '>',
INTERFACE_NAME=self._interface_type_info.interface_name(),
FACTORY=factory,
METHOD=method,
ARGUMENTS=arguments)
- return emitter.Format(
- '$FACTORY.$METHOD($ARGUMENTS)$CAST',
- FACTORY=factory,
- METHOD=method,
- ARGUMENTS=arguments,
- CAST=' as ' + self._interface_type_info.interface_name() \
- if self._nnbd else '')
+ return emitter.Format('$FACTORY.$METHOD($ARGUMENTS)$CAST',
+ FACTORY=factory,
+ METHOD=method,
+ ARGUMENTS=arguments,
+ CAST=' as ' +
+ self._interface_type_info.interface_name())
def _HasUnreliableFactoryConstructor(self):
return self._interface.doc_js_name in _js_unreliable_element_factories
@@ -1361,8 +1352,8 @@
nullable=nullable),
# If the type of the operation is not nullable but the getter
# is, we must assert non-null.
- NULLASSERT='!' if self._nnbd and not nullable and \
- indexed_getter_nullable else '')
+ NULLASSERT='!' if not nullable and indexed_getter_nullable \
+ else '')
if 'CustomIndexedSetter' in self._interface.ext_attrs:
self._members_emitter.Emit(
@@ -1370,7 +1361,7 @@
' void operator[]=(int index, $TYPE$NULLABLE value) {'
' JS("void", "#[#] = #", this, index, value); }',
TYPE=self._NarrowInputType(element_type),
- NULLABLE='?' if self._nnbd and nullable else '')
+ NULLABLE='?' if nullable else '')
else:
theType = self._NarrowInputType(element_type)
if theType == 'DomRectList':
@@ -1382,7 +1373,7 @@
' throw new UnsupportedError("Cannot assign element of immutable List.");\n'
' }\n',
TYPE=theType,
- NULLABLE='?' if self._nnbd and nullable else '')
+ NULLABLE='?' if nullable else '')
self.EmitListMixin(self._DartType(element_type), nullable)
@@ -1460,7 +1451,7 @@
return
input_type = self._NarrowInputType(attribute.type.id)
- if self._nnbd and attribute.type.nullable:
+ if attribute.type.nullable:
input_type += '?'
if not read_only:
if attribute.type.id == 'Promise':
@@ -1483,8 +1474,8 @@
promiseCall = 'promiseToFutureAsMap'
output_conversion = self._OutputConversion("Dictionary",
None)
- nullability = '?' if self._nnbd and \
- output_conversion.nullable_output else ''
+ nullability = '?' if output_conversion.nullable_output \
+ else ''
promiseType = 'Future<Map<String, dynamic>' + \
nullability + '>'
else:
@@ -1492,7 +1483,7 @@
promiseCall = 'promiseToFuture<%s>' % paramType
promiseType = 'Future<%s>' % paramType
- if self._nnbd and attribute.type.nullable:
+ if attribute.type.nullable:
promiseType += '?'
template = '\n $RENAME$(ANNOTATIONS)$TYPE get $NAME => $PROMISE_CALL(JS("", "#.$NAME", this));\n'
@@ -1592,11 +1583,11 @@
HTML_NAME=html_name,
NAME=attr.id,
RETURN_TYPE=conversion.output_type,
- NULLABLE_OUT='?' if nullable_out and self._nnbd else '',
+ NULLABLE_OUT='?' if nullable_out else '',
NATIVE_TYPE=conversion.input_type,
- NULLABLE_IN='?' if nullable_in and self._nnbd else '',
+ NULLABLE_IN='?' if nullable_in else '',
NULLASSERT='!' if nullable_in and \
- not conversion.nullable_input and self._nnbd else '')
+ not conversion.nullable_input else '')
def _AddConvertingSetter(self, attr, html_name, conversion):
# If the attribute is nullable, the setter should be nullable.
@@ -1617,11 +1608,11 @@
HTML_NAME=html_name,
NAME=attr.id,
INPUT_TYPE=conversion.input_type,
- NULLABLE_IN='?' if nullable_in and self._nnbd else '',
+ NULLABLE_IN='?' if nullable_in else '',
NATIVE_TYPE=conversion.output_type,
- NULLABLE_OUT='?' if nullable_out and self._nnbd else '',
+ NULLABLE_OUT='?' if nullable_out else '',
NULLASSERT='!' if nullable_in and \
- not conversion.nullable_input and self._nnbd else '')
+ not conversion.nullable_input else '')
def AmendIndexer(self, element_type):
pass
@@ -1765,8 +1756,8 @@
promiseCall = 'promiseToFutureAsMap'
output_conversion = self._OutputConversion("Dictionary",
None)
- nullability = '?' if self._nnbd and \
- output_conversion.nullable_output else ''
+ nullability = '?' if output_conversion.nullable_output \
+ else ''
promiseType = 'Future<Map<String, dynamic>' + \
nullability + '>'
else:
@@ -1777,7 +1768,7 @@
dictionary_argument = info.dictionaryArgumentName()
codeTemplate = self._promiseToFutureCode(argsNames,
dictionary_argument)
- if self._nnbd and info.type_nullable:
+ if info.type_nullable:
promiseType += '?'
self._members_emitter.Emit(
codeTemplate,
@@ -1852,8 +1843,7 @@
if output_conversion:
call = '%s(%s)' % (output_conversion.function_name, call)
- if self._nnbd and output_conversion.nullable_output and \
- not info.type_nullable:
+ if output_conversion.nullable_output and not info.type_nullable:
# Return type of operation is not nullable while conversion
# is, so we need to assert non-null.
call += '!'
@@ -1904,7 +1894,7 @@
html_name.startswith('_')) else True
nullsafe_return_type = return_type;
- if self._nnbd and info.type_nullable:
+ if info.type_nullable:
nullsafe_return_type += '?'
declaration = '%s%s%s %s(%s)' % (
@@ -1970,18 +1960,15 @@
return_type = self.SecureOutputType(idl_type)
native_type = self._NarrowToImplementationType(idl_type)
- null_union = '' if self._nnbd and not nullable else '|Null'
+ null_union = '' if not nullable else '|Null'
if native_type != return_type:
anns = anns + [
"@Returns('%s%s')" % (native_type, null_union),
"@Creates('%s')" % native_type,
]
- if dart_type == 'dynamic' or \
- (not self._nnbd and dart_type == 'Object') or \
- (self._nnbd and dart_type == 'Object?'):
- # If we're generating nnbd code, we emit non-nullable Object
- # annotations but exclude nullable Object annotations since that's
- # the default.
+ if dart_type == 'dynamic' or dart_type == 'Object?':
+ # We emit non-nullable Object annotations but exclude nullable
+ # Object annotations since that's the default.
def js_type_annotation(ann):
return re.search('^@.*Returns', ann) or re.search(
@@ -2097,7 +2084,7 @@
emitters = library_emitter.Emit(
self._template,
AUXILIARY_DIR=massage_path(auxiliary_dir),
- NULLABLE='?' if global_options_hack.nnbd else '')
+ NULLABLE='?')
if isinstance(emitters, tuple):
imports_emitter, map_emitter = emitters
else:
diff --git a/tools/dom/src/AttributeMap.dart b/tools/dom/src/AttributeMap.dart
index 71579e5..88f3bd4 100644
--- a/tools/dom/src/AttributeMap.dart
+++ b/tools/dom/src/AttributeMap.dart
@@ -16,7 +16,7 @@
}
Map<K, V> cast<K, V>() => Map.castFrom<String, String, K, V>(this);
- bool containsValue(Object value) {
+ bool containsValue(Object? value) {
for (var v in this.values) {
if (value == v) {
return true;
@@ -29,7 +29,7 @@
if (!containsKey(key)) {
this[key] = ifAbsent();
}
- return this[key];
+ return this[key] as String;
}
void clear() {
@@ -41,7 +41,7 @@
void forEach(void f(String key, String value)) {
for (var key in keys) {
var value = this[key];
- f(key, value);
+ f(key, value as String);
}
}
@@ -50,7 +50,7 @@
var attributes = _element._attributes;
var keys = <String>[];
for (int i = 0, len = attributes.length; i < len; i++) {
- _Attr attr = attributes[i];
+ _Attr attr = attributes[i] as _Attr;
if (_matches(attr)) {
keys.add(attr.name);
}
@@ -63,7 +63,7 @@
var attributes = _element._attributes;
var values = <String>[];
for (int i = 0, len = attributes.length; i < len; i++) {
- _Attr attr = attributes[i];
+ _Attr attr = attributes[i] as _Attr;
if (_matches(attr)) {
values.add(attr.value);
}
@@ -95,12 +95,12 @@
class _ElementAttributeMap extends _AttributeMap {
_ElementAttributeMap(Element element) : super(element);
- bool containsKey(Object key) {
- return _element._hasAttribute(key);
+ bool containsKey(Object? key) {
+ return key is String && _element._hasAttribute(key);
}
- String operator [](Object key) {
- return _element.getAttribute(key);
+ String? operator [](Object? key) {
+ return _element.getAttribute(key as String);
}
void operator []=(String key, String value) {
@@ -108,7 +108,7 @@
}
@pragma('dart2js:tryInline')
- String remove(Object key) => key is String ? _remove(_element, key) : null;
+ String? remove(Object? key) => key is String ? _remove(_element, key) : null;
/**
* The number of {key, value} pairs in the map.
@@ -122,8 +122,8 @@
// Inline this because almost all call sites of [remove] do not use [value],
// and the annotations on the `getAttribute` call allow it to be removed.
@pragma('dart2js:tryInline')
- static String _remove(Element element, String key) {
- String value = JS(
+ static String? _remove(Element element, String key) {
+ String? value = JS(
// throws:null(1) is not accurate since [key] could be malformed, but
// [key] is checked again by `removeAttributeNS`.
'returns:String|Null;depends:all;effects:none;throws:null(1)',
@@ -139,16 +139,16 @@
* Wrapper to expose namespaced attributes as a typed map.
*/
class _NamespacedAttributeMap extends _AttributeMap {
- final String _namespace;
+ final String? _namespace;
_NamespacedAttributeMap(Element element, this._namespace) : super(element);
- bool containsKey(Object key) {
- return _element._hasAttributeNS(_namespace, key);
+ bool containsKey(Object? key) {
+ return key is String && _element._hasAttributeNS(_namespace, key);
}
- String operator [](Object key) {
- return _element.getAttributeNS(_namespace, key);
+ String? operator [](Object? key) {
+ return _element.getAttributeNS(_namespace, key as String);
}
void operator []=(String key, String value) {
@@ -156,7 +156,7 @@
}
@pragma('dart2js:tryInline')
- String remove(Object key) =>
+ String? remove(Object? key) =>
key is String ? _remove(_namespace, _element, key) : null;
/**
@@ -172,8 +172,8 @@
// returned [value], and the annotations on the `getAttributeNS` call allow it
// to be removed.
@pragma('dart2js:tryInline')
- static String _remove(String namespace, Element element, String key) {
- String value = JS(
+ static String? _remove(String? namespace, Element element, String key) {
+ String? value = JS(
// throws:null(1) is not accurate since [key] could be malformed, but
// [key] is checked again by `removeAttributeNS`.
'returns:String|Null;depends:all;effects:none;throws:null(1)',
@@ -205,11 +205,12 @@
Map<K, V> cast<K, V>() => Map.castFrom<String, String, K, V>(this);
// TODO: Use lazy iterator when it is available on Map.
- bool containsValue(Object value) => values.any((v) => v == value);
+ bool containsValue(Object? value) => values.any((v) => v == value);
- bool containsKey(Object key) => _attributes.containsKey(_attr(key));
+ bool containsKey(Object? key) =>
+ _attributes.containsKey(_attr(key as String));
- String operator [](Object key) => _attributes[_attr(key)];
+ String? operator [](Object? key) => _attributes[_attr(key as String)];
void operator []=(String key, String value) {
_attributes[_attr(key)] = value;
@@ -218,7 +219,7 @@
String putIfAbsent(String key, String ifAbsent()) =>
_attributes.putIfAbsent(_attr(key), ifAbsent);
- String remove(Object key) => _attributes.remove(_attr(key));
+ String? remove(Object? key) => _attributes.remove(_attr(key as String));
void clear() {
// Needs to operate on a snapshot since we are mutating the collection.
diff --git a/tools/dom/src/CrossFrameTypes.dart b/tools/dom/src/CrossFrameTypes.dart
index a2aa545..7d210a3 100644
--- a/tools/dom/src/CrossFrameTypes.dart
+++ b/tools/dom/src/CrossFrameTypes.dart
@@ -61,7 +61,7 @@
* WindowBase otherWindow = thisWindow.open('http://www.example.com/', 'foo');
* print(otherWindow.opener == thisWindow); // 'true'
*/
- WindowBase get opener;
+ WindowBase? get opener;
/**
* A reference to the parent of this window.
@@ -75,7 +75,7 @@
*
* print(window.parent == window) // 'true'
*/
- WindowBase get parent;
+ WindowBase? get parent;
/**
* A reference to the topmost window in the window hierarchy.
@@ -96,7 +96,7 @@
*
* print(window.top == window) // 'true'
*/
- WindowBase get top;
+ WindowBase? get top;
// Methods.
/**
@@ -142,7 +142,7 @@
* from WHATWG.
*/
void postMessage(var message, String targetOrigin,
- [List<MessagePort> messagePorts]);
+ [List<MessagePort>? messagePorts]);
}
abstract class LocationBase {
diff --git a/tools/dom/src/CssClassSet.dart b/tools/dom/src/CssClassSet.dart
index d1507bc..6a76202 100644
--- a/tools/dom/src/CssClassSet.dart
+++ b/tools/dom/src/CssClassSet.dart
@@ -24,7 +24,7 @@
* non-empty string containing no whitespace. To toggle multiple classes, use
* [toggleAll].
*/
- bool toggle(String value, [bool shouldAdd]);
+ bool toggle(String value, [bool? shouldAdd]);
/**
* Returns [:true:] if classes cannot be added or removed from this
@@ -41,7 +41,7 @@
* [value] must be a valid 'token' representing a single class, i.e. a
* non-empty string containing no whitespace.
*/
- bool contains(Object value);
+ bool contains(Object? value);
/**
* Add the class [value] to element.
@@ -72,7 +72,7 @@
* non-empty string containing no whitespace. To remove multiple classes, use
* [removeAll].
*/
- bool remove(Object value);
+ bool remove(Object? value);
/**
* Add all classes specified in [iterable] to element.
@@ -94,7 +94,7 @@
* Each element of [iterable] must be a valid 'token' representing a single
* class, i.e. a non-empty string containing no whitespace.
*/
- void removeAll(Iterable<Object> iterable);
+ void removeAll(Iterable<Object?> iterable);
/**
* Toggles all classes specified in [iterable] on element.
@@ -109,5 +109,5 @@
* Each element of [iterable] must be a valid 'token' representing a single
* class, i.e. a non-empty string containing no whitespace.
*/
- void toggleAll(Iterable<String> iterable, [bool shouldAdd]);
+ void toggleAll(Iterable<String> iterable, [bool? shouldAdd]);
}
diff --git a/tools/dom/src/CssRectangle.dart b/tools/dom/src/CssRectangle.dart
index 9cdef06..d26aac7 100644
--- a/tools/dom/src/CssRectangle.dart
+++ b/tools/dom/src/CssRectangle.dart
@@ -75,9 +75,9 @@
class _ContentCssListRect extends _ContentCssRect {
List<Element> _elementList;
- _ContentCssListRect(List<Element> elementList) : super(elementList.first) {
- _elementList = elementList;
- }
+ _ContentCssListRect(List<Element> elementList)
+ : _elementList = elementList,
+ super(elementList.first);
/**
* Set the height to `newHeight`.
@@ -299,7 +299,7 @@
* Returns the intersection of this and `other`, or `null` if they don't
* intersect.
*/
- Rectangle<num> intersection(Rectangle<num> other) {
+ Rectangle<num>? intersection(Rectangle<num> other) {
var x0 = max(left, other.left);
var x1 = min(left + width, other.left + other.width);
diff --git a/tools/dom/src/Dimension.dart b/tools/dom/src/Dimension.dart
index 502b68d..7ed02192 100644
--- a/tools/dom/src/Dimension.dart
+++ b/tools/dom/src/Dimension.dart
@@ -57,7 +57,9 @@
* `inherit` or invalid CSS will cause this constructor to throw a
* FormatError.
*/
- Dimension.css(String cssValue) {
+ Dimension.css(String cssValue)
+ : _unit = '',
+ _value = 0 {
if (cssValue == '') cssValue = '0px';
if (cssValue.endsWith('%')) {
_unit = '%';
diff --git a/tools/dom/src/EventStreamProvider.dart b/tools/dom/src/EventStreamProvider.dart
index 83b8c07..fe01be9 100644
--- a/tools/dom/src/EventStreamProvider.dart
+++ b/tools/dom/src/EventStreamProvider.dart
@@ -34,7 +34,7 @@
* * [EventTarget.addEventListener](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener)
* from MDN.
*/
- Stream<T> forTarget(EventTarget e, {bool useCapture: false}) =>
+ Stream<T> forTarget(EventTarget? e, {bool useCapture: false}) =>
new _EventStream<T>(e, _eventType, useCapture);
/**
@@ -126,7 +126,7 @@
* Adapter for exposing DOM events as Dart streams.
*/
class _EventStream<T extends Event> extends Stream<T> {
- final EventTarget _target;
+ final EventTarget? _target;
final String _eventType;
final bool _useCapture;
@@ -134,16 +134,16 @@
// DOM events are inherently multi-subscribers.
Stream<T> asBroadcastStream(
- {void onListen(StreamSubscription<T> subscription),
- void onCancel(StreamSubscription<T> subscription)}) =>
+ {void onListen(StreamSubscription<T> subscription)?,
+ void onCancel(StreamSubscription<T> subscription)?}) =>
this;
bool get isBroadcast => true;
// TODO(9757): Inlining should be smart and inline only when inlining would
// enable scalar replacement of an immediately allocated receiver.
@pragma('dart2js:tryInline')
- StreamSubscription<T> listen(void onData(T event),
- {Function onError, void onDone(), bool cancelOnError}) {
+ StreamSubscription<T> listen(void onData(T event)?,
+ {Function? onError, void onDone()?, bool? cancelOnError}) {
return new _EventStreamSubscription<T>(
this._target, this._eventType, onData, this._useCapture);
}
@@ -194,8 +194,8 @@
});
// Delegate all regular Stream behavior to a wrapped Stream.
- StreamSubscription<T> listen(void onData(T event),
- {Function onError, void onDone(), bool cancelOnError}) {
+ StreamSubscription<T> listen(void onData(T event)?,
+ {Function? onError, void onDone()?, bool? cancelOnError}) {
var pool = new _StreamPool<T>.broadcast();
for (var target in _targetList) {
pool.add(new _EventStream<T>(target, _eventType, _useCapture));
@@ -213,8 +213,8 @@
}
Stream<T> asBroadcastStream(
- {void onListen(StreamSubscription<T> subscription),
- void onCancel(StreamSubscription<T> subscription)}) =>
+ {void onListen(StreamSubscription<T> subscription)?,
+ void onCancel(StreamSubscription<T> subscription)?}) =>
this;
bool get isBroadcast => true;
}
@@ -225,9 +225,9 @@
class _EventStreamSubscription<T extends Event> extends StreamSubscription<T> {
int _pauseCount = 0;
- EventTarget _target;
+ EventTarget? _target;
final String _eventType;
- EventListener _onData;
+ EventListener? _onData;
final bool _useCapture;
// TODO(leafp): It would be better to write this as
@@ -240,7 +240,7 @@
// which are typed correctly. But this currently runs afoul of restrictions
// on is checks for compatibility with the VM.
_EventStreamSubscription(
- this._target, this._eventType, void onData(T event), this._useCapture)
+ this._target, this._eventType, void onData(T event)?, this._useCapture)
: _onData = onData == null
? null
: _wrapZone<Event>((e) => (onData as dynamic)(e)) {
@@ -248,18 +248,23 @@
}
Future cancel() {
- if (_canceled) return null;
+ // Check for strong mode. This function can no longer return null in strong
+ // mode, so only return null in weak mode to preserve synchronous timing.
+ // See issue 41653 for more details.
+ dynamic emptyFuture =
+ typeAcceptsNull<Event>() ? null : Future<void>.value();
+ if (_canceled) return emptyFuture as Future;
_unlisten();
// Clear out the target to indicate this is complete.
_target = null;
_onData = null;
- return null;
+ return emptyFuture as Future;
}
bool get _canceled => _target == null;
- void onData(void handleData(T event)) {
+ void onData(void handleData(T event)?) {
if (_canceled) {
throw new StateError("Subscription has been canceled.");
}
@@ -272,12 +277,12 @@
}
/// Has no effect.
- void onError(Function handleError) {}
+ void onError(Function? handleError) {}
/// Has no effect.
- void onDone(void handleDone()) {}
+ void onDone(void handleDone()?) {}
- void pause([Future resumeSignal]) {
+ void pause([Future? resumeSignal]) {
if (_canceled) return;
++_pauseCount;
_unlisten();
@@ -297,17 +302,17 @@
void _tryResume() {
if (_onData != null && !isPaused) {
- _target.addEventListener(_eventType, _onData, _useCapture);
+ _target!.addEventListener(_eventType, _onData, _useCapture);
}
}
void _unlisten() {
if (_onData != null) {
- _target.removeEventListener(_eventType, _onData, _useCapture);
+ _target!.removeEventListener(_eventType, _onData, _useCapture);
}
}
- Future<E> asFuture<E>([E futureValue]) {
+ Future<E> asFuture<E>([E? futureValue]) {
// We just need a future that will never succeed or fail.
var completer = new Completer<E>();
return completer.future;
@@ -332,21 +337,20 @@
/** The type of event this stream is providing (e.g. "keydown"). */
String _type;
- _CustomEventStreamImpl(String type) {
- _type = type;
- _streamController = new StreamController.broadcast(sync: true);
- }
+ _CustomEventStreamImpl(String type)
+ : _type = type,
+ _streamController = new StreamController.broadcast(sync: true);
// Delegate all regular Stream behavior to our wrapped Stream.
- StreamSubscription<T> listen(void onData(T event),
- {Function onError, void onDone(), bool cancelOnError}) {
+ StreamSubscription<T> listen(void onData(T event)?,
+ {Function? onError, void onDone()?, bool? cancelOnError}) {
return _streamController.stream.listen(onData,
onError: onError, onDone: onDone, cancelOnError: cancelOnError);
}
Stream<T> asBroadcastStream(
- {void onListen(StreamSubscription<T> subscription),
- void onCancel(StreamSubscription<T> subscription)}) =>
+ {void onListen(StreamSubscription<T> subscription)?,
+ void onCancel(StreamSubscription<T> subscription)?}) =>
_streamController.stream;
bool get isBroadcast => true;
@@ -362,7 +366,7 @@
void add(KeyEvent event) {
if (event.type == _type) {
- event.currentTarget.dispatchEvent(event._parent);
+ event.currentTarget!.dispatchEvent(event._parent);
_streamController.add(event);
}
}
@@ -374,7 +378,7 @@
*/
// TODO (efortuna): Remove this when Issue 12218 is addressed.
class _StreamPool<T> {
- StreamController<T> _controller;
+ StreamController<T>? _controller;
/// Subscriptions to the streams that make up the pool.
var _subscriptions = new Map<Stream<T>, StreamSubscription<T>>();
@@ -394,7 +398,7 @@
/**
* The stream through which all events from streams in the pool are emitted.
*/
- Stream<T> get stream => _controller.stream;
+ Stream<T> get stream => _controller!.stream;
/**
* Adds [stream] as a member of this pool.
@@ -405,8 +409,8 @@
*/
void add(Stream<T> stream) {
if (_subscriptions.containsKey(stream)) return;
- _subscriptions[stream] = stream.listen(_controller.add,
- onError: _controller.addError, onDone: () => remove(stream));
+ _subscriptions[stream] = stream.listen(_controller!.add,
+ onError: _controller!.addError, onDone: () => remove(stream));
}
/** Removes [stream] as a member of this pool. */
@@ -421,7 +425,7 @@
subscription.cancel();
}
_subscriptions.clear();
- _controller.close();
+ _controller!.close();
}
}
@@ -434,7 +438,7 @@
final _eventTypeGetter;
const _CustomEventStreamProvider(this._eventTypeGetter);
- Stream<T> forTarget(EventTarget e, {bool useCapture: false}) {
+ Stream<T> forTarget(EventTarget? e, {bool useCapture: false}) {
return new _EventStream<T>(e, _eventTypeGetter(e), useCapture);
}
diff --git a/tools/dom/src/Html5NodeValidator.dart b/tools/dom/src/Html5NodeValidator.dart
index 8d04a1b..e6bdede 100644
--- a/tools/dom/src/Html5NodeValidator.dart
+++ b/tools/dom/src/Html5NodeValidator.dart
@@ -408,8 +408,8 @@
* All known URI attributes will be validated against the UriPolicy, if
* [uriPolicy] is null then a default UriPolicy will be used.
*/
- _Html5NodeValidator({UriPolicy uriPolicy})
- : uriPolicy = uriPolicy != null ? uriPolicy : new UriPolicy() {
+ _Html5NodeValidator({UriPolicy? uriPolicy})
+ : uriPolicy = uriPolicy ?? UriPolicy() {
if (_attributeValidators.isEmpty) {
for (var attr in _standardAttributes) {
_attributeValidators[attr] = _standardAttributeValidator;
diff --git a/tools/dom/src/ImmutableListMixin.dart b/tools/dom/src/ImmutableListMixin.dart
index ecf5e7c..9910ebe 100644
--- a/tools/dom/src/ImmutableListMixin.dart
+++ b/tools/dom/src/ImmutableListMixin.dart
@@ -22,11 +22,11 @@
throw new UnsupportedError("Cannot add to immutable List.");
}
- void sort([int compare(E a, E b)]) {
+ void sort([int compare(E a, E b)?]) {
throw new UnsupportedError("Cannot sort immutable List.");
}
- void shuffle([Random random]) {
+ void shuffle([Random? random]) {
throw new UnsupportedError("Cannot shuffle immutable List.");
}
@@ -50,7 +50,7 @@
throw new UnsupportedError("Cannot remove from immutable List.");
}
- bool remove(Object object) {
+ bool remove(Object? object) {
throw new UnsupportedError("Cannot remove from immutable List.");
}
@@ -74,7 +74,7 @@
throw new UnsupportedError("Cannot modify an immutable List.");
}
- void fillRange(int start, int end, [E fillValue]) {
+ void fillRange(int start, int end, [E? fillValue]) {
throw new UnsupportedError("Cannot modify an immutable List.");
}
}
diff --git a/tools/dom/src/KeyboardEventStream.dart b/tools/dom/src/KeyboardEventStream.dart
index 48f7a68..399c037 100644
--- a/tools/dom/src/KeyboardEventStream.dart
+++ b/tools/dom/src/KeyboardEventStream.dart
@@ -22,7 +22,7 @@
final String _type;
/** The element we are watching for events to happen on. */
- final EventTarget _target;
+ final EventTarget? _target;
// The distance to shift from upper case alphabet Roman letters to lower case.
static final int _ROMAN_ALPHABET_OFFSET = "a".codeUnits[0] - "A".codeUnits[0];
@@ -65,7 +65,7 @@
/** Return a stream for KeyEvents for the specified target. */
// Note: this actually functions like a factory constructor.
- CustomStream<KeyEvent> forTarget(EventTarget e, {bool useCapture: false}) {
+ CustomStream<KeyEvent> forTarget(EventTarget? e, {bool useCapture: false}) {
var handler =
new _KeyboardEventHandler.initializeAllEventListeners(_type, e);
return handler._stream;
@@ -85,7 +85,8 @@
* and charcodes when they are not provided.
*/
_KeyboardEventHandler.initializeAllEventListeners(this._type, this._target)
- : super(_EVENT_TYPE) {
+ : _stream = new _CustomKeyEventStreamImpl(_type),
+ super(_EVENT_TYPE) {
Element.keyDownEvent
.forTarget(_target, useCapture: true)
.listen(processKeyDown);
@@ -95,7 +96,6 @@
Element.keyUpEvent
.forTarget(_target, useCapture: true)
.listen(processKeyUp);
- _stream = new _CustomKeyEventStreamImpl(_type);
}
/** Determine if caps lock is one of the currently depressed keys. */
@@ -337,7 +337,7 @@
_keyIdentifier.containsKey(e._shadowKeyIdentifier)) {
// This is needed for Safari Windows because it currently doesn't give a
// keyCode/which for non printable keys.
- e._shadowKeyCode = _keyIdentifier[e._shadowKeyIdentifier];
+ e._shadowKeyCode = _keyIdentifier[e._shadowKeyIdentifier]!;
}
e._shadowAltKey = _keyDownList.any((var element) => element.altKey);
_stream.add(e);
@@ -346,7 +346,7 @@
/** Handle keyup events. */
void processKeyUp(KeyboardEvent event) {
var e = new KeyEvent.wrap(event);
- KeyboardEvent toRemove = null;
+ KeyboardEvent? toRemove = null;
for (var key in _keyDownList) {
if (key.keyCode == e.keyCode) {
toRemove = key;
diff --git a/tools/dom/src/NodeValidatorBuilder.dart b/tools/dom/src/NodeValidatorBuilder.dart
index 92cfb3a..59f2b27 100644
--- a/tools/dom/src/NodeValidatorBuilder.dart
+++ b/tools/dom/src/NodeValidatorBuilder.dart
@@ -58,7 +58,7 @@
* The UriPolicy can be used to restrict the locations the navigation elements
* are allowed to direct to. By default this will use the default [UriPolicy].
*/
- void allowNavigation([UriPolicy uriPolicy]) {
+ void allowNavigation([UriPolicy? uriPolicy]) {
if (uriPolicy == null) {
uriPolicy = new UriPolicy();
}
@@ -71,7 +71,7 @@
* The UriPolicy can be used to restrict the locations the images may be
* loaded from. By default this will use the default [UriPolicy].
*/
- void allowImages([UriPolicy uriPolicy]) {
+ void allowImages([UriPolicy? uriPolicy]) {
if (uriPolicy == null) {
uriPolicy = new UriPolicy();
}
@@ -112,7 +112,7 @@
* If [tagName] is not specified then this allows inline styles on all
* elements. Otherwise tagName limits the styles to the specified elements.
*/
- void allowInlineStyles({String tagName}) {
+ void allowInlineStyles({String? tagName}) {
if (tagName == null) {
tagName = '*';
} else {
@@ -130,7 +130,7 @@
* Common things which are not allowed are script elements, style attributes
* and any script handlers.
*/
- void allowHtml5({UriPolicy uriPolicy}) {
+ void allowHtml5({UriPolicy? uriPolicy}) {
add(new _Html5NodeValidator(uriPolicy: uriPolicy));
}
@@ -149,9 +149,9 @@
* tag extensions.
*/
void allowCustomElement(String tagName,
- {UriPolicy uriPolicy,
- Iterable<String> attributes,
- Iterable<String> uriAttributes}) {
+ {UriPolicy? uriPolicy,
+ Iterable<String>? attributes,
+ Iterable<String>? uriAttributes}) {
var tagNameUpper = tagName.toUpperCase();
var attrs = attributes
?.map<String>((name) => '$tagNameUpper::${name.toLowerCase()}');
@@ -174,9 +174,9 @@
* custom tags.
*/
void allowTagExtension(String tagName, String baseName,
- {UriPolicy uriPolicy,
- Iterable<String> attributes,
- Iterable<String> uriAttributes}) {
+ {UriPolicy? uriPolicy,
+ Iterable<String>? attributes,
+ Iterable<String>? uriAttributes}) {
var baseNameUpper = baseName.toUpperCase();
var tagNameUpper = tagName.toUpperCase();
var attrs = attributes
@@ -192,9 +192,9 @@
}
void allowElement(String tagName,
- {UriPolicy uriPolicy,
- Iterable<String> attributes,
- Iterable<String> uriAttributes}) {
+ {UriPolicy? uriPolicy,
+ Iterable<String>? attributes,
+ Iterable<String>? uriAttributes}) {
allowCustomElement(tagName,
uriPolicy: uriPolicy,
attributes: attributes,
@@ -236,7 +236,7 @@
final Set<String> allowedElements = new Set<String>();
final Set<String> allowedAttributes = new Set<String>();
final Set<String> allowedUriAttributes = new Set<String>();
- final UriPolicy uriPolicy;
+ final UriPolicy? uriPolicy;
factory _SimpleNodeValidator.allowNavigation(UriPolicy uriPolicy) {
return new _SimpleNodeValidator(uriPolicy, allowedElements: const [
@@ -311,9 +311,9 @@
* lowercase attribute name. For example `'IMG:src'`.
*/
_SimpleNodeValidator(this.uriPolicy,
- {Iterable<String> allowedElements,
- Iterable<String> allowedAttributes,
- Iterable<String> allowedUriAttributes}) {
+ {Iterable<String>? allowedElements,
+ Iterable<String>? allowedAttributes,
+ Iterable<String>? allowedUriAttributes}) {
this.allowedElements.addAll(allowedElements ?? const []);
allowedAttributes = allowedAttributes ?? const [];
allowedUriAttributes = allowedUriAttributes ?? const [];
@@ -333,9 +333,9 @@
bool allowsAttribute(Element element, String attributeName, String value) {
var tagName = Element._safeTagName(element);
if (allowedUriAttributes.contains('$tagName::$attributeName')) {
- return uriPolicy.allowsUri(value);
+ return uriPolicy!.allowsUri(value);
} else if (allowedUriAttributes.contains('*::$attributeName')) {
- return uriPolicy.allowsUri(value);
+ return uriPolicy!.allowsUri(value);
} else if (allowedAttributes.contains('$tagName::$attributeName')) {
return true;
} else if (allowedAttributes.contains('*::$attributeName')) {
@@ -356,8 +356,8 @@
_CustomElementNodeValidator(
UriPolicy uriPolicy,
Iterable<String> allowedElements,
- Iterable<String> allowedAttributes,
- Iterable<String> allowedUriAttributes,
+ Iterable<String>? allowedAttributes,
+ Iterable<String>? allowedUriAttributes,
bool allowTypeExtension,
bool allowCustomTag)
: this.allowTypeExtension = allowTypeExtension == true,
diff --git a/tools/dom/src/Validators.dart b/tools/dom/src/Validators.dart
index ce706ef..a498da9 100644
--- a/tools/dom/src/Validators.dart
+++ b/tools/dom/src/Validators.dart
@@ -19,7 +19,7 @@
*
* If a uriPolicy is not specified then the default uriPolicy will be used.
*/
- factory NodeValidator({UriPolicy uriPolicy}) =>
+ factory NodeValidator({UriPolicy? uriPolicy}) =>
new _Html5NodeValidator(uriPolicy: uriPolicy);
factory NodeValidator.throws(NodeValidator base) =>
@@ -163,12 +163,12 @@
_ValidatingTreeSanitizer(this.validator) {}
void sanitizeTree(Node node) {
- void walk(Node node, Node parent) {
+ void walk(Node node, Node? parent) {
sanitizeNode(node, parent);
var child = node.lastChild;
while (null != child) {
- Node nextChild;
+ Node? nextChild;
try {
// Child may be removed during the walk, and we may not even be able
// to get its previousNode. But it's also possible that previousNode
@@ -199,7 +199,7 @@
}
/// Aggressively try to remove node.
- void _removeNode(Node node, Node parent) {
+ void _removeNode(Node node, Node? parent) {
// If we have the parent, it's presumably already passed more sanitization
// or is the fragment, so ask it to remove the child. And if that fails
// try to set the outer html.
@@ -212,7 +212,7 @@
}
/// Sanitize the element, assuming we can't trust anything about it.
- void _sanitizeUntrustedElement(/* Element */ element, Node parent) {
+ void _sanitizeUntrustedElement(/* Element */ element, Node? parent) {
// If the _hasCorruptedAttributes does not successfully return false,
// then we consider it corrupted and remove.
// TODO(alanknight): This is a workaround because on Firefox
@@ -261,8 +261,8 @@
/// Having done basic sanity checking on the element, and computed the
/// important attributes we want to check, remove it if it's not valid
/// or not allowed, either as a whole or particular attributes.
- void _sanitizeElement(Element element, Node parent, bool corrupted,
- String text, String tag, Map attrs, String isAttr) {
+ void _sanitizeElement(Element element, Node? parent, bool corrupted,
+ String text, String tag, Map attrs, String? isAttr) {
if (false != corrupted) {
_removeNode(element, parent);
window.console
@@ -304,7 +304,7 @@
}
/// Sanitize the node and its children recursively.
- void sanitizeNode(Node node, Node parent) {
+ void sanitizeNode(Node node, Node? parent) {
switch (node.nodeType) {
case Node.ELEMENT_NODE:
_sanitizeUntrustedElement(node, parent);
diff --git a/tools/dom/src/WrappedList.dart b/tools/dom/src/WrappedList.dart
index f3dcc53..65d0d94 100644
--- a/tools/dom/src/WrappedList.dart
+++ b/tools/dom/src/WrappedList.dart
@@ -26,7 +26,7 @@
_list.add(element);
}
- bool remove(Object element) => _list.remove(element);
+ bool remove(Object? element) => _list.remove(element);
void clear() {
_list.clear();
@@ -34,7 +34,7 @@
// List APIs
- E operator [](int index) => _list[index];
+ E operator [](int index) => _list[index] as E;
void operator []=(int index, E value) {
_list[index] = value;
@@ -44,19 +44,23 @@
_list.length = newLength;
}
- void sort([int compare(E a, E b)]) {
- // Implicit downcast on argument from Node to E-extends-Node.
- _list.sort((Node a, Node b) => compare(a, b));
+ void sort([int compare(E a, E b)?]) {
+ if (compare == null) {
+ _list.sort();
+ } else {
+ _list.sort((Node a, Node b) => compare(a as E, b as E));
+ }
}
- int indexOf(Object element, [int start = 0]) => _list.indexOf(element, start);
+ int indexOf(Object? element, [int start = 0]) =>
+ _list.indexOf(element as Node, start);
- int lastIndexOf(Object element, [int start]) =>
- _list.lastIndexOf(element, start);
+ int lastIndexOf(Object? element, [int? start]) =>
+ _list.lastIndexOf(element as Node, start);
void insert(int index, E element) => _list.insert(index, element);
- E removeAt(int index) => _list.removeAt(index);
+ E removeAt(int index) => _list.removeAt(index) as E;
void setRange(int start, int end, Iterable<E> iterable, [int skipCount = 0]) {
_list.setRange(start, end, iterable, skipCount);
@@ -70,7 +74,7 @@
_list.replaceRange(start, end, iterable);
}
- void fillRange(int start, int end, [E fillValue]) {
+ void fillRange(int start, int end, [E? fillValue]) {
_list.fillRange(start, end, fillValue);
}
@@ -89,5 +93,5 @@
return _iterator.moveNext();
}
- E get current => _iterator.current;
+ E get current => _iterator.current as E;
}
diff --git a/tools/dom/src/_ListIterators.dart b/tools/dom/src/_ListIterators.dart
index 0a9080e..74aaddc 100644
--- a/tools/dom/src/_ListIterators.dart
+++ b/tools/dom/src/_ListIterators.dart
@@ -9,7 +9,7 @@
final List<T> _array;
final int _length; // Cache array length for faster access.
int _position;
- T _current;
+ T? _current;
FixedSizeListIterator(List<T> array)
: _array = array,
@@ -28,14 +28,14 @@
return false;
}
- T get current => _current;
+ T get current => _current as T;
}
// Iterator for arrays with variable size.
class _VariableSizeListIterator<T> implements Iterator<T> {
final List<T> _array;
int _position;
- T _current;
+ T? _current;
_VariableSizeListIterator(List<T> array)
: _array = array,
@@ -53,5 +53,5 @@
return false;
}
- T get current => _current;
+ T get current => _current as T;
}
diff --git a/tools/dom/src/dart2js_Console.dart b/tools/dom/src/dart2js_Console.dart
index 5ed9206..b3297d2 100644
--- a/tools/dom/src/dart2js_Console.dart
+++ b/tools/dom/src/dart2js_Console.dart
@@ -10,7 +10,7 @@
bool get _isConsoleDefined => JS('bool', 'typeof console != "undefined"');
- MemoryInfo get memory =>
+ MemoryInfo? get memory =>
_isConsoleDefined ? JS('MemoryInfo', 'window.console.memory') : null;
void assertCondition(bool condition, Object arg) => _isConsoleDefined
diff --git a/tools/dom/src/dart2js_Conversions.dart b/tools/dom/src/dart2js_Conversions.dart
index aa78ec1..cb943b4 100644
--- a/tools/dom/src/dart2js_Conversions.dart
+++ b/tools/dom/src/dart2js_Conversions.dart
@@ -10,12 +10,12 @@
part of html;
-WindowBase _convertNativeToDart_Window(win) {
+WindowBase? _convertNativeToDart_Window(win) {
if (win == null) return null;
return _DOMWindowCrossFrame._createSafe(win);
}
-EventTarget _convertNativeToDart_EventTarget(e) {
+EventTarget? _convertNativeToDart_EventTarget(e) {
if (e == null) {
return null;
}
@@ -33,7 +33,7 @@
return e;
}
-EventTarget _convertDartToNative_EventTarget(e) {
+EventTarget? _convertDartToNative_EventTarget(e) {
if (e is _DOMWindowCrossFrame) {
return e._window;
} else {
diff --git a/tools/dom/src/dart2js_CssClassSet.dart b/tools/dom/src/dart2js_CssClassSet.dart
index 5ae6795..00d80bc 100644
--- a/tools/dom/src/dart2js_CssClassSet.dart
+++ b/tools/dom/src/dart2js_CssClassSet.dart
@@ -54,7 +54,7 @@
* TODO(sra): It seems wrong to collect a 'changed' flag like this when the
* underlying toggle returns an 'is set' flag.
*/
- bool toggle(String value, [bool shouldAdd]) => _sets.fold(
+ bool toggle(String value, [bool? shouldAdd]) => _sets.fold(
false,
(bool changed, CssClassSetImpl e) =>
e.toggle(value, shouldAdd) || changed);
@@ -66,7 +66,7 @@
* This is the Dart equivalent of jQuery's
* [removeClass](http://api.jquery.com/removeClass/).
*/
- bool remove(Object value) => _sets.fold(
+ bool remove(Object? value) => _sets.fold(
false, (bool changed, CssClassSetImpl e) => e.remove(value) || changed);
}
@@ -100,7 +100,7 @@
_element.className = '';
}
- bool contains(Object value) {
+ bool contains(Object? value) {
return _contains(_element, value);
}
@@ -108,11 +108,11 @@
return _add(_element, value);
}
- bool remove(Object value) {
+ bool remove(Object? value) {
return value is String && _remove(_element, value);
}
- bool toggle(String value, [bool shouldAdd]) {
+ bool toggle(String value, [bool? shouldAdd]) {
return _toggle(_element, value, shouldAdd);
}
@@ -120,11 +120,11 @@
_addAll(_element, iterable);
}
- void removeAll(Iterable<Object> iterable) {
+ void removeAll(Iterable<Object?> iterable) {
_removeAll(_element, iterable);
}
- void retainAll(Iterable<Object> iterable) {
+ void retainAll(Iterable<Object?> iterable) {
_removeWhere(_element, iterable.toSet().contains, false);
}
@@ -136,7 +136,7 @@
_removeWhere(_element, test, false);
}
- static bool _contains(Element _element, Object value) {
+ static bool _contains(Element _element, Object? value) {
return value is String && _classListContains(_classListOf(_element), value);
}
@@ -157,7 +157,7 @@
return removed;
}
- static bool _toggle(Element _element, String value, bool shouldAdd) {
+ static bool _toggle(Element _element, String value, bool? shouldAdd) {
// There is no value that can be passed as the second argument of
// DomTokenList.toggle that behaves the same as passing one argument.
// `null` is seen as false, meaning 'remove'.
@@ -171,13 +171,13 @@
return _classListToggle1(list, value);
}
- static bool _toggleOnOff(Element _element, String value, bool shouldAdd) {
+ static bool _toggleOnOff(Element _element, String value, bool? shouldAdd) {
DomTokenList list = _classListOf(_element);
// IE's toggle does not take a second parameter. We would prefer:
//
// return _classListToggle2(list, value, shouldAdd);
//
- if (shouldAdd) {
+ if (shouldAdd ?? false) {
_classListAdd(list, value);
return true;
} else {
@@ -193,10 +193,10 @@
}
}
- static void _removeAll(Element _element, Iterable<Object> iterable) {
+ static void _removeAll(Element _element, Iterable<Object?> iterable) {
DomTokenList list = _classListOf(_element);
- for (String value in iterable) {
- _classListRemove(list, value);
+ for (Object? value in iterable) {
+ _classListRemove(list, value as String);
}
}
@@ -205,7 +205,7 @@
DomTokenList list = _classListOf(_element);
int i = 0;
while (i < _classListLength(list)) {
- String item = list.item(i);
+ String item = list.item(i)!;
if (doRemove == test(item)) {
_classListRemove(list, item);
} else {
@@ -254,7 +254,7 @@
}
static bool _classListToggle2(
- DomTokenList list, String value, bool shouldAdd) {
+ DomTokenList list, String value, bool? shouldAdd) {
return JS('bool', '#.toggle(#, #)', list, value, shouldAdd);
}
}
diff --git a/tools/dom/src/dart2js_CustomElementSupport.dart b/tools/dom/src/dart2js_CustomElementSupport.dart
index 98c3c24..bbe6a37 100644
--- a/tools/dom/src/dart2js_CustomElementSupport.dart
+++ b/tools/dom/src/dart2js_CustomElementSupport.dart
@@ -66,7 +66,7 @@
}
}
-Function _registerCustomElement(context, document, String tag, [Map options]) {
+Function _registerCustomElement(context, document, String tag, [Map? options]) {
// Function follows the same pattern as the following JavaScript code for
// registering a custom element.
//
@@ -82,7 +82,7 @@
// var e = document.createElement('x-foo');
var extendsTagName = '';
- Type type;
+ Type? type;
if (options != null) {
extendsTagName = options['extends'];
type = options['prototype'];
@@ -162,7 +162,7 @@
var _constructor;
var _nativeType;
- _JSElementUpgrader(Document document, Type type, String extendsTag) {
+ _JSElementUpgrader(Document document, Type type, String? extendsTag) {
var interceptorClass = findInterceptorConstructorForType(type);
if (interceptorClass == null) {
throw new ArgumentError(type);
diff --git a/tools/dom/src/dart2js_DOMImplementation.dart b/tools/dom/src/dart2js_DOMImplementation.dart
index 393ec72..8016f6e 100644
--- a/tools/dom/src/dart2js_DOMImplementation.dart
+++ b/tools/dom/src/dart2js_DOMImplementation.dart
@@ -14,8 +14,8 @@
// Fields.
HistoryBase get history =>
_HistoryCrossFrame._createSafe(JS('HistoryBase', '#.history', _window));
- LocationBase get location => _LocationCrossFrame
- ._createSafe(JS('LocationBase', '#.location', _window));
+ LocationBase get location => _LocationCrossFrame._createSafe(
+ JS('LocationBase', '#.location', _window));
// TODO(vsm): Add frames to navigate subframes. See 2312.
@@ -30,8 +30,7 @@
// Methods.
void close() => JS('void', '#.close()', _window);
- void postMessage(var message, String targetOrigin,
- [List messagePorts = null]) {
+ void postMessage(var message, String targetOrigin, [List? messagePorts]) {
if (messagePorts == null) {
JS('void', '#.postMessage(#,#)', _window,
convertDartToNative_SerializedScriptValue(message), targetOrigin);
@@ -63,26 +62,26 @@
Events get on => throw new UnsupportedError(
'You can only attach EventListeners to your own window.');
// TODO(efortuna): Remove this method. dartbug.com/16814
- void _addEventListener(String type, EventListener listener,
- [bool useCapture]) =>
+ void _addEventListener(String? type, EventListener? listener,
+ [bool? useCapture]) =>
throw new UnsupportedError(
'You can only attach EventListeners to your own window.');
// TODO(efortuna): Remove this method. dartbug.com/16814
- void addEventListener(String type, EventListener listener,
- [bool useCapture]) =>
+ void addEventListener(String type, EventListener? listener,
+ [bool? useCapture]) =>
throw new UnsupportedError(
'You can only attach EventListeners to your own window.');
// TODO(efortuna): Remove this method. dartbug.com/16814
bool dispatchEvent(Event event) => throw new UnsupportedError(
'You can only attach EventListeners to your own window.');
// TODO(efortuna): Remove this method. dartbug.com/16814
- void _removeEventListener(String type, EventListener listener,
- [bool useCapture]) =>
+ void _removeEventListener(String? type, EventListener? listener,
+ [bool? useCapture]) =>
throw new UnsupportedError(
'You can only attach EventListeners to your own window.');
// TODO(efortuna): Remove this method. dartbug.com/16814
- void removeEventListener(String type, EventListener listener,
- [bool useCapture]) =>
+ void removeEventListener(String type, EventListener? listener,
+ [bool? useCapture]) =>
throw new UnsupportedError(
'You can only attach EventListeners to your own window.');
}
diff --git a/tools/dom/src/dart2js_KeyEvent.dart b/tools/dom/src/dart2js_KeyEvent.dart
index ad8c905..422c7ad 100644
--- a/tools/dom/src/dart2js_KeyEvent.dart
+++ b/tools/dom/src/dart2js_KeyEvent.dart
@@ -60,9 +60,10 @@
bool get _realAltKey => JS('bool', '#.altKey', _parent);
/** Shadows on top of the parent's currentTarget. */
- EventTarget _currentTarget;
+ EventTarget? _currentTarget;
- final InputDeviceCapabilities sourceCapabilities;
+ InputDeviceCapabilities? get sourceCapabilities =>
+ JS('InputDeviceCapabilities', '#.sourceCapabilities', this);
/**
* The value we want to use for this object's dispatch. Created here so it is
@@ -77,7 +78,12 @@
}
/** Construct a KeyEvent with [parent] as the event we're emulating. */
- KeyEvent.wrap(KeyboardEvent parent) : super(parent) {
+ KeyEvent.wrap(KeyboardEvent parent)
+ : _parent = parent,
+ _shadowAltKey = false,
+ _shadowCharCode = 0,
+ _shadowKeyCode = 0,
+ super(parent) {
_parent = parent;
_shadowAltKey = _realAltKey;
_shadowCharCode = _realCharCode;
@@ -87,7 +93,7 @@
/** Programmatically create a new KeyEvent (and KeyboardEvent). */
factory KeyEvent(String type,
- {Window view,
+ {Window? view,
bool canBubble: true,
bool cancelable: true,
int keyCode: 0,
@@ -97,12 +103,12 @@
bool altKey: false,
bool shiftKey: false,
bool metaKey: false,
- EventTarget currentTarget}) {
+ EventTarget? currentTarget}) {
if (view == null) {
view = window;
}
- var eventObj;
+ dynamic eventObj;
// Currently this works on everything but Safari. Safari throws an
// "Attempting to change access mechanism for an unconfigurable property"
@@ -119,17 +125,17 @@
JS(
'void',
"Object.defineProperty(#, 'keyCode', {"
- " get : function() { return this.keyCodeVal; } })",
+ " get : function() { return this.keyCodeVal; } })",
eventObj);
JS(
'void',
"Object.defineProperty(#, 'which', {"
- " get : function() { return this.keyCodeVal; } })",
+ " get : function() { return this.keyCodeVal; } })",
eventObj);
JS(
'void',
"Object.defineProperty(#, 'charCode', {"
- " get : function() { return this.charCodeVal; } })",
+ " get : function() { return this.charCodeVal; } })",
eventObj);
var keyIdentifier = _convertToHexString(charCode, keyCode);
@@ -152,10 +158,10 @@
static bool get canUseDispatchEvent => JS(
'bool',
'(typeof document.body.dispatchEvent == "function")'
- '&& document.body.dispatchEvent.length > 0');
+ '&& document.body.dispatchEvent.length > 0');
/** The currently registered target for this event. */
- EventTarget get currentTarget => _currentTarget;
+ EventTarget? get currentTarget => _currentTarget;
// This is an experimental method to be sure.
static String _convertToHexString(int charCode, int keyCode) {
@@ -198,9 +204,9 @@
bool get metaKey => _parent.metaKey;
/** True if the shift key was pressed during this event. */
bool get shiftKey => _parent.shiftKey;
- Window get view => _parent.view;
+ WindowBase? get view => _parent.view;
void _initUIEvent(
- String type, bool canBubble, bool cancelable, Window view, int detail) {
+ String type, bool canBubble, bool cancelable, Window? view, int detail) {
throw new UnsupportedError("Cannot initialize a UI Event from a KeyEvent.");
}
@@ -218,9 +224,9 @@
String type,
bool canBubble,
bool cancelable,
- Window view,
+ Window? view,
String keyIdentifier,
- int location,
+ int? location,
bool ctrlKey,
bool altKey,
bool shiftKey,
diff --git a/tools/dom/src/dart2js_Platform.dart b/tools/dom/src/dart2js_Platform.dart
index 5988011..995f4f4 100644
--- a/tools/dom/src/dart2js_Platform.dart
+++ b/tools/dom/src/dart2js_Platform.dart
@@ -10,7 +10,7 @@
* browser. If false, using these types will generate a runtime
* error.
*/
- static final supportsTypedData = JS('bool', '!!(window.ArrayBuffer)');
+ static final bool supportsTypedData = JS('bool', '!!(window.ArrayBuffer)');
/**
* Returns true if SIMD types in dart:typed_data types are supported
diff --git a/tools/dom/src/dart2js_WrappedEvent.dart b/tools/dom/src/dart2js_WrappedEvent.dart
index 4d2ac28..dff9e38 100644
--- a/tools/dom/src/dart2js_WrappedEvent.dart
+++ b/tools/dom/src/dart2js_WrappedEvent.dart
@@ -11,7 +11,7 @@
final Event wrapped;
/** The CSS selector involved with event delegation. */
- String _selector;
+ String? _selector;
_WrappedEvent(this.wrapped);
@@ -21,7 +21,7 @@
bool get composed => wrapped.composed;
- EventTarget get currentTarget => wrapped.currentTarget;
+ EventTarget? get currentTarget => wrapped.currentTarget;
bool get defaultPrevented => wrapped.defaultPrevented;
@@ -29,13 +29,13 @@
bool get isTrusted => wrapped.isTrusted;
- EventTarget get target => wrapped.target;
+ EventTarget? get target => wrapped.target;
- double get timeStamp => wrapped.timeStamp;
+ double get timeStamp => wrapped.timeStamp as double;
String get type => wrapped.type;
- void _initEvent(String type, [bool bubbles, bool cancelable]) {
+ void _initEvent(String type, [bool? bubbles, bool? cancelable]) {
throw new UnsupportedError('Cannot initialize this Event.');
}
@@ -63,13 +63,12 @@
throw new UnsupportedError('Cannot call matchingTarget if this Event did'
' not arise as a result of event delegation.');
}
- Element currentTarget = this.currentTarget;
- Element target = this.target;
- var matchedTarget;
+ Element? currentTarget = this.currentTarget as Element?;
+ Element? target = this.target as Element?;
do {
- if (target.matches(_selector)) return target;
+ if (target!.matches(_selector!)) return target;
target = target.parent;
- } while (target != null && target != currentTarget.parent);
+ } while (target != null && target != currentTarget!.parent);
throw new StateError('No selector matched for populating matchedTarget.');
}
@@ -83,7 +82,7 @@
* from W3C.
*/
// https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#extensions-to-event
- List<Node> get path => wrapped.path;
+ List<Node> get path => wrapped.path as List<Node>;
dynamic get _get_currentTarget => wrapped._get_currentTarget;
diff --git a/tools/dom/src/native_DOMImplementation.dart b/tools/dom/src/native_DOMImplementation.dart
index ee81750..79f5d04 100644
--- a/tools/dom/src/native_DOMImplementation.dart
+++ b/tools/dom/src/native_DOMImplementation.dart
@@ -137,7 +137,7 @@
return matches.toList();
}
- static setLibrary([String name]) {
+ static setLibrary([String? name]) {
// Bust cache in case library list has changed. Ideally we would listen for
// when libraries are loaded and invalidate based on that.
_validCache = false;
@@ -1084,16 +1084,16 @@
var history = _blink.BlinkWindow.instance.history_Getter_(this);
return history is _HistoryCrossFrame
? history
- : _blink.Blink_Utils
- .setInstanceInterceptor(history, _HistoryCrossFrame);
+ : _blink.Blink_Utils.setInstanceInterceptor(
+ history, _HistoryCrossFrame);
}
LocationBase get location {
var location = _blink.BlinkWindow.instance.location_Getter_(this);
return location is _LocationCrossFrame
? location
- : _blink.Blink_Utils
- .setInstanceInterceptor(location, _LocationCrossFrame);
+ : _blink.Blink_Utils.setInstanceInterceptor(
+ location, _LocationCrossFrame);
}
bool get closed => _blink.BlinkWindow.instance.closed_Getter_(this);
@@ -1107,7 +1107,7 @@
// Methods.
void close() => _blink.BlinkWindow.instance.close_Callback_0_(this);
void postMessage(Object message, String targetOrigin,
- [List<MessagePort> transfer]) =>
+ [List<MessagePort>? transfer]) =>
_blink.BlinkWindow.instance.postMessage_Callback_3_(
this,
convertDartToNative_SerializedScriptValue(message),
@@ -1122,12 +1122,12 @@
'You can only attach EventListeners to your own window.');
// TODO(efortuna): Remove this method. dartbug.com/16814
void _addEventListener(
- [String type, EventListener listener, bool useCapture]) =>
+ [String? type, EventListener? listener, bool? useCapture]) =>
throw new UnsupportedError(
'You can only attach EventListeners to your own window.');
// TODO(efortuna): Remove this method. dartbug.com/16814
- void addEventListener(String type, EventListener listener,
- [bool useCapture]) =>
+ void addEventListener(String type, EventListener? listener,
+ [bool? useCapture]) =>
throw new UnsupportedError(
'You can only attach EventListeners to your own window.');
// TODO(efortuna): Remove this method. dartbug.com/16814
@@ -1135,12 +1135,12 @@
'You can only attach EventListeners to your own window.');
// TODO(efortuna): Remove this method. dartbug.com/16814
void _removeEventListener(
- [String type, EventListener listener, bool useCapture]) =>
+ [String? type, EventListener? listener, bool? useCapture]) =>
throw new UnsupportedError(
'You can only attach EventListeners to your own window.');
// TODO(efortuna): Remove this method. dartbug.com/16814
- void removeEventListener(String type, EventListener listener,
- [bool useCapture]) =>
+ void removeEventListener(String type, EventListener? listener,
+ [bool? useCapture]) =>
throw new UnsupportedError(
'You can only attach EventListeners to your own window.');
}
@@ -1151,7 +1151,7 @@
// Methods.
void back() => _blink.BlinkHistory.instance.back_Callback_0_(this);
void forward() => _blink.BlinkHistory.instance.forward_Callback_0_(this);
- void go([int delta]) {
+ void go([int? delta]) {
if (delta != null) {
_blink.BlinkHistory.instance.go_Callback_1_(this, delta);
return;
diff --git a/tools/dom/src/shared_SVGFactoryProviders.dart b/tools/dom/src/shared_SVGFactoryProviders.dart
index a4aab93..2c729f7 100644
--- a/tools/dom/src/shared_SVGFactoryProviders.dart
+++ b/tools/dom/src/shared_SVGFactoryProviders.dart
@@ -8,6 +8,6 @@
static SvgElement createSvgElement_tag(String tag) {
final Element temp =
document.createElementNS("http://www.w3.org/2000/svg", tag);
- return temp;
+ return temp as SvgElement;
}
}
diff --git a/tools/dom/src/shared_html.dart b/tools/dom/src/shared_html.dart
index f2aa8a9..f86f288 100644
--- a/tools/dom/src/shared_html.dart
+++ b/tools/dom/src/shared_html.dart
@@ -4,14 +4,15 @@
part of dart.dom.html;
-void Function(T) _wrapZone<T>(void Function(T) callback) {
+void Function(T)? _wrapZone<T>(void Function(T)? callback) {
// For performance reasons avoid wrapping if we are in the root zone.
if (Zone.current == Zone.root) return callback;
if (callback == null) return null;
return Zone.current.bindUnaryCallbackGuarded(callback);
}
-void Function(T1, T2) _wrapBinaryZone<T1, T2>(void Function(T1, T2) callback) {
+void Function(T1, T2)? _wrapBinaryZone<T1, T2>(
+ void Function(T1, T2)? callback) {
// For performance reasons avoid wrapping if we are in the root zone.
if (Zone.current == Zone.root) return callback;
if (callback == null) return null;
@@ -35,7 +36,7 @@
* For details about CSS selector syntax, see the
* [CSS selector specification](http://www.w3.org/TR/css3-selectors/).
*/
-Element querySelector(String selectors) => document.querySelector(selectors);
+Element? querySelector(String selectors) => document.querySelector(selectors);
/**
* Finds all descendant elements of this document that match the specified
diff --git a/tools/migration/bin/test.dart b/tools/migration/bin/test.dart
index 561b100..875c5b7 100644
--- a/tools/migration/bin/test.dart
+++ b/tools/migration/bin/test.dart
@@ -36,6 +36,14 @@
"vm": "dartk-weak-asserts-linux-release-x64",
};
+const legacyConfigurations = {
+ "analyzer": "analyzer-asserts-linux",
+ "cfe": "cfe-linux",
+ "dart2js": "dart2js-hostasserts-linux-x64-d8",
+ "ddc": "dartdevk-checked-linux-release-chrome",
+ "vm": "dartk-checked-linux-release-x64",
+};
+
final _failurePattern = RegExp(r"FAILED: [a-z0-9_-]+ [a-z0-9_-]+ (.*)");
void main(List<String> arguments) async {
@@ -157,8 +165,15 @@
// configuration. Otherwise, use the right named configuration.
List<String> testArgs;
if (Platform.isLinux || compiler != "ddc") {
- var configurations = isStrong ? strongConfigurations : weakConfigurations;
- var configuration = configurations[compiler];
+ String configuration;
+ if (isLegacy) {
+ configuration = legacyConfigurations[compiler];
+ } else if (isStrong) {
+ configuration = strongConfigurations[compiler];
+ } else {
+ configuration = weakConfigurations[compiler];
+ }
+
if (!Platform.isLinux) {
// TODO(rnystrom): We'll probably never need to run this script on
// Windows, but if we do... do that.
diff --git a/utils/dartdevc/BUILD.gn b/utils/dartdevc/BUILD.gn
index 9591612..15bfe16 100644
--- a/utils/dartdevc/BUILD.gn
+++ b/utils/dartdevc/BUILD.gn
@@ -322,16 +322,13 @@
]
if (invoker.sound_null_safety) {
- args += [
- "--nnbd-strong",
- ]
+ args += [ "--nnbd-strong" ]
outputs = [
sdk_full_sound_dill,
sdk_outline_sound_dill,
]
} else {
-
outputs = [
sdk_full_dill,
sdk_outline_dill,
@@ -387,7 +384,7 @@
"$js_gen_dir/legacy/dart_sdk.js.map",
]
- script = "../../pkg/dev_compiler/tool/compile_dartdevc_sdk.dart"
+ script = "../../pkg/dev_compiler/bin/dartdevc.dart"
args = [
"--multi-root-scheme",