Correctly format builtin constructors in stack traces

CallSite::IsConstructor() was unable to recognize builtin construct stubs
(NumberConstructor_ConstructStub and StringConstructor_ConstructStub) as
constructors, and thus these frames were not formatted correctly in stack
traces.

Fix this by explicitly marking their Code objects as construct stubs and
passing along a special receiver value when we encounter such cases in
CaptureSimpleStackTrace.

R=mstarzinger@chromium.org, yangguo@chromium.org
BUG=

Review-Url: https://codereview.chromium.org/2125163004
Cr-Commit-Position: refs/heads/master@{#37631}
This commit is contained in:
jgruber 2016-07-11 03:03:02 -07:00 committed by Commit bot
parent 71becab88d
commit be5808bff8
10 changed files with 64 additions and 28 deletions

View File

@ -1226,7 +1226,7 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
function_fun->set_prototype_or_initial_map(
*sloppy_function_map_writable_prototype_);
function_fun->shared()->DontAdaptArguments();
function_fun->shared()->set_construct_stub(
function_fun->shared()->SetConstructStub(
*isolate->builtins()->FunctionConstructor());
function_fun->shared()->set_length(1);
InstallWithIntrinsicDefaultProto(isolate, function_fun,
@ -1308,7 +1308,7 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
CacheInitialJSArrayMaps(native_context(), initial_map);
ArrayConstructorStub array_constructor_stub(isolate);
Handle<Code> code = array_constructor_stub.GetCode();
array_function->shared()->set_construct_stub(*code);
array_function->shared()->SetConstructStub(*code);
Handle<JSFunction> is_arraylike = SimpleInstallFunction(
array_function, isolate->factory()->InternalizeUtf8String("isArray"),
@ -1321,7 +1321,7 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
global, "Number", JS_VALUE_TYPE, JSValue::kSize,
isolate->initial_object_prototype(), Builtins::kNumberConstructor);
number_fun->shared()->DontAdaptArguments();
number_fun->shared()->set_construct_stub(
number_fun->shared()->SetConstructStub(
*isolate->builtins()->NumberConstructor_ConstructStub());
number_fun->shared()->set_length(1);
InstallWithIntrinsicDefaultProto(isolate, number_fun,
@ -1360,7 +1360,7 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
isolate->initial_object_prototype(),
Builtins::kBooleanConstructor);
boolean_fun->shared()->DontAdaptArguments();
boolean_fun->shared()->set_construct_stub(
boolean_fun->shared()->SetConstructStub(
*isolate->builtins()->BooleanConstructor_ConstructStub());
boolean_fun->shared()->set_length(1);
InstallWithIntrinsicDefaultProto(isolate, boolean_fun,
@ -1387,7 +1387,7 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
Handle<JSFunction> string_fun = InstallFunction(
global, "String", JS_VALUE_TYPE, JSValue::kSize,
isolate->initial_object_prototype(), Builtins::kStringConstructor);
string_fun->shared()->set_construct_stub(
string_fun->shared()->SetConstructStub(
*isolate->builtins()->StringConstructor_ConstructStub());
string_fun->shared()->DontAdaptArguments();
string_fun->shared()->set_length(1);
@ -1452,7 +1452,7 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
Handle<JSFunction> symbol_fun =
InstallFunction(global, "Symbol", JS_VALUE_TYPE, JSValue::kSize,
prototype, Builtins::kSymbolConstructor);
symbol_fun->shared()->set_construct_stub(
symbol_fun->shared()->SetConstructStub(
*isolate->builtins()->SymbolConstructor_ConstructStub());
symbol_fun->shared()->set_length(0);
symbol_fun->shared()->DontAdaptArguments();
@ -1497,7 +1497,7 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
Builtins::kDateConstructor);
InstallWithIntrinsicDefaultProto(isolate, date_fun,
Context::DATE_FUNCTION_INDEX);
date_fun->shared()->set_construct_stub(
date_fun->shared()->SetConstructStub(
*isolate->builtins()->DateConstructor_ConstructStub());
date_fun->shared()->set_length(7);
date_fun->shared()->DontAdaptArguments();
@ -1630,7 +1630,7 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
Builtins::kIllegal);
InstallWithIntrinsicDefaultProto(isolate, regexp_fun,
Context::REGEXP_FUNCTION_INDEX);
regexp_fun->shared()->set_construct_stub(
regexp_fun->shared()->SetConstructStub(
*isolate->builtins()->JSBuiltinsConstructStub());
DCHECK(regexp_fun->has_initial_map());
@ -1874,7 +1874,7 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
Builtins::kDataViewConstructor);
InstallWithIntrinsicDefaultProto(isolate, data_view_fun,
Context::DATA_VIEW_FUN_INDEX);
data_view_fun->shared()->set_construct_stub(
data_view_fun->shared()->SetConstructStub(
*isolate->builtins()->DataViewConstructor_ConstructStub());
data_view_fun->shared()->set_length(3);
data_view_fun->shared()->DontAdaptArguments();
@ -1971,7 +1971,7 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
proxy_function, Handle<Map>(native_context()->proxy_map(), isolate),
factory->null_value());
proxy_function->shared()->set_construct_stub(
proxy_function->shared()->SetConstructStub(
*isolate->builtins()->ProxyConstructor_ConstructStub());
proxy_function->shared()->set_internal_formal_parameter_count(2);
proxy_function->shared()->set_length(2);
@ -2512,7 +2512,7 @@ void Bootstrapper::ExportFromRuntime(Isolate* isolate,
generator_function_function->set_prototype_or_initial_map(
native_context->sloppy_generator_function_map());
generator_function_function->shared()->DontAdaptArguments();
generator_function_function->shared()->set_construct_stub(
generator_function_function->shared()->SetConstructStub(
*isolate->builtins()->GeneratorFunctionConstructor());
generator_function_function->shared()->set_length(1);
InstallWithIntrinsicDefaultProto(
@ -2709,7 +2709,7 @@ void Bootstrapper::ExportFromRuntime(Isolate* isolate,
async_function_prototype, Builtins::kAsyncFunctionConstructor,
kUseStrictFunctionMap);
async_function_constructor->shared()->DontAdaptArguments();
async_function_constructor->shared()->set_construct_stub(
async_function_constructor->shared()->SetConstructStub(
*isolate->builtins()->AsyncFunctionConstructor());
async_function_constructor->shared()->set_length(1);
InstallWithIntrinsicDefaultProto(isolate, async_function_constructor,
@ -2920,7 +2920,7 @@ Handle<JSFunction> Genesis::InstallArrayBuffer(Handle<JSObject> target,
InstallFunction(target, name, JS_ARRAY_BUFFER_TYPE,
JSArrayBuffer::kSizeWithInternalFields, prototype,
Builtins::kArrayBufferConstructor);
array_buffer_fun->shared()->set_construct_stub(
array_buffer_fun->shared()->SetConstructStub(
*isolate()->builtins()->ArrayBufferConstructor_ConstructStub());
array_buffer_fun->shared()->DontAdaptArguments();
array_buffer_fun->shared()->set_length(1);
@ -2957,7 +2957,7 @@ Handle<JSFunction> Genesis::InstallInternalArray(Handle<JSObject> target,
InternalArrayConstructorStub internal_array_constructor_stub(isolate());
Handle<Code> code = internal_array_constructor_stub.GetCode();
array_function->shared()->set_construct_stub(*code);
array_function->shared()->SetConstructStub(*code);
array_function->shared()->DontAdaptArguments();
Handle<Map> original_map(array_function->initial_map());
@ -3147,7 +3147,7 @@ bool Genesis::InstallNatives(GlobalContextType context_type) {
JSReceiver::GetProperty(global_object, key).ToHandleChecked());
JSFunction::EnsureHasInitialMap(function);
function->initial_map()->set_instance_type(JS_PROMISE_TYPE);
function->shared()->set_construct_stub(
function->shared()->SetConstructStub(
*isolate()->builtins()->JSBuiltinsConstructStub());
InstallWithIntrinsicDefaultProto(isolate(), function,
Context::PROMISE_FUNCTION_INDEX);

View File

@ -1744,7 +1744,7 @@ Handle<SharedFunctionInfo> Compiler::GetSharedFunctionInfoForNative(
Handle<SharedFunctionInfo> shared = isolate->factory()->NewSharedFunctionInfo(
name, fun->shared()->num_literals(), FunctionKind::kNormalFunction, code,
Handle<ScopeInfo>(fun->shared()->scope_info()));
shared->set_construct_stub(*construct_stub);
shared->SetConstructStub(*construct_stub);
shared->set_feedback_metadata(fun->shared()->feedback_metadata());
// Copy the function data to the shared function info.

View File

@ -2119,7 +2119,7 @@ Handle<SharedFunctionInfo> Factory::NewSharedFunctionInfo(
Handle<Code> construct_stub =
is_constructor ? isolate()->builtins()->JSConstructStubGeneric()
: isolate()->builtins()->ConstructedNonConstructable();
share->set_construct_stub(*construct_stub);
share->SetConstructStub(*construct_stub);
share->set_instance_class_name(*Object_string());
share->set_function_data(*undefined_value(), SKIP_WRITE_BARRIER);
share->set_script(*undefined_value(), SKIP_WRITE_BARRIER);

View File

@ -446,6 +446,14 @@ Handle<Object> Isolate::CaptureSimpleStackTrace(Handle<JSReceiver> error_object,
Handle<Object> recv = frames[i].receiver();
Handle<AbstractCode> abstract_code = frames[i].abstract_code();
if (frame->type() == StackFrame::BUILTIN) {
// Help CallSite::IsConstructor correctly detect hand-written
// construct stubs.
Code* code = Code::cast(*abstract_code);
if (code->is_construct_stub()) {
recv = handle(heap()->call_site_constructor_symbol(), this);
}
}
Handle<Smi> offset(Smi::FromInt(frames[i].code_offset()), this);
elements = MaybeGrow(this, elements, cursor, cursor + 4);

View File

@ -4987,6 +4987,18 @@ inline void Code::set_can_have_weak_objects(bool value) {
WRITE_UINT32_FIELD(this, kKindSpecificFlags1Offset, updated);
}
inline bool Code::is_construct_stub() {
DCHECK(kind() == BUILTIN);
return IsConstructStubField::decode(
READ_UINT32_FIELD(this, kKindSpecificFlags1Offset));
}
inline void Code::set_is_construct_stub(bool value) {
DCHECK(kind() == BUILTIN);
int previous = READ_UINT32_FIELD(this, kKindSpecificFlags1Offset);
int updated = IsConstructStubField::update(previous, value);
WRITE_UINT32_FIELD(this, kKindSpecificFlags1Offset, updated);
}
bool Code::has_deoptimization_support() {
DCHECK_EQ(FUNCTION, kind());

View File

@ -995,7 +995,7 @@ Handle<SharedFunctionInfo> FunctionTemplateInfo::GetOrCreateSharedFunctionInfo(
Handle<SharedFunctionInfo> result =
isolate->factory()->NewSharedFunctionInfo(name, code, is_constructor);
if (is_constructor) {
result->set_construct_stub(*isolate->builtins()->JSConstructStubApi());
result->SetConstructStub(*isolate->builtins()->JSConstructStubApi());
}
result->set_length(info->length());
@ -13244,7 +13244,7 @@ void SharedFunctionInfo::InitFromFunctionLiteral(
shared_info->set_never_compiled(true);
shared_info->set_kind(lit->kind());
if (!IsConstructable(lit->kind(), lit->language_mode())) {
shared_info->set_construct_stub(
shared_info->SetConstructStub(
*shared_info->GetIsolate()->builtins()->ConstructedNonConstructable());
}
shared_info->set_needs_home_object(lit->scope()->NeedsHomeObject());
@ -13263,6 +13263,10 @@ bool SharedFunctionInfo::VerifyBailoutId(BailoutId id) {
return true; // Return true if there was no DCHECK.
}
void SharedFunctionInfo::SetConstructStub(Code* code) {
if (code->kind() == Code::BUILTIN) code->set_is_construct_stub(true);
set_construct_stub(code);
}
void Map::StartInobjectSlackTracking() {
DCHECK(!IsInobjectSlackTrackingInProgress());

View File

@ -5063,6 +5063,12 @@ class Code: public HeapObject {
inline bool can_have_weak_objects();
inline void set_can_have_weak_objects(bool value);
// [is_construct_stub]: For kind BUILTIN, tells whether the code object
// represents a hand-written construct stub
// (e.g., NumberConstructor_ConstructStub).
inline bool is_construct_stub();
inline void set_is_construct_stub(bool value);
// [has_deoptimization_support]: For FUNCTION kind, tells if it has
// deoptimization support.
inline bool has_deoptimization_support();
@ -5398,9 +5404,11 @@ class Code: public HeapObject {
kStackSlotsFirstBit + kStackSlotsBitCount;
static const int kIsTurbofannedBit = kMarkedForDeoptimizationBit + 1;
static const int kCanHaveWeakObjects = kIsTurbofannedBit + 1;
// Could be moved to overlap previous bits when we need more space.
static const int kIsConstructStub = kCanHaveWeakObjects + 1;
STATIC_ASSERT(kStackSlotsFirstBit + kStackSlotsBitCount <= 32);
STATIC_ASSERT(kCanHaveWeakObjects + 1 <= 32);
STATIC_ASSERT(kIsConstructStub + 1 <= 32);
class StackSlotsField: public BitField<int,
kStackSlotsFirstBit, kStackSlotsBitCount> {}; // NOLINT
@ -5410,6 +5418,8 @@ class Code: public HeapObject {
}; // NOLINT
class CanHaveWeakObjectsField
: public BitField<bool, kCanHaveWeakObjects, 1> {}; // NOLINT
class IsConstructStubField : public BitField<bool, kIsConstructStub, 1> {
}; // NOLINT
// KindSpecificFlags2 layout (ALL)
static const int kIsCrankshaftedBit = 0;
@ -6878,6 +6888,10 @@ class SharedFunctionInfo: public HeapObject {
// [construct stub]: Code stub for constructing instances of this function.
DECL_ACCESSORS(construct_stub, Code)
// Sets the given code as the construct stub, and marks builtin code objects
// as a construct stub.
void SetConstructStub(Code* code);
// Returns if this function has been compiled to native code yet.
inline bool is_compiled();

View File

@ -130,7 +130,7 @@ static MaybeHandle<Object> DefineClass(Isolate* isolate,
// constructor. Hence we can reuse the builtins construct stub for derived
// classes.
Handle<Code> stub(isolate->builtins()->JSBuiltinsConstructStubForDerived());
constructor->shared()->set_construct_stub(*stub);
constructor->shared()->SetConstructStub(*stub);
}
JSFunction::SetPrototype(constructor, prototype);

View File

@ -49,7 +49,7 @@ RUNTIME_FUNCTION(Runtime_FunctionRemovePrototype) {
CONVERT_ARG_CHECKED(JSFunction, f, 0);
CHECK(f->RemovePrototype());
f->shared()->set_construct_stub(
f->shared()->SetConstructStub(
*isolate->builtins()->ConstructedNonConstructable());
return isolate->heap()->undefined_value();

View File

@ -33,12 +33,10 @@ testBuiltinInStackTrace("Date.prototype.getUTCDate.call('')",
"at String.getUTCDate");
testBuiltinInStackTrace("Date.prototype.getTime.call('')", "at String.getTime");
// TODO(jgruber): These use a more generic expected string until detection of
// assembly builtin constructors is fixed.
testBuiltinInStackTrace("Number(thrower);", "Number");
testBuiltinInStackTrace("new Number(thrower);", "Number");
testBuiltinInStackTrace("String(thrower);", "String");
testBuiltinInStackTrace("new String(thrower);", "String");
testBuiltinInStackTrace("Number(thrower);", "at Number");
testBuiltinInStackTrace("new Number(thrower);", "at new Number");
testBuiltinInStackTrace("String(thrower);", "at String");
testBuiltinInStackTrace("new String(thrower);", "at new String");
// Ensure we correctly pick up the receiver's string tag.
testBuiltinInStackTrace("Math.acos(thrower);", "at Math.acos");