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:
yangguo@chromium.org 2012-12-27 13:12:27 +00:00
parent 074038058a
commit 2f821f1ed9
18 changed files with 59 additions and 487 deletions

View File

@ -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>();

View File

@ -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

View File

@ -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());
}

View File

@ -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;

View File

@ -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_;

View File

@ -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;
}

View File

@ -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]);

View File

@ -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;

View File

@ -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(

View File

@ -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(

View File

@ -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);

View File

@ -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) \

View File

@ -161,7 +161,6 @@ void DeclarationContext::Check(const char* source,
CHECK_EQ(value, catcher.Exception());
}
}
HEAP->CollectAllAvailableGarbage(); // Clean slate for the next test.
}

View File

@ -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;

View File

@ -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

View File

@ -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;
}

View File

@ -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();

View File

@ -289,41 +289,3 @@ testOmittedBuiltin(function(){ [thrower, 2].sort(function (a,b) {
// 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);