Version 2.15.0-222.0.dev
Merge commit '938e7ae54e44f877042133258c58f4663512b6f8' into 'dev'
diff --git a/DEPS b/DEPS
index 3c180ca..6fb957c 100644
--- a/DEPS
+++ b/DEPS
@@ -46,7 +46,7 @@
# has.
"co19_rev": "cd6510c8346444792b37f6f26feffcc234a44dd1",
# This line prevents conflicts when both packages are rolled simultaneously.
- "co19_2_rev": "13344ad01472df9badfa78fd6aa411f042e13354",
+ "co19_2_rev": "a93f8484f7265f0849692d3b1208323393733f8d",
# The internal benchmarks to use. See go/dart-benchmarks-internal
"benchmarks_internal_rev": "076df10d9b77af337f2d8029725787155eb1cd52",
diff --git a/pkg/compiler/lib/src/js/size_estimator.dart b/pkg/compiler/lib/src/js/size_estimator.dart
index 022c4c2..33a45ed 100644
--- a/pkg/compiler/lib/src/js/size_estimator.dart
+++ b/pkg/compiler/lib/src/js/size_estimator.dart
@@ -843,6 +843,13 @@
out("=>");
int closingPosition;
Node body = fun.body;
+ // Simplify arrow functions that return a single expression.
+ if (fun.implicitReturnAllowed && body is Block) {
+ final statement = unwrapBlockIfSingleStatement(body);
+ if (statement is Return) {
+ body = statement.value;
+ }
+ }
if (body is Block) {
closingPosition = blockOut(body);
} else {
@@ -854,7 +861,7 @@
visitNestedExpression(body, ASSIGNMENT,
newInForInit: false, newAtStatementBegin: false);
if (needsParens) out(")");
- closingPosition = charCount - 1;
+ closingPosition = charCount;
}
return closingPosition;
}
diff --git a/pkg/compiler/test/js/js_parser_test.dart b/pkg/compiler/test/js/js_parser_test.dart
index d338224..abbed83 100644
--- a/pkg/compiler/test/js/js_parser_test.dart
+++ b/pkg/compiler/test/js/js_parser_test.dart
@@ -18,6 +18,32 @@
}
}
+/// Tests an arrow expression with implicit returns allowed and disallowed.
+///
+/// Only checks the immediate, outermost arrow function.
+testArrowFunction(String arrowExpression,
+ [String implicitReturnExpect = "", String noImplicitReturnExpect = ""]) {
+ jsAst.ArrowFunction fun = js(arrowExpression);
+ jsAst.ArrowFunction implicitReturnFun = jsAst.ArrowFunction(
+ fun.params, fun.body,
+ asyncModifier: fun.asyncModifier, implicitReturnAllowed: true);
+ jsAst.ArrowFunction noImplicitReturnFun = jsAst.ArrowFunction(
+ fun.params, fun.body,
+ asyncModifier: fun.asyncModifier, implicitReturnAllowed: false);
+ String implicitReturnText =
+ jsAst.prettyPrint(implicitReturnFun, allowVariableMinification: false);
+ String noImplicitReturnText =
+ jsAst.prettyPrint(noImplicitReturnFun, allowVariableMinification: false);
+ String comparison =
+ implicitReturnExpect == "" ? arrowExpression : implicitReturnExpect;
+ Expect.stringEquals(comparison, implicitReturnText);
+ if (noImplicitReturnExpect == "") {
+ Expect.stringEquals(comparison, noImplicitReturnText);
+ } else {
+ Expect.stringEquals(noImplicitReturnExpect, noImplicitReturnText);
+ }
+}
+
testError(String expression, [String expect = ""]) {
bool doCheck(exception) {
final exceptionText = '$exception';
@@ -193,13 +219,17 @@
testExpression("a = b = c");
testExpression("var a = b = c");
// Arrow functions.
- testExpression("(x) => x", "x => x");
- testExpression("(x, y) => {\n return x + y;\n}");
- testExpression("() => 42");
- testExpression('() => ({foo: "bar"})');
- testExpression("() => {}", """
+ testArrowFunction("(x) => x", "x => x");
+ testArrowFunction(
+ "(x) => {\n return x;\n}", "x => x", "x => {\n return x;\n}");
+ testArrowFunction("(x, y) => {\n return x + y;\n}", "(x, y) => x + y",
+ "(x, y) => {\n return x + y;\n}");
+ testArrowFunction("() => 42");
+ testArrowFunction('() => ({foo: "bar"})');
+ testArrowFunction("() => {}", """
() => {
}""");
+ // Arrow function invocation.
testExpression("(() => 1)()");
testExpression("((x) => x)(y)", "(x => x)(y)");
testExpression("(() => {x = 1;})()", """
diff --git a/pkg/js_ast/lib/src/nodes.dart b/pkg/js_ast/lib/src/nodes.dart
index 9ded615..b84ef51 100644
--- a/pkg/js_ast/lib/src/nodes.dart
+++ b/pkg/js_ast/lib/src/nodes.dart
@@ -1516,8 +1516,13 @@
@override
final AsyncModifier asyncModifier;
+ /// Indicates whether it is permissible to try to emit this arrow function
+ /// in a form with an implicit 'return'.
+ final bool implicitReturnAllowed;
+
ArrowFunction(this.params, this.body,
- {this.asyncModifier = AsyncModifier.sync});
+ {this.asyncModifier = AsyncModifier.sync,
+ this.implicitReturnAllowed = true});
T accept<T>(NodeVisitor<T> visitor) => visitor.visitArrowFunction(this);
@@ -1534,8 +1539,9 @@
body.accept1(visitor, arg);
}
- ArrowFunction _clone() =>
- ArrowFunction(params, body, asyncModifier: asyncModifier);
+ ArrowFunction _clone() => ArrowFunction(params, body,
+ asyncModifier: asyncModifier,
+ implicitReturnAllowed: implicitReturnAllowed);
int get precedenceLevel => ASSIGNMENT;
}
diff --git a/pkg/js_ast/lib/src/printer.dart b/pkg/js_ast/lib/src/printer.dart
index af0c66a..5c5620e 100644
--- a/pkg/js_ast/lib/src/printer.dart
+++ b/pkg/js_ast/lib/src/printer.dart
@@ -1128,6 +1128,15 @@
spaceOut();
int closingPosition;
Node body = fun.body;
+ // Simplify arrow functions that return a single expression.
+ // Note that this can result in some sourcemapped positions disappearing
+ // around the elided Return. See http://dartbug.com/47354
+ if (fun.implicitReturnAllowed && body is Block) {
+ final statement = unwrapBlockIfSingleStatement(body);
+ if (statement is Return) {
+ body = statement.value;
+ }
+ }
if (body is Block) {
closingPosition =
blockOut(body, shouldIndent: false, needsNewline: false);
@@ -1140,7 +1149,7 @@
visitNestedExpression(body, ASSIGNMENT,
newInForInit: false, newAtStatementBegin: false);
if (needsParens) out(")");
- closingPosition = _charCount - 1;
+ closingPosition = _charCount;
}
localNamer.leaveScope();
return closingPosition;
diff --git a/runtime/vm/heap/heap.cc b/runtime/vm/heap/heap.cc
index 980f2e4..be2bfa6 100644
--- a/runtime/vm/heap/heap.cc
+++ b/runtime/vm/heap/heap.cc
@@ -365,51 +365,57 @@
void Heap::NotifyIdle(int64_t deadline) {
Thread* thread = Thread::Current();
TIMELINE_FUNCTION_GC_DURATION(thread, "NotifyIdle");
- GcSafepointOperationScope safepoint_operation(thread);
+ {
+ GcSafepointOperationScope safepoint_operation(thread);
- // Check if we want to collect new-space first, because if we want to collect
- // both new-space and old-space, the new-space collection should run first
- // to shrink the root set (make old-space GC faster) and avoid
- // intergenerational garbage (make old-space GC free more memory).
- if (new_space_.ShouldPerformIdleScavenge(deadline)) {
- CollectNewSpaceGarbage(thread, GCReason::kIdle);
+ // Check if we want to collect new-space first, because if we want to
+ // collect both new-space and old-space, the new-space collection should run
+ // first to shrink the root set (make old-space GC faster) and avoid
+ // intergenerational garbage (make old-space GC free more memory).
+ if (new_space_.ShouldPerformIdleScavenge(deadline)) {
+ CollectNewSpaceGarbage(thread, GCReason::kIdle);
+ }
+
+ // Check if we want to collect old-space, in decreasing order of cost.
+ // Because we use a deadline instead of a timeout, we automatically take any
+ // time used up by a scavenge into account when deciding if we can complete
+ // a mark-sweep on time.
+ if (old_space_.ShouldPerformIdleMarkCompact(deadline)) {
+ // We prefer mark-compact over other old space GCs if we have enough time,
+ // since it removes old space fragmentation and frees up most memory.
+ // Blocks for O(heap), roughtly twice as costly as mark-sweep.
+ CollectOldSpaceGarbage(thread, GCType::kMarkCompact, GCReason::kIdle);
+ } else if (old_space_.ReachedHardThreshold()) {
+ // Even though the following GC may exceed our idle deadline, we need to
+ // ensure than that promotions during idle scavenges do not lead to
+ // unbounded growth of old space. If a program is allocating only in new
+ // space and all scavenges happen during idle time, then NotifyIdle will
+ // be the only place that checks the old space allocation limit.
+ // Compare the tail end of Heap::CollectNewSpaceGarbage.
+ // Blocks for O(heap).
+ CollectOldSpaceGarbage(thread, GCType::kMarkSweep, GCReason::kIdle);
+ } else if (old_space_.ShouldStartIdleMarkSweep(deadline) ||
+ old_space_.ReachedSoftThreshold()) {
+ // If we have both work to do and enough time, start or finish GC.
+ // If we have crossed the soft threshold, ignore time; the next old-space
+ // allocation will trigger this work anyway, so we try to pay at least
+ // some of that cost with idle time.
+ // Blocks for O(roots).
+ PageSpace::Phase phase;
+ {
+ MonitorLocker ml(old_space_.tasks_lock());
+ phase = old_space_.phase();
+ }
+ if (phase == PageSpace::kAwaitingFinalization) {
+ CollectOldSpaceGarbage(thread, GCType::kMarkSweep, GCReason::kFinalize);
+ } else if (phase == PageSpace::kDone) {
+ StartConcurrentMarking(thread);
+ }
+ }
}
- // Check if we want to collect old-space, in decreasing order of cost.
- // Because we use a deadline instead of a timeout, we automatically take any
- // time used up by a scavenge into account when deciding if we can complete
- // a mark-sweep on time.
- if (old_space_.ShouldPerformIdleMarkCompact(deadline)) {
- // We prefer mark-compact over other old space GCs if we have enough time,
- // since it removes old space fragmentation and frees up most memory.
- // Blocks for O(heap), roughtly twice as costly as mark-sweep.
- CollectOldSpaceGarbage(thread, GCType::kMarkCompact, GCReason::kIdle);
- } else if (old_space_.ReachedHardThreshold()) {
- // Even though the following GC may exceed our idle deadline, we need to
- // ensure than that promotions during idle scavenges do not lead to
- // unbounded growth of old space. If a program is allocating only in new
- // space and all scavenges happen during idle time, then NotifyIdle will be
- // the only place that checks the old space allocation limit.
- // Compare the tail end of Heap::CollectNewSpaceGarbage.
- // Blocks for O(heap).
- CollectOldSpaceGarbage(thread, GCType::kMarkSweep, GCReason::kIdle);
- } else if (old_space_.ShouldStartIdleMarkSweep(deadline) ||
- old_space_.ReachedSoftThreshold()) {
- // If we have both work to do and enough time, start or finish GC.
- // If we have crossed the soft threshold, ignore time; the next old-space
- // allocation will trigger this work anyway, so we try to pay at least some
- // of that cost with idle time.
- // Blocks for O(roots).
- PageSpace::Phase phase;
- {
- MonitorLocker ml(old_space_.tasks_lock());
- phase = old_space_.phase();
- }
- if (phase == PageSpace::kAwaitingFinalization) {
- CollectOldSpaceGarbage(thread, GCType::kMarkSweep, GCReason::kFinalize);
- } else if (phase == PageSpace::kDone) {
- StartConcurrentMarking(thread);
- }
+ if (OS::GetCurrentMonotonicMicros() < deadline) {
+ SemiSpace::DrainCache();
}
}
diff --git a/runtime/vm/heap/scavenger.cc b/runtime/vm/heap/scavenger.cc
index 5b236c2..8897dda 100644
--- a/runtime/vm/heap/scavenger.cc
+++ b/runtime/vm/heap/scavenger.cc
@@ -651,15 +651,17 @@
page_cache_mutex = new Mutex(NOT_IN_PRODUCT("page_cache_mutex"));
}
-void SemiSpace::Cleanup() {
- {
- MutexLocker ml(page_cache_mutex);
- ASSERT(page_cache_size >= 0);
- ASSERT(page_cache_size <= kPageCacheCapacity);
- while (page_cache_size > 0) {
- delete page_cache[--page_cache_size];
- }
+void SemiSpace::DrainCache() {
+ MutexLocker ml(page_cache_mutex);
+ ASSERT(page_cache_size >= 0);
+ ASSERT(page_cache_size <= kPageCacheCapacity);
+ while (page_cache_size > 0) {
+ delete page_cache[--page_cache_size];
}
+}
+
+void SemiSpace::Cleanup() {
+ DrainCache();
delete page_cache_mutex;
page_cache_mutex = nullptr;
}
diff --git a/runtime/vm/heap/scavenger.h b/runtime/vm/heap/scavenger.h
index 414b62e..f538f3a 100644
--- a/runtime/vm/heap/scavenger.h
+++ b/runtime/vm/heap/scavenger.h
@@ -168,6 +168,7 @@
class SemiSpace {
public:
static void Init();
+ static void DrainCache();
static void Cleanup();
static intptr_t CachedSize();
diff --git a/tools/FAKE_COMMITS b/tools/FAKE_COMMITS
index 1599948..b709239 100644
--- a/tools/FAKE_COMMITS
+++ b/tools/FAKE_COMMITS
@@ -4,6 +4,7 @@
File for landing text only commits in the dart repo.
+Test
Tracer update
Force build after chrome-bot messup
Testing svn server
diff --git a/tools/VERSION b/tools/VERSION
index 80892437..87e5deb 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 15
PATCH 0
-PRERELEASE 221
+PRERELEASE 222
PRERELEASE_PATCH 0
\ No newline at end of file