From 8007a98b5bb8020d598fef007ae27399b2ac5dc5 Mon Sep 17 00:00:00 2001 From: John McCutchan Date: Thu, 27 Oct 2016 07:29:22 -0700 Subject: [PATCH] Produce the correct assertion failure expression in the face of inlining Fixes #27671 BUG= R=asiva@google.com Review URL: https://codereview.chromium.org/2453263002 . --- runtime/lib/errors.cc | 43 +++++++++++++++++++++- tests/language/vm/regress_27671_other.dart | 10 +++++ tests/language/vm/regress_27671_test.dart | 32 ++++++++++++++++ 3 files changed, 83 insertions(+), 2 deletions(-) create mode 100644 tests/language/vm/regress_27671_other.dart create mode 100644 tests/language/vm/regress_27671_test.dart diff --git a/runtime/lib/errors.cc b/runtime/lib/errors.cc index 28d4ef49c41..b039efa9725 100644 --- a/runtime/lib/errors.cc +++ b/runtime/lib/errors.cc @@ -7,9 +7,49 @@ #include "vm/object_store.h" #include "vm/runtime_entry.h" #include "vm/stack_frame.h" +#include "vm/symbols.h" namespace dart { +// Scan the stack until we hit the first function in the _AssertionError +// class. We then return the next frame's script taking inlining into account. +static RawScript* FindScript(DartFrameIterator* iterator) { + StackFrame* stack_frame = iterator->NextFrame(); + Code& code = Code::Handle(); + Function& func = Function::Handle(); + const Class& assert_error_class = Class::Handle( + Library::LookupCoreClass(Symbols::AssertionError())); + ASSERT(!assert_error_class.IsNull()); + bool hit_assertion_error = false; + while (stack_frame != NULL) { + code ^= stack_frame->LookupDartCode(); + if (code.is_optimized()) { + InlinedFunctionsIterator inlined_iterator(code, stack_frame->pc()); + while (!inlined_iterator.Done()) { + func ^= inlined_iterator.function(); + if (hit_assertion_error) { + return func.script(); + } + ASSERT(!hit_assertion_error); + hit_assertion_error = (func.Owner() == assert_error_class.raw()); + inlined_iterator.Advance(); + } + } else { + func ^= code.function(); + ASSERT(!func.IsNull()); + if (hit_assertion_error) { + return func.script(); + } + ASSERT(!hit_assertion_error); + hit_assertion_error = (func.Owner() == assert_error_class.raw()); + } + stack_frame = iterator->NextFrame(); + } + UNREACHABLE(); + return Script::null(); +} + + // Allocate and throw a new AssertionError. // Arg0: index of the first token of the failed assertion. // Arg1: index of the first token after the failed assertion. @@ -26,8 +66,7 @@ DEFINE_NATIVE_ENTRY(AssertionError_throwNew, 2) { DartFrameIterator iterator; iterator.NextFrame(); // Skip native call. - iterator.NextFrame(); // Skip _AssertionError._checkAssertion frame - const Script& script = Script::Handle(Exceptions::GetCallerScript(&iterator)); + const Script& script = Script::Handle(FindScript(&iterator)); // Initialize argument 'failed_assertion' with source snippet. intptr_t from_line, from_column; diff --git a/tests/language/vm/regress_27671_other.dart b/tests/language/vm/regress_27671_other.dart new file mode 100644 index 00000000000..a180d182995 --- /dev/null +++ b/tests/language/vm/regress_27671_other.dart @@ -0,0 +1,10 @@ +// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'regress_27671_test.dart'; + +@AlwaysInline +void check(f, x) { + assert(f(x) && true); +} diff --git a/tests/language/vm/regress_27671_test.dart b/tests/language/vm/regress_27671_test.dart new file mode 100644 index 00000000000..fa258dabf0e --- /dev/null +++ b/tests/language/vm/regress_27671_test.dart @@ -0,0 +1,32 @@ +// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +// +// VMOptions=--enable_asserts --enable-inlining-annotations --optimization-counter-threshold=10 --no-background-compilation + +import 'package:expect/expect.dart'; +import 'regress_27671_other.dart'; + +const AlwaysInline = "AlwaysInline"; +const NeverInline = "NeverInline"; + +@AlwaysInline +bounce(x) { + for (int i = 0; i < 10; i++) { + check(f, x); + } +} + +@AlwaysInline +bool f(y) => y > 0; + +main() { + for (int i = 0; i < 100; i++) { + bounce(1); + } + try { + bounce(-1); + } catch (e) { + Expect.isTrue(e.toString().contains('f(x) && true')); + } +}