Add new FrameArray type

A FrameArray encodes information about a set of stack frames into a fixed
array.

This commit is a pure refactoring to make the structure of fixed array-encoded
frames explicit.

BUG=

Review-Url: https://codereview.chromium.org/2270783002
Cr-Commit-Position: refs/heads/master@{#38852}
This commit is contained in:
jgruber 2016-08-24 01:48:34 -07:00 committed by Commit bot
parent 2053d142e0
commit 19e8380261
9 changed files with 272 additions and 123 deletions

View File

@ -178,6 +178,14 @@ Handle<FixedArrayBase> Factory::NewFixedDoubleArrayWithHoles(
return array;
}
Handle<FrameArray> Factory::NewFrameArray(int number_of_frames,
PretenureFlag pretenure) {
DCHECK_LE(0, number_of_frames);
Handle<FixedArray> result =
NewFixedArrayWithHoles(FrameArray::LengthFor(number_of_frames));
result->set(FrameArray::kFrameCountIndex, Smi::FromInt(0));
return Handle<FrameArray>::cast(result);
}
Handle<OrderedHashSet> Factory::NewOrderedHashSet() {
return OrderedHashSet::Allocate(isolate(), OrderedHashSet::kMinCapacity);

View File

@ -52,6 +52,9 @@ class Factory final {
int size,
PretenureFlag pretenure = NOT_TENURED);
Handle<FrameArray> NewFrameArray(int number_of_frames,
PretenureFlag pretenure = NOT_TENURED);
Handle<OrderedHashSet> NewOrderedHashSet();
Handle<OrderedHashMap> NewOrderedHashMap();

View File

@ -128,6 +128,7 @@
V(sourceText_string, "sourceText") \
V(source_url_string, "source_url") \
V(stack_string, "stack") \
V(stackTraceLimit_string, "stackTraceLimit") \
V(strict_compare_ic_string, "===") \
V(string_string, "string") \
V(String_string, "String") \

View File

@ -315,21 +315,7 @@ void Isolate::PushStackTraceAndDie(unsigned int magic, void* ptr1, void* ptr2,
base::OS::Abort();
}
static Handle<FixedArray> MaybeGrow(Isolate* isolate,
Handle<FixedArray> elements,
int cur_position, int new_size) {
if (new_size > elements->length()) {
int new_capacity = JSObject::NewElementsCapacity(elements->length());
Handle<FixedArray> new_elements =
isolate->factory()->NewFixedArrayWithHoles(new_capacity);
for (int i = 0; i < cur_position; i++) {
new_elements->set(i, elements->get(i));
}
elements = new_elements;
}
DCHECK(new_size <= elements->length());
return elements;
}
namespace {
class StackTraceHelper {
public:
@ -435,8 +421,6 @@ class StackTraceHelper {
bool encountered_strict_function_;
};
namespace {
// TODO(jgruber): Fix all cases in which frames give us a hole value (e.g. the
// receiver in RegExp constructor frames.
Handle<Object> TheHoleToUndefined(Isolate* isolate, Handle<Object> in) {
@ -444,35 +428,36 @@ Handle<Object> TheHoleToUndefined(Isolate* isolate, Handle<Object> in) {
? Handle<Object>::cast(isolate->factory()->undefined_value())
: in;
}
bool GetStackTraceLimit(Isolate* isolate, int* result) {
Handle<JSObject> error = isolate->error_function();
Handle<String> key = isolate->factory()->stackTraceLimit_string();
Handle<Object> stack_trace_limit = JSReceiver::GetDataProperty(error, key);
if (!stack_trace_limit->IsNumber()) return false;
// Ensure that limit is not negative.
*result = Max(FastD2IChecked(stack_trace_limit->Number()), 0);
return true;
}
} // namespace
Handle<Object> Isolate::CaptureSimpleStackTrace(Handle<JSReceiver> error_object,
FrameSkipMode mode,
Handle<Object> caller) {
DisallowJavascriptExecution no_js(this);
// Get stack trace limit.
Handle<JSObject> error = error_function();
Handle<String> stackTraceLimit =
factory()->InternalizeUtf8String("stackTraceLimit");
DCHECK(!stackTraceLimit.is_null());
Handle<Object> stack_trace_limit =
JSReceiver::GetDataProperty(error, stackTraceLimit);
if (!stack_trace_limit->IsNumber()) return factory()->undefined_value();
int limit = FastD2IChecked(stack_trace_limit->Number());
limit = Max(limit, 0); // Ensure that limit is not negative.
int limit;
if (!GetStackTraceLimit(this, &limit)) return factory()->undefined_value();
int initial_size = Min(limit, 10);
Handle<FixedArray> elements =
factory()->NewFixedArrayWithHoles(initial_size * 4 + 1);
const int initial_size = Min(limit, 10);
Handle<FrameArray> elements = factory()->NewFrameArray(initial_size);
StackTraceHelper helper(this, mode, caller);
// First element is reserved to store the number of sloppy frames.
int cursor = 1;
int frames_seen = 0;
for (StackFrameIterator iter(this); !iter.done() && frames_seen < limit;
iter.Advance()) {
for (StackFrameIterator iter(this);
!iter.done() && elements->FrameCount() < limit; iter.Advance()) {
StackFrame* frame = iter.frame();
switch (frame->type()) {
@ -502,14 +487,11 @@ Handle<Object> Isolate::CaptureSimpleStackTrace(Handle<JSReceiver> error_object,
recv = handle(heap()->call_site_constructor_symbol(), this);
}
}
Handle<Smi> offset(Smi::FromInt(frames[i].code_offset()), this);
const int offset = frames[i].code_offset();
elements = MaybeGrow(this, elements, cursor, cursor + 4);
elements->set(cursor++, *TheHoleToUndefined(this, recv));
elements->set(cursor++, *fun);
elements->set(cursor++, *abstract_code);
elements->set(cursor++, *offset);
frames_seen++;
elements = FrameArray::AppendJSFrame(elements,
TheHoleToUndefined(this, recv),
fun, abstract_code, offset);
}
} break;
@ -534,39 +516,39 @@ Handle<Object> Isolate::CaptureSimpleStackTrace(Handle<JSReceiver> error_object,
recv = handle(exit_frame->receiver(), this);
}
elements = MaybeGrow(this, elements, cursor, cursor + 4);
elements->set(cursor++, *recv);
elements->set(cursor++, *fun);
elements->set(cursor++, *code);
elements->set(cursor++, Smi::FromInt(offset));
frames_seen++;
elements = FrameArray::AppendJSFrame(
elements, recv, fun, Handle<AbstractCode>::cast(code), offset);
} break;
case StackFrame::WASM: {
WasmFrame* wasm_frame = WasmFrame::cast(frame);
Handle<Object> wasm_object = handle(wasm_frame->wasm_obj(), this);
const int wasm_function_index = wasm_frame->function_index();
Code* code = wasm_frame->unchecked_code();
Handle<AbstractCode> abstract_code =
Handle<AbstractCode>(AbstractCode::cast(code), this);
int offset =
const int offset =
static_cast<int>(wasm_frame->pc() - code->instruction_start());
elements = MaybeGrow(this, elements, cursor, cursor + 4);
elements->set(cursor++, wasm_frame->wasm_obj());
elements->set(cursor++, Smi::FromInt(wasm_frame->function_index()));
elements->set(cursor++, *abstract_code);
elements->set(cursor++, Smi::FromInt(offset));
frames_seen++;
// TODO(wasm): The wasm object returned by the WasmFrame should always
// be a wasm object.
DCHECK(wasm::IsWasmObject(*wasm_object) ||
wasm_object->IsUndefined(this));
elements = FrameArray::AppendWasmFrame(
elements, wasm_object, wasm_function_index, abstract_code, offset);
} break;
default:
break;
}
}
elements->set(0, Smi::FromInt(helper.sloppy_frames()));
elements->Shrink(cursor);
Handle<JSArray> result = factory()->NewJSArrayWithElements(elements);
result->set_length(Smi::FromInt(cursor));
elements->SetSloppyFrameCount(helper.sloppy_frames());
elements->ShrinkToFit();
// TODO(yangguo): Queue this structured stack trace for preprocessing on GC.
return result;
return factory()->NewJSArrayWithElements(elements);
}
MaybeHandle<JSReceiver> Isolate::CaptureAndSetDetailedStackTrace(
@ -773,19 +755,6 @@ class CaptureStackTraceHelper {
Handle<String> constructor_key_;
};
int PositionFromStackTrace(Handle<FixedArray> elements, int index) {
DisallowHeapAllocation no_gc;
Object* maybe_code = elements->get(index + 2);
if (maybe_code->IsSmi()) {
return Smi::cast(maybe_code)->value();
} else {
AbstractCode* abstract_code = AbstractCode::cast(maybe_code);
int code_offset = Smi::cast(elements->get(index + 3))->value();
return abstract_code->SourcePosition(code_offset);
}
}
Handle<JSArray> Isolate::CaptureCurrentStackTrace(
int frame_limit, StackTrace::StackTraceOptions options) {
DisallowJavascriptExecution no_js(this);
@ -1531,22 +1500,25 @@ bool Isolate::ComputeLocationFromStackTrace(MessageLocation* target,
if (!property->IsJSArray()) return false;
Handle<JSArray> simple_stack_trace = Handle<JSArray>::cast(property);
Handle<FixedArray> elements(FixedArray::cast(simple_stack_trace->elements()));
int elements_limit = Smi::cast(simple_stack_trace->length())->value();
Handle<FrameArray> elements(FrameArray::cast(simple_stack_trace->elements()));
for (int i = 1; i < elements_limit; i += 4) {
Handle<Object> fun_obj = handle(elements->get(i + 1), this);
if (fun_obj->IsSmi()) {
const int frame_count = elements->FrameCount();
for (int i = 0; i < frame_count; i++) {
if (elements->IsWasmFrame(i)) {
// TODO(clemensh): handle wasm frames
return false;
}
Handle<JSFunction> fun = Handle<JSFunction>::cast(fun_obj);
Handle<JSFunction> fun = handle(elements->Function(i), this);
if (!fun->shared()->IsSubjectToDebugging()) continue;
Object* script = fun->shared()->script();
if (script->IsScript() &&
!(Script::cast(script)->source()->IsUndefined(this))) {
int pos = PositionFromStackTrace(elements, i);
AbstractCode* abstract_code = elements->Code(i);
const int code_offset = elements->Offset(i)->value();
const int pos = abstract_code->SourcePosition(code_offset);
Handle<Script> casted_script(Script::cast(script));
*target = MessageLocation(casted_script, pos, pos + 1);
return true;

View File

@ -495,44 +495,52 @@ MaybeHandle<FixedArray> GetStackFrames(Isolate* isolate,
Handle<JSArray> raw_stack_array = Handle<JSArray>::cast(raw_stack);
DCHECK(raw_stack_array->elements()->IsFixedArray());
Handle<FixedArray> raw_stack_elements =
handle(FixedArray::cast(raw_stack_array->elements()), isolate);
Handle<FrameArray> elems(FrameArray::cast(raw_stack_array->elements()));
const int raw_stack_len = raw_stack_elements->length();
DCHECK(raw_stack_len % 4 == 1); // Multiples of 4 plus sloppy frames count.
const int frame_count = (raw_stack_len - 1) / 4;
const int frame_count = elems->FrameCount();
Handle<Object> sloppy_frames_obj =
FixedArray::get(*raw_stack_elements, 0, isolate);
int sloppy_frames = Handle<Smi>::cast(sloppy_frames_obj)->value();
int sloppy_frames = elems->SloppyFrameCount();
int dst_ix = 0;
Handle<FixedArray> frames = isolate->factory()->NewFixedArray(frame_count);
for (int i = 1; i < raw_stack_len; i += 4) {
Handle<Object> recv = FixedArray::get(*raw_stack_elements, i, isolate);
Handle<Object> fun = FixedArray::get(*raw_stack_elements, i + 1, isolate);
Handle<AbstractCode> code = Handle<AbstractCode>::cast(
FixedArray::get(*raw_stack_elements, i + 2, isolate));
Handle<Smi> pc =
Handle<Smi>::cast(FixedArray::get(*raw_stack_elements, i + 3, isolate));
Handle<Object> pos =
(fun->IsSmi() && pc->value() < 0)
? handle(Smi::FromInt(-1 - pc->value()), isolate)
: handle(Smi::FromInt(code->SourcePosition(pc->value())), isolate);
for (int i = 0; i < frame_count; i++) {
Handle<AbstractCode> code(elems->Code(i), isolate);
Handle<Smi> pc(elems->Offset(i), isolate);
sloppy_frames--;
Handle<Object> strict = isolate->factory()->ToBoolean(sloppy_frames < 0);
Handle<Object> callsite;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, callsite,
CallSiteUtils::Construct(isolate, recv, fun, pos, strict), FixedArray);
if (elems->IsWasmFrame(i)) {
Handle<Object> wasm_obj(elems->WasmObject(i), isolate);
Handle<Smi> wasm_fun_ix(elems->WasmFunctionIndex(i), isolate);
frames->set(dst_ix++, *callsite);
Handle<Object> pos((pc->value() < 0)
? Smi::FromInt(-1 - pc->value())
: Smi::FromInt(code->SourcePosition(pc->value())),
isolate);
Handle<Object> callsite;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, callsite,
CallSiteUtils::Construct(isolate, wasm_obj, wasm_fun_ix, pos, strict),
FixedArray);
frames->set(i, *callsite);
} else {
Handle<Object> recv(elems->Receiver(i), isolate);
Handle<Object> fun(elems->Function(i), isolate);
Handle<Object> pos(Smi::FromInt(code->SourcePosition(pc->value())),
isolate);
Handle<Object> callsite;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, callsite,
CallSiteUtils::Construct(isolate, recv, fun, pos, strict),
FixedArray);
frames->set(i, *callsite);
}
}
DCHECK_EQ(frame_count, dst_ix);
return frames;
}

View File

@ -709,6 +709,8 @@ bool HeapObject::IsJSCollection() const { return IsJSMap() || IsJSSet(); }
bool HeapObject::IsDescriptorArray() const { return IsFixedArray(); }
bool HeapObject::IsFrameArray() const { return IsFixedArray(); }
bool HeapObject::IsArrayList() const { return IsFixedArray(); }
bool Object::IsLayoutDescriptor() const {
@ -2610,6 +2612,38 @@ Object** FixedArray::RawFieldOfElementAt(int index) {
return HeapObject::RawField(this, OffsetOfElementAt(index));
}
#define DEFINE_FRAME_ARRAY_ACCESSORS(name, type) \
type* FrameArray::name(int frame_ix) const { \
Object* obj = \
get(kFirstIndex + frame_ix * kElementsPerFrame + k##name##Offset); \
return type::cast(obj); \
} \
\
void FrameArray::Set##name(int frame_ix, type* value) { \
set(kFirstIndex + frame_ix * kElementsPerFrame + k##name##Offset, value); \
}
FRAME_ARRAY_FIELD_LIST(DEFINE_FRAME_ARRAY_ACCESSORS)
#undef DEFINE_FRAME_ARRAY_ACCESSORS
int FrameArray::SloppyFrameCount() const {
return Smi::cast(get(kSloppyFramesIndex))->value();
}
void FrameArray::SetSloppyFrameCount(int count) {
return set(kSloppyFramesIndex, Smi::FromInt(count));
}
bool FrameArray::IsWasmFrame(int frame_ix) const {
Object* obj = get(kFirstIndex + frame_ix * kElementsPerFrame +
kWasmFunctionIndexOffset);
return obj->IsSmi();
}
int FrameArray::FrameCount() const {
const int frame_count = Smi::cast(get(kFrameCountIndex))->value();
DCHECK_LE(0, frame_count);
return frame_count;
}
bool DescriptorArray::IsEmpty() {
DCHECK(length() >= kFirstIndex ||
@ -3223,6 +3257,7 @@ CAST_ACCESSOR(FixedDoubleArray)
CAST_ACCESSOR(FixedTypedArrayBase)
CAST_ACCESSOR(Float32x4)
CAST_ACCESSOR(Foreign)
CAST_ACCESSOR(FrameArray)
CAST_ACCESSOR(GlobalDictionary)
CAST_ACCESSOR(HandlerTable)
CAST_ACCESSOR(HeapObject)

View File

@ -10177,22 +10177,74 @@ bool ArrayList::IsFull() {
return kFirstIndex + Length() == capacity;
}
namespace {
Handle<ArrayList> ArrayList::EnsureSpace(Handle<ArrayList> array, int length) {
Handle<FixedArray> EnsureSpaceInFixedArray(Handle<FixedArray> array,
int length) {
int capacity = array->length();
bool empty = (capacity == 0);
if (capacity < kFirstIndex + length) {
if (capacity < length) {
Isolate* isolate = array->GetIsolate();
int new_capacity = kFirstIndex + length;
int new_capacity = length;
new_capacity = new_capacity + Max(new_capacity / 2, 2);
int grow_by = new_capacity - capacity;
array = Handle<ArrayList>::cast(
isolate->factory()->CopyFixedArrayAndGrow(array, grow_by));
if (empty) array->SetLength(0);
}
return array;
}
} // namespace
Handle<ArrayList> ArrayList::EnsureSpace(Handle<ArrayList> array, int length) {
const bool empty = (array->length() == 0);
auto ret = Handle<ArrayList>::cast(
EnsureSpaceInFixedArray(array, kFirstIndex + length));
if (empty) ret->SetLength(0);
return ret;
}
// static
Handle<FrameArray> FrameArray::AppendJSFrame(Handle<FrameArray> in,
Handle<Object> receiver,
Handle<JSFunction> function,
Handle<AbstractCode> code,
int offset) {
const int frame_count = in->FrameCount();
const int new_length = LengthFor(frame_count + 1);
Handle<FrameArray> array = EnsureSpace(in, new_length);
array->SetReceiver(frame_count, *receiver);
array->SetFunction(frame_count, *function);
array->SetCode(frame_count, *code);
array->SetOffset(frame_count, Smi::FromInt(offset));
array->set(kFrameCountIndex, Smi::FromInt(frame_count + 1));
return array;
}
// static
Handle<FrameArray> FrameArray::AppendWasmFrame(Handle<FrameArray> in,
Handle<Object> wasm_object,
int wasm_function_index,
Handle<AbstractCode> code,
int offset) {
const int frame_count = in->FrameCount();
const int new_length = LengthFor(frame_count + 1);
Handle<FrameArray> array = EnsureSpace(in, new_length);
array->SetWasmObject(frame_count, *wasm_object);
array->SetWasmFunctionIndex(frame_count, Smi::FromInt(wasm_function_index));
array->SetCode(frame_count, *code);
array->SetOffset(frame_count, Smi::FromInt(offset));
array->set(kFrameCountIndex, Smi::FromInt(frame_count + 1));
return array;
}
void FrameArray::ShrinkToFit() { Shrink(LengthFor(FrameCount())); }
// static
Handle<FrameArray> FrameArray::EnsureSpace(Handle<FrameArray> array,
int length) {
return Handle<FrameArray>::cast(EnsureSpaceInFixedArray(array, length));
}
Handle<DescriptorArray> DescriptorArray::Allocate(Isolate* isolate,
int number_of_descriptors,
int slack,

View File

@ -76,6 +76,7 @@
// - BytecodeArray
// - FixedArray
// - DescriptorArray
// - FrameArray
// - LiteralsArray
// - HashTable
// - Dictionary
@ -862,7 +863,7 @@ enum class ComparisonResult {
INLINE(static type* cast(Object* object)); \
INLINE(static const type* cast(const Object* object));
class AbstractCode;
class AccessorPair;
class AllocationSite;
class AllocationSiteCreationContext;
@ -961,6 +962,7 @@ template <class C> inline bool Is(Object* obj);
V(JSGeneratorObject) \
V(Map) \
V(DescriptorArray) \
V(FrameArray) \
V(TransitionArray) \
V(LiteralsArray) \
V(TypeFeedbackMetadata) \
@ -2895,7 +2897,6 @@ class WeakFixedArray : public FixedArray {
DISALLOW_IMPLICIT_CONSTRUCTORS(WeakFixedArray);
};
// Generic array grows dynamically with O(1) amortized insertion.
class ArrayList : public FixedArray {
public:
@ -2925,6 +2926,76 @@ class ArrayList : public FixedArray {
DISALLOW_IMPLICIT_CONSTRUCTORS(ArrayList);
};
#define FRAME_ARRAY_FIELD_LIST(V) \
V(WasmObject, Object) \
V(WasmFunctionIndex, Smi) \
V(Receiver, Object) \
V(Function, JSFunction) \
V(Code, AbstractCode) \
V(Offset, Smi)
// Container object for data collected during simple stack trace captures.
class FrameArray : public FixedArray {
public:
#define DECLARE_FRAME_ARRAY_ACCESSORS(name, type) \
inline type* name(int frame_ix) const; \
inline void Set##name(int frame_ix, type* value);
FRAME_ARRAY_FIELD_LIST(DECLARE_FRAME_ARRAY_ACCESSORS)
#undef DECLARE_FRAME_ARRAY_ACCESSORS
inline void SetSloppyFrameCount(int count);
inline int SloppyFrameCount() const;
inline bool IsWasmFrame(int frame_ix) const;
inline int FrameCount() const;
void ShrinkToFit();
static Handle<FrameArray> AppendJSFrame(Handle<FrameArray> in,
Handle<Object> receiver,
Handle<JSFunction> function,
Handle<AbstractCode> code,
int offset);
static Handle<FrameArray> AppendWasmFrame(Handle<FrameArray> in,
Handle<Object> wasm_object,
int wasm_function_index,
Handle<AbstractCode> code,
int offset);
DECLARE_CAST(FrameArray)
private:
// The underlying fixed array embodies a captured stack trace. The number of
// sloppy frames is stored at array[kFirstIndex]. Frame i occupies indices
// kFirstIndex + 1 + [i * kElementsPerFrame, (i + 1) * kElementsPerFrame[,
// with internal offsets as below:
static const int kWasmObjectOffset = 0;
static const int kWasmFunctionIndexOffset = 1;
static const int kReceiverOffset = 0;
static const int kFunctionOffset = 1;
static const int kCodeOffset = 2;
static const int kOffsetOffset = 3;
static const int kElementsPerFrame = 4;
// Array layout indices.
static const int kFrameCountIndex = 0;
static const int kSloppyFramesIndex = 1;
static const int kFirstIndex = 2;
static int LengthFor(int frame_count) {
return kFirstIndex + frame_count * kElementsPerFrame;
}
static Handle<FrameArray> EnsureSpace(Handle<FrameArray> array, int length);
friend class Factory;
DISALLOW_IMPLICIT_CONSTRUCTORS(FrameArray);
};
// DescriptorArrays are fixed arrays used to hold instance descriptors.
// The format of the these objects is:

View File

@ -120,18 +120,17 @@ RUNTIME_FUNCTION(Runtime_ThrowWasmError) {
error, isolate->factory()->stack_trace_symbol());
// Patch the stack trace (array of <receiver, function, code, position>).
if (stack_trace_obj->IsJSArray()) {
Handle<FixedArray> stack_elements(
FixedArray::cast(JSArray::cast(*stack_trace_obj)->elements()));
DCHECK_EQ(1, stack_elements->length() % 4);
DCHECK(Code::cast(stack_elements->get(3))->kind() == Code::WASM_FUNCTION);
DCHECK(stack_elements->get(4)->IsSmi() &&
Smi::cast(stack_elements->get(4))->value() >= 0);
stack_elements->set(4, Smi::FromInt(-1 - byte_offset));
Handle<FrameArray> stack_elements(
FrameArray::cast(JSArray::cast(*stack_trace_obj)->elements()));
DCHECK(stack_elements->Code(0)->kind() == AbstractCode::WASM_FUNCTION);
DCHECK(stack_elements->Offset(0)->value() >= 0);
stack_elements->SetOffset(0, Smi::FromInt(-1 - byte_offset));
}
Handle<Object> detailed_stack_trace_obj = JSReceiver::GetDataProperty(
error, isolate->factory()->detailed_stack_trace_symbol());
// Patch the detailed stack trace (array of JSObjects with various
// properties).
Handle<Object> detailed_stack_trace_obj = JSReceiver::GetDataProperty(
error, isolate->factory()->detailed_stack_trace_symbol());
if (detailed_stack_trace_obj->IsJSArray()) {
Handle<FixedArray> stack_elements(
FixedArray::cast(JSArray::cast(*detailed_stack_trace_obj)->elements()));