Version 0.6.17.0 .
svn merge -r 25973:25988 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
git-svn-id: http://dart.googlecode.com/svn/trunk@25990 260f80e4-7a28-3924-810f-c04153c831b5
diff --git a/pkg/barback/lib/barback.dart b/pkg/barback/lib/barback.dart
index d10827f..a211873 100644
--- a/pkg/barback/lib/barback.dart
+++ b/pkg/barback/lib/barback.dart
@@ -11,4 +11,5 @@
export 'src/errors.dart';
export 'src/package_provider.dart';
export 'src/transform.dart' show Transform;
-export 'src/transformer.dart';
\ No newline at end of file
+export 'src/transform_logger.dart';
+export 'src/transformer.dart';
diff --git a/pkg/barback/lib/src/transform.dart b/pkg/barback/lib/src/transform.dart
index d61e15c..c254778 100644
--- a/pkg/barback/lib/src/transform.dart
+++ b/pkg/barback/lib/src/transform.dart
@@ -11,6 +11,7 @@
import 'asset_node.dart';
import 'asset_set.dart';
import 'errors.dart';
+import 'transform_logger.dart';
import 'transform_node.dart';
import 'utils.dart';
@@ -45,6 +46,9 @@
/// would be secondary inputs.
AssetId get primaryId => _node.primary.id;
+ /// A logger so that the [Transformer] can report build details.
+ TransformLogger get logger => _logger;
+
/// Gets the asset for the primary input.
Future<Asset> get primaryInput => getInput(primaryId);
@@ -65,3 +69,6 @@
_outputs.add(output);
}
}
+
+// TODO(sigmund,rnystrom): create a separate logger for each Transfom.
+final TransformLogger _logger = new TransformLogger(true);
diff --git a/pkg/barback/lib/src/transform_logger.dart b/pkg/barback/lib/src/transform_logger.dart
new file mode 100644
index 0000000..131be29
--- /dev/null
+++ b/pkg/barback/lib/src/transform_logger.dart
@@ -0,0 +1,40 @@
+// 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.
+
+library barback.transform_logger;
+
+import 'package:source_maps/span.dart';
+
+/// Object used to report warnings and errors encountered while running a
+/// transformer.
+class TransformLogger {
+
+ bool _shouldPrint;
+
+ TransformLogger(this._shouldPrint);
+
+ /// Logs a warning message.
+ ///
+ /// If present, [span] indicates the location in the input asset that caused
+ /// the warning.
+ void warning(String message, [Span span]) {
+ _printMessage('warning', message, span);
+ }
+
+ /// Logs an error message.
+ ///
+ /// If present, [span] indicates the location in the input asset that caused
+ /// the error.
+ // TODO(sigmund,nweiz): clarify when an error should be logged or thrown.
+ void error(String message, [Span span]) {
+ _printMessage('error', message, span);
+ }
+
+ // TODO(sigmund,rnystrom): do something better than printing.
+ _printMessage(String prefix, String message, Span span) {
+ if (!_shouldPrint) return;
+ print(span == null ? '$prefix $message'
+ : '$prefix ${span.getLocationMessage(message)}');
+ }
+}
diff --git a/pkg/barback/pubspec.yaml b/pkg/barback/pubspec.yaml
index e0786a8..9cebb0b 100644
--- a/pkg/barback/pubspec.yaml
+++ b/pkg/barback/pubspec.yaml
@@ -13,6 +13,7 @@
responsiveness.
dependencies:
path: any
+ source_maps: any
stack_trace: any
dev_dependencies:
scheduled_test: any
diff --git a/pkg/barback/test/package_graph/errors_test.dart b/pkg/barback/test/package_graph/errors_test.dart
index 973155b..6e7f2fb 100644
--- a/pkg/barback/test/package_graph/errors_test.dart
+++ b/pkg/barback/test/package_graph/errors_test.dart
@@ -38,7 +38,7 @@
expectAsset("app|foo.c", "foo.c");
buildShouldSucceed();
- schedule(() => updateSources(["app|foo.b"]));
+ updateSources(["app|foo.b"]);
buildShouldFail([isAssetCollisionException("app|foo.c")]);
});
@@ -54,7 +54,7 @@
test("reports an error for an unprovided package", () {
initGraph();
- expect(() => updateSources(["unknown|foo.txt"]), throwsArgumentError);
+ expect(() => updateSourcesSync(["unknown|foo.txt"]), throwsArgumentError);
});
test("reports an error for an unprovided source", () {
@@ -69,11 +69,9 @@
[new ManyToOneTransformer("txt")]
]});
- buildShouldFail([isMissingInputException("app|a.inc")]);
-
updateSources(["app|a.txt"]);
-
expectNoAsset("app|a.out");
+ buildShouldFail([isMissingInputException("app|a.inc")]);
});
test("reports an error if a transformer emits an asset for another package",
@@ -82,9 +80,8 @@
"app": [[new CreateAssetTransformer("wrong|foo.txt")]]
});
- buildShouldFail([isInvalidOutputException("app", "wrong|foo.txt")]);
-
updateSources(["app|foo.txt"]);
+ buildShouldFail([isInvalidOutputException("app", "wrong|foo.txt")]);
});
test("fails if a non-primary input is removed", () {
@@ -101,10 +98,7 @@
expectAsset("app|a.out", "abc");
buildShouldSucceed();
- schedule(() {
- removeSources(["app|b.inc"]);
- });
-
+ removeSources(["app|b.inc"]);
buildShouldFail([isMissingInputException("app|b.inc")]);
expectNoAsset("app|a.out");
});
@@ -114,12 +108,8 @@
[new BadTransformer(["app|foo.out"])]
]});
- schedule(() {
- updateSources(["app|foo.txt"]);
- });
-
+ updateSources(["app|foo.txt"]);
expectNoAsset("app|foo.out");
-
buildShouldFail([equals(BadTransformer.ERROR)]);
});
@@ -128,22 +118,15 @@
[new BadTransformer(["app|foo.txt"])]
]});
- schedule(() {
- updateSources(["app|foo.txt"]);
- });
-
+ updateSources(["app|foo.txt"]);
expectNoAsset("app|foo.txt");
});
test("catches errors even if nothing is waiting for process results", () {
initGraph(["app|foo.txt"], {"app": [[new BadTransformer([])]]});
- schedule(() {
- updateSources(["app|foo.txt"]);
- });
-
+ updateSources(["app|foo.txt"]);
// Note: No asset requests here.
-
buildShouldFail([equals(BadTransformer.ERROR)]);
});
@@ -152,10 +135,7 @@
[new BadTransformer(["a.out", "b.out"])]
]});
- schedule(() {
- updateSources(["app|foo.txt"]);
- });
-
+ updateSources(["app|foo.txt"]);
expectNoAsset("app|a.out");
});
@@ -163,10 +143,7 @@
initGraph(["pkg1|foo.txt", "pkg2|foo.txt"],
{"pkg1": [[new BadTransformer([])]]});
- schedule(() {
- updateSources(["pkg1|foo.txt", "pkg2|foo.txt"]);
- });
-
+ updateSources(["pkg1|foo.txt", "pkg2|foo.txt"]);
expectAsset("pkg2|foo.txt", "foo");
buildShouldFail([equals(BadTransformer.ERROR)]);
});
@@ -177,10 +154,7 @@
"pkg2": [[new BadTransformer([])]]
});
- schedule(() {
- updateSources(["pkg1|foo.txt", "pkg2|foo.txt"]);
- });
-
+ updateSources(["pkg1|foo.txt", "pkg2|foo.txt"]);
buildShouldFail([
equals(BadTransformer.ERROR),
equals(BadTransformer.ERROR)
@@ -191,7 +165,7 @@
initGraph(["app|foo.txt"]);
setAssetError("app|foo.txt");
- schedule(() => updateSources(["app|foo.txt"]));
+ updateSources(["app|foo.txt"]);
expectNoAsset("app|foo.txt");
buildShouldFail([isMockLoadException("app|foo.txt")]);
});
diff --git a/pkg/barback/test/package_graph/source_test.dart b/pkg/barback/test/package_graph/source_test.dart
index e6627e7..4c47df3 100644
--- a/pkg/barback/test/package_graph/source_test.dart
+++ b/pkg/barback/test/package_graph/source_test.dart
@@ -61,10 +61,7 @@
expectAsset("app|foo.txt");
buildShouldSucceed();
- schedule(() {
- removeSources(["app|foo.txt"]);
- });
-
+ removeSources(["app|foo.txt"]);
expectNoAsset("app|foo.txt");
buildShouldSucceed();
});
@@ -75,26 +72,24 @@
schedule(() {
// Make a bunch of synchronous update calls.
- updateSources(["app|foo.blub"]);
- updateSources(["app|foo.blub"]);
- updateSources(["app|foo.blub"]);
- updateSources(["app|foo.blub"]);
+ updateSourcesSync(["app|foo.blub"]);
+ updateSourcesSync(["app|foo.blub"]);
+ updateSourcesSync(["app|foo.blub"]);
+ updateSourcesSync(["app|foo.blub"]);
});
expectAsset("app|foo.blab", "foo.blab");
buildShouldSucceed();
- schedule(() {
- expect(transformer.numRuns, equals(1));
- });
+ expect(transformer.numRuns, completion(equals(1)));
});
test("a removal cancels out an update", () {
initGraph(["app|foo.txt"]);
schedule(() {
- updateSources(["app|foo.txt"]);
- removeSources(["app|foo.txt"]);
+ updateSourcesSync(["app|foo.txt"]);
+ removeSourcesSync(["app|foo.txt"]);
});
expectNoAsset("app|foo.txt");
@@ -105,8 +100,8 @@
initGraph(["app|foo.txt"]);
schedule(() {
- removeSources(["app|foo.txt"]);
- updateSources(["app|foo.txt"]);
+ removeSourcesSync(["app|foo.txt"]);
+ updateSourcesSync(["app|foo.txt"]);
});
expectAsset("app|foo.txt");
@@ -117,14 +112,12 @@
initGraph({"app|foo.txt": "foo"});
pauseProvider();
- schedule(() {
- // The mock provider synchronously loads the value of the assets, so this
- // will kick off two loads with different values. The second one should
- // win.
- updateSources(["app|foo.txt"]);
- modifyAsset("app|foo.txt", "bar");
- updateSources(["app|foo.txt"]);
- });
+ // The mock provider synchronously loads the value of the assets, so this
+ // will kick off two loads with different values. The second one should
+ // win.
+ updateSources(["app|foo.txt"]);
+ modifyAsset("app|foo.txt", "bar");
+ updateSources(["app|foo.txt"]);
resumeProvider();
expectAsset("app|foo.txt", "bar");
@@ -145,23 +138,16 @@
// Make the provider slow to load a source.
pauseProvider();
- schedule(() {
- // Update an asset that doesn't trigger any transformers.
- updateSources(["app|other.bar"]);
- });
+ // Update an asset that doesn't trigger any transformers.
+ updateSources(["app|other.bar"]);
- schedule(() {
- // Now update an asset that does trigger a transformer.
- updateSources(["app|foo.txt"]);
- });
+ // Now update an asset that does trigger a transformer.
+ updateSources(["app|foo.txt"]);
resumeProvider();
buildShouldSucceed();
- waitForBuild();
- schedule(() {
- expect(transformer.numRuns, equals(2));
- });
+ expect(transformer.numRuns, completion(equals(2)));
});
}
diff --git a/pkg/barback/test/package_graph/transform_test.dart b/pkg/barback/test/package_graph/transform_test.dart
index c413528..c24bae8 100644
--- a/pkg/barback/test/package_graph/transform_test.dart
+++ b/pkg/barback/test/package_graph/transform_test.dart
@@ -95,9 +95,7 @@
expectAsset("app|foo.b", "foo.b");
expectAsset("app|foo.c", "foo.c");
buildShouldSucceed();
- schedule(() {
- expect(transformer.numRuns, equals(1));
- });
+ expect(transformer.numRuns, completion(equals(1)));
});
test("runs transforms in the same phase in parallel", () {
@@ -108,21 +106,17 @@
transformerA.pauseApply();
transformerB.pauseApply();
- schedule(() {
- updateSources(["app|foo.txt"]);
+ updateSources(["app|foo.txt"]);
- // Wait for them both to start.
- return Future.wait([transformerA.started, transformerB.started]);
- });
+ transformerA.waitUntilStarted();
+ transformerB.waitUntilStarted();
- schedule(() {
- // They should both still be running.
- expect(transformerA.isRunning, isTrue);
- expect(transformerB.isRunning, isTrue);
+ // They should both still be running.
+ expect(transformerA.isRunning, completion(isTrue));
+ expect(transformerB.isRunning, completion(isTrue));
- transformerA.resumeApply();
- transformerB.resumeApply();
- });
+ transformerA.resumeApply();
+ transformerB.resumeApply();
expectAsset("app|foo.a", "foo.a");
expectAsset("app|foo.b", "foo.b");
@@ -150,39 +144,26 @@
expectAsset("app|foo.blab", "foo.blab");
buildShouldSucceed();
- schedule(() {
- expect(transformer.numRuns, equals(1));
- });
+ expect(transformer.numRuns, completion(equals(1)));
});
test("reapplies a transform when its input is modified", () {
var transformer = new RewriteTransformer("blub", "blab");
initGraph(["app|foo.blub"], {"app": [[transformer]]});
- schedule(() {
- updateSources(["app|foo.blub"]);
- });
-
+ updateSources(["app|foo.blub"]);
expectAsset("app|foo.blab", "foo.blab");
buildShouldSucceed();
- schedule(() {
- updateSources(["app|foo.blub"]);
- });
-
+ updateSources(["app|foo.blub"]);
expectAsset("app|foo.blab", "foo.blab");
buildShouldSucceed();
- schedule(() {
- updateSources(["app|foo.blub"]);
- });
-
+ updateSources(["app|foo.blub"]);
expectAsset("app|foo.blab", "foo.blab");
buildShouldSucceed();
- schedule(() {
- expect(transformer.numRuns, equals(3));
- });
+ expect(transformer.numRuns, completion(equals(3)));
});
test("does not reapply transform when a removed input is modified", () {
@@ -200,23 +181,18 @@
// Remove the dependency on the non-primary input.
modifyAsset("app|a.txt", "a.inc");
- schedule(() => updateSources(["app|a.txt"]));
+ updateSources(["app|a.txt"]);
// Process it again.
expectAsset("app|a.out", "a");
buildShouldSucceed();
// Now touch the removed input. It should not trigger another build.
- schedule(() {
- updateSources(["app|b.inc"]);
- });
-
+ updateSources(["app|b.inc"]);
expectAsset("app|a.out", "a");
buildShouldSucceed();
- schedule(() {
- expect(transformer.numRuns, equals(2));
- });
+ expect(transformer.numRuns, completion(equals(2)));
});
test("allows a transform to generate multiple outputs", () {
@@ -248,18 +224,13 @@
expectAsset("app|foo.bbb", "foo.bb.bbb");
buildShouldSucceed();
- schedule(() {
- updateSources(["app|foo.a"]);
- });
-
+ updateSources(["app|foo.a"]);
expectAsset("app|foo.aaa", "foo.aa.aaa");
expectAsset("app|foo.bbb", "foo.bb.bbb");
buildShouldSucceed();
- schedule(() {
- expect(aa.numRuns, equals(2));
- expect(bb.numRuns, equals(1));
- });
+ expect(aa.numRuns, completion(equals(2)));
+ expect(bb.numRuns, completion(equals(1)));
});
test("doesn't get an output from a transform whose primary input is removed",
@@ -272,10 +243,7 @@
expectAsset("app|foo.out", "foo.out");
buildShouldSucceed();
- schedule(() {
- removeSources(["app|foo.txt"]);
- });
-
+ removeSources(["app|foo.txt"]);
expectNoAsset("app|foo.out");
buildShouldSucceed();
});
@@ -287,12 +255,10 @@
rewrite.pauseApply();
updateSources(["app|foo.txt"]);
- schedule(() => rewrite.started);
- schedule(() {
- removeSources(["app|foo.txt"]);
- rewrite.resumeApply();
- });
+ rewrite.waitUntilStarted();
+ removeSources(["app|foo.txt"]);
+ rewrite.resumeApply();
expectNoAsset("app|foo.out");
buildShouldSucceed();
});
@@ -308,7 +274,7 @@
buildShouldSucceed();
modifyAsset("app|a.inc", "after");
- schedule(() => updateSources(["app|a.inc"]));
+ updateSources(["app|a.inc"]);
expectAsset("app|a.out", "after");
buildShouldSucceed();
@@ -324,7 +290,7 @@
buildShouldSucceed();
modifyAsset("app|foo.txt", "that");
- schedule(() => updateSources(["app|foo.txt"]));
+ updateSources(["app|foo.txt"]);
expectAsset("app|foo.txt", "that and the other");
buildShouldSucceed();
@@ -344,10 +310,8 @@
schedule(pumpEventQueue);
modifyAsset("app|foo.txt", "second");
- schedule(() {
- updateSources(["app|foo.txt"]);
- check1.resumeIsPrimary("app|foo.txt");
- });
+ updateSources(["app|foo.txt"]);
+ check1.resumeIsPrimary("app|foo.txt");
expectAsset("app|foo.txt", "second#2");
buildShouldSucceed();
@@ -366,12 +330,10 @@
// Ensure that we're waiting on check1's isPrimary.
schedule(pumpEventQueue);
- schedule(() => removeSources(["app|foo.txt"]));
+ removeSources(["app|foo.txt"]);
modifyAsset("app|foo.txt", "second");
- schedule(() {
- updateSources(["app|foo.txt"]);
- check1.resumeIsPrimary("app|foo.txt");
- });
+ updateSources(["app|foo.txt"]);
+ check1.resumeIsPrimary("app|foo.txt");
expectAsset("app|foo.txt", "second#2");
buildShouldSucceed();
@@ -383,25 +345,17 @@
transformer.pauseApply();
- schedule(() {
- updateSources(["app|foo.txt"]);
+ updateSources(["app|foo.txt"]);
+ transformer.waitUntilStarted();
- // Wait for the transform to start.
- return transformer.started;
- });
-
- schedule(() {
- // Now update the graph during it.
- updateSources(["app|foo.txt"]);
- transformer.resumeApply();
- });
+ // Now update the graph during it.
+ updateSources(["app|foo.txt"]);
+ transformer.resumeApply();
expectAsset("app|foo.out", "foo.out");
buildShouldSucceed();
- schedule(() {
- expect(transformer.numRuns, equals(2));
- });
+ expect(transformer.numRuns, completion(equals(2)));
});
test("aborts processing if the primary input is removed during processing",
@@ -411,25 +365,17 @@
transformer.pauseApply();
- schedule(() {
- updateSources(["app|foo.txt"]);
+ updateSources(["app|foo.txt"]);
+ transformer.waitUntilStarted();
- // Wait for the transform to start.
- return transformer.started;
- });
-
- schedule(() {
- // Now remove its primary input while it's running.
- removeSources(["app|foo.txt"]);
- transformer.resumeApply();
- });
+ // Now remove its primary input while it's running.
+ removeSources(["app|foo.txt"]);
+ transformer.resumeApply();
expectNoAsset("app|foo.out");
buildShouldSucceed();
- schedule(() {
- expect(transformer.numRuns, equals(1));
- });
+ expect(transformer.numRuns, completion(equals(1)));
});
test("restarts processing if a change to a new secondary input occurs during "
@@ -443,27 +389,24 @@
transformer.pauseApply();
updateSources(["app|foo.txt", "app|bar.inc"]);
- // Wait for the transform to start.
- schedule(() => transformer.started);
+ transformer.waitUntilStarted();
// Give the transform time to load bar.inc the first time.
schedule(pumpEventQueue);
// Now update the secondary input before the transform finishes.
modifyAsset("app|bar.inc", "baz");
- schedule(() => updateSources(["app|bar.inc"]));
+ updateSources(["app|bar.inc"]);
// Give bar.inc enough time to be loaded and marked available before the
// transformer completes.
schedule(pumpEventQueue);
- schedule(transformer.resumeApply);
+ transformer.resumeApply();
expectAsset("app|foo.out", "baz");
buildShouldSucceed();
- schedule(() {
- expect(transformer.numRuns, equals(2));
- });
+ expect(transformer.numRuns, completion(equals(2)));
});
test("doesn't restart processing if a change to an old secondary input "
@@ -479,31 +422,25 @@
expectAsset("app|foo.out", "bar");
buildShouldSucceed();
- schedule(transformer.pauseApply);
+ transformer.pauseApply();
modifyAsset("app|foo.txt", "baz.inc");
- schedule(() {
- updateSources(["app|foo.txt"]);
- // Wait for the transform to start.
- return transformer.started;
- });
+ updateSources(["app|foo.txt"]);
+ transformer.waitUntilStarted();
// Now update the old secondary input before the transform finishes.
modifyAsset("app|bar.inc", "new bar");
- schedule(() => updateSources(["app|bar.inc"]));
+ updateSources(["app|bar.inc"]);
// Give bar.inc enough time to be loaded and marked available before the
// transformer completes.
schedule(pumpEventQueue);
- schedule(transformer.resumeApply);
-
+ transformer.resumeApply();
expectAsset("app|foo.out", "baz");
buildShouldSucceed();
- schedule(() {
- // Should have run once the first time, then again when switching to
- // baz.inc. Should not run a third time because of bar.inc being modified.
- expect(transformer.numRuns, equals(2));
- });
+ // Should have run once the first time, then again when switching to
+ // baz.inc. Should not run a third time because of bar.inc being modified.
+ expect(transformer.numRuns, completion(equals(2)));
});
test("handles an output moving from one transformer to another", () {
@@ -526,7 +463,7 @@
// transformer.
modifyAsset("app|a.a", "a.out");
modifyAsset("app|b.b", "b.out,shared.out");
- schedule(() => updateSources(["app|a.a", "app|b.b"]));
+ updateSources(["app|a.a", "app|b.b"]);
expectAsset("app|a.out", "spread a");
expectAsset("app|b.out", "spread b");
@@ -542,31 +479,20 @@
txtToInt.pauseApply();
- schedule(() {
- updateSources(["app|foo.txt"]);
+ updateSources(["app|foo.txt"]);
+ txtToInt.waitUntilStarted();
- // Wait for the first transform to start.
- return txtToInt.started;
- });
-
- schedule(() {
- // Now update the graph during it.
- updateSources(["app|bar.txt"]);
- });
-
- schedule(() {
- txtToInt.resumeApply();
- });
+ // Now update the graph during it.
+ updateSources(["app|bar.txt"]);
+ txtToInt.resumeApply();
expectAsset("app|foo.out", "foo.int.out");
expectAsset("app|bar.out", "bar.int.out");
buildShouldSucceed();
- schedule(() {
- // Should only have run each transform once for each primary.
- expect(txtToInt.numRuns, equals(2));
- expect(intToOut.numRuns, equals(2));
- });
+ // Should only have run each transform once for each primary.
+ expect(txtToInt.numRuns, completion(equals(2)));
+ expect(intToOut.numRuns, completion(equals(2)));
});
test("applies transforms to the correct packages", () {
@@ -606,7 +532,7 @@
pauseProvider();
modifyAsset("app|foo.in", "new");
- schedule(() => updateSources(["app|foo.in"]));
+ updateSources(["app|foo.in"]);
expectAssetDoesNotComplete("app|foo.out");
buildShouldNotBeDone();
@@ -623,7 +549,7 @@
updateSources(["app|foo.txt"]);
expectAssetDoesNotComplete("app|foo.txt");
- schedule(rewrite.resumeApply);
+ rewrite.resumeApply();
expectAsset("app|foo.txt", "foo.txt");
buildShouldSucceed();
});
@@ -637,7 +563,7 @@
updateSources(["app|foo.a"]);
expectAssetDoesNotComplete("app|foo.a");
- schedule(() => rewrite.resumeIsPrimary("app|foo.a"));
+ rewrite.resumeIsPrimary("app|foo.a");
expectAsset("app|foo.a", "foo");
buildShouldSucceed();
});
@@ -651,11 +577,11 @@
expectAsset("app|foo.txt", "foo.txt");
buildShouldSucceed();
- schedule(() => rewrite.pauseIsPrimary("app|foo.txt"));
- schedule(() => updateSources(["app|foo.txt"]));
+ rewrite.pauseIsPrimary("app|foo.txt");
+ updateSources(["app|foo.txt"]);
expectAssetDoesNotComplete("app|foo.txt");
- schedule(() => rewrite.resumeIsPrimary("app|foo.txt"));
+ rewrite.resumeIsPrimary("app|foo.txt");
expectAsset("app|foo.txt", "foo.txt");
buildShouldSucceed();
});
@@ -669,10 +595,8 @@
// Make sure we're waiting on isPrimary.
schedule(pumpEventQueue);
- schedule(() {
- removeSources(["app|foo.txt"]);
- rewrite.resumeIsPrimary("app|foo.txt");
- });
+ removeSources(["app|foo.txt"]);
+ rewrite.resumeIsPrimary("app|foo.txt");
expectNoAsset("app|foo.txt");
buildShouldSucceed();
});
@@ -690,10 +614,8 @@
schedule(pumpEventQueue);
modifyAsset("app|foo.txt", "don't");
- schedule(() {
- updateSources(["app|foo.txt"]);
- check.resumeIsPrimary("app|foo.txt");
- });
+ updateSources(["app|foo.txt"]);
+ check.resumeIsPrimary("app|foo.txt");
expectAsset("app|foo.txt", "don't");
buildShouldSucceed();
@@ -712,10 +634,8 @@
schedule(pumpEventQueue);
modifyAsset("app|foo.txt", "do");
- schedule(() {
- updateSources(["app|foo.txt"]);
- check.resumeIsPrimary("app|foo.txt");
- });
+ updateSources(["app|foo.txt"]);
+ check.resumeIsPrimary("app|foo.txt");
expectAsset("app|foo.txt", "done");
buildShouldSucceed();
@@ -732,10 +652,8 @@
// Make sure we're waiting on the correct isPrimary.
schedule(pumpEventQueue);
- schedule(() {
- removeSources(["app|foo.txt"]);
- rewrite2.resumeIsPrimary("app|foo.md");
- });
+ removeSources(["app|foo.txt"]);
+ rewrite2.resumeIsPrimary("app|foo.md");
expectNoAsset("app|foo.txt");
expectAsset("app|foo.md", "foo.md");
buildShouldSucceed();
@@ -756,10 +674,8 @@
schedule(pumpEventQueue);
modifyAsset("app|foo.txt", "don't");
- schedule(() {
- updateSources(["app|foo.txt"]);
- rewrite.resumeIsPrimary("app|foo.md");
- });
+ updateSources(["app|foo.txt"]);
+ rewrite.resumeIsPrimary("app|foo.md");
expectAsset("app|foo.txt", "don't");
expectAsset("app|foo.md", "foo.md");
@@ -781,10 +697,8 @@
schedule(pumpEventQueue);
modifyAsset("app|foo.txt", "do");
- schedule(() {
- updateSources(["app|foo.txt"]);
- rewrite.resumeIsPrimary("app|foo.md");
- });
+ updateSources(["app|foo.txt"]);
+ rewrite.resumeIsPrimary("app|foo.md");
expectAsset("app|foo.txt", "done");
expectAsset("app|foo.md", "foo.md");
@@ -802,7 +716,7 @@
expectAsset("app|foo.out", "foo.mid.out");
buildShouldSucceed();
- schedule(() => removeSources(["app|foo.txt"]));
+ removeSources(["app|foo.txt"]);
expectNoAsset("app|foo.out");
buildShouldSucceed();
});
@@ -819,7 +733,7 @@
buildShouldSucceed();
modifyAsset("app|foo.txt", "bar.mid");
- schedule(() => updateSources(["app|foo.txt"]));
+ updateSources(["app|foo.txt"]);
expectNoAsset('app|foo.out');
expectAsset('app|bar.out', 'spread txt.out');
buildShouldSucceed();
@@ -838,7 +752,7 @@
pauseProvider();
modifyAsset("app|foo.in", "new");
- schedule(() => updateSources(["app|foo.in"]));
+ updateSources(["app|foo.in"]);
expectAssetDoesNotComplete("app|foo.out");
expectAsset("app|bar.out", "bar.out");
buildShouldNotBeDone();
@@ -858,7 +772,7 @@
buildShouldSucceed();
pauseProvider();
- schedule(() => updateSources(["app|foo.in"]));
+ updateSources(["app|foo.in"]);
expectAssetDoesNotComplete("app|foo.out");
expectAssetDoesNotComplete("app|non-existent.out");
buildShouldNotBeDone();
@@ -885,7 +799,7 @@
// pkg1 is still successful, but pkg2 is waiting on the provider, so the
// overall build shouldn't finish.
pauseProvider();
- schedule(() => updateSources(["pkg2|foo.txt"]));
+ updateSources(["pkg2|foo.txt"]);
expectAsset("pkg1|foo.out", "foo.out");
buildShouldNotBeDone();
@@ -934,7 +848,7 @@
buildShouldSucceed();
modifyAsset("pkg2|a.inc", "new a");
- schedule(() => updateSources(["pkg2|a.inc"]));
+ updateSources(["pkg2|a.inc"]);
expectAsset("pkg1|a.out", "new a");
buildShouldSucceed();
});
@@ -954,7 +868,7 @@
buildShouldSucceed();
modifyAsset("pkg2|a.txt", "new a");
- schedule(() => updateSources(["pkg2|a.txt"]));
+ updateSources(["pkg2|a.txt"]);
expectAsset("pkg1|a.out", "new a.inc");
buildShouldSucceed();
});
@@ -982,7 +896,7 @@
buildShouldSucceed();
modifyAsset("pkg2|a.inc", "b,c.md");
- schedule(() => updateSources(["pkg2|a.inc"]));
+ updateSources(["pkg2|a.inc"]);
expectAsset("pkg1|b", "spread out");
expectAsset("pkg1|c.done", "spread out.done");
buildShouldSucceed();
@@ -1007,7 +921,7 @@
buildShouldSucceed();
modifyAsset("pkg2|a.inc", "b");
- schedule(() => updateSources(["pkg2|a.inc"]));
+ updateSources(["pkg2|a.inc"]);
expectAsset("pkg1|b", "spread out");
expectNoAsset("pkg1|c.done");
buildShouldSucceed();
diff --git a/pkg/barback/test/transformer/mock.dart b/pkg/barback/test/transformer/mock.dart
index b831cf7..0887002 100644
--- a/pkg/barback/test/transformer/mock.dart
+++ b/pkg/barback/test/transformer/mock.dart
@@ -8,6 +8,7 @@
import 'package:barback/barback.dart';
import 'package:barback/src/utils.dart';
+import 'package:scheduled_test/scheduled_test.dart';
/// The abstract base class for transformers used to test barback.
///
@@ -20,7 +21,10 @@
/// [getPrimary] rather than [transform.getInput] and [transform.primaryInput].
abstract class MockTransformer extends Transformer {
/// The number of times the transformer has been applied.
- int get numRuns => _numRuns;
+ ///
+ /// This is scheduled. The Future will complete at the point in the schedule
+ /// that this is called.
+ Future<int> get numRuns => schedule(() => _numRuns);
var _numRuns = 0;
/// The number of currently running transforms.
@@ -37,56 +41,83 @@
// the [Transform].
final _getInput = new Map<AssetId, Completer>();
- /// A future that completes when this transformer begins running.
+ /// A completer that completes once this transformer begins running.
///
/// Once this transformer finishes running, this is reset to a new completer,
/// so it can be used multiple times.
- Future get started => _started.future;
var _started = new Completer();
/// `true` if any transforms are currently running.
- bool get isRunning => _runningTransforms > 0;
+ ///
+ /// This is scheduled. The Future will complete at the point in the schedule
+ /// that this is called.
+ Future<bool> get isRunning => schedule(() => _runningTransforms > 0);
+
+ /// Pauses the schedule until this transformer begins running.
+ void waitUntilStarted() {
+ schedule(() => _started.future, "wait until $this starts");
+ }
/// Causes the transformer to pause after running [apply] but before the
/// returned Future completes.
///
- /// This can be resumed by calling [resumeApply].
+ /// This can be resumed by calling [resumeApply]. This operation is scheduled.
void pauseApply() {
- _apply = new Completer();
+ schedule(() {
+ _apply = new Completer();
+ }, "pause apply for $this");
}
/// Resumes the transformer's [apply] call after [pauseApply] was called.
+ ///
+ /// This operation is scheduled.
void resumeApply() {
- _apply.complete();
- _apply = null;
+ schedule(() {
+ _apply.complete();
+ _apply = null;
+ }, "resume apply for $this");
}
/// Causes the transformer to pause after running [isPrimary] on the asset
/// with the given [name], but before the returned Future completes.
///
- /// This can be resumed by calling [resumeIsPrimary].
+ /// This can be resumed by calling [resumeIsPrimary]. This operation is
+ /// scheduled.
void pauseIsPrimary(String name) {
- _isPrimary[new AssetId.parse(name)] = new Completer();
+ schedule(() {
+ _isPrimary[new AssetId.parse(name)] = new Completer();
+ }, "pause isPrimary($name) for $this");
}
/// Resumes the transformer's [isPrimary] call on the asset with the given
/// [name] after [pauseIsPrimary] was called.
+ ///
+ /// This operation is scheduled.
void resumeIsPrimary(String name) {
- _isPrimary.remove(new AssetId.parse(name)).complete();
+ schedule(() {
+ _isPrimary.remove(new AssetId.parse(name)).complete();
+ }, "resume isPrimary($name) for $this");
}
/// Causes the transformer to pause while loading the input with the given
/// [name]. This can be the primary input or a secondary input.
///
- /// This can be resumed by calling [resumeGetInput].
+ /// This can be resumed by calling [resumeGetInput]. This operation is
+ /// scheduled.
void pauseGetInput(String name) {
- _getInput[new AssetId.parse(name)] = new Completer();
+ schedule(() {
+ _getInput[new AssetId.parse(name)] = new Completer();
+ }, "pause getInput($name) for $this");
}
/// Resumes the transformer's loading of the input with the given [name] after
/// [pauseGetInput] was called.
+ ///
+ /// This operation is scheduled.
void resumeGetInput(String name) {
- _getInput.remove(new AssetId.parse(name)).complete();
+ schedule(() {
+ _getInput.remove(new AssetId.parse(name)).complete();
+ }, "resume getInput($name) for $this");
}
/// Like [Transform.getInput], but respects [pauseGetInput].
diff --git a/pkg/barback/test/utils.dart b/pkg/barback/test/utils.dart
index 57d9c43..1de4ac9 100644
--- a/pkg/barback/test/utils.dart
+++ b/pkg/barback/test/utils.dart
@@ -69,29 +69,45 @@
/// Updates [assets] in the current [PackageProvider].
///
/// Each item in the list may either be an [AssetId] or a string that can be
-/// parsed as one. Note that this method is not automatically scheduled.
+/// parsed as one.
void updateSources(Iterable assets) {
- // Allow strings as asset IDs.
- assets = assets.map((asset) {
- if (asset is String) return new AssetId.parse(asset);
- return asset;
- });
+ assets = _parseAssets(assets);
+ schedule(() => _barback.updateSources(assets),
+ "updating ${assets.join(', ')}");
+}
- _barback.updateSources(assets);
+/// Updates [assets] in the current [PackageProvider].
+///
+/// Each item in the list may either be an [AssetId] or a string that can be
+/// parsed as one. Unlike [updateSources], this is not automatically scheduled
+/// and will be run synchronously when called.
+void updateSourcesSync(Iterable assets) =>
+ _barback.updateSources(_parseAssets(assets));
+
+/// Removes [assets] from the current [PackageProvider].
+///
+/// Each item in the list may either be an [AssetId] or a string that can be
+/// parsed as one.
+void removeSources(Iterable assets) {
+ assets = _parseAssets(assets);
+ schedule(() => _barback.removeSources(assets),
+ "removing ${assets.join(', ')}");
}
/// Removes [assets] from the current [PackageProvider].
///
/// Each item in the list may either be an [AssetId] or a string that can be
-/// parsed as one. Note that this method is not automatically scheduled.
-void removeSources(Iterable assets) {
- // Allow strings as asset IDs.
- assets = assets.map((asset) {
+/// parsed as one. Unlike [removeSources], this is not automatically scheduled
+/// and will be run synchronously when called.
+void removeSourcesSync(Iterable assets) =>
+ _barback.removeSources(_parseAssets(assets));
+
+/// Parse a list of strings or [AssetId]s into a list of [AssetId]s.
+List<AssetId> _parseAssets(Iterable assets) {
+ return assets.map((asset) {
if (asset is String) return new AssetId.parse(asset);
return asset;
- });
-
- _barback.removeSources(assets);
+ }).toList();
}
/// Schedules a change to the contents of an asset identified by [name] to
@@ -143,7 +159,7 @@
/// Expects that the next [BuildResult] is a build success.
void buildShouldSucceed() {
- expect(_getNextBuildResult().then((result) {
+ expect(_getNextBuildResult("build should succeed").then((result) {
result.errors.forEach(currentSchedule.signalError);
expect(result.succeeded, isTrue);
}), completes);
@@ -155,7 +171,7 @@
/// build to fail. Every matcher is expected to match an error, but the order of
/// matchers is unimportant.
void buildShouldFail(List matchers) {
- expect(_getNextBuildResult().then((result) {
+ expect(_getNextBuildResult("build should fail").then((result) {
expect(result.succeeded, isFalse);
expect(result.errors.length, equals(matchers.length));
for (var matcher in matchers) {
@@ -164,18 +180,10 @@
}), completes);
}
-Future<BuildResult> _getNextBuildResult() =>
- _barback.results.elementAt(_nextBuildResult++);
-
-/// Pauses the schedule until the currently running build completes.
-///
-/// Validates that the build completed successfully.
-void waitForBuild() {
- schedule(() {
- return _barback.results.first.then((result) {
- expect(result.succeeded, isTrue);
- });
- }, "wait for build");
+Future<BuildResult> _getNextBuildResult(String description) {
+ var result = currentSchedule.wrapFuture(
+ _barback.results.elementAt(_nextBuildResult++));
+ return schedule(() => result, description);
}
/// Schedules an expectation that the graph will deliver an asset matching
diff --git a/pkg/custom_element/lib/custom_element.dart b/pkg/custom_element/lib/custom_element.dart
index aa540e5..010b392 100644
--- a/pkg/custom_element/lib/custom_element.dart
+++ b/pkg/custom_element/lib/custom_element.dart
@@ -397,6 +397,9 @@
bool matches(String selectors) => host.matches(selectors);
+ bool matchesWithAncestors(String selectors) =>
+ host.matchesWithAncestors(selectors);
+
@deprecated
void requestFullScreen(int flags) { requestFullscreen(); }
diff --git a/pkg/observe/lib/transform.dart b/pkg/observe/lib/transform.dart
new file mode 100644
index 0000000..99cb13b
--- /dev/null
+++ b/pkg/observe/lib/transform.dart
@@ -0,0 +1,338 @@
+// 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.
+
+/**
+ * Code transform for @observable. The core transformation is relatively
+ * straightforward, and essentially like an editor refactoring.
+ */
+library observe.transform;
+
+import 'dart:async';
+import 'package:path/path.dart' as path;
+import 'package:analyzer_experimental/src/generated/ast.dart';
+import 'package:analyzer_experimental/src/generated/error.dart';
+import 'package:analyzer_experimental/src/generated/parser.dart';
+import 'package:analyzer_experimental/src/generated/scanner.dart';
+import 'package:barback/barback.dart';
+import 'package:source_maps/refactor.dart';
+import 'package:source_maps/span.dart' show SourceFile;
+
+/**
+ * A [Transformer] that replaces observables based on dirty-checking with an
+ * implementation based on change notifications.
+ *
+ * The transformation adds hooks for field setters and notifies the observation
+ * system of the change.
+ */
+class ObservableTransformer extends Transformer {
+
+ Future<bool> isPrimary(Asset input) {
+ if (input.id.extension != '.dart') return new Future.value(false);
+ // Note: technically we should parse the file to find accurately the
+ // observable annotation, but that seems expensive. It would require almost
+ // as much work as applying the transform. We rather have some false
+ // positives here, and then generate no outputs when we apply this
+ // transform.
+ return input.readAsString().then((c) => c.contains("@observable"));
+ }
+
+ Future apply(Transform transform) {
+ return transform.primaryInput
+ .then((input) => input.readAsString().then((content) {
+ var id = transform.primaryId;
+ // TODO(sigmund): improve how we compute this url
+ var url = id.path.startsWith('lib/')
+ ? 'package:${id.package}/${id.path.substring(4)}' : id.path;
+ var sourceFile = new SourceFile.text(url, content);
+ var transaction = _transformCompilationUnit(
+ content, sourceFile, transform.logger);
+ if (!transaction.hasEdits) {
+ transform.addOutput(input);
+ return;
+ }
+ var printer = transaction.commit();
+ // TODO(sigmund): emit source maps when barback supports it (see
+ // dartbug.com/12340)
+ printer.build(url);
+ transform.addOutput(new Asset.fromString(id, printer.text));
+ }));
+ }
+}
+
+TextEditTransaction _transformCompilationUnit(
+ String inputCode, SourceFile sourceFile, TransformLogger logger) {
+ var unit = _parseCompilationUnit(inputCode);
+ return transformObservables(unit, sourceFile, inputCode, logger);
+}
+
+// TODO(sigmund): make this private. This is currently public so it can be used
+// by the polymer.dart package which is not yet entirely migrated to use
+// pub-serve and pub-deploy.
+TextEditTransaction transformObservables(CompilationUnit unit,
+ SourceFile sourceFile, String content, TransformLogger logger) {
+ var code = new TextEditTransaction(content, sourceFile);
+ for (var directive in unit.directives) {
+ if (directive is LibraryDirective && _hasObservable(directive)) {
+ logger.warning('@observable on a library no longer has any effect. '
+ 'It should be placed on individual fields.',
+ _getSpan(sourceFile, directive));
+ break;
+ }
+ }
+
+ for (var declaration in unit.declarations) {
+ if (declaration is ClassDeclaration) {
+ _transformClass(declaration, code, sourceFile, logger);
+ } else if (declaration is TopLevelVariableDeclaration) {
+ if (_hasObservable(declaration)) {
+ logger.warning('Top-level fields can no longer be observable. '
+ 'Observable fields should be put in an observable objects.',
+ _getSpan(sourceFile, declaration));
+ }
+ }
+ }
+ return code;
+}
+
+/** Parse [code] using analyzer_experimental. */
+CompilationUnit _parseCompilationUnit(String code) {
+ var errorListener = new _ErrorCollector();
+ var scanner = new StringScanner(null, code, errorListener);
+ var token = scanner.tokenize();
+ var parser = new Parser(null, errorListener);
+ return parser.parseCompilationUnit(token);
+}
+
+class _ErrorCollector extends AnalysisErrorListener {
+ final errors = <AnalysisError>[];
+ onError(error) => errors.add(error);
+}
+
+_getSpan(SourceFile file, ASTNode node) => file.span(node.offset, node.end);
+
+/** True if the node has the `@observable` annotation. */
+bool _hasObservable(AnnotatedNode node) => _hasAnnotation(node, 'observable');
+
+bool _hasAnnotation(AnnotatedNode node, String name) {
+ // TODO(jmesserly): this isn't correct if the annotation has been imported
+ // with a prefix, or cases like that. We should technically be resolving, but
+ // that is expensive.
+ return node.metadata.any((m) => m.name.name == name &&
+ m.constructorName == null && m.arguments == null);
+}
+
+void _transformClass(ClassDeclaration cls, TextEditTransaction code,
+ SourceFile file, TransformLogger logger) {
+
+ if (_hasObservable(cls)) {
+ logger.warning('@observable on a class no longer has any effect. '
+ 'It should be placed on individual fields.',
+ _getSpan(file, cls));
+ }
+
+ // We'd like to track whether observable was declared explicitly, otherwise
+ // report a warning later below. Because we don't have type analysis (only
+ // syntactic understanding of the code), we only report warnings that are
+ // known to be true.
+ var declaresObservable = false;
+ if (cls.extendsClause != null) {
+ var id = _getSimpleIdentifier(cls.extendsClause.superclass.name);
+ if (id.name == 'ObservableBase') {
+ code.edit(id.offset, id.end, 'ChangeNotifierBase');
+ declaresObservable = true;
+ } else if (id.name == 'ChangeNotifierBase') {
+ declaresObservable = true;
+ } else if (id.name != 'PolymerElement' && id.name != 'CustomElement'
+ && id.name != 'Object') {
+ // TODO(sigmund): this is conservative, consider using type-resolution to
+ // improve this check.
+ declaresObservable = true;
+ }
+ }
+
+ if (cls.withClause != null) {
+ for (var type in cls.withClause.mixinTypes) {
+ var id = _getSimpleIdentifier(type.name);
+ if (id.name == 'ObservableMixin') {
+ code.edit(id.offset, id.end, 'ChangeNotifierMixin');
+ declaresObservable = true;
+ break;
+ } else if (id.name == 'ChangeNotifierMixin') {
+ declaresObservable = true;
+ break;
+ } else {
+ // TODO(sigmund): this is conservative, consider using type-resolution
+ // to improve this check.
+ declaresObservable = true;
+ }
+ }
+ }
+
+ if (!declaresObservable && cls.implementsClause != null) {
+ // TODO(sigmund): consider adding type-resolution to give a more precise
+ // answer.
+ declaresObservable = true;
+ }
+
+ // Track fields that were transformed.
+ var instanceFields = new Set<String>();
+ var getters = new List<String>();
+ var setters = new List<String>();
+
+ for (var member in cls.members) {
+ if (member is FieldDeclaration) {
+ bool isStatic = _hasKeyword(member.keyword, Keyword.STATIC);
+ if (isStatic) {
+ if (_hasObservable(member)){
+ logger.warning('Static fields can no longer be observable. '
+ 'Observable fields should be put in an observable objects.',
+ _getSpan(file, member));
+ }
+ continue;
+ }
+ if (_hasObservable(member)) {
+ if (!declaresObservable) {
+ logger.warning('Observable fields should be put in an observable'
+ ' objects. Please declare that this class extends from '
+ 'ObservableBase, includes ObservableMixin, or implements '
+ 'Observable.',
+ _getSpan(file, member));
+
+ }
+ _transformFields(member.fields, code, member.offset, member.end);
+
+ var names = member.fields.variables.map((v) => v.name.name);
+
+ getters.addAll(names);
+ if (!_isReadOnly(member.fields)) {
+ setters.addAll(names);
+ instanceFields.addAll(names);
+ }
+ }
+ }
+ // TODO(jmesserly): this is a temporary workaround until we can remove
+ // getValueWorkaround and setValueWorkaround.
+ if (member is MethodDeclaration) {
+ if (_hasKeyword(member.propertyKeyword, Keyword.GET)) {
+ getters.add(member.name.name);
+ } else if (_hasKeyword(member.propertyKeyword, Keyword.SET)) {
+ setters.add(member.name.name);
+ }
+ }
+ }
+
+ // If nothing was @observable, bail.
+ if (instanceFields.length == 0) return;
+
+ // Fix initializers, because they aren't allowed to call the setter.
+ for (var member in cls.members) {
+ if (member is ConstructorDeclaration) {
+ _fixConstructor(member, code, instanceFields);
+ }
+ }
+}
+
+SimpleIdentifier _getSimpleIdentifier(Identifier id) =>
+ id is PrefixedIdentifier ? (id as PrefixedIdentifier).identifier : id;
+
+
+bool _hasKeyword(Token token, Keyword keyword) =>
+ token is KeywordToken && (token as KeywordToken).keyword == keyword;
+
+String _getOriginalCode(TextEditTransaction code, ASTNode node) =>
+ code.original.substring(node.offset, node.end);
+
+void _fixConstructor(ConstructorDeclaration ctor, TextEditTransaction code,
+ Set<String> changedFields) {
+
+ // Fix normal initializers
+ for (var initializer in ctor.initializers) {
+ if (initializer is ConstructorFieldInitializer) {
+ var field = initializer.fieldName;
+ if (changedFields.contains(field.name)) {
+ code.edit(field.offset, field.end, '__\$${field.name}');
+ }
+ }
+ }
+
+ // Fix "this." initializer in parameter list. These are tricky:
+ // we need to preserve the name and add an initializer.
+ // Preserving the name is important for named args, and for dartdoc.
+ // BEFORE: Foo(this.bar, this.baz) { ... }
+ // AFTER: Foo(bar, baz) : __$bar = bar, __$baz = baz { ... }
+
+ var thisInit = [];
+ for (var param in ctor.parameters.parameters) {
+ if (param is DefaultFormalParameter) {
+ param = param.parameter;
+ }
+ if (param is FieldFormalParameter) {
+ var name = param.identifier.name;
+ if (changedFields.contains(name)) {
+ thisInit.add(name);
+ // Remove "this." but keep everything else.
+ code.edit(param.thisToken.offset, param.period.end, '');
+ }
+ }
+ }
+
+ if (thisInit.length == 0) return;
+
+ // TODO(jmesserly): smarter formatting with indent, etc.
+ var inserted = thisInit.map((i) => '__\$$i = $i').join(', ');
+
+ int offset;
+ if (ctor.separator != null) {
+ offset = ctor.separator.end;
+ inserted = ' $inserted,';
+ } else {
+ offset = ctor.parameters.end;
+ inserted = ' : $inserted';
+ }
+
+ code.edit(offset, offset, inserted);
+}
+
+bool _isReadOnly(VariableDeclarationList fields) {
+ return _hasKeyword(fields.keyword, Keyword.CONST) ||
+ _hasKeyword(fields.keyword, Keyword.FINAL);
+}
+
+void _transformFields(VariableDeclarationList fields, TextEditTransaction code,
+ int begin, int end) {
+
+ if (_isReadOnly(fields)) return;
+
+ var indent = guessIndent(code.original, begin);
+ var replace = new StringBuffer();
+
+ // Unfortunately "var" doesn't work in all positions where type annotations
+ // are allowed, such as "var get name". So we use "dynamic" instead.
+ var type = 'dynamic';
+ if (fields.type != null) {
+ type = _getOriginalCode(code, fields.type);
+ }
+
+ for (var field in fields.variables) {
+ var initializer = '';
+ if (field.initializer != null) {
+ initializer = ' = ${_getOriginalCode(code, field.initializer)}';
+ }
+
+ var name = field.name.name;
+
+ // TODO(jmesserly): should we generate this one one line, so source maps
+ // don't break?
+ if (replace.length > 0) replace.write('\n\n$indent');
+ replace.write('''
+$type __\$$name$initializer;
+$type get $name => __\$$name;
+set $name($type value) {
+ __\$$name = notifyPropertyChange(const Symbol('$name'), __\$$name, value);
+}
+'''.replaceAll('\n', '\n$indent'));
+ }
+
+ code.edit(begin, end, '$replace');
+}
diff --git a/pkg/observe/pubspec.yaml b/pkg/observe/pubspec.yaml
index 77f2bc4..7324aef 100644
--- a/pkg/observe/pubspec.yaml
+++ b/pkg/observe/pubspec.yaml
@@ -8,6 +8,10 @@
immediately assigned to the model.
homepage: https://github.com/dart-lang/web-ui
dependencies:
+ analyzer_experimental: any
+ barback: any
logging: any
+ path: any
+ source_maps: any
dev_dependencies:
unittest: any
diff --git a/pkg/observe/test/transform_test.dart b/pkg/observe/test/transform_test.dart
new file mode 100644
index 0000000..13b67cd
--- /dev/null
+++ b/pkg/observe/test/transform_test.dart
@@ -0,0 +1,121 @@
+// 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 'dart:async';
+import 'package:barback/barback.dart';
+import 'package:observe/transform.dart';
+import 'package:unittest/compact_vm_config.dart';
+import 'package:unittest/unittest.dart';
+
+main() {
+ useCompactVMConfiguration();
+
+ group('replaces Observable for ChangeNotifier', () {
+ _testClause('extends ObservableBase', 'extends ChangeNotifierBase');
+ _testClause('extends Base with ObservableMixin',
+ 'extends Base with ChangeNotifierMixin');
+ _testClause('extends Base<T> with ObservableMixin',
+ 'extends Base<T> with ChangeNotifierMixin');
+ _testClause('extends Base with Mixin, ObservableMixin',
+ 'extends Base with Mixin, ChangeNotifierMixin');
+ _testClause('extends Base with ObservableMixin, Mixin',
+ 'extends Base with ChangeNotifierMixin, Mixin');
+ _testClause('extends Base with Mixin<T>, ObservableMixin',
+ 'extends Base with Mixin<T>, ChangeNotifierMixin');
+ _testClause('extends Base with Mixin, ObservableMixin, Mixin2',
+ 'extends Base with Mixin, ChangeNotifierMixin, Mixin2');
+ _testClause('extends ObservableBase implements Interface',
+ 'extends ChangeNotifierBase implements Interface');
+ _testClause('extends ObservableBase implements Interface<T>',
+ 'extends ChangeNotifierBase implements Interface<T>');
+ _testClause('extends Base with ObservableMixin implements Interface',
+ 'extends Base with ChangeNotifierMixin implements Interface');
+ _testClause(
+ 'extends Base with Mixin, ObservableMixin implements I1, I2',
+ 'extends Base with Mixin, ChangeNotifierMixin implements I1, I2');
+ });
+
+ group('fixes contructor calls ', () {
+ _testInitializers('this.a', '(a) : __\$a = a');
+ _testInitializers('{this.a}', '({a}) : __\$a = a');
+ _testInitializers('[this.a]', '([a]) : __\$a = a');
+ _testInitializers('this.a, this.b', '(a, b) : __\$a = a, __\$b = b');
+ _testInitializers('{this.a, this.b}', '({a, b}) : __\$a = a, __\$b = b');
+ _testInitializers('[this.a, this.b]', '([a, b]) : __\$a = a, __\$b = b');
+ _testInitializers('this.a, [this.b]', '(a, [b]) : __\$a = a, __\$b = b');
+ _testInitializers('this.a, {this.b}', '(a, {b}) : __\$a = a, __\$b = b');
+ });
+}
+
+_testClause(String clauses, String expected) {
+ test(clauses, () {
+ var className = 'MyClass';
+ if (clauses.contains('<T>')) className += '<T>';
+ var code = '''
+ class $className $clauses {
+ @observable var field;
+ }''';
+
+ return _transform(code).then((output) {
+ var classPos = output.indexOf(className) + className.length;
+ var actualClauses = output.substring(classPos,
+ output.indexOf('{')).trim().replaceAll(' ', ' ');
+ expect(actualClauses, expected);
+ });
+ });
+}
+
+_testInitializers(String args, String expected) {
+ test(args, () {
+ var constructor = 'MyClass(';
+ var code = '''
+ class MyClass {
+ @observable var a;
+ @observable var b;
+ MyClass($args);
+ }''';
+
+ return _transform(code).then((output) {
+ var begin = output.indexOf(constructor) + constructor.length - 1;
+ var end = output.indexOf(';', begin);
+ if (end == -1) end = output.length;
+ var init = output.substring(begin, end).trim().replaceAll(' ', ' ');
+ expect(init, expected);
+ });
+ });
+}
+
+/** Helper that applies the transform by creating mock assets. */
+Future<String> _transform(String code) {
+ var id = new AssetId('foo', 'a/b/c.dart');
+ var asset = new Asset.fromString(id, code);
+ var transformer = new ObservableTransformer();
+ return transformer.isPrimary(asset).then((isPrimary) {
+ expect(isPrimary, isTrue);
+ var transform = new _MockTransform(asset);
+ return transformer.apply(transform).then((_) {
+ expect(transform.outs, hasLength(1));
+ expect(transform.outs[0].id, id);
+ return transform.outs.first.readAsString();
+ });
+ });
+}
+
+class _MockTransform implements Transform {
+ List<Asset> outs = [];
+ Asset _asset;
+ AssetId get primaryId => _asset.id;
+ TransformLogger logger = new TransformLogger(false);
+ Future<Asset> get primaryInput => new Future.value(_asset);
+
+ _MockTransform(this._asset);
+ Future<Asset> getInput(Asset id) {
+ if (id == primaryId) return primaryInput;
+ fail();
+ }
+
+ void addOutput(Asset output) {
+ outs.add(output);
+ }
+}
diff --git a/pkg/pkg.status b/pkg/pkg.status
index 0c4304c..03873c1 100644
--- a/pkg/pkg.status
+++ b/pkg/pkg.status
@@ -119,6 +119,7 @@
oauth2/test/client_test: Fail, OK # Uses dart:io.
oauth2/test/credentials_test: Fail, OK # Uses dart:io.
oauth2/test/handle_access_token_response_test: Fail, OK # Uses dart:io.
+observe/test/transform_test: Fail, OK # Uses dart:io.
path/test/io_test: Fail, OK # Uses dart:io.
watcher/test/*: Fail, OK # Uses dart:io.
@@ -185,7 +186,3 @@
[ $runtime == safari || $runtime == chrome || $runtime == ie9 || $runtime == ff || $runtime == dartium || $runtime == drt ]
docgen/test/single_library_test: Skip # Uses dart:io
-
-[ $compiler == none && ($runtime == drt || $runtime == dartium) ]
-# TODO(vsm): Triage from latest Blink merge
-custom_element/test/custom_element_test: Skip
diff --git a/pkg/source_maps/lib/printer.dart b/pkg/source_maps/lib/printer.dart
index 333aadc..95539d2 100644
--- a/pkg/source_maps/lib/printer.dart
+++ b/pkg/source_maps/lib/printer.dart
@@ -12,8 +12,8 @@
const int _LF = 10;
const int _CR = 13;
-/// A printer that keeps track of offset locations and records source maps
-/// locations.
+/// A simple printer that keeps track of offset locations and records source
+/// maps locations.
class Printer {
final String filename;
final StringBuffer _buff = new StringBuffer();
@@ -87,3 +87,159 @@
_loc = loc;
}
}
+
+/// A more advanced printer that keeps track of offset locations to record
+/// source maps, but additionally allows nesting of different kind of items,
+/// including [NestedPrinter]s, and it let's you automatically indent text.
+///
+/// This class is especially useful when doing code generation, where different
+/// peices of the code are generated independently on separate printers, and are
+/// finally put together in the end.
+class NestedPrinter implements NestedItem {
+
+ /// Items recoded by this printer, which can be [String] literals,
+ /// [NestedItem]s, and source map information like [Location] and [Span].
+ List _items = [];
+
+ /// Internal buffer to merge consecutive strings added to this printer.
+ StringBuffer _buff;
+
+ /// Current indentation, which can be updated from outside this class.
+ int indent;
+
+ /// Item used to indicate that the following item is copied from the original
+ /// source code, and hence we should preserve source-maps on every new line.
+ static final _ORIGINAL = new Object();
+
+ NestedPrinter([this.indent = 0]);
+
+ /// Adds [object] to this printer. [object] can be a [String],
+ /// [NestedPrinter], or anything implementing [NestedItem]. If [object] is a
+ /// [String], the value is appended directly, without doing any formatting
+ /// changes. If you wish to add a line of code with automatic indentation, use
+ /// [addLine] instead. [NestedPrinter]s and [NestedItem]s are not processed
+ /// until [build] gets called later on. We ensure that [build] emits every
+ /// object in the order that they were added to this printer.
+ ///
+ /// The [location] and [span] parameters indicate the corresponding source map
+ /// location of [object] in the original input. Only one, [location] or
+ /// [span], should be provided at a time.
+ ///
+ /// Indicate [isOriginal] when [object] is copied directly from the user code.
+ /// Setting [isOriginal] will make this printer propagate source map locations
+ /// on every line-break.
+ void add(object, {Location location, Span span, bool isOriginal: false}) {
+ if (object is! String || location != null || span != null || isOriginal) {
+ _flush();
+ assert(location == null || span == null);
+ if (location != null) _items.add(location);
+ if (span != null) _items.add(span);
+ if (isOriginal) _items.add(_ORIGINAL);
+ }
+
+ if (object is String) {
+ _appendString(object);
+ } else {
+ _items.add(object);
+ }
+ }
+
+ /// Append `2 * indent` spaces to this printer.
+ void insertIndent() => _indent(indent);
+
+ /// Add a [line], autoindenting to the current value of [indent]. Note,
+ /// indentation is not inferred from the contents added to this printer. If a
+ /// line starts or ends an indentation block, you need to also update [indent]
+ /// accordingly. Also, indentation is not adapted for nested printers. If
+ /// you add a [NestedPrinter] to this printer, its indentation is set
+ /// separately and will not include any the indentation set here.
+ ///
+ /// The [location] and [span] parameters indicate the corresponding source map
+ /// location of [object] in the original input. Only one, [location] or
+ /// [span], should be provided at a time.
+ void addLine(String line, {Location location, Span span}) {
+ if (location != null || span != null) {
+ _flush();
+ assert(location == null || span == null);
+ if (location != null) _items.add(location);
+ if (span != null) _items.add(span);
+ }
+ if (line == null) return;
+ if (line != '') {
+ // We don't indent empty lines.
+ _indent(indent);
+ _appendString(line);
+ }
+ _appendString('\n');
+ }
+
+ /// Appends a string merging it with any previous strings, if possible.
+ void _appendString(String s) {
+ if (_buff == null) _buff = new StringBuffer();
+ _buff.write(s);
+ }
+
+ /// Adds all of the current [_buff] contents as a string item.
+ void _flush() {
+ if (_buff != null) {
+ _items.add(_buff.toString());
+ _buff = null;
+ }
+ }
+
+ void _indent(int indent) {
+ for (int i = 0; i < indent; i++) _appendString(' ');
+ }
+
+ /// Returns a string representation of all the contents appended to this
+ /// printer, including source map location tokens.
+ String toString() {
+ _flush();
+ return (new StringBuffer()..writeAll(_items)).toString();
+ }
+
+ /// [Printer] used during the last call to [build], if any.
+ Printer printer;
+
+ /// Returns the text produced after calling [build].
+ String get text => printer.text;
+
+ /// Returns the source-map information produced after calling [build].
+ String get map => printer.map;
+
+ /// Builds the output of this printer and source map information. After
+ /// calling this function, you can use [text] and [map] to retrieve the
+ /// geenrated code and source map information, respectively.
+ void build(String filename) {
+ writeTo(printer = new Printer(filename));
+ }
+
+ /// Implements the [NestedItem] interface.
+ void writeTo(Printer printer) {
+ _flush();
+ bool propagate = false;
+ for (var item in _items) {
+ if (item is NestedItem) {
+ item.writeTo(printer);
+ } else if (item is String) {
+ printer.add(item, projectMarks: propagate);
+ propagate = false;
+ } else if (item is Location || item is Span) {
+ printer.mark(item);
+ } else if (item == _ORIGINAL) {
+ // we insert booleans when we are about to quote text that was copied
+ // from the original source. In such case, we will propagate marks on
+ // every new-line.
+ propagate = true;
+ } else {
+ throw new UnsupportedError('Unknown item type: $item');
+ }
+ }
+ }
+}
+
+/// An item added to a [NestedPrinter].
+abstract class NestedItem {
+ /// Write the contents of this item into [printer].
+ void writeTo(Printer printer);
+}
diff --git a/pkg/source_maps/lib/refactor.dart b/pkg/source_maps/lib/refactor.dart
new file mode 100644
index 0000000..45fb069
--- /dev/null
+++ b/pkg/source_maps/lib/refactor.dart
@@ -0,0 +1,131 @@
+// 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.
+
+/// Tools to help implement refactoring like transformations to Dart code.
+///
+/// [TextEditTransaction] supports making a series of changes to a text buffer.
+/// [guessIndent] helps to guess the appropriate indentiation for the new code.
+library source_maps.refactor;
+
+import 'span.dart';
+import 'printer.dart';
+
+/// Editable text transaction.
+///
+/// Applies a series of edits using original location
+/// information, and composes them into the edited string.
+class TextEditTransaction {
+ final SourceFile file;
+ final String original;
+ final _edits = <_TextEdit>[];
+
+ TextEditTransaction(this.original, this.file);
+
+ bool get hasEdits => _edits.length > 0;
+
+ /// Edit the original text, replacing text on the range [begin] and [end]
+ /// with the [replacement]. [replacement] can be either a string or a
+ /// [NestedPrinter].
+ void edit(int begin, int end, replacement) {
+ _edits.add(new _TextEdit(begin, end, replacement));
+ }
+
+ /// Create a source map [Location] for [offset].
+ Location _loc(int offset) =>
+ file != null ? file.location(offset) : null;
+
+ /// Applies all pending [edit]s and returns a [NestedPrinter] containing the
+ /// rewritten string and source map information. [filename] is given to the
+ /// underlying printer to indicate the name of the generated file that will
+ /// contains the source map information.
+ ///
+ /// Throws [UnsupportedError] if the edits were overlapping. If no edits were
+ /// made, the printer simply contains the original string.
+ NestedPrinter commit() {
+ var printer = new NestedPrinter();
+ if (_edits.length == 0) {
+ return printer..add(original, location: _loc(0), isOriginal: true);
+ }
+
+ // Sort edits by start location.
+ _edits.sort();
+
+ int consumed = 0;
+ for (var edit in _edits) {
+ if (consumed > edit.begin) {
+ var sb = new StringBuffer();
+ sb..write(file.location(edit.begin).formatString)
+ ..write(': overlapping edits. Insert at offset ')
+ ..write(edit.begin)
+ ..write(' but have consumed ')
+ ..write(consumed)
+ ..write(' input characters. List of edits:');
+ for (var e in _edits) sb..write('\n ')..write(e);
+ throw new UnsupportedError(sb.toString());
+ }
+
+ // Add characters from the original string between this edit and the last
+ // one, if any.
+ var betweenEdits = original.substring(consumed, edit.begin);
+ printer..add(betweenEdits, location: _loc(consumed), isOriginal: true)
+ ..add(edit.replace, location: _loc(edit.begin));
+ consumed = edit.end;
+ }
+
+ // Add any text from the end of the original string that was not replaced.
+ printer.add(original.substring(consumed),
+ location: _loc(consumed), isOriginal: true);
+ return printer;
+ }
+}
+
+class _TextEdit implements Comparable<_TextEdit> {
+ final int begin;
+ final int end;
+
+ /// The replacement used by the edit, can be a string or a [NestedPrinter].
+ final replace;
+
+ _TextEdit(this.begin, this.end, this.replace);
+
+ int get length => end - begin;
+
+ String toString() => '(Edit @ $begin,$end: "$replace")';
+
+ int compareTo(_TextEdit other) {
+ int diff = begin - other.begin;
+ if (diff != 0) return diff;
+ return end - other.end;
+ }
+}
+
+/// Returns all whitespace characters at the start of [charOffset]'s line.
+String guessIndent(String code, int charOffset) {
+ // Find the beginning of the line
+ int lineStart = 0;
+ for (int i = charOffset - 1; i >= 0; i--) {
+ var c = code.codeUnitAt(i);
+ if (c == _LF || c == _CR) {
+ lineStart = i + 1;
+ break;
+ }
+ }
+
+ // Grab all the whitespace
+ int whitespaceEnd = code.length;
+ for (int i = lineStart; i < code.length; i++) {
+ var c = code.codeUnitAt(i);
+ if (c != _SPACE && c != _TAB) {
+ whitespaceEnd = i;
+ break;
+ }
+ }
+
+ return code.substring(lineStart, whitespaceEnd);
+}
+
+const int _CR = 13;
+const int _LF = 10;
+const int _TAB = 9;
+const int _SPACE = 32;
diff --git a/pkg/source_maps/lib/source_maps.dart b/pkg/source_maps/lib/source_maps.dart
index 8fc48c9..2d4a4cc 100644
--- a/pkg/source_maps/lib/source_maps.dart
+++ b/pkg/source_maps/lib/source_maps.dart
@@ -39,4 +39,5 @@
export "builder.dart";
export "parser.dart";
export "printer.dart";
+export "refactor.dart";
export "span.dart";
diff --git a/pkg/source_maps/test/printer_test.dart b/pkg/source_maps/test/printer_test.dart
index d038ad5..eaeae2a 100644
--- a/pkg/source_maps/test/printer_test.dart
+++ b/pkg/source_maps/test/printer_test.dart
@@ -79,4 +79,44 @@
expect(printer2.text, out);
expect(printer2.map, printer.map);
});
+
+ group('nested printer', () {
+ test('simple use', () {
+ var printer = new NestedPrinter();
+ printer..add('var ')
+ ..add('x = 3;\n', span: inputVar1)
+ ..add('f(', span: inputFunction)
+ ..add('y) => ', span: inputVar2)
+ ..add('x + y;\n', span: inputExpr)
+ ..build('output.dart');
+ expect(printer.text, OUTPUT);
+ expect(printer.map, json.stringify(EXPECTED_MAP));
+ });
+
+ test('nested use', () {
+ var printer = new NestedPrinter();
+ printer..add('var ')
+ ..add(new NestedPrinter()..add('x = 3;\n', span: inputVar1))
+ ..add('f(', span: inputFunction)
+ ..add(new NestedPrinter()..add('y) => ', span: inputVar2))
+ ..add('x + y;\n', span: inputExpr)
+ ..build('output.dart');
+ expect(printer.text, OUTPUT);
+ expect(printer.map, json.stringify(EXPECTED_MAP));
+ });
+
+ test('add indentation', () {
+ var out = INPUT.replaceAll('long', '_s');
+ var lines = INPUT.trim().split('\n');
+ expect(lines.length, 7);
+ var printer = new NestedPrinter();
+ for (int i = 0; i < lines.length; i++) {
+ if (i == 5) printer.indent++;
+ printer.addLine(lines[i].replaceAll('long', '_s').trim());
+ if (i == 5) printer.indent--;
+ }
+ printer.build('output.dart');
+ expect(printer.text, out);
+ });
+ });
}
diff --git a/pkg/source_maps/test/refactor_test.dart b/pkg/source_maps/test/refactor_test.dart
new file mode 100644
index 0000000..c153d90
--- /dev/null
+++ b/pkg/source_maps/test/refactor_test.dart
@@ -0,0 +1,96 @@
+// 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.
+
+library polymer.test.refactor_test;
+
+import 'package:unittest/unittest.dart';
+import 'package:source_maps/refactor.dart';
+import 'package:source_maps/span.dart';
+import 'package:source_maps/parser.dart' show parse, Mapping;
+
+main() {
+ group('conflict detection', () {
+ var original = "0123456789abcdefghij";
+ var file = new SourceFile.text('', original);
+
+ test('no conflict, in order', () {
+ var txn = new TextEditTransaction(original, file);
+ txn.edit(2, 4, '.');
+ txn.edit(5, 5, '|');
+ txn.edit(6, 6, '-');
+ txn.edit(6, 7, '_');
+ expect((txn.commit()..build('')).text, "01.4|5-_789abcdefghij");
+ });
+
+ test('no conflict, out of order', () {
+ var txn = new TextEditTransaction(original, file);
+ txn.edit(2, 4, '.');
+ txn.edit(5, 5, '|');
+
+ // Regresion test for issue #404: there is no conflict/overlap for edits
+ // that don't remove any of the original code.
+ txn.edit(6, 7, '_');
+ txn.edit(6, 6, '-');
+ expect((txn.commit()..build('')).text, "01.4|5-_789abcdefghij");
+
+ });
+
+ test('conflict', () {
+ var txn = new TextEditTransaction(original, file);
+ txn.edit(2, 4, '.');
+ txn.edit(3, 3, '-');
+ expect(() => txn.commit(), throwsA(predicate(
+ (e) => e.toString().contains('overlapping edits'))));
+ });
+ });
+
+ test('generated source maps', () {
+ var original =
+ "0123456789\n0*23456789\n01*3456789\nabcdefghij\nabcd*fghij\n";
+ var file = new SourceFile.text('', original);
+ var txn = new TextEditTransaction(original, file);
+ txn.edit(27, 29, '__\n ');
+ txn.edit(34, 35, '___');
+ var printer = (txn.commit()..build(''));
+ var output = printer.text;
+ var map = parse(printer.map);
+ expect(output,
+ "0123456789\n0*23456789\n01*34__\n 789\na___cdefghij\nabcd*fghij\n");
+
+ // Line 1 and 2 are unmodified: mapping any column returns the beginning
+ // of the corresponding line:
+ expect(_span(1, 1, map, file), ":1:1: \n0123456789");
+ expect(_span(1, 5, map, file), ":1:1: \n0123456789");
+ expect(_span(2, 1, map, file), ":2:1: \n0*23456789");
+ expect(_span(2, 8, map, file), ":2:1: \n0*23456789");
+
+ // Line 3 is modified part way: mappings before the edits have the right
+ // mapping, after the edits the mapping is null.
+ expect(_span(3, 1, map, file), ":3:1: \n01*3456789");
+ expect(_span(3, 5, map, file), ":3:1: \n01*3456789");
+
+ // Start of edits map to beginning of the edit secion:
+ expect(_span(3, 6, map, file), ":3:6: \n01*3456789");
+ expect(_span(3, 7, map, file), ":3:6: \n01*3456789");
+
+ // Lines added have no mapping (they should inherit the last mapping),
+ // but the end of the edit region continues were we left off:
+ expect(_span(4, 1, map, file), isNull);
+ expect(_span(4, 5, map, file), ":3:8: \n01*3456789");
+
+ // Subsequent lines are still mapped correctly:
+ expect(_span(5, 1, map, file), ":4:1: \nabcdefghij"); // a (in a___cd...)
+ expect(_span(5, 2, map, file), ":4:2: \nabcdefghij"); // _ (in a___cd...)
+ expect(_span(5, 3, map, file), ":4:2: \nabcdefghij"); // _ (in a___cd...)
+ expect(_span(5, 4, map, file), ":4:2: \nabcdefghij"); // _ (in a___cd...)
+ expect(_span(5, 5, map, file), ":4:3: \nabcdefghij"); // c (in a___cd...)
+ expect(_span(6, 1, map, file), ":5:1: \nabcd*fghij");
+ expect(_span(6, 8, map, file), ":5:1: \nabcd*fghij");
+ });
+}
+
+String _span(int line, int column, Mapping map, SourceFile file) {
+ var span = map.spanFor(line - 1, column - 1, files: {'': file});
+ return span == null ? null : span.getLocationMessage('').trim();
+}
diff --git a/pkg/source_maps/test/run.dart b/pkg/source_maps/test/run.dart
index 9a19785..876cff7 100755
--- a/pkg/source_maps/test/run.dart
+++ b/pkg/source_maps/test/run.dart
@@ -13,6 +13,7 @@
import 'end2end_test.dart' as end2end_test;
import 'parser_test.dart' as parser_test;
import 'printer_test.dart' as printer_test;
+import 'refactor_test.dart' as refactor_test;
import 'span_test.dart' as span_test;
import 'utils_test.dart' as utils_test;
import 'vlq_test.dart' as vlq_test;
@@ -32,6 +33,7 @@
addGroup('end2end_test.dart', end2end_test.main);
addGroup('parser_test.dart', parser_test.main);
addGroup('printer_test.dart', printer_test.main);
+ addGroup('refactor_test.dart', refactor_test.main);
addGroup('span_test.dart', span_test.main);
addGroup('utils_test.dart', utils_test.main);
addGroup('vlq_test.dart', vlq_test.main);
diff --git a/runtime/vm/assembler_arm.cc b/runtime/vm/assembler_arm.cc
index e722032..7752441 100644
--- a/runtime/vm/assembler_arm.cc
+++ b/runtime/vm/assembler_arm.cc
@@ -1731,9 +1731,8 @@
if (!CanEncodeBranchOffset(offset)) {
ASSERT(!use_far_branches());
- const Error& error = Error::Handle(LanguageError::New(
- String::Handle(String::New("Branch offset overflow"))));
- Isolate::Current()->long_jump_base()->Jump(1, error);
+ Isolate::Current()->long_jump_base()->Jump(
+ 1, Object::branch_offset_error());
}
// Properly preserve only the bits supported in the instruction.
diff --git a/runtime/vm/assembler_mips.cc b/runtime/vm/assembler_mips.cc
index 7f06822..bb4a2c3 100644
--- a/runtime/vm/assembler_mips.cc
+++ b/runtime/vm/assembler_mips.cc
@@ -52,9 +52,8 @@
int32_t Assembler::EncodeBranchOffset(int32_t offset, int32_t instr) {
if (!CanEncodeBranchOffset(offset)) {
ASSERT(!use_far_branches());
- const Error& error = Error::Handle(LanguageError::New(
- String::Handle(String::New("Branch offset overflow"))));
- Isolate::Current()->long_jump_base()->Jump(1, error);
+ Isolate::Current()->long_jump_base()->Jump(
+ 1, Object::branch_offset_error());
}
// Properly preserve only the bits supported in the instruction.
diff --git a/runtime/vm/class_table.cc b/runtime/vm/class_table.cc
index 3b34490..2eac4c9 100644
--- a/runtime/vm/class_table.cc
+++ b/runtime/vm/class_table.cc
@@ -29,7 +29,6 @@
table_[i] = vm_class_table->At(i);
}
table_[kFreeListElement] = vm_class_table->At(kFreeListElement);
- table_[kNullCid] = vm_class_table->At(kNullCid);
table_[kDynamicCid] = vm_class_table->At(kDynamicCid);
table_[kVoidCid] = vm_class_table->At(kVoidCid);
}
diff --git a/runtime/vm/compiler.cc b/runtime/vm/compiler.cc
index 8adb292..73c6d63 100644
--- a/runtime/vm/compiler.cc
+++ b/runtime/vm/compiler.cc
@@ -548,21 +548,21 @@
done = true;
} else {
// We bailed out.
- const Error& bailout_error = Error::Handle(
- isolate->object_store()->sticky_error());
- ASSERT(bailout_error.IsLanguageError());
- const LanguageError& le = LanguageError::CheckedHandle(
- isolate->object_store()->sticky_error());
- const String& msg = String::Handle(le.message());
- if (msg.Equals("Branch offset overflow")) {
+ if (isolate->object_store()->sticky_error() ==
+ Object::branch_offset_error().raw()) {
+ // Compilation failed due to an out of range branch offset in the
+ // assembler. We try again (done = false) with far branches enabled.
done = false;
ASSERT(!use_far_branches);
use_far_branches = true;
} else {
- // If not for a branch offset overflow, we only bail out from
- // generating ssa code.
+ // If the error isn't due to an out of range branch offset, we don't
+ // try again (done = true), and indicate that we did not finish
+ // compiling (is_compiled = false).
if (FLAG_trace_bailout) {
+ const Error& bailout_error = Error::Handle(
+ isolate->object_store()->sticky_error());
OS::Print("%s\n", bailout_error.ToErrorCString());
}
done = true;
diff --git a/runtime/vm/dart_api_impl_test.cc b/runtime/vm/dart_api_impl_test.cc
index 8be7d0a..dc3017c3 100644
--- a/runtime/vm/dart_api_impl_test.cc
+++ b/runtime/vm/dart_api_impl_test.cc
@@ -203,7 +203,7 @@
{
Isolate* isolate = Isolate::Current();
DARTSCOPE(isolate);
- Dart_Handle class1 = Api::NewHandle(isolate, Object::null_class());
+ Dart_Handle class1 = Api::NewHandle(isolate, Object::void_class());
Dart_Handle class2 = Api::NewHandle(isolate, Object::class_class());
EXPECT(Dart_IdentityEquals(class1, class1));
@@ -4464,6 +4464,7 @@
TEST_CASE(GetType) {
const char* kScriptChars =
+ "library testlib;\n"
"class Class {\n"
" static var name = 'Class';\n"
"}\n"
@@ -4495,7 +4496,7 @@
// Lookup a class that does not exist.
type = Dart_GetType(lib, NewString("DoesNotExist"), 0, NULL);
EXPECT(Dart_IsError(type));
- EXPECT_STREQ("Type 'DoesNotExist' not found in library 'dart:test-lib'.",
+ EXPECT_STREQ("Type 'DoesNotExist' not found in library 'testlib'.",
Dart_GetError(type));
// Lookup a class from an error library. The error propagates.
@@ -4654,6 +4655,7 @@
TEST_CASE(RootLibrary) {
const char* kScriptChars =
+ "library testlib;"
"main() {"
" return 12345;"
"}";
@@ -4673,7 +4675,14 @@
EXPECT(!Dart_IsNull(root_lib));
const char* name_cstr = "";
EXPECT_VALID(Dart_StringToCString(lib_name, &name_cstr));
- EXPECT_STREQ(TestCase::url(), name_cstr);
+ EXPECT_STREQ("testlib", name_cstr);
+
+ Dart_Handle lib_uri = Dart_LibraryUrl(root_lib);
+ EXPECT_VALID(lib_uri);
+ EXPECT(!Dart_IsNull(lib_uri));
+ const char* uri_cstr = "";
+ EXPECT_VALID(Dart_StringToCString(lib_uri, &uri_cstr));
+ EXPECT_STREQ(TestCase::url(), uri_cstr);
}
diff --git a/runtime/vm/intermediate_language_ia32.cc b/runtime/vm/intermediate_language_ia32.cc
index 6a90823..69e744c 100644
--- a/runtime/vm/intermediate_language_ia32.cc
+++ b/runtime/vm/intermediate_language_ia32.cc
@@ -4493,6 +4493,9 @@
new LocationSummary(kNumInputs, kNumTemps, LocationSummary::kNoCall);
summary->set_in(0, Location::RequiresFpuRegister());
summary->set_out(Location::SameAsFirstInput());
+ if (FLAG_throw_on_javascript_int_overflow) {
+ summary->set_temp(0, Location::RequiresRegister());
+ }
return summary;
}
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 29c3262..a6d7364 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -88,10 +88,10 @@
Bool* Object::bool_false_ = NULL;
Smi* Object::smi_illegal_cid_ = NULL;
LanguageError* Object::snapshot_writer_error_ = NULL;
+LanguageError* Object::branch_offset_error_ = NULL;
RawObject* Object::null_ = reinterpret_cast<RawObject*>(RAW_NULL);
RawClass* Object::class_class_ = reinterpret_cast<RawClass*>(RAW_NULL);
-RawClass* Object::null_class_ = reinterpret_cast<RawClass*>(RAW_NULL);
RawClass* Object::dynamic_class_ = reinterpret_cast<RawClass*>(RAW_NULL);
RawClass* Object::void_class_ = reinterpret_cast<RawClass*>(RAW_NULL);
RawClass* Object::unresolved_class_class_ =
@@ -360,6 +360,8 @@
bool_false_ = Bool::ReadOnlyHandle();
smi_illegal_cid_ = Smi::ReadOnlyHandle();
snapshot_writer_error_ = LanguageError::ReadOnlyHandle();
+ branch_offset_error_ = LanguageError::ReadOnlyHandle();
+
// Allocate and initialize the null instance.
// 'null_' must be the first object allocated as it is used in allocation to
@@ -413,7 +415,7 @@
cls = Class::New<Instance>(kNullCid);
cls.set_is_finalized();
cls.set_is_type_finalized();
- null_class_ = cls.raw();
+ isolate->object_store()->set_null_class(cls);
// Allocate and initialize the free list element class.
cls = Class::New<FreeListElement::FakeInstance>(kFreeListElement);
@@ -571,8 +573,12 @@
*bool_false_ = Bool::New(false);
*smi_illegal_cid_ = Smi::New(kIllegalCid);
- *snapshot_writer_error_ =
- LanguageError::New(String::Handle(String::New("SnapshotWriter Error")));
+
+ String& error_str = String::Handle();
+ error_str = String::New("SnapshotWriter Error");
+ *snapshot_writer_error_ = LanguageError::New(error_str);
+ error_str = String::New("Branch offset overflow");
+ *branch_offset_error_ = LanguageError::New(error_str);
ASSERT(!null_object_->IsSmi());
ASSERT(!null_array_->IsSmi());
@@ -600,6 +606,8 @@
ASSERT(smi_illegal_cid_->IsSmi());
ASSERT(!snapshot_writer_error_->IsSmi());
ASSERT(snapshot_writer_error_->IsLanguageError());
+ ASSERT(!branch_offset_error_->IsSmi());
+ ASSERT(branch_offset_error_->IsLanguageError());
}
@@ -612,7 +620,6 @@
// Set up names for all VM singleton classes.
SET_CLASS_NAME(class, Class);
- SET_CLASS_NAME(null, Null);
SET_CLASS_NAME(dynamic, Dynamic);
SET_CLASS_NAME(void, Void);
SET_CLASS_NAME(unresolved_class, UnresolvedClass);
@@ -833,6 +840,20 @@
RegisterClass(cls, Symbols::Bool(), core_lib);
pending_classes.Add(cls, Heap::kOld);
+ cls = Class::New<Instance>(kNullCid);
+ cls.set_name(Symbols::Null());
+ // We immediately mark Null as finalized because it has no corresponding
+ // source.
+ cls.set_is_finalized();
+ cls.set_is_type_finalized();
+ object_store->set_null_class(cls);
+ cls.set_library(core_lib); // A sort of fiction for the mirrors.
+ // When/if we promote Null to an ordinary class, it should be added to the
+ // core library, given source and added to the list of classes pending
+ // finalization.
+ // RegisterClass(cls, Symbols::Null(), core_lib);
+ // pending_classes.Add(cls, Heap::kOld);
+
cls = object_store->array_class(); // Was allocated above.
RegisterPrivateClass(cls, Symbols::ObjectArray(), core_lib);
pending_classes.Add(cls, Heap::kOld);
@@ -1091,22 +1112,25 @@
type = Type::NewNonParameterizedType(cls);
object_store->set_mint_type(type);
- // The classes 'Null' and 'void' are not registered in the class dictionary,
- // because their names are reserved keywords. Their names are not heap
- // allocated, because the classes reside in the VM isolate.
+ // The class 'Null' is not register in the class dictionary because it is not
+ // The classes 'void' and 'dynamic' are phoney classes to make type checking
+ // more regular; they live in the VM isolate. The class 'void' is not
+ // registered in the class dictionary because its name is a reserved word.
+ // The class 'dynamic' is registered in the class dictionary because its name
+ // is a built-in identifier (this is wrong).
// The corresponding types are stored in the object store.
- cls = null_class();
+ cls = object_store->null_class();
type = Type::NewNonParameterizedType(cls);
object_store->set_null_type(type);
+ // Consider removing when/if Null becomes an ordinary class.
+ type = object_store->object_type();
+ cls.set_super_type(type);
+
cls = void_class();
type = Type::NewNonParameterizedType(cls);
object_store->set_void_type(type);
- // The class 'dynamic' is registered in the class dictionary because its name
- // is a built-in identifier, rather than a reserved keyword. Its name is not
- // heap allocated, because the class resides in the VM isolate.
- // The corresponding type, the "unknown type", is stored in the object store.
cls = dynamic_class();
type = Type::NewNonParameterizedType(cls);
object_store->set_dynamic_type(type);
@@ -1211,6 +1235,9 @@
cls = Class::New<Bool>();
object_store->set_bool_class(cls);
+ cls = Class::New<Instance>(kNullCid);
+ object_store->set_null_class(cls);
+
cls = Class::New<Stacktrace>();
object_store->set_stacktrace_class(cls);
@@ -7056,7 +7083,7 @@
RawLibrary* Library::NewLibraryHelper(const String& url,
bool import_core_lib) {
const Library& result = Library::Handle(Library::New());
- result.StorePointer(&result.raw_ptr()->name_, url.raw());
+ result.StorePointer(&result.raw_ptr()->name_, Symbols::Empty().raw());
result.StorePointer(&result.raw_ptr()->url_, url.raw());
result.raw_ptr()->private_key_ = Scanner::AllocatePrivateKey(result);
result.raw_ptr()->dictionary_ = Object::empty_array().raw();
@@ -10434,6 +10461,12 @@
}
+bool AbstractType::IsNullType() const {
+ return HasResolvedTypeClass() &&
+ (type_class() == Type::Handle(Type::NullType()).type_class());
+}
+
+
bool AbstractType::IsBoolType() const {
return HasResolvedTypeClass() &&
(type_class() == Type::Handle(Type::BoolType()).type_class());
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index a1455845..ed73e6b 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -404,8 +404,12 @@
return *snapshot_writer_error_;
}
+ static const LanguageError& branch_offset_error() {
+ ASSERT(branch_offset_error_ != NULL);
+ return *branch_offset_error_;
+ }
+
static RawClass* class_class() { return class_class_; }
- static RawClass* null_class() { return null_class_; }
static RawClass* dynamic_class() { return dynamic_class_; }
static RawClass* void_class() { return void_class_; }
static RawClass* unresolved_class_class() { return unresolved_class_class_; }
@@ -553,7 +557,6 @@
static RawObject* null_;
static RawClass* class_class_; // Class of the Class vm object.
- static RawClass* null_class_; // Class of the null object.
static RawClass* dynamic_class_; // Class of the 'dynamic' type.
static RawClass* void_class_; // Class of the 'void' type.
static RawClass* unresolved_class_class_; // Class of UnresolvedClass.
@@ -604,6 +607,7 @@
static Bool* bool_false_;
static Smi* smi_illegal_cid_;
static LanguageError* snapshot_writer_error_;
+ static LanguageError* branch_offset_error_;
friend void ClassTable::Register(const Class& cls);
friend void RawObject::Validate(Isolate* isolate) const;
@@ -3745,9 +3749,7 @@
}
// Check if this type represents the 'Null' type.
- bool IsNullType() const {
- return HasResolvedTypeClass() && (type_class() == Object::null_class());
- }
+ bool IsNullType() const;
// Check if this type represents the 'void' type.
bool IsVoidType() const {
diff --git a/runtime/vm/object_store.cc b/runtime/vm/object_store.cc
index 11482f7..f6602b0 100644
--- a/runtime/vm/object_store.cc
+++ b/runtime/vm/object_store.cc
@@ -17,6 +17,7 @@
ObjectStore::ObjectStore()
: object_class_(Class::null()),
object_type_(Type::null()),
+ null_class_(Class::null()),
null_type_(Type::null()),
dynamic_type_(Type::null()),
void_type_(Type::null()),
diff --git a/runtime/vm/object_store.h b/runtime/vm/object_store.h
index 76a2bcf..1d51a8c 100644
--- a/runtime/vm/object_store.h
+++ b/runtime/vm/object_store.h
@@ -50,6 +50,12 @@
object_type_ = value.raw();
}
+ RawClass* null_class() const {
+ ASSERT(null_class_ != Object::null());
+ return null_class_;
+ }
+ void set_null_class(const Class& value) { null_class_ = value.raw(); }
+
RawType* null_type() const { return null_type_; }
void set_null_type(const Type& value) {
null_type_ = value.raw();
@@ -427,6 +433,7 @@
RawObject** from() { return reinterpret_cast<RawObject**>(&object_class_); }
RawClass* object_class_;
RawType* object_type_;
+ RawClass* null_class_;
RawType* null_type_;
RawType* dynamic_type_;
RawType* void_type_;
diff --git a/runtime/vm/raw_object.cc b/runtime/vm/raw_object.cc
index 535c785..b8ecacb 100644
--- a/runtime/vm/raw_object.cc
+++ b/runtime/vm/raw_object.cc
@@ -24,7 +24,7 @@
void RawObject::Validate(Isolate* isolate) const {
- if (Object::null_class_ == reinterpret_cast<RawClass*>(kHeapObjectTag)) {
+ if (Object::void_class_ == reinterpret_cast<RawClass*>(kHeapObjectTag)) {
// Validation relies on properly initialized class classes. Skip if the
// VM is still being initialized.
return;
diff --git a/runtime/vm/snapshot.cc b/runtime/vm/snapshot.cc
index 9e89eae..a6be317 100644
--- a/runtime/vm/snapshot.cc
+++ b/runtime/vm/snapshot.cc
@@ -470,7 +470,7 @@
ASSERT(isolate()->no_gc_scope_depth() != 0);
if (class_id < kNumPredefinedCids) {
ASSERT((class_id >= kInstanceCid) &&
- (class_id <= kExternalTypedDataFloat32x4ArrayCid));
+ (class_id <= kNullCid));
return isolate()->class_table()->At(class_id);
}
cls_ = Object::class_class();
diff --git a/runtime/vm/snapshot_test.cc b/runtime/vm/snapshot_test.cc
index f3d8e43..c1781d2 100644
--- a/runtime/vm/snapshot_test.cc
+++ b/runtime/vm/snapshot_test.cc
@@ -460,7 +460,6 @@
uint8_t* buffer;
MessageWriter writer(&buffer, &malloc_allocator);
writer.WriteObject(Object::class_class());
- writer.WriteObject(Object::null_class());
writer.WriteObject(Object::type_arguments_class());
writer.WriteObject(Object::instantiated_type_arguments_class());
writer.WriteObject(Object::function_class());
@@ -480,7 +479,6 @@
SnapshotReader reader(buffer, buffer_len, Snapshot::kMessage,
Isolate::Current());
EXPECT(Object::class_class() == reader.ReadObject());
- EXPECT(Object::null_class() == reader.ReadObject());
EXPECT(Object::type_arguments_class() == reader.ReadObject());
EXPECT(Object::instantiated_type_arguments_class() == reader.ReadObject());
EXPECT(Object::function_class() == reader.ReadObject());
diff --git a/sdk/lib/html/dart2js/html_dart2js.dart b/sdk/lib/html/dart2js/html_dart2js.dart
index dfaa5ed..643dd58 100644
--- a/sdk/lib/html/dart2js/html_dart2js.dart
+++ b/sdk/lib/html/dart2js/html_dart2js.dart
@@ -9316,30 +9316,30 @@
/**
* Checks if this element matches the CSS selectors.
- *
- * If `includeAncestors` is true, we examine all of this element's parent
- * elements and also return true if any of its parent elements matches
- * `selectors`.
*/
@Experimental()
- bool matches(String selectors, [includeAncestors = false]) {
+ bool matches(String selectors) {
+ if (JS('bool', '!!#.matches', this)) {
+ return JS('bool', '#.matches(#)', this, selectors);
+ } else if (JS('bool', '!!#.webkitMatchesSelector', this)) {
+ return JS('bool', '#.webkitMatchesSelector(#)', this, selectors);
+ } else if (JS('bool', '!!#.mozMatchesSelector', this)) {
+ return JS('bool', '#.mozMatchesSelector(#)', this, selectors);
+ } else if (JS('bool', '!!#.msMatchesSelector', this)) {
+ return matches = JS('bool', '#.msMatchesSelector(#)', this, selectors);
+ } else {
+ throw new UnsupportedError("Not supported on this platform");
+ }
+ }
+
+ /** Checks if this element or any of its parents match the CSS selectors. */
+ @Experimental()
+ bool matchesWithAncestors(String selectors) {
var elem = this;
do {
- bool matches = false;
- if (JS('bool', '!!#.matches', elem)) {
- matches = JS('bool', '#.matches(#)', elem, selectors);
- } else if (JS('bool', '!!#.webkitMatchesSelector', elem)) {
- matches = JS('bool', '#.webkitMatchesSelector(#)', elem, selectors);
- } else if (JS('bool', '!!#.mozMatchesSelector', elem)) {
- matches = JS('bool', '#.mozMatchesSelector(#)', elem, selectors);
- } else if (JS('bool', '!!#.msMatchesSelector', elem)) {
- matches = JS('bool', '#.msMatchesSelector(#)', elem, selectors);
- } else {
- throw new UnsupportedError("Not supported on this platform");
- }
- if (matches) return true;
+ if (elem.matches(selectors)) return true;
elem = elem.parent;
- } while(includeAncestors && elem != null);
+ } while(elem != null);
return false;
}
@@ -28810,7 +28810,7 @@
super(target, eventType, useCapture);
Stream<T> matches(String selector) =>
- this.where((event) => event.target.matches(selector, true));
+ this.where((event) => event.target.matchesWithAncestors(selector));
}
/**
@@ -28832,7 +28832,7 @@
}
Stream<T> matches(String selector) =>
- this.where((event) => event.target.matches(selector, true));
+ this.where((event) => event.target.matchesWithAncestors(selector));
// Delegate all regular Stream behavor to our wrapped Stream.
StreamSubscription<T> listen(void onData(T event),
diff --git a/sdk/lib/html/dartium/html_dartium.dart b/sdk/lib/html/dartium/html_dartium.dart
index 1524bc9..9e93000 100644
--- a/sdk/lib/html/dartium/html_dartium.dart
+++ b/sdk/lib/html/dartium/html_dartium.dart
@@ -9734,6 +9734,17 @@
}
+ /** Checks if this element or any of its parents match the CSS selectors. */
+ @Experimental()
+ bool matchesWithAncestors(String selectors) {
+ var elem = this;
+ do {
+ if (elem.matches(selectors)) return true;
+ elem = elem.parent;
+ } while(elem != null);
+ return false;
+ }
+
Element _templateInstanceRef;
// Note: only used if `this is! TemplateElement`
@@ -30351,7 +30362,7 @@
super(target, eventType, useCapture);
Stream<T> matches(String selector) =>
- this.where((event) => event.target.matches(selector, true));
+ this.where((event) => event.target.matchesWithAncestors(selector));
}
/**
@@ -30373,7 +30384,7 @@
}
Stream<T> matches(String selector) =>
- this.where((event) => event.target.matches(selector, true));
+ this.where((event) => event.target.matchesWithAncestors(selector));
// Delegate all regular Stream behavor to our wrapped Stream.
StreamSubscription<T> listen(void onData(T event),
diff --git a/tests/co19/co19-analyzer.status b/tests/co19/co19-analyzer.status
index 78dc78a..a79a085 100644
--- a/tests/co19/co19-analyzer.status
+++ b/tests/co19/co19-analyzer.status
@@ -42,7 +42,7 @@
LibTest/core/String/splitChars_A01_t01: fail, OK
# co19 issue #389, ceil, floor, truncate and round return integers
-LibTest/core/int/operator_division_A01_t01: fail
+LibTest/core/int/operator_division_A01_t01: fail, OK
# co19 issue #395, uses dart:io API (outdated)
Language/15_Reference/1_Lexical_Rules_A01_t10: Fail, OK
@@ -168,8 +168,8 @@
LibTest/core/RegExp/Pattern_semantics/firstMatch_CharacterEscape_A09_t01: fail, OK
#co19 issue #432, missing @static-warning annotation
-Language/14_Types/8_Parameterized_Types_A03_t03: fail,OK
-Language/14_Types/8_Parameterized_Types_A03_t05: fail,OK
+Language/14_Types/8_Parameterized_Types_A03_t03: fail, OK
+Language/14_Types/8_Parameterized_Types_A03_t05: fail, OK
# co19 issue #433, missing @static-warning annotation
Language/11_Expressions/15_Method_Invocation/3_Static_Invocation_A03_t01: fail, OK
@@ -191,8 +191,8 @@
LibTest/core/AssertionError/url_A01_t01: fail, OK
# co19 issue 459, FallThroughError is no longer const
-LibTest/core/FallThroughError/toString_A01_t01: Fail
-LibTest/core/FallThroughError/FallThroughError_A01_t01: Fail
+LibTest/core/FallThroughError/toString_A01_t01: fail, OK
+LibTest/core/FallThroughError/FallThroughError_A01_t01: Fail, OK
# co19 issue #437, annotation should be constant _variable_ or constant constructor invocation
Language/07_Classes/07_Classes_A01_t20: fail, OK
@@ -452,3 +452,6 @@
Language/13_Libraries_and_Scripts/1_Imports_A03_t68: fail, OK
Language/13_Libraries_and_Scripts/1_Imports_A03_t69: fail, OK
Language/13_Libraries_and_Scripts/1_Imports_A03_t70: fail, OK
+
+# co19 issue 483, instance access to static member
+Language/11_Expressions/15_Method_Invocation/2_Cascaded_Invocation_A01_t19: fail, OK
diff --git a/tests/html/html.status b/tests/html/html.status
index 1000c5c..10747a7 100644
--- a/tests/html/html.status
+++ b/tests/html/html.status
@@ -71,8 +71,6 @@
[ $compiler == none && ($runtime == drt || $runtime == dartium) ]
request_animation_frame_test: Skip # drt hangs; requestAnimationFrame not implemented
worker_api_test: Fail # http://dartbug.com/10223
-element_test/click: Fail #TODO(efortuna)
-element_test/eventDelegation: Fail #TODO(efortuna)
[ $compiler == none && ($runtime == drt || $runtime == dartium) && $system == windows]
websql_test: Skip # Issue 4941: stderr contains a backtrace.
@@ -98,6 +96,8 @@
canvasrenderingcontext2d_test/drawImage_video_element: Fail # IE does not support drawImage w/ video element
canvasrenderingcontext2d_test/drawImage_video_element_dataUrl: Fail # IE does not support drawImage w/ video element
worker_test/functional: Fail # IE uses incorrect security context for Blob URIs.
+element_test/eventDelegation: Fail # TODO(efortuna)
+element_test/matches: Fail # TODO(efortuna)
# IE10 Feature support statuses-
# All changes should be accompanied by platform support annotation changes.
@@ -160,6 +160,7 @@
canvasrenderingcontext2d_test/drawImage_video_element_dataUrl: Fail # IE does not support drawImage w/ video element
canvasrenderingcontext2d_test/drawImage_image_element: Pass, Fail # Issue: 11416
input_element_test/attributes: Fail # IE returns null while others ''
+element_test/eventDelegation: Fail # TODO(efortuna)
# IE9 Feature support statuses-
# All changes should be accompanied by platform support annotation changes.
diff --git a/tests/language/language_analyzer.status b/tests/language/language_analyzer.status
index e8a968b..194c299 100644
--- a/tests/language/language_analyzer.status
+++ b/tests/language/language_analyzer.status
@@ -80,11 +80,6 @@
# TBF: hiding at start of the block and declared after declaration statement
scope_negative_test: fail
-# TBF: 'instance.staticMethod()' is static warning
-static_field_test/01: fail
-static_field_test/02: fail
-static_field_test/03: fail
-
# TBF
method_override2_test/00: fail # issue 11497
method_override2_test/01: fail # issue 11497
diff --git a/tests/lib/lib.status b/tests/lib/lib.status
index 31a0738..6967e4c 100644
--- a/tests/lib/lib.status
+++ b/tests/lib/lib.status
@@ -22,6 +22,7 @@
mirrors/null_test : Fail # Issue 12129
mirrors/library_metadata_test: Fail # Issue 10905
mirrors/library_uri_io_test: Skip # Not intended for dart2js as it uses dart:io.
+mirrors/unnamed_library_test: Fail # Issue 10580
async/run_async3_test: Fail # _enqueueImmediate runs after Timer. http://dartbug.com/9002
async/run_async4_test: Pass, Fail # no global exception handler in isolates. http://dartbug.com/9012
async/run_async6_test: Fail # global error handling is not supported. http://dartbug.com/5958
@@ -101,7 +102,6 @@
async/run_async3_test: Fail # _enqueueImmediate runs after Timer. http://dartbug.com/9001.
mirrors/parameter_test: Fail # http://dartbug.com/11567
mirrors/operator_test: Fail # http://dartbug.com/11944
-mirrors/null_test: Fail # Issue 12128
[ $compiler == none && $runtime == drt ]
async/timer_isolate_test: Skip # See Issue 4997
diff --git a/tests/lib/mirrors/null_test.dart b/tests/lib/mirrors/null_test.dart
index 9142c6d..2a8a8ba 100644
--- a/tests/lib/mirrors/null_test.dart
+++ b/tests/lib/mirrors/null_test.dart
@@ -11,6 +11,8 @@
main() {
InstanceMirror nullMirror = reflect(null);
Expect.isTrue(nullMirror.getField(const Symbol('hashCode')).reflectee is int);
+ Expect.equals(null.hashCode,
+ nullMirror.getField(const Symbol('hashCode')).reflectee);
Expect.equals('Null',
nullMirror.getField(const Symbol('runtimeType')).reflectee
.toString());
@@ -27,5 +29,7 @@
Expect.equals(const Symbol('Null'), NullMirror.simpleName);
Expect.equals(const Symbol('Object'), NullMirror.superclass.simpleName);
Expect.equals(null, NullMirror.superclass.superclass);
- Expect.equals(null, NullMirror.owner); // Null belongs to no library.
+ Expect.listEquals([], NullMirror.superinterfaces);
+ Expect.equals(currentMirrorSystem().libraries[Uri.parse('dart:core')],
+ NullMirror.owner);
}
diff --git a/tests/lib/mirrors/unnamed_library_test.dart b/tests/lib/mirrors/unnamed_library_test.dart
new file mode 100644
index 0000000..1e81eaa
--- /dev/null
+++ b/tests/lib/mirrors/unnamed_library_test.dart
@@ -0,0 +1,15 @@
+// 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.
+
+// No library declaration.
+
+import 'dart:mirrors';
+
+import 'package:expect/expect.dart';
+
+class C {}
+
+main() {
+ Expect.equals(const Symbol(""), reflectClass(C).owner.simpleName);
+}
diff --git a/tools/VERSION b/tools/VERSION
index 4a9b649..4a2aa46 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -1,4 +1,4 @@
MAJOR 0
MINOR 6
-BUILD 16
+BUILD 17
PATCH 0
diff --git a/tools/dom/src/EventStreamProvider.dart b/tools/dom/src/EventStreamProvider.dart
index a4ad4c0f..a9c293d 100644
--- a/tools/dom/src/EventStreamProvider.dart
+++ b/tools/dom/src/EventStreamProvider.dart
@@ -52,7 +52,7 @@
super(target, eventType, useCapture);
Stream<T> matches(String selector) =>
- this.where((event) => event.target.matches(selector, true));
+ this.where((event) => event.target.matchesWithAncestors(selector));
}
/**
@@ -74,7 +74,7 @@
}
Stream<T> matches(String selector) =>
- this.where((event) => event.target.matches(selector, true));
+ this.where((event) => event.target.matchesWithAncestors(selector));
// Delegate all regular Stream behavor to our wrapped Stream.
StreamSubscription<T> listen(void onData(T event),
diff --git a/tools/dom/templates/html/impl/impl_Element.darttemplate b/tools/dom/templates/html/impl/impl_Element.darttemplate
index e708395..991fa21 100644
--- a/tools/dom/templates/html/impl/impl_Element.darttemplate
+++ b/tools/dom/templates/html/impl/impl_Element.darttemplate
@@ -877,35 +877,35 @@
/**
* Checks if this element matches the CSS selectors.
- *
- * If `includeAncestors` is true, we examine all of this element's parent
- * elements and also return true if any of its parent elements matches
- * `selectors`.
*/
@Experimental()
- bool matches(String selectors, [includeAncestors = false]) {
- var elem = this;
- do {
- bool matches = false;
- if (JS('bool', '!!#.matches', elem)) {
- matches = JS('bool', '#.matches(#)', elem, selectors);
- } else if (JS('bool', '!!#.webkitMatchesSelector', elem)) {
- matches = JS('bool', '#.webkitMatchesSelector(#)', elem, selectors);
- } else if (JS('bool', '!!#.mozMatchesSelector', elem)) {
- matches = JS('bool', '#.mozMatchesSelector(#)', elem, selectors);
- } else if (JS('bool', '!!#.msMatchesSelector', elem)) {
- matches = JS('bool', '#.msMatchesSelector(#)', elem, selectors);
- } else {
- throw new UnsupportedError("Not supported on this platform");
- }
- if (matches) return true;
- elem = elem.parent;
- } while(includeAncestors && elem != null);
- return false;
+ bool matches(String selectors) {
+ if (JS('bool', '!!#.matches', this)) {
+ return JS('bool', '#.matches(#)', this, selectors);
+ } else if (JS('bool', '!!#.webkitMatchesSelector', this)) {
+ return JS('bool', '#.webkitMatchesSelector(#)', this, selectors);
+ } else if (JS('bool', '!!#.mozMatchesSelector', this)) {
+ return JS('bool', '#.mozMatchesSelector(#)', this, selectors);
+ } else if (JS('bool', '!!#.msMatchesSelector', this)) {
+ return matches = JS('bool', '#.msMatchesSelector(#)', this, selectors);
+ } else {
+ throw new UnsupportedError("Not supported on this platform");
+ }
}
$else
$endif
+ /** Checks if this element or any of its parents match the CSS selectors. */
+ @Experimental()
+ bool matchesWithAncestors(String selectors) {
+ var elem = this;
+ do {
+ if (elem.matches(selectors)) return true;
+ elem = elem.parent;
+ } while(elem != null);
+ return false;
+ }
+
$if DART2JS
@Creates('Null') // Set from Dart code; does not instantiate a native type.
$endif