Skip to content

Commit 1f4a0e3

Browse files
committed
Version 1.17.1
An earlier commit was also named 1.17.1, but that was an error. That earlier commit was released as 1.17.0. Cherry-pick 'f4029bf3ee72c6667f4099a0e2e747b22fe45c32' to stable Cherry-pick 'e803457081ee5c0efcd05d924c168ceb4dc534c7' to stable Cherry-pick '97a8c4caffd2e758c60a3e4502f07d6886ee0363' to stable Cherry-pick '9d5e1331149062cd5de4ed5a54841c03872f8edd' to stable
2 parents 1c11253 + 6c590f4 commit 1f4a0e3

File tree

7 files changed

+176
-78
lines changed

7 files changed

+176
-78
lines changed

CHANGELOG.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,14 @@
1-
## 1.17.0 - 2016-06-06
1+
## 1.17.1 - 2016-06-10
2+
3+
Patch release, resolves two issues:
4+
5+
* VM: Fixes a bug that caused crashes in async functions.
6+
(SDK issue [26668](https://github.com/dart-lang/sdk/issues/26668))
7+
8+
* VM: Fixes a bug that caused garbage collection of reachable weak properties.
9+
(https://codereview.chromium.org/2041413005)
10+
11+
## 1.17.0 - 2016-06-08
212

313
### Core library changes
414
* `dart:convert`

runtime/platform/assert.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,12 @@ T DynamicAssertionHelper::NotNull(const T p) {
281281
#endif // if defined(DEBUG)
282282

283283

284+
#define RELEASE_ASSERT(cond) \
285+
do { \
286+
if (!(cond)) dart::Assert(__FILE__, __LINE__).Fail("expected: %s", #cond); \
287+
} while (false)
288+
289+
284290
// The COMPILE_ASSERT macro can be used to verify that a compile time
285291
// expression is true. For example, you could use it to verify the
286292
// size of a static array:

runtime/vm/gc_marker.cc

Lines changed: 94 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -177,12 +177,43 @@ class MarkingVisitorBase : public ObjectPointerVisitor {
177177
return class_stats_size_[class_id];
178178
}
179179

180-
// Returns true if some non-zero amount of work was performed.
181-
bool DrainMarkingStack() {
180+
bool ProcessPendingWeakProperties() {
181+
bool marked = false;
182+
RawWeakProperty* cur_weak = delayed_weak_properties_;
183+
delayed_weak_properties_ = NULL;
184+
while (cur_weak != NULL) {
185+
uword next_weak = cur_weak->ptr()->next_;
186+
RawObject* raw_key = cur_weak->ptr()->key_;
187+
// Reset the next pointer in the weak property.
188+
cur_weak->ptr()->next_ = 0;
189+
if (raw_key->IsMarked()) {
190+
RawObject* raw_val = cur_weak->ptr()->value_;
191+
marked = marked || (raw_val->IsHeapObject() && !raw_val->IsMarked());
192+
193+
// The key is marked so we make sure to properly visit all pointers
194+
// originating from this weak property.
195+
VisitingOldObject(cur_weak);
196+
cur_weak->VisitPointers(this);
197+
} else {
198+
// Requeue this weak property to be handled later.
199+
EnqueueWeakProperty(cur_weak);
200+
}
201+
// Advance to next weak property in the queue.
202+
cur_weak = reinterpret_cast<RawWeakProperty*>(next_weak);
203+
}
204+
VisitingOldObject(NULL);
205+
return marked;
206+
}
207+
208+
void DrainMarkingStack() {
182209
RawObject* raw_obj = work_list_.Pop();
210+
if ((raw_obj == NULL) && ProcessPendingWeakProperties()) {
211+
raw_obj = work_list_.Pop();
212+
}
213+
183214
if (raw_obj == NULL) {
184215
ASSERT(visiting_old_object_ == NULL);
185-
return false;
216+
return;
186217
}
187218
do {
188219
do {
@@ -200,33 +231,13 @@ class MarkingVisitorBase : public ObjectPointerVisitor {
200231
} while (raw_obj != NULL);
201232

202233
// Marking stack is empty.
203-
// Process all the pending weak properties in this visitor.
204-
RawWeakProperty* cur_weak = delayed_weak_properties_;
205-
delayed_weak_properties_ = NULL;
206-
while (cur_weak != NULL) {
207-
uword next_weak = cur_weak->ptr()->next_;
208-
RawObject* raw_key = cur_weak->ptr()->key_;
209-
// Reset the next pointer in the weak property.
210-
cur_weak->ptr()->next_ = 0;
211-
if (raw_key->IsMarked()) {
212-
// The key is marked so we make sure to properly visit all pointers
213-
// originating from this weak property.
214-
VisitingOldObject(cur_weak);
215-
cur_weak->VisitPointers(this);
216-
} else {
217-
// Requeue this weak property to be handled later.
218-
EnqueueWeakProperty(cur_weak);
219-
}
220-
// Advance to next weak property in the queue.
221-
cur_weak = reinterpret_cast<RawWeakProperty*>(next_weak);
222-
}
234+
ProcessPendingWeakProperties();
223235

224236
// Check whether any further work was pushed either by other markers or
225237
// by the handling of weak properties.
226238
raw_obj = work_list_.Pop();
227239
} while (raw_obj != NULL);
228240
VisitingOldObject(NULL);
229-
return true;
230241
}
231242

232243
void VisitPointers(RawObject** first, RawObject** last) {
@@ -282,6 +293,7 @@ class MarkingVisitorBase : public ObjectPointerVisitor {
282293
while (cur_weak != NULL) {
283294
uword next_weak = cur_weak->ptr()->next_;
284295
cur_weak->ptr()->next_ = 0;
296+
RELEASE_ASSERT(!cur_weak->ptr()->key_->IsMarked());
285297
WeakProperty::Clear(cur_weak);
286298
weak_properties_cleared++;
287299
// Advance to next weak property in the queue.
@@ -569,28 +581,54 @@ class MarkTask : public ThreadPool::Task {
569581
skipped_code_functions);
570582
// Phase 1: Iterate over roots and drain marking stack in tasks.
571583
marker_->IterateRoots(isolate_, &visitor, task_index_, num_tasks_);
572-
do {
573-
visitor.DrainMarkingStack();
574584

575-
// I can't find more work right now. If no other task is busy,
576-
// then there will never be more work (NB: 1 is *before* decrement).
577-
if (AtomicOperations::FetchAndDecrement(num_busy_) == 1) break;
578-
579-
// Wait for some work to appear.
580-
// TODO(iposva): Replace busy-waiting with a solution using Monitor,
581-
// and redraw the boundaries between stack/visitor/task as needed.
582-
while (marking_stack_->IsEmpty() &&
583-
AtomicOperations::LoadRelaxed(num_busy_) > 0) {
585+
bool more_to_mark = false;
586+
do {
587+
do {
588+
visitor.DrainMarkingStack();
589+
590+
// I can't find more work right now. If no other task is busy,
591+
// then there will never be more work (NB: 1 is *before* decrement).
592+
if (AtomicOperations::FetchAndDecrement(num_busy_) == 1) break;
593+
594+
// Wait for some work to appear.
595+
// TODO(iposva): Replace busy-waiting with a solution using Monitor,
596+
// and redraw the boundaries between stack/visitor/task as needed.
597+
while (marking_stack_->IsEmpty() &&
598+
AtomicOperations::LoadRelaxed(num_busy_) > 0) {
599+
}
600+
601+
// If no tasks are busy, there will never be more work.
602+
if (AtomicOperations::LoadRelaxed(num_busy_) == 0) break;
603+
604+
// I saw some work; get busy and compete for it.
605+
AtomicOperations::FetchAndIncrement(num_busy_);
606+
} while (true);
607+
// Wait for all markers to stop.
608+
barrier_->Sync();
609+
ASSERT(AtomicOperations::LoadRelaxed(num_busy_) == 0);
610+
611+
// Check if we have any pending properties with marked keys.
612+
// Those might have been marked by another marker.
613+
more_to_mark = visitor.ProcessPendingWeakProperties();
614+
if (more_to_mark) {
615+
// We have more work to do. Notify others.
616+
AtomicOperations::FetchAndIncrement(num_busy_);
584617
}
585618

586-
// If no tasks are busy, there will never be more work.
587-
if (AtomicOperations::LoadRelaxed(num_busy_) == 0) break;
588-
589-
// I saw some work; get busy and compete for it.
590-
AtomicOperations::FetchAndIncrement(num_busy_);
591-
} while (true);
592-
ASSERT(AtomicOperations::LoadRelaxed(num_busy_) == 0);
593-
barrier_->Sync();
619+
// Wait for all other markers to finish processing their pending
620+
// weak properties and decide if they need to continue marking.
621+
// Caveat: we need two barriers here to make this decision in lock step
622+
// between all markers and the main thread.
623+
barrier_->Sync();
624+
if (!more_to_mark && (AtomicOperations::LoadRelaxed(num_busy_) > 0)) {
625+
// All markers continue to marker as long as any single marker has
626+
// some work to do.
627+
AtomicOperations::FetchAndIncrement(num_busy_);
628+
more_to_mark = true;
629+
}
630+
barrier_->Sync();
631+
} while (more_to_mark);
594632

595633
// Phase 2: Weak processing and follow-up marking on main thread.
596634
barrier_->Sync();
@@ -688,7 +726,19 @@ void GCMarker::MarkObjects(Isolate* isolate,
688726
ThreadPool* pool = Dart::thread_pool();
689727
pool->Run(mark_task);
690728
}
691-
barrier.Sync();
729+
bool more_to_mark = false;
730+
do {
731+
// Wait for all markers to stop.
732+
barrier.Sync();
733+
734+
// Wait for all markers to go through weak properties and verify
735+
// that there are no more objects to mark.
736+
// Note: we need to have two barriers here because we want all markers
737+
// and main thread to make decisions in lock step.
738+
barrier.Sync();
739+
more_to_mark = AtomicOperations::LoadRelaxed(&num_busy) > 0;
740+
barrier.Sync();
741+
} while (more_to_mark);
692742

693743
// Phase 2: Weak processing on main thread.
694744
{

runtime/vm/parser.cc

Lines changed: 44 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -10057,25 +10057,28 @@ AstNode* Parser::ParseStatement() {
1005710057
ConsumeToken();
1005810058
if (CurrentToken() != Token::kSEMICOLON) {
1005910059
const TokenPosition expr_pos = TokenPos();
10060+
const int function_level = current_block_->scope->function_level();
1006010061
if (current_function().IsGenerativeConstructor() &&
10061-
(current_block_->scope->function_level() == 0)) {
10062+
(function_level == 0)) {
1006210063
ReportError(expr_pos,
1006310064
"return of a value is not allowed in constructors");
1006410065
} else if (current_function().IsGeneratorClosure() &&
10065-
(current_block_->scope->function_level() == 0)) {
10066+
(function_level == 0)) {
1006610067
ReportError(expr_pos, "generator functions may not return a value");
1006710068
}
1006810069
AstNode* expr = ParseAwaitableExpr(kAllowConst, kConsumeCascades, NULL);
1006910070
if (I->type_checks() &&
10070-
current_function().IsAsyncClosure() &&
10071-
(current_block_->scope->function_level() == 0)) {
10071+
(((function_level == 0) && current_function().IsAsyncClosure()) ||
10072+
((function_level > 0) && current_function().IsAsyncFunction()))) {
1007210073
// In checked mode, when the declared result type is Future<T>, verify
1007310074
// that the returned expression is of type T or Future<T> as follows:
1007410075
// return temp = expr, temp is Future ? temp as Future<T> : temp as T;
1007510076
// In case of a mismatch, we need a TypeError and not a CastError, so
1007610077
// we do not actually implement an "as" test, but an "assignable" test.
10077-
const Function& async_func =
10078-
Function::Handle(Z, current_function().parent_function());
10078+
Function& async_func = Function::Handle(Z, current_function().raw());
10079+
if (function_level == 0) {
10080+
async_func = async_func.parent_function();
10081+
}
1007910082
const AbstractType& result_type =
1008010083
AbstractType::ZoneHandle(Z, async_func.result_type());
1008110084
const Class& future_class =
@@ -10087,32 +10090,41 @@ AstNode* Parser::ParseStatement() {
1008710090
if (!result_type_args.IsNull() && (result_type_args.Length() == 1)) {
1008810091
const AbstractType& result_type_arg =
1008910092
AbstractType::ZoneHandle(Z, result_type_args.TypeAt(0));
10090-
LetNode* checked_expr = new(Z) LetNode(expr_pos);
10091-
LocalVariable* temp = checked_expr->AddInitializer(expr);
10092-
temp->set_is_final();
10093-
const AbstractType& future_type =
10094-
AbstractType::ZoneHandle(Z, future_class.RareType());
10095-
AstNode* is_future = new(Z) LoadLocalNode(expr_pos, temp);
10096-
is_future = new(Z) ComparisonNode(expr_pos,
10097-
Token::kIS,
10098-
is_future,
10099-
new(Z) TypeNode(expr_pos,
10100-
future_type));
10101-
AstNode* as_future_t = new(Z) LoadLocalNode(expr_pos, temp);
10102-
as_future_t = new(Z) AssignableNode(expr_pos,
10103-
as_future_t,
10104-
result_type,
10105-
Symbols::FunctionResult());
10106-
AstNode* as_t = new(Z) LoadLocalNode(expr_pos, temp);
10107-
as_t = new(Z) AssignableNode(expr_pos,
10108-
as_t,
10109-
result_type_arg,
10110-
Symbols::FunctionResult());
10111-
checked_expr->AddNode(new(Z) ConditionalExprNode(expr_pos,
10112-
is_future,
10113-
as_future_t,
10114-
as_t));
10115-
expr = checked_expr;
10093+
if (function_level == 0) {
10094+
// Parsing and generating code for async closure.
10095+
LetNode* checked_expr = new(Z) LetNode(expr_pos);
10096+
LocalVariable* temp = checked_expr->AddInitializer(expr);
10097+
temp->set_is_final();
10098+
const AbstractType& future_type =
10099+
AbstractType::ZoneHandle(Z, future_class.RareType());
10100+
AstNode* is_future = new(Z) LoadLocalNode(expr_pos, temp);
10101+
is_future = new(Z) ComparisonNode(expr_pos,
10102+
Token::kIS,
10103+
is_future,
10104+
new(Z) TypeNode(expr_pos,
10105+
future_type));
10106+
AstNode* as_future_t = new(Z) LoadLocalNode(expr_pos, temp);
10107+
as_future_t = new(Z) AssignableNode(expr_pos,
10108+
as_future_t,
10109+
result_type,
10110+
Symbols::FunctionResult());
10111+
AstNode* as_t = new(Z) LoadLocalNode(expr_pos, temp);
10112+
as_t = new(Z) AssignableNode(expr_pos,
10113+
as_t,
10114+
result_type_arg,
10115+
Symbols::FunctionResult());
10116+
checked_expr->AddNode(new(Z) ConditionalExprNode(expr_pos,
10117+
is_future,
10118+
as_future_t,
10119+
as_t));
10120+
expr = checked_expr;
10121+
} else {
10122+
// Parsing async function, but not generating async closure code.
10123+
if (!result_type_arg.IsInstantiated()) {
10124+
// Make sure that the instantiator is captured.
10125+
CaptureInstantiator();
10126+
}
10127+
}
1011610128
}
1011710129
}
1011810130
}

tests/language/language_analyzer2.status

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
[ $compiler == dart2analyzer ]
66

7+
regress_26668_test: Fail # Issue 26678
8+
79
# Runtime negative test. No static errors or warnings.
810
closure_call_wrong_argument_count_negative_test: skip
911

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
// Tests that the VM does not crash on weird corner cases of class Math.
5+
6+
import 'dart:async';
7+
8+
main() async {
9+
var myClass = new CustomClass<int>();
10+
await myClass.processData();
11+
}
12+
13+
class CustomClass<T> {
14+
Future<T> processData() async {
15+
return 0;
16+
}
17+
}
18+

tools/VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,6 @@
2626
CHANNEL stable
2727
MAJOR 1
2828
MINOR 17
29-
PATCH 0
29+
PATCH 1
3030
PRERELEASE 0
3131
PRERELEASE_PATCH 0

0 commit comments

Comments
 (0)