[runtime] Fix detection of construct frames in stack traces.

This removes the heuristic from {JSStackFrame::IsConstructor} that tried
to infer whether a frame was called as a constructor or not from the
receiver value. We are now carrying along the appropriate bit derived
from the frame type instead.

R=jgruber@chromium.org
TEST=message/regress/regress-5727
BUG=v8:5727

Change-Id: I0e2f1d0f95485c84c4ebcd3cbfe0123c6afd2e01
Reviewed-on: https://chromium-review.googlesource.com/500313
Commit-Queue: Michael Starzinger <mstarzinger@chromium.org>
Reviewed-by: Jakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#45972}
This commit is contained in:
Michael Starzinger 2017-06-16 10:48:58 +02:00 committed by Commit Bot
parent d5dd51ae6d
commit e47f37ebd0
7 changed files with 34 additions and 20 deletions

View File

@ -518,18 +518,18 @@ Handle<Object> Isolate::CaptureSimpleStackTrace(Handle<JSReceiver> error_object,
Handle<AbstractCode> abstract_code = summ.abstract_code();
const int offset = frames[i].code_offset();
bool force_constructor = false;
bool is_constructor = frames[i].is_constructor();
if (frame->type() == StackFrame::BUILTIN) {
// Help CallSite::IsConstructor correctly detect hand-written
// construct stubs.
if (Code::cast(*abstract_code)->is_construct_stub()) {
force_constructor = true;
is_constructor = true;
}
}
int flags = 0;
if (helper.IsStrictFrame(*fun)) flags |= FrameArray::kIsStrict;
if (force_constructor) flags |= FrameArray::kForceConstructor;
if (is_constructor) flags |= FrameArray::kIsConstructor;
elements = FrameArray::AppendJSFrame(
elements, TheHoleToUndefined(this, recv), fun, abstract_code,
@ -551,7 +551,7 @@ Handle<Object> Isolate::CaptureSimpleStackTrace(Handle<JSReceiver> error_object,
int flags = 0;
if (helper.IsStrictFrame(*fun)) flags |= FrameArray::kIsStrict;
if (exit_frame->IsConstructor()) flags |= FrameArray::kForceConstructor;
if (exit_frame->IsConstructor()) flags |= FrameArray::kIsConstructor;
elements = FrameArray::AppendJSFrame(elements, recv, fun,
Handle<AbstractCode>::cast(code),

View File

@ -302,7 +302,7 @@ void JSStackFrame::FromFrameArray(Isolate* isolate, Handle<FrameArray> array,
offset_ = array->Offset(frame_ix)->value();
const int flags = array->Flags(frame_ix)->value();
force_constructor_ = (flags & FrameArray::kForceConstructor) != 0;
is_constructor_ = (flags & FrameArray::kIsConstructor) != 0;
is_strict_ = (flags & FrameArray::kIsStrict) != 0;
}
@ -316,7 +316,7 @@ JSStackFrame::JSStackFrame(Isolate* isolate, Handle<Object> receiver,
function_(function),
code_(code),
offset_(offset),
force_constructor_(false),
is_constructor_(false),
is_strict_(false) {}
Handle<Object> JSStackFrame::GetFunction() const {
@ -458,15 +458,6 @@ bool JSStackFrame::IsToplevel() {
return receiver_->IsJSGlobalProxy() || receiver_->IsNullOrUndefined(isolate_);
}
bool JSStackFrame::IsConstructor() {
if (force_constructor_) return true;
if (!receiver_->IsJSObject()) return false;
Handle<Object> constructor =
JSReceiver::GetDataProperty(Handle<JSObject>::cast(receiver_),
isolate_->factory()->constructor_string());
return constructor.is_identical_to(function_);
}
namespace {
bool IsNonEmptyString(Handle<Object> object) {

View File

@ -106,7 +106,7 @@ class JSStackFrame : public StackFrameBase {
bool IsNative() override;
bool IsToplevel() override;
bool IsConstructor() override;
bool IsConstructor() override { return is_constructor_; }
bool IsStrict() const override { return is_strict_; }
MaybeHandle<String> ToString() override;
@ -123,7 +123,7 @@ class JSStackFrame : public StackFrameBase {
Handle<AbstractCode> code_;
int offset_;
bool force_constructor_;
bool is_constructor_;
bool is_strict_;
friend class FrameArrayIterator;

View File

@ -48,7 +48,7 @@ class FrameArray : public FixedArray {
kIsWasmInterpretedFrame = 1 << 1,
kIsAsmJsWasmFrame = 1 << 2,
kIsStrict = 1 << 3,
kForceConstructor = 1 << 4,
kIsConstructor = 1 << 4,
kAsmJsAtNumberConversion = 1 << 5
};

View File

@ -0,0 +1,11 @@
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
function Foo(do_throw) {
if (do_throw) throw new Error("get me outta here");
}
var foo = new Foo(false);
(function caller() {
Foo.call(foo, true);
})();

View File

@ -0,0 +1,11 @@
# Copyright 2017 the V8 project authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
*%(basename)s:6: Error: get me outta here
if (do_throw) throw new Error("get me outta here");
^
Error: get me outta here
at Foo (*%(basename)s:6:23)
at caller (*%(basename)s:10:7)
at *%(basename)s:11:3

View File

@ -46,7 +46,8 @@ function getStack(error) {
filter(function(line) {
return /^\s*at @?[a-zA-Z0-9_]/.test(line);
}).
map(line => line.replace(/^\s*at (@?[a-zA-Z0-9_\.\[\]]+)(.*)/, "$1"));
map(line =>
line.replace(/^\s*at (@?(?:new )?[a-zA-Z0-9_\.\[\]]+)(.*)/, "$1"));
// remove `Promise.then()` invocation by assertEqualsAsync()
if (stack[2] === "assertEqualsAsync") return [];
@ -96,6 +97,6 @@ assertEqualsAsync(
}),
"should call Promise[@@Species] after non-internal Then");
assertEquals([
"@@Species: [@testThenOnReturnedPromise > Promise.then > FakePromise]",
"@@Species: [@testThenOnReturnedPromise > Promise.then > new FakePromise]",
"Then: foo"
], log);