diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc index 8282ed526b9..970ea77ffdf 100644 --- a/runtime/vm/object.cc +++ b/runtime/vm/object.cc @@ -7175,11 +7175,16 @@ RawString* Function::QualifiedName(NameVisibility name_visibility) const { } const Class& cls = Class::Handle(Owner()); if (!cls.IsTopLevel()) { - result = String::Concat(Symbols::Dot(), result, Heap::kOld); - const String& cls_name = String::Handle(name_visibility == kScrubbedName - ? cls.ScrubbedName() - : cls.UserVisibleName()); - result = String::Concat(cls_name, result, Heap::kOld); + if (kind() == RawFunction::kConstructor) { + result = String::Concat(Symbols::ConstructorStacktracePrefix(), result, + Heap::kOld); + } else { + result = String::Concat(Symbols::Dot(), result, Heap::kOld); + const String& cls_name = String::Handle(name_visibility == kScrubbedName + ? cls.ScrubbedName() + : cls.UserVisibleName()); + result = String::Concat(cls_name, result, Heap::kOld); + } } return result.raw(); } diff --git a/runtime/vm/symbols.h b/runtime/vm/symbols.h index 2707462a568..d9fa6f25b18 100644 --- a/runtime/vm/symbols.h +++ b/runtime/vm/symbols.h @@ -422,6 +422,7 @@ class ObjectPointerVisitor; V(removeLast, "removeLast") \ V(add, "add") \ V(ConstructorClosurePrefix, "new#") \ + V(ConstructorStacktracePrefix, "new ") \ V(_runExtension, "_runExtension") \ V(_runPendingImmediateCallback, "_runPendingImmediateCallback") \ V(DartLibrary, "dart.library.") \ diff --git a/tests/language/language.status b/tests/language/language.status index 51a59bc7445..302bbe5703d 100644 --- a/tests/language/language.status +++ b/tests/language/language.status @@ -233,6 +233,7 @@ map_literal4_test: SkipByDesign # Requires checked mode. vm/type_vm_test: Fail,OK # Expects exact type name. +stacktrace_demangle_ctors_test: SkipByDesign # Names are not scrubbed. [ ($compiler == none || $compiler == precompiler || $compiler == app_jit) && $browser ] # The following tests are supposed to fail. diff --git a/tests/language/language_dart2js.status b/tests/language/language_dart2js.status index b7a9301cd85..a02e605664a 100644 --- a/tests/language/language_dart2js.status +++ b/tests/language/language_dart2js.status @@ -55,6 +55,8 @@ covariant_test/32: MissingCompileTimeError, OK # Accepts `covariant` for statics covariant_test/34: MissingCompileTimeError, OK # Accepts `covariant` for statics/top-level. covariant_test/36: MissingCompileTimeError, OK # Accepts `covariant` for statics/top-level. +stacktrace_demangle_ctors_test: Fail # dart2js stack traces are not always compliant. + [ $compiler == dart2js && $fast_startup ] const_evaluation_test/*: Fail # mirrors not supported deferred_constraints_constants_test/none: Fail # mirrors not supported diff --git a/tests/language/stacktrace_demangle_ctors_test.dart b/tests/language/stacktrace_demangle_ctors_test.dart new file mode 100644 index 00000000000..697ed127675 --- /dev/null +++ b/tests/language/stacktrace_demangle_ctors_test.dart @@ -0,0 +1,59 @@ +// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +// Test that stack traces are properly demangled in constructors (#28740). + +import "package:expect/expect.dart"; + +class SomeClass { + SomeClass.namedConstructor() { + throw new Exception(); + } + + SomeClass() { + throw new Exception(); + } + + factory SomeClass.useFactory() { + throw new Exception(); + } +} + +class OnlyHasFactory { + factory OnlyHasFactory() { + throw new Exception(); + } +} + +void main() { + try { + new SomeClass(); + } on Exception catch (e, st) { + final stString = st.toString(); + Expect.isTrue(stString.contains("new SomeClass")); + Expect.isFalse(stString.contains("SomeClass.")); + } + + try { + new SomeClass.namedConstructor(); + } on Exception catch (e, st) { + final stString = st.toString(); + Expect.isTrue(stString.contains("new SomeClass.namedConstructor")); + } + + try { + new OnlyHasFactory(); + } on Exception catch (e, st) { + final stString = st.toString(); + Expect.isTrue(stString.contains("new OnlyHasFactory")); + Expect.isFalse(stString.contains("OnlyHasFactory.")); + } + + try { + new SomeClass.useFactory(); + } on Exception catch (e, st) { + final stString = st.toString(); + Expect.isTrue(stString.contains("new SomeClass.useFactory")); + } +}