Revert r13188, r13194, r13256 (Deferred formatting of error stack trace during GC).
R=ulan@chromium.org BUG= Review URL: https://chromiumcodereview.appspot.com/11678006 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@13279 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
074038058a
commit
2f821f1ed9
@ -1851,7 +1851,8 @@ v8::Local<Value> v8::TryCatch::StackTrace() const {
|
||||
if (!raw_obj->IsJSObject()) return v8::Local<Value>();
|
||||
i::HandleScope scope(isolate_);
|
||||
i::Handle<i::JSObject> obj(i::JSObject::cast(raw_obj), isolate_);
|
||||
i::Handle<i::String> name = isolate_->factory()->stack_symbol();
|
||||
i::Handle<i::String> name =
|
||||
isolate_->factory()->LookupOneByteSymbol(STATIC_ASCII_VECTOR("stack"));
|
||||
if (!obj->HasProperty(*name)) return v8::Local<Value>();
|
||||
i::Handle<i::Object> value = i::GetProperty(obj, name);
|
||||
if (value.is_null()) return v8::Local<Value>();
|
||||
|
@ -375,15 +375,6 @@ Handle<JSValue> GetScriptWrapper(Handle<Script> script) {
|
||||
Handle<JSFunction> constructor = isolate->script_function();
|
||||
Handle<JSValue> result =
|
||||
Handle<JSValue>::cast(isolate->factory()->NewJSObject(constructor));
|
||||
|
||||
// The allocation might have triggered a GC, which could have called this
|
||||
// function recursively, and a wrapper has already been created and cached.
|
||||
// In that case, simply return the cached wrapper.
|
||||
if (script->wrapper()->foreign_address() != NULL) {
|
||||
return Handle<JSValue>(
|
||||
reinterpret_cast<JSValue**>(script->wrapper()->foreign_address()));
|
||||
}
|
||||
|
||||
result->set_value(*script);
|
||||
|
||||
// Create a new weak global handle and use it to cache the wrapper
|
||||
|
@ -666,19 +666,6 @@ void ExternalStringTable::ShrinkNewStrings(int position) {
|
||||
}
|
||||
|
||||
|
||||
void ErrorObjectList::Add(JSObject* object) {
|
||||
list_.Add(object);
|
||||
}
|
||||
|
||||
|
||||
void ErrorObjectList::Iterate(ObjectVisitor* v) {
|
||||
if (!list_.is_empty()) {
|
||||
Object** start = &list_[0];
|
||||
v->VisitPointers(start, start + list_.length());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Heap::ClearInstanceofCache() {
|
||||
set_instanceof_cache_function(the_hole_value());
|
||||
}
|
||||
|
118
src/heap.cc
118
src/heap.cc
@ -550,8 +550,6 @@ void Heap::GarbageCollectionEpilogue() {
|
||||
#ifdef ENABLE_DEBUGGER_SUPPORT
|
||||
isolate_->debug()->AfterGarbageCollection();
|
||||
#endif // ENABLE_DEBUGGER_SUPPORT
|
||||
|
||||
error_object_list_.DeferredFormatStackTrace(isolate());
|
||||
}
|
||||
|
||||
|
||||
@ -1356,8 +1354,6 @@ void Heap::Scavenge() {
|
||||
UpdateNewSpaceReferencesInExternalStringTable(
|
||||
&UpdateNewSpaceReferenceInExternalStringTableEntry);
|
||||
|
||||
error_object_list_.UpdateReferencesInNewSpace(this);
|
||||
|
||||
promotion_queue_.Destroy();
|
||||
|
||||
LiveObjectList::UpdateReferencesForScavengeGC();
|
||||
@ -5937,7 +5933,6 @@ void Heap::IterateWeakRoots(ObjectVisitor* v, VisitMode mode) {
|
||||
mode != VISIT_ALL_IN_SWEEP_NEWSPACE) {
|
||||
// Scavenge collections have special processing for this.
|
||||
external_string_table_.Iterate(v);
|
||||
error_object_list_.Iterate(v);
|
||||
}
|
||||
v->Synchronize(VisitorSynchronization::kExternalStringsTable);
|
||||
}
|
||||
@ -6311,8 +6306,6 @@ void Heap::TearDown() {
|
||||
|
||||
external_string_table_.TearDown();
|
||||
|
||||
error_object_list_.TearDown();
|
||||
|
||||
new_space_.TearDown();
|
||||
|
||||
if (old_pointer_space_ != NULL) {
|
||||
@ -7219,8 +7212,6 @@ void ExternalStringTable::CleanUp() {
|
||||
}
|
||||
}
|
||||
new_space_strings_.Rewind(last);
|
||||
new_space_strings_.Trim();
|
||||
|
||||
last = 0;
|
||||
for (int i = 0; i < old_space_strings_.length(); ++i) {
|
||||
if (old_space_strings_[i] == heap_->the_hole_value()) {
|
||||
@ -7230,7 +7221,6 @@ void ExternalStringTable::CleanUp() {
|
||||
old_space_strings_[last++] = old_space_strings_[i];
|
||||
}
|
||||
old_space_strings_.Rewind(last);
|
||||
old_space_strings_.Trim();
|
||||
#ifdef VERIFY_HEAP
|
||||
if (FLAG_verify_heap) {
|
||||
Verify();
|
||||
@ -7245,114 +7235,6 @@ void ExternalStringTable::TearDown() {
|
||||
}
|
||||
|
||||
|
||||
// Update all references.
|
||||
void ErrorObjectList::UpdateReferences() {
|
||||
for (int i = 0; i < list_.length(); i++) {
|
||||
HeapObject* object = HeapObject::cast(list_[i]);
|
||||
MapWord first_word = object->map_word();
|
||||
if (first_word.IsForwardingAddress()) {
|
||||
list_[i] = first_word.ToForwardingAddress();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Unforwarded objects in new space are dead and removed from the list.
|
||||
void ErrorObjectList::UpdateReferencesInNewSpace(Heap* heap) {
|
||||
if (!nested_) {
|
||||
int write_index = 0;
|
||||
for (int i = 0; i < list_.length(); i++) {
|
||||
MapWord first_word = HeapObject::cast(list_[i])->map_word();
|
||||
if (first_word.IsForwardingAddress()) {
|
||||
list_[write_index++] = first_word.ToForwardingAddress();
|
||||
}
|
||||
}
|
||||
list_.Rewind(write_index);
|
||||
} else {
|
||||
// If a GC is triggered during DeferredFormatStackTrace, we do not move
|
||||
// objects in the list, just remove dead ones, as to not confuse the
|
||||
// loop in DeferredFormatStackTrace.
|
||||
for (int i = 0; i < list_.length(); i++) {
|
||||
MapWord first_word = HeapObject::cast(list_[i])->map_word();
|
||||
list_[i] = first_word.IsForwardingAddress()
|
||||
? first_word.ToForwardingAddress()
|
||||
: heap->the_hole_value();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ErrorObjectList::DeferredFormatStackTrace(Isolate* isolate) {
|
||||
// If formatting the stack trace causes a GC, this method will be
|
||||
// recursively called. In that case, skip the recursive call, since
|
||||
// the loop modifies the list while iterating over it.
|
||||
if (nested_ || isolate->has_pending_exception()) return;
|
||||
nested_ = true;
|
||||
HandleScope scope(isolate);
|
||||
Handle<String> stack_key = isolate->factory()->stack_symbol();
|
||||
int write_index = 0;
|
||||
int budget = kBudgetPerGC;
|
||||
for (int i = 0; i < list_.length(); i++) {
|
||||
Object* object = list_[i];
|
||||
JSFunction* getter_fun;
|
||||
|
||||
{ AssertNoAllocation assert;
|
||||
// Skip possible holes in the list.
|
||||
if (object->IsTheHole()) continue;
|
||||
if (isolate->heap()->InNewSpace(object) || budget == 0) {
|
||||
list_[write_index++] = object;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check whether the stack property is backed by the original getter.
|
||||
LookupResult lookup(isolate);
|
||||
JSObject::cast(object)->LocalLookupRealNamedProperty(*stack_key, &lookup);
|
||||
if (!lookup.IsFound() || lookup.type() != CALLBACKS) continue;
|
||||
Object* callback = lookup.GetCallbackObject();
|
||||
if (!callback->IsAccessorPair()) continue;
|
||||
Object* getter_obj = AccessorPair::cast(callback)->getter();
|
||||
if (!getter_obj->IsJSFunction()) continue;
|
||||
getter_fun = JSFunction::cast(getter_obj);
|
||||
String* key = isolate->heap()->hidden_stack_trace_symbol();
|
||||
if (key != getter_fun->GetHiddenProperty(key)) continue;
|
||||
}
|
||||
|
||||
budget--;
|
||||
HandleScope scope(isolate);
|
||||
bool has_exception = false;
|
||||
Handle<Object> object_handle(object, isolate);
|
||||
Handle<Object> getter_handle(getter_fun, isolate);
|
||||
Execution::Call(getter_handle, object_handle, 0, NULL, &has_exception);
|
||||
if (has_exception) {
|
||||
// Hit an exception (most likely a stack overflow).
|
||||
// Wrap up this pass and retry after another GC.
|
||||
isolate->clear_pending_exception();
|
||||
// We use the handle since calling the getter might have caused a GC.
|
||||
list_[write_index++] = *object_handle;
|
||||
budget = 0;
|
||||
}
|
||||
}
|
||||
list_.Rewind(write_index);
|
||||
list_.Trim();
|
||||
nested_ = false;
|
||||
}
|
||||
|
||||
|
||||
void ErrorObjectList::RemoveUnmarked(Heap* heap) {
|
||||
for (int i = 0; i < list_.length(); i++) {
|
||||
HeapObject* object = HeapObject::cast(list_[i]);
|
||||
if (!Marking::MarkBitFrom(object).Get()) {
|
||||
list_[i] = heap->the_hole_value();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ErrorObjectList::TearDown() {
|
||||
list_.Free();
|
||||
}
|
||||
|
||||
|
||||
void Heap::QueueMemoryChunkForFree(MemoryChunk* chunk) {
|
||||
chunk->set_next_chunk(chunks_queued_for_free_);
|
||||
chunks_queued_for_free_ = chunk;
|
||||
|
42
src/heap.h
42
src/heap.h
@ -209,7 +209,6 @@ namespace internal {
|
||||
V(char_at_symbol, "CharAt") \
|
||||
V(undefined_symbol, "undefined") \
|
||||
V(value_of_symbol, "valueOf") \
|
||||
V(stack_symbol, "stack") \
|
||||
V(InitializeVarGlobal_symbol, "InitializeVarGlobal") \
|
||||
V(InitializeConstGlobal_symbol, "InitializeConstGlobal") \
|
||||
V(KeyedLoadElementMonomorphic_symbol, \
|
||||
@ -428,41 +427,6 @@ class ExternalStringTable {
|
||||
};
|
||||
|
||||
|
||||
// The stack property of an error object is implemented as a getter that
|
||||
// formats the attached raw stack trace into a string. This raw stack trace
|
||||
// keeps code and function objects alive until the getter is called the first
|
||||
// time. To release those objects, we call the getter after each GC for
|
||||
// newly tenured error objects that are kept in a list.
|
||||
class ErrorObjectList {
|
||||
public:
|
||||
inline void Add(JSObject* object);
|
||||
|
||||
inline void Iterate(ObjectVisitor* v);
|
||||
|
||||
void TearDown();
|
||||
|
||||
void RemoveUnmarked(Heap* heap);
|
||||
|
||||
void DeferredFormatStackTrace(Isolate* isolate);
|
||||
|
||||
void UpdateReferences();
|
||||
|
||||
void UpdateReferencesInNewSpace(Heap* heap);
|
||||
|
||||
private:
|
||||
static const int kBudgetPerGC = 16;
|
||||
|
||||
ErrorObjectList() : nested_(false) { }
|
||||
|
||||
friend class Heap;
|
||||
|
||||
List<Object*> list_;
|
||||
bool nested_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ErrorObjectList);
|
||||
};
|
||||
|
||||
|
||||
enum ArrayStorageAllocationMode {
|
||||
DONT_INITIALIZE_ARRAY_ELEMENTS,
|
||||
INITIALIZE_ARRAY_ELEMENTS_WITH_HOLE
|
||||
@ -1613,10 +1577,6 @@ class Heap {
|
||||
return &external_string_table_;
|
||||
}
|
||||
|
||||
ErrorObjectList* error_object_list() {
|
||||
return &error_object_list_;
|
||||
}
|
||||
|
||||
// Returns the current sweep generation.
|
||||
int sweep_generation() {
|
||||
return sweep_generation_;
|
||||
@ -2193,8 +2153,6 @@ class Heap {
|
||||
|
||||
ExternalStringTable external_string_table_;
|
||||
|
||||
ErrorObjectList error_object_list_;
|
||||
|
||||
VisitorDispatchTable<ScavengingCallback> scavenging_visitors_table_;
|
||||
|
||||
MemoryChunk* chunks_queued_for_free_;
|
||||
|
@ -634,7 +634,6 @@ Handle<JSArray> Isolate::CaptureSimpleStackTrace(Handle<JSObject> error_object,
|
||||
}
|
||||
Handle<JSArray> result = factory()->NewJSArrayWithElements(elements);
|
||||
result->set_length(Smi::FromInt(cursor));
|
||||
heap()->error_object_list()->Add(*error_object);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -85,9 +85,8 @@ void List<T, P>::ResizeAddInternal(const T& element, P alloc) {
|
||||
|
||||
template<typename T, class P>
|
||||
void List<T, P>::Resize(int new_capacity, P alloc) {
|
||||
ASSERT_LE(length_, new_capacity);
|
||||
T* new_data = NewData(new_capacity, alloc);
|
||||
memcpy(new_data, data_, length_ * sizeof(T));
|
||||
memcpy(new_data, data_, capacity_ * sizeof(T));
|
||||
List<T, P>::DeleteData(data_);
|
||||
data_ = new_data;
|
||||
capacity_ = new_capacity;
|
||||
@ -162,14 +161,6 @@ void List<T, P>::Rewind(int pos) {
|
||||
}
|
||||
|
||||
|
||||
template<typename T, class P>
|
||||
void List<T, P>::Trim(P alloc) {
|
||||
if (length_ < capacity_ / 4) {
|
||||
Resize(capacity_ / 2, alloc);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template<typename T, class P>
|
||||
void List<T, P>::Iterate(void (*callback)(T* x)) {
|
||||
for (int i = 0; i < length_; i++) callback(&data_[i]);
|
||||
|
@ -149,9 +149,6 @@ class List {
|
||||
// Drop the last 'count' elements from the list.
|
||||
INLINE(void RewindBy(int count)) { Rewind(length_ - count); }
|
||||
|
||||
// Halve the capacity if fill level is less than a quarter.
|
||||
INLINE(void Trim(AllocationPolicy allocator = AllocationPolicy()));
|
||||
|
||||
bool Contains(const T& elm) const;
|
||||
int CountOccurrences(const T& elm, int start, int end) const;
|
||||
|
||||
|
@ -835,6 +835,8 @@ void MarkCompactCollector::Finish() {
|
||||
// GC, because it relies on the new address of certain old space
|
||||
// objects (empty string, illegal builtin).
|
||||
heap()->isolate()->stub_cache()->Clear();
|
||||
|
||||
heap()->external_string_table_.CleanUp();
|
||||
}
|
||||
|
||||
|
||||
@ -2028,7 +2030,6 @@ void MarkCompactCollector::AfterMarking() {
|
||||
symbol_table->ElementsRemoved(v.PointersRemoved());
|
||||
heap()->external_string_table_.Iterate(&v);
|
||||
heap()->external_string_table_.CleanUp();
|
||||
heap()->error_object_list_.RemoveUnmarked(heap());
|
||||
|
||||
// Process the weak references.
|
||||
MarkCompactWeakObjectRetainer mark_compact_object_retainer;
|
||||
@ -3068,9 +3069,6 @@ void MarkCompactCollector::EvacuateNewSpaceAndCandidates() {
|
||||
heap_->UpdateReferencesInExternalStringTable(
|
||||
&UpdateReferenceInExternalStringTableEntry);
|
||||
|
||||
// Update pointers in the new error object list.
|
||||
heap_->error_object_list()->UpdateReferences();
|
||||
|
||||
if (!FLAG_watch_ic_patching) {
|
||||
// Update JSFunction pointers from the runtime profiler.
|
||||
heap()->isolate()->runtime_profiler()->UpdateSamplesAfterCompact(
|
||||
|
120
src/messages.js
120
src/messages.js
@ -820,7 +820,7 @@ function CallSiteGetMethodName() {
|
||||
%_CallFunction(this.receiver,
|
||||
ownName,
|
||||
ObjectLookupSetter) === this.fun ||
|
||||
%GetDataProperty(this.receiver, ownName) === this.fun)) {
|
||||
this.receiver[ownName] === this.fun)) {
|
||||
// To handle DontEnum properties we guess that the method has
|
||||
// the same name as the function.
|
||||
return ownName;
|
||||
@ -829,7 +829,8 @@ function CallSiteGetMethodName() {
|
||||
for (var prop in this.receiver) {
|
||||
if (%_CallFunction(this.receiver, prop, ObjectLookupGetter) === this.fun ||
|
||||
%_CallFunction(this.receiver, prop, ObjectLookupSetter) === this.fun ||
|
||||
%GetDataProperty(this.receiver, prop) === this.fun) {
|
||||
(!%_CallFunction(this.receiver, prop, ObjectLookupGetter) &&
|
||||
this.receiver[prop] === this.fun)) {
|
||||
// If we find more than one match bail out to avoid confusion.
|
||||
if (name) {
|
||||
return null;
|
||||
@ -932,14 +933,12 @@ function CallSiteToString() {
|
||||
var typeName = GetTypeName(this, true);
|
||||
var methodName = this.getMethodName();
|
||||
if (functionName) {
|
||||
if (typeName &&
|
||||
%_CallFunction(functionName, typeName, StringIndexOf) != 0) {
|
||||
if (typeName && functionName.indexOf(typeName) != 0) {
|
||||
line += typeName + ".";
|
||||
}
|
||||
line += functionName;
|
||||
if (methodName &&
|
||||
(%_CallFunction(functionName, "." + methodName, StringIndexOf) !=
|
||||
functionName.length - methodName.length - 1)) {
|
||||
if (methodName && functionName.lastIndexOf("." + methodName) !=
|
||||
functionName.length - methodName.length - 1) {
|
||||
line += " [as " + methodName + "]";
|
||||
}
|
||||
} else {
|
||||
@ -1017,37 +1016,17 @@ function FormatEvalOrigin(script) {
|
||||
return eval_origin;
|
||||
}
|
||||
|
||||
|
||||
function FormatErrorString(error) {
|
||||
function FormatStackTrace(error, frames) {
|
||||
var lines = [];
|
||||
try {
|
||||
return %_CallFunction(error, ErrorToString);
|
||||
lines.push(error.toString());
|
||||
} catch (e) {
|
||||
try {
|
||||
return "<error: " + e + ">";
|
||||
lines.push("<error: " + e + ">");
|
||||
} catch (ee) {
|
||||
return "<error>";
|
||||
lines.push("<error>");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function GetStackFrames(raw_stack) {
|
||||
var frames = new InternalArray();
|
||||
for (var i = 0; i < raw_stack.length; i += 4) {
|
||||
var recv = raw_stack[i];
|
||||
var fun = raw_stack[i + 1];
|
||||
var code = raw_stack[i + 2];
|
||||
var pc = raw_stack[i + 3];
|
||||
var pos = %FunctionGetPositionForOffset(code, pc);
|
||||
frames.push(new CallSite(recv, fun, pos));
|
||||
}
|
||||
return frames;
|
||||
}
|
||||
|
||||
|
||||
function FormatStackTrace(error_string, frames) {
|
||||
var lines = new InternalArray();
|
||||
lines.push(error_string);
|
||||
for (var i = 0; i < frames.length; i++) {
|
||||
var frame = frames[i];
|
||||
var line;
|
||||
@ -1063,9 +1042,25 @@ function FormatStackTrace(error_string, frames) {
|
||||
}
|
||||
lines.push(" at " + line);
|
||||
}
|
||||
return %_CallFunction(lines, "\n", ArrayJoin);
|
||||
return lines.join("\n");
|
||||
}
|
||||
|
||||
function FormatRawStackTrace(error, raw_stack) {
|
||||
var frames = [ ];
|
||||
for (var i = 0; i < raw_stack.length; i += 4) {
|
||||
var recv = raw_stack[i];
|
||||
var fun = raw_stack[i + 1];
|
||||
var code = raw_stack[i + 2];
|
||||
var pc = raw_stack[i + 3];
|
||||
var pos = %FunctionGetPositionForOffset(code, pc);
|
||||
frames.push(new CallSite(recv, fun, pos));
|
||||
}
|
||||
if (IS_FUNCTION($Error.prepareStackTrace)) {
|
||||
return $Error.prepareStackTrace(error, frames);
|
||||
} else {
|
||||
return FormatStackTrace(error, frames);
|
||||
}
|
||||
}
|
||||
|
||||
function GetTypeName(obj, requireConstructor) {
|
||||
var constructor = obj.receiver.constructor;
|
||||
@ -1081,51 +1076,23 @@ function GetTypeName(obj, requireConstructor) {
|
||||
return constructorName;
|
||||
}
|
||||
|
||||
|
||||
// Flag to prevent recursive call of Error.prepareStackTrace.
|
||||
var formatting_custom_stack_trace = false;
|
||||
|
||||
|
||||
function captureStackTrace(obj, cons_opt) {
|
||||
var stackTraceLimit = $Error.stackTraceLimit;
|
||||
if (!stackTraceLimit || !IS_NUMBER(stackTraceLimit)) return;
|
||||
if (stackTraceLimit < 0 || stackTraceLimit > 10000) {
|
||||
stackTraceLimit = 10000;
|
||||
}
|
||||
var stack = %CollectStackTrace(obj,
|
||||
cons_opt ? cons_opt : captureStackTrace,
|
||||
stackTraceLimit);
|
||||
|
||||
// Don't be lazy if the error stack formatting is custom (observable).
|
||||
if (IS_FUNCTION($Error.prepareStackTrace) && !formatting_custom_stack_trace) {
|
||||
var array = [];
|
||||
%MoveArrayContents(GetStackFrames(stack), array);
|
||||
formatting_custom_stack_trace = true;
|
||||
try {
|
||||
obj.stack = $Error.prepareStackTrace(obj, array);
|
||||
} catch (e) {
|
||||
throw e; // The custom formatting function threw. Rethrow.
|
||||
} finally {
|
||||
formatting_custom_stack_trace = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
var error_string = FormatErrorString(obj);
|
||||
var raw_stack = %CollectStackTrace(obj,
|
||||
cons_opt ? cons_opt : captureStackTrace,
|
||||
stackTraceLimit);
|
||||
// Note that 'obj' and 'this' maybe different when called on objects that
|
||||
// have the error object on its prototype chain. The getter replaces itself
|
||||
// with a data property as soon as the stack trace has been formatted.
|
||||
// The getter must not change the object layout as it may be called after GC.
|
||||
var getter = function() {
|
||||
if (IS_STRING(stack)) return stack;
|
||||
// Stack is still a raw array awaiting to be formatted.
|
||||
stack = FormatStackTrace(error_string, GetStackFrames(stack));
|
||||
// Release context value.
|
||||
error_string = void 0;
|
||||
return stack;
|
||||
var value = FormatRawStackTrace(obj, raw_stack);
|
||||
%DefineOrRedefineDataProperty(obj, 'stack', value, NONE);
|
||||
return value;
|
||||
};
|
||||
%MarkOneShotGetter(getter);
|
||||
|
||||
// The 'stack' property of the receiver is set as data property. If
|
||||
// the receiver is the same as holder, this accessor pair is replaced.
|
||||
var setter = function(v) {
|
||||
@ -1272,32 +1239,23 @@ function SetUpStackOverflowBoilerplate() {
|
||||
// error object copy, but can be found on the prototype chain of 'this'.
|
||||
// When the stack trace is formatted, this accessor property is replaced by
|
||||
// a data property.
|
||||
var error_string = boilerplate.name + ": " + boilerplate.message;
|
||||
|
||||
// The getter must not change the object layout as it may be called after GC.
|
||||
function getter() {
|
||||
var holder = this;
|
||||
while (!IS_ERROR(holder)) {
|
||||
holder = %GetPrototype(holder);
|
||||
if (holder == null) return MakeSyntaxError('illegal_access', []);
|
||||
}
|
||||
var stack = %GetOverflowedStackTrace(holder);
|
||||
if (IS_STRING(stack)) return stack;
|
||||
if (IS_ARRAY(stack)) {
|
||||
var result = FormatStackTrace(error_string, GetStackFrames(stack));
|
||||
%SetOverflowedStackTrace(holder, result);
|
||||
return result;
|
||||
}
|
||||
return void 0;
|
||||
var raw_stack = %GetOverflowedRawStackTrace(holder);
|
||||
var result = IS_ARRAY(raw_stack) ? FormatRawStackTrace(holder, raw_stack)
|
||||
: void 0;
|
||||
%DefineOrRedefineDataProperty(holder, 'stack', result, NONE);
|
||||
return result;
|
||||
}
|
||||
%MarkOneShotGetter(getter);
|
||||
|
||||
// The 'stack' property of the receiver is set as data property. If
|
||||
// the receiver is the same as holder, this accessor pair is replaced.
|
||||
function setter(v) {
|
||||
%DefineOrRedefineDataProperty(this, 'stack', v, NONE);
|
||||
// Release the stack trace that is stored as hidden property, if exists.
|
||||
%SetOverflowedStackTrace(this, void 0);
|
||||
}
|
||||
|
||||
%DefineOrRedefineAccessorProperty(
|
||||
|
@ -13206,49 +13206,19 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_CollectStackTrace) {
|
||||
}
|
||||
|
||||
|
||||
// Mark a function to recognize when called after GC to format the stack trace.
|
||||
RUNTIME_FUNCTION(MaybeObject*, Runtime_MarkOneShotGetter) {
|
||||
ASSERT_EQ(args.length(), 1);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSFunction, fun, 0);
|
||||
HandleScope scope(isolate);
|
||||
Handle<String> key = isolate->factory()->hidden_stack_trace_symbol();
|
||||
JSObject::SetHiddenProperty(fun, key, key);
|
||||
return *fun;
|
||||
}
|
||||
|
||||
|
||||
// Retrieve the stack trace. This could be the raw stack trace collected
|
||||
// on stack overflow or the already formatted stack trace string.
|
||||
RUNTIME_FUNCTION(MaybeObject*, Runtime_GetOverflowedStackTrace) {
|
||||
HandleScope scope(isolate);
|
||||
// Retrieve the raw stack trace collected on stack overflow and delete
|
||||
// it since it is used only once to avoid keeping it alive.
|
||||
RUNTIME_FUNCTION(MaybeObject*, Runtime_GetOverflowedRawStackTrace) {
|
||||
ASSERT_EQ(args.length(), 1);
|
||||
CONVERT_ARG_CHECKED(JSObject, error_object, 0);
|
||||
String* key = isolate->heap()->hidden_stack_trace_symbol();
|
||||
Object* result = error_object->GetHiddenProperty(key);
|
||||
RUNTIME_ASSERT(result->IsJSArray() ||
|
||||
result->IsString() ||
|
||||
result->IsUndefined());
|
||||
RUNTIME_ASSERT(result->IsJSArray() || result->IsUndefined());
|
||||
error_object->DeleteHiddenProperty(key);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// Set or clear the stack trace attached to an stack overflow error object.
|
||||
RUNTIME_FUNCTION(MaybeObject*, Runtime_SetOverflowedStackTrace) {
|
||||
HandleScope scope(isolate);
|
||||
ASSERT_EQ(args.length(), 2);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSObject, error_object, 0);
|
||||
CONVERT_ARG_HANDLE_CHECKED(HeapObject, value, 1);
|
||||
Handle<String> key = isolate->factory()->hidden_stack_trace_symbol();
|
||||
if (value->IsUndefined()) {
|
||||
error_object->DeleteHiddenProperty(*key);
|
||||
} else {
|
||||
RUNTIME_ASSERT(value->IsString());
|
||||
JSObject::SetHiddenProperty(error_object, key, value);
|
||||
}
|
||||
return *error_object;
|
||||
}
|
||||
|
||||
|
||||
// Returns V8 version as a string.
|
||||
RUNTIME_FUNCTION(MaybeObject*, Runtime_GetV8Version) {
|
||||
ASSERT_EQ(args.length(), 0);
|
||||
|
@ -237,9 +237,7 @@ namespace internal {
|
||||
F(FunctionIsBuiltin, 1, 1) \
|
||||
F(GetScript, 1, 1) \
|
||||
F(CollectStackTrace, 3, 1) \
|
||||
F(MarkOneShotGetter, 1, 1) \
|
||||
F(GetOverflowedStackTrace, 1, 1) \
|
||||
F(SetOverflowedStackTrace, 2, 1) \
|
||||
F(GetOverflowedRawStackTrace, 1, 1) \
|
||||
F(GetV8Version, 0, 1) \
|
||||
\
|
||||
F(ClassOf, 1, 1) \
|
||||
|
@ -161,7 +161,6 @@ void DeclarationContext::Check(const char* source,
|
||||
CHECK_EQ(value, catcher.Exception());
|
||||
}
|
||||
}
|
||||
HEAP->CollectAllAvailableGarbage(); // Clean slate for the next test.
|
||||
}
|
||||
|
||||
|
||||
|
@ -2429,7 +2429,11 @@ void ReleaseStackTraceDataTest(const char* source) {
|
||||
CHECK(!resource->IsDisposed());
|
||||
}
|
||||
HEAP->CollectAllAvailableGarbage();
|
||||
// External source is being retained by the stack trace.
|
||||
CHECK(!resource->IsDisposed());
|
||||
|
||||
CompileRun("error.stack;");
|
||||
HEAP->CollectAllAvailableGarbage();
|
||||
// External source has been released.
|
||||
CHECK(resource->IsDisposed());
|
||||
delete resource;
|
||||
|
@ -28,6 +28,3 @@
|
||||
*%(basename)s:31: TypeError: Cannot read property 'x' of undefined
|
||||
undefined.x
|
||||
^
|
||||
TypeError: Cannot read property 'x' of undefined
|
||||
at *%(basename)s:31:10
|
||||
|
||||
|
@ -26,13 +26,12 @@
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Return the stack frames of an Error object.
|
||||
|
||||
Error.prepareStackTrace = function(error, frames) {
|
||||
return frames;
|
||||
}
|
||||
|
||||
Error.prototype.getFrames = function() {
|
||||
Error.prepareStackTrace = function(error, frames) {
|
||||
return frames;
|
||||
}
|
||||
var frames = this.stack;
|
||||
Error.prepareStackTrace = undefined;
|
||||
return frames;
|
||||
}
|
||||
|
||||
|
@ -1,119 +0,0 @@
|
||||
// Copyright 2012 the V8 project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following
|
||||
// disclaimer in the documentation and/or other materials provided
|
||||
// with the distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Flags: --expose-gc --allow-natives-syntax
|
||||
|
||||
var fired = [];
|
||||
for (var i = 0; i < 100; i++) fired[i] = false;
|
||||
|
||||
function getter_function(i) {
|
||||
return %MarkOneShotGetter( function() { fired[i] = true; } );
|
||||
}
|
||||
|
||||
// Error objects that die young.
|
||||
for (var i = 0; i < 100; i++) {
|
||||
var error = new Error();
|
||||
// Replace the getter to observe whether it has been fired,
|
||||
// and disguise it as original getter.
|
||||
var getter = getter_function(i);
|
||||
error.__defineGetter__("stack", getter);
|
||||
|
||||
error = undefined;
|
||||
}
|
||||
|
||||
gc();
|
||||
for (var i = 0; i < 100; i++) {
|
||||
assertFalse(fired[i]);
|
||||
}
|
||||
|
||||
// Error objects that are kept alive.
|
||||
var array = [];
|
||||
for (var i = 0; i < 100; i++) {
|
||||
var error = new Error();
|
||||
var getter = getter_function(i);
|
||||
// Replace the getter to observe whether it has been fired,
|
||||
// and disguise it as original getter.
|
||||
error.__defineGetter__("stack", getter);
|
||||
|
||||
array.push(error);
|
||||
error = undefined;
|
||||
}
|
||||
|
||||
gc();
|
||||
// We don't expect all stack traces to be formatted after only one GC.
|
||||
assertTrue(fired[0]);
|
||||
|
||||
for (var i = 0; i < 10; i++) gc();
|
||||
for (var i = 0; i < 100; i++) assertTrue(fired[i]);
|
||||
|
||||
// Error objects with custom stack getter.
|
||||
var custom_error = new Error();
|
||||
var custom_getter_fired = false;
|
||||
custom_error.__defineGetter__("stack",
|
||||
function() { custom_getter_fired = true; });
|
||||
gc();
|
||||
assertFalse(custom_getter_fired);
|
||||
|
||||
// Check that formatting caused by GC is not somehow observable.
|
||||
var error;
|
||||
|
||||
var obj = { foo: function foo() { throw new Error(); } };
|
||||
|
||||
try {
|
||||
obj.foo();
|
||||
} catch (e) {
|
||||
delete obj.foo;
|
||||
Object.defineProperty(obj, 'foo', {
|
||||
get: function() { assertUnreachable(); }
|
||||
});
|
||||
error = e;
|
||||
}
|
||||
|
||||
gc();
|
||||
|
||||
Object.defineProperty(Array.prototype, '0', {
|
||||
get: function() { assertUnreachable(); }
|
||||
});
|
||||
|
||||
try {
|
||||
throw new Error();
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
|
||||
gc();
|
||||
|
||||
String.prototype.indexOf = function() { assertUnreachable(); };
|
||||
String.prototype.lastIndexOf = function() { assertUnreachable(); };
|
||||
var obj = { method: function() { throw Error(); } };
|
||||
try {
|
||||
obj.method();
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
|
||||
gc();
|
@ -288,42 +288,4 @@ testOmittedBuiltin(function(){ [thrower, 2].sort(function (a,b) {
|
||||
}, "QuickSort");
|
||||
|
||||
// Omitted because ADD from runtime.js is non-native builtin.
|
||||
testOmittedBuiltin(function(){ thrower + 2; }, "ADD");
|
||||
|
||||
var error = new Error();
|
||||
error.toString = function() { assertUnreachable(); };
|
||||
error.stack;
|
||||
|
||||
error = new Error();
|
||||
error.name = { toString: function() { assertUnreachable(); }};
|
||||
error.message = { toString: function() { assertUnreachable(); }};
|
||||
error.stack;
|
||||
|
||||
error = new Error();
|
||||
Array.prototype.push = function(x) { assertUnreachable(); };
|
||||
Array.prototype.join = function(x) { assertUnreachable(); };
|
||||
error.stack;
|
||||
|
||||
var fired = false;
|
||||
error = new Error({ toString: function() { fired = true; } });
|
||||
assertTrue(fired);
|
||||
error.stack;
|
||||
assertTrue(fired);
|
||||
|
||||
// Check that throwing exception in a custom stack trace formatting function
|
||||
// does not lead to recursion.
|
||||
Error.prepareStackTrace = function() { throw new Error("abc"); };
|
||||
var message;
|
||||
try {
|
||||
throw new Error();
|
||||
} catch (e) {
|
||||
message = e.message;
|
||||
}
|
||||
|
||||
assertEquals("abc", message);
|
||||
|
||||
// Test that modifying Error.prepareStackTrace by itself works.
|
||||
Error.prepareStackTrace = function() { Error.prepareStackTrace = "custom"; };
|
||||
new Error();
|
||||
|
||||
assertEquals("custom", Error.prepareStackTrace);
|
||||
testOmittedBuiltin(function(){ thrower + 2; }, "ADD");
|
Loading…
Reference in New Issue
Block a user