From 048a1b8b9e5184f959ce924e76c61f8cd9e91089 Mon Sep 17 00:00:00 2001 From: Ryan Date: Fri, 21 Aug 2009 13:13:04 +0200 Subject: [PATCH] Upgrade v8 to 1.3.6 --- deps/v8/ChangeLog | 16 ++ deps/v8/include/v8.h | 63 ++++++ deps/v8/src/api.cc | 45 +++- deps/v8/src/arm/codegen-arm.cc | 64 +++--- deps/v8/src/codegen.h | 7 +- deps/v8/src/execution.cc | 20 +- deps/v8/src/execution.h | 11 +- deps/v8/src/global-handles.cc | 36 +++- deps/v8/src/heap.cc | 19 +- deps/v8/src/heap.h | 26 ++- deps/v8/src/ia32/codegen-ia32.cc | 54 +++-- deps/v8/src/mark-compact.cc | 3 +- deps/v8/src/mark-compact.h | 10 + deps/v8/src/messages.cc | 6 +- deps/v8/src/objects-debug.cc | 3 +- deps/v8/src/objects-inl.h | 1 + deps/v8/src/scanner.cc | 3 +- deps/v8/src/scanner.h | 4 +- deps/v8/src/spaces.cc | 4 +- deps/v8/src/third_party/dtoa/dtoa.c | 2 +- deps/v8/src/top.cc | 61 ++++-- deps/v8/src/top.h | 12 +- deps/v8/src/v8.cc | 1 + deps/v8/src/v8threads.cc | 21 +- deps/v8/src/v8threads.h | 9 + deps/v8/src/version.cc | 2 +- deps/v8/src/x64/codegen-x64.cc | 64 +++--- deps/v8/test/cctest/SConscript | 1 + deps/v8/test/cctest/test-api.cc | 78 ++++++- .../v8/test/cctest/test-thread-termination.cc | 195 ++++++++++++++++++ deps/v8/test/cctest/test-threads.cc | 2 - deps/v8/test/mozilla/mozilla.status | 6 +- deps/v8/tools/gyp/v8.gyp | 22 +- 33 files changed, 726 insertions(+), 145 deletions(-) create mode 100644 deps/v8/test/cctest/test-thread-termination.cc diff --git a/deps/v8/ChangeLog b/deps/v8/ChangeLog index c59661d366..ff39f04e36 100644 --- a/deps/v8/ChangeLog +++ b/deps/v8/ChangeLog @@ -1,3 +1,19 @@ +2009-08-21: Version 1.3.6 + + Add support for forceful termination of JavaScript execution. + + Add low memory notification to the API. The embedding host can signal + a low memory situation to V8. + + Changed the handling of global handles (persistent handles in the API + sense) to avoid issues regarding allocation of new global handles + during weak handle callbacks. + + Changed the growth policy of the young space. + + Fixed a GC issue introduced in version 1.3.5. + + 2009-08-19: Version 1.3.5 Optimize initialization of some arrays in the builtins. diff --git a/deps/v8/include/v8.h b/deps/v8/include/v8.h index a0cc4ea8b0..c7cc3154ab 100644 --- a/deps/v8/include/v8.h +++ b/deps/v8/include/v8.h @@ -2223,6 +2223,47 @@ class V8EXPORT V8 { */ static int GetLogLines(int from_pos, char* dest_buf, int max_size); + /** + * Retrieve the V8 thread id of the calling thread. + * + * The thread id for a thread should only be retrieved after the V8 + * lock has been acquired with a Locker object with that thread. + */ + static int GetCurrentThreadId(); + + /** + * Forcefully terminate execution of a JavaScript thread. This can + * be used to terminate long-running scripts. + * + * TerminateExecution should only be called when then V8 lock has + * been acquired with a Locker object. Therefore, in order to be + * able to terminate long-running threads, preemption must be + * enabled to allow the user of TerminateExecution to acquire the + * lock. + * + * The termination is achieved by throwing an exception that is + * uncatchable by JavaScript exception handlers. Termination + * exceptions act as if they were caught by a C++ TryCatch exception + * handlers. If forceful termination is used, any C++ TryCatch + * exception handler that catches an exception should check if that + * exception is a termination exception and immediately return if + * that is the case. Returning immediately in that case will + * continue the propagation of the termination exception if needed. + * + * The thread id passed to TerminateExecution must have been + * obtained by calling GetCurrentThreadId on the thread in question. + * + * \param thread_id The thread id of the thread to terminate. + */ + static void TerminateExecution(int thread_id); + + /** + * Forcefully terminate the current thread of JavaScript execution. + * + * This method can be used by any thread even if that thread has not + * acquired the V8 lock with a Locker object. + */ + static void TerminateExecution(); /** * Releases any resources used by v8 and stops any utility threads @@ -2243,6 +2284,12 @@ class V8EXPORT V8 { */ static void IdleNotification(bool is_high_priority); + /** + * Optional notification that the system is running low on memory. + * V8 uses these notifications to attempt to free memory. + */ + static void LowMemoryNotification(); + private: V8(); @@ -2281,6 +2328,21 @@ class V8EXPORT TryCatch { */ bool HasCaught() const; + /** + * For certain types of exceptions, it makes no sense to continue + * execution. + * + * Currently, the only type of exception that can be caught by a + * TryCatch handler and for which it does not make sense to continue + * is termination exception. Such exceptions are thrown when the + * TerminateExecution methods are called to terminate a long-running + * script. + * + * If CanContinue returns false, the correct action is to perform + * any C++ cleanup needed and then return. + */ + bool CanContinue() const; + /** * Returns the exception caught by this try/catch block. If no exception has * been caught an empty handle is returned. @@ -2337,6 +2399,7 @@ class V8EXPORT TryCatch { void* exception_; void* message_; bool is_verbose_; + bool can_continue_; bool capture_message_; void* js_handler_; }; diff --git a/deps/v8/src/api.cc b/deps/v8/src/api.cc index 88cf2bcde0..7d97fc658c 100644 --- a/deps/v8/src/api.cc +++ b/deps/v8/src/api.cc @@ -75,7 +75,7 @@ namespace v8 { i::V8::FatalProcessOutOfMemory(NULL); \ } \ bool call_depth_is_zero = thread_local.CallDepthIsZero(); \ - i::Top::optional_reschedule_exception(call_depth_is_zero); \ + i::Top::OptionalRescheduleException(call_depth_is_zero, false); \ return value; \ } \ } while (false) @@ -1208,6 +1208,11 @@ bool v8::TryCatch::HasCaught() const { } +bool v8::TryCatch::CanContinue() const { + return can_continue_; +} + + v8::Local v8::TryCatch::Exception() const { if (HasCaught()) { // Check for out of memory exception. @@ -2599,10 +2604,18 @@ bool v8::V8::Dispose() { } -void v8::V8::IdleNotification(bool is_high_priority) { +void v8::V8::IdleNotification(bool is_high_priority) { i::V8::IdleNotification(is_high_priority); } + +void v8::V8::LowMemoryNotification() { +#if defined(ANDROID) + i::Heap::CollectAllGarbage(true); +#endif +} + + const char* v8::V8::GetVersion() { static v8::internal::EmbeddedVector buffer; v8::internal::Version::GetString(buffer); @@ -3354,6 +3367,34 @@ int V8::GetLogLines(int from_pos, char* dest_buf, int max_size) { return 0; } + +int V8::GetCurrentThreadId() { + API_ENTRY_CHECK("V8::GetCurrentThreadId()"); + EnsureInitialized("V8::GetCurrentThreadId()"); + return i::Top::thread_id(); +} + + +void V8::TerminateExecution(int thread_id) { + if (!i::V8::IsRunning()) return; + API_ENTRY_CHECK("V8::GetCurrentThreadId()"); + // If the thread_id identifies the current thread just terminate + // execution right away. Otherwise, ask the thread manager to + // terminate the thread with the given id if any. + if (thread_id == i::Top::thread_id()) { + i::StackGuard::TerminateExecution(); + } else { + i::ThreadManager::TerminateExecution(thread_id); + } +} + + +void V8::TerminateExecution() { + if (!i::V8::IsRunning()) return; + i::StackGuard::TerminateExecution(); +} + + String::Utf8Value::Utf8Value(v8::Handle obj) { EnsureInitialized("v8::String::Utf8Value::Utf8Value()"); if (obj.IsEmpty()) { diff --git a/deps/v8/src/arm/codegen-arm.cc b/deps/v8/src/arm/codegen-arm.cc index 40b0ac8160..71ffaa2419 100644 --- a/deps/v8/src/arm/codegen-arm.cc +++ b/deps/v8/src/arm/codegen-arm.cc @@ -5701,7 +5701,8 @@ void CEntryStub::GenerateThrowTOS(MacroAssembler* masm) { } -void CEntryStub::GenerateThrowOutOfMemory(MacroAssembler* masm) { +void CEntryStub::GenerateThrowUncatchable(MacroAssembler* masm, + UncatchableExceptionType type) { // Adjust this code if not the case. ASSERT(StackHandlerConstants::kSize == 4 * kPointerSize); @@ -5725,20 +5726,22 @@ void CEntryStub::GenerateThrowOutOfMemory(MacroAssembler* masm) { // Set the top handler address to next handler past the current ENTRY handler. ASSERT(StackHandlerConstants::kNextOffset == 0); - __ pop(r0); - __ str(r0, MemOperand(r3)); - - // Set external caught exception to false. - ExternalReference external_caught(Top::k_external_caught_exception_address); - __ mov(r0, Operand(false)); - __ mov(r2, Operand(external_caught)); - __ str(r0, MemOperand(r2)); + __ pop(r2); + __ str(r2, MemOperand(r3)); - // Set pending exception and r0 to out of memory exception. - Failure* out_of_memory = Failure::OutOfMemoryException(); - __ mov(r0, Operand(reinterpret_cast(out_of_memory))); - __ mov(r2, Operand(ExternalReference(Top::k_pending_exception_address))); - __ str(r0, MemOperand(r2)); + if (type == OUT_OF_MEMORY) { + // Set external caught exception to false. + ExternalReference external_caught(Top::k_external_caught_exception_address); + __ mov(r0, Operand(false)); + __ mov(r2, Operand(external_caught)); + __ str(r0, MemOperand(r2)); + + // Set pending exception and r0 to out of memory exception. + Failure* out_of_memory = Failure::OutOfMemoryException(); + __ mov(r0, Operand(reinterpret_cast(out_of_memory))); + __ mov(r2, Operand(ExternalReference(Top::k_pending_exception_address))); + __ str(r0, MemOperand(r2)); + } // Stack layout at this point. See also StackHandlerConstants. // sp -> state (ENTRY) @@ -5768,6 +5771,7 @@ void CEntryStub::GenerateThrowOutOfMemory(MacroAssembler* masm) { void CEntryStub::GenerateCore(MacroAssembler* masm, Label* throw_normal_exception, + Label* throw_termination_exception, Label* throw_out_of_memory_exception, StackFrame::Type frame_type, bool do_gc, @@ -5838,10 +5842,10 @@ void CEntryStub::GenerateCore(MacroAssembler* masm, __ tst(r0, Operand(((1 << kFailureTypeTagSize) - 1) << kFailureTagSize)); __ b(eq, &retry); - Label continue_exception; - // If the returned failure is EXCEPTION then promote Top::pending_exception(). - __ cmp(r0, Operand(reinterpret_cast(Failure::Exception()))); - __ b(ne, &continue_exception); + // Special handling of out of memory exceptions. + Failure* out_of_memory = Failure::OutOfMemoryException(); + __ cmp(r0, Operand(reinterpret_cast(out_of_memory))); + __ b(eq, throw_out_of_memory_exception); // Retrieve the pending exception and clear the variable. __ mov(ip, Operand(ExternalReference::the_hole_value_location())); @@ -5850,11 +5854,10 @@ void CEntryStub::GenerateCore(MacroAssembler* masm, __ ldr(r0, MemOperand(ip)); __ str(r3, MemOperand(ip)); - __ bind(&continue_exception); - // Special handling of out of memory exception. - Failure* out_of_memory = Failure::OutOfMemoryException(); - __ cmp(r0, Operand(reinterpret_cast(out_of_memory))); - __ b(eq, throw_out_of_memory_exception); + // Special handling of termination exceptions which are uncatchable + // by javascript code. + __ cmp(r0, Operand(Factory::termination_exception())); + __ b(eq, throw_termination_exception); // Handle normal exception. __ jmp(throw_normal_exception); @@ -5887,11 +5890,14 @@ void CEntryStub::GenerateBody(MacroAssembler* masm, bool is_debug_break) { // r5: pointer to builtin function (C callee-saved) // r6: pointer to first argument (C callee-saved) - Label throw_out_of_memory_exception; Label throw_normal_exception; + Label throw_termination_exception; + Label throw_out_of_memory_exception; // Call into the runtime system. - GenerateCore(masm, &throw_normal_exception, + GenerateCore(masm, + &throw_normal_exception, + &throw_termination_exception, &throw_out_of_memory_exception, frame_type, false, @@ -5900,6 +5906,7 @@ void CEntryStub::GenerateBody(MacroAssembler* masm, bool is_debug_break) { // Do space-specific GC and retry runtime call. GenerateCore(masm, &throw_normal_exception, + &throw_termination_exception, &throw_out_of_memory_exception, frame_type, true, @@ -5910,14 +5917,17 @@ void CEntryStub::GenerateBody(MacroAssembler* masm, bool is_debug_break) { __ mov(r0, Operand(reinterpret_cast(failure))); GenerateCore(masm, &throw_normal_exception, + &throw_termination_exception, &throw_out_of_memory_exception, frame_type, true, true); __ bind(&throw_out_of_memory_exception); - GenerateThrowOutOfMemory(masm); - // control flow for generated will not return. + GenerateThrowUncatchable(masm, OUT_OF_MEMORY); + + __ bind(&throw_termination_exception); + GenerateThrowUncatchable(masm, TERMINATION); __ bind(&throw_normal_exception); GenerateThrowTOS(masm); diff --git a/deps/v8/src/codegen.h b/deps/v8/src/codegen.h index 243d87ca64..d6967b7aff 100644 --- a/deps/v8/src/codegen.h +++ b/deps/v8/src/codegen.h @@ -70,6 +70,9 @@ // Mode to overwrite BinaryExpression values. enum OverwriteMode { NO_OVERWRITE, OVERWRITE_LEFT, OVERWRITE_RIGHT }; +// Types of uncatchable exceptions. +enum UncatchableExceptionType { OUT_OF_MEMORY, TERMINATION }; + #if V8_TARGET_ARCH_IA32 #include "ia32/codegen-ia32.h" @@ -291,12 +294,14 @@ class CEntryStub : public CodeStub { void GenerateBody(MacroAssembler* masm, bool is_debug_break); void GenerateCore(MacroAssembler* masm, Label* throw_normal_exception, + Label* throw_termination_exception, Label* throw_out_of_memory_exception, StackFrame::Type frame_type, bool do_gc, bool always_allocate_scope); void GenerateThrowTOS(MacroAssembler* masm); - void GenerateThrowOutOfMemory(MacroAssembler* masm); + void GenerateThrowUncatchable(MacroAssembler* masm, + UncatchableExceptionType type); private: Major MajorKey() { return CEntry; } diff --git a/deps/v8/src/execution.cc b/deps/v8/src/execution.cc index 9080f5eaaa..0ad55bd558 100644 --- a/deps/v8/src/execution.cc +++ b/deps/v8/src/execution.cc @@ -156,7 +156,8 @@ Handle Execution::TryCall(Handle func, ASSERT(catcher.HasCaught()); ASSERT(Top::has_pending_exception()); ASSERT(Top::external_caught_exception()); - Top::optional_reschedule_exception(true); + bool is_bottom_call = HandleScopeImplementer::instance()->CallDepthIsZero(); + Top::OptionalRescheduleException(is_bottom_call, true); result = v8::Utils::OpenHandle(*catcher.Exception()); } @@ -328,6 +329,19 @@ void StackGuard::Preempt() { } +bool StackGuard::IsTerminateExecution() { + ExecutionAccess access; + return thread_local_.interrupt_flags_ & TERMINATE; +} + + +void StackGuard::TerminateExecution() { + ExecutionAccess access; + thread_local_.interrupt_flags_ |= TERMINATE; + set_limits(kInterruptLimit, access); +} + + #ifdef ENABLE_DEBUGGER_SUPPORT bool StackGuard::IsDebugBreak() { ExecutionAccess access; @@ -638,6 +652,10 @@ Object* Execution::HandleStackGuardInterrupt() { } #endif if (StackGuard::IsPreempted()) RuntimePreempt(); + if (StackGuard::IsTerminateExecution()) { + StackGuard::Continue(TERMINATE); + return Top::TerminateExecution(); + } if (StackGuard::IsInterrupted()) { // interrupt StackGuard::Continue(INTERRUPT); diff --git a/deps/v8/src/execution.h b/deps/v8/src/execution.h index fba696e72c..456cbe74d6 100644 --- a/deps/v8/src/execution.h +++ b/deps/v8/src/execution.h @@ -37,7 +37,8 @@ enum InterruptFlag { INTERRUPT = 1 << 0, DEBUGBREAK = 1 << 1, DEBUGCOMMAND = 1 << 2, - PREEMPT = 1 << 3 + PREEMPT = 1 << 3, + TERMINATE = 1 << 4 }; class Execution : public AllStatic { @@ -164,13 +165,15 @@ class StackGuard BASE_EMBEDDED { static void Preempt(); static bool IsInterrupted(); static void Interrupt(); - static void Continue(InterruptFlag after_what); + static bool IsTerminateExecution(); + static void TerminateExecution(); #ifdef ENABLE_DEBUGGER_SUPPORT - static void DebugBreak(); - static void DebugCommand(); static bool IsDebugBreak(); + static void DebugBreak(); static bool IsDebugCommand(); + static void DebugCommand(); #endif + static void Continue(InterruptFlag after_what); private: // You should hold the ExecutionAccess lock when calling this method. diff --git a/deps/v8/src/global-handles.cc b/deps/v8/src/global-handles.cc index ed4e262e62..e51c4aadf1 100644 --- a/deps/v8/src/global-handles.cc +++ b/deps/v8/src/global-handles.cc @@ -144,8 +144,8 @@ class GlobalHandles::Node : public Malloced { // Returns the callback for this weak handle. WeakReferenceCallback callback() { return callback_; } - void PostGarbageCollectionProcessing() { - if (state_ != Node::PENDING) return; + bool PostGarbageCollectionProcessing() { + if (state_ != Node::PENDING) return false; LOG(HandleEvent("GlobalHandle::Processing", handle().location())); void* par = parameter(); state_ = NEAR_DEATH; @@ -153,14 +153,19 @@ class GlobalHandles::Node : public Malloced { // The callback function is resolved as late as possible to preserve old // behavior. WeakReferenceCallback func = callback(); - if (func != NULL) { - v8::Persistent object = ToApi(handle()); - { - // Leaving V8. - VMState state(EXTERNAL); - func(object, par); - } + if (func == NULL) return false; + + v8::Persistent object = ToApi(handle()); + { + // Forbid reuse of destroyed nodes as they might be already deallocated. + // It's fine though to reuse nodes that were destroyed in weak callback + // as those cannot be deallocated until we are back from the callback. + set_first_free(NULL); + // Leaving V8. + VMState state(EXTERNAL); + func(object, par); } + return true; } // Place the handle address first to avoid offset computation. @@ -271,15 +276,26 @@ void GlobalHandles::IdentifyWeakHandles(WeakSlotCallback f) { } +int post_gc_processing_count = 0; + void GlobalHandles::PostGarbageCollectionProcessing() { // Process weak global handle callbacks. This must be done after the // GC is completely done, because the callbacks may invoke arbitrary // API functions. // At the same time deallocate all DESTROYED nodes ASSERT(Heap::gc_state() == Heap::NOT_IN_GC); + const int initial_post_gc_processing_count = ++post_gc_processing_count; Node** p = &head_; while (*p != NULL) { - (*p)->PostGarbageCollectionProcessing(); + if ((*p)->PostGarbageCollectionProcessing()) { + if (initial_post_gc_processing_count != post_gc_processing_count) { + // Weak callback triggered another GC and another round of + // PostGarbageCollection processing. The current node might + // have been deleted in that round, so we need to bail out (or + // restart the processing). + break; + } + } if ((*p)->state_ == Node::DESTROYED) { // Delete the link. Node* node = *p; diff --git a/deps/v8/src/heap.cc b/deps/v8/src/heap.cc index 7d6e442b87..ad25f93efb 100644 --- a/deps/v8/src/heap.cc +++ b/deps/v8/src/heap.cc @@ -73,7 +73,7 @@ int Heap::amount_of_external_allocated_memory_at_last_global_gc_ = 0; int Heap::semispace_size_ = 512*KB; int Heap::old_generation_size_ = 128*MB; int Heap::initial_semispace_size_ = 128*KB; -#elseif defined(V8_TARGET_ARCH_X64) +#elif defined(V8_TARGET_ARCH_X64) int Heap::semispace_size_ = 8*MB; int Heap::old_generation_size_ = 1*GB; int Heap::initial_semispace_size_ = 1*MB; @@ -315,11 +315,13 @@ void Heap::GarbageCollectionEpilogue() { } -void Heap::CollectAllGarbage() { +void Heap::CollectAllGarbage(bool force_compaction) { // Since we are ignoring the return value, the exact choice of space does // not matter, so long as we do not specify NEW_SPACE, which would not // cause a full GC. + MarkCompactCollector::SetForceCompaction(force_compaction); CollectGarbage(0, OLD_POINTER_SPACE); + MarkCompactCollector::SetForceCompaction(false); } @@ -485,7 +487,10 @@ void Heap::PerformGarbageCollection(AllocationSpace space, void Heap::PostGarbageCollectionProcessing() { // Process weak handles post gc. - GlobalHandles::PostGarbageCollectionProcessing(); + { + DisableAssertNoAllocation allow_allocation; + GlobalHandles::PostGarbageCollectionProcessing(); + } // Update flat string readers. FlatStringReader::PostGarbageCollectionProcessing(); } @@ -1412,6 +1417,9 @@ bool Heap::CreateInitialObjects() { if (obj->IsFailure()) return false; set_no_interceptor_result_sentinel(obj); + obj = CreateOddball(oddball_map(), "termination_exception", Smi::FromInt(-3)); + if (obj->IsFailure()) return false; + set_termination_exception(obj); // Allocate the empty string. obj = AllocateRawAsciiString(0, TENURED); @@ -2084,8 +2092,9 @@ Object* Heap::AllocateInitialMap(JSFunction* fun) { if (count > in_object_properties) { count = in_object_properties; } - DescriptorArray* descriptors = *Factory::NewDescriptorArray(count); - if (descriptors->IsFailure()) return descriptors; + Object* descriptors_obj = DescriptorArray::Allocate(count); + if (descriptors_obj->IsFailure()) return descriptors_obj; + DescriptorArray* descriptors = DescriptorArray::cast(descriptors_obj); for (int i = 0; i < count; i++) { String* name = fun->shared()->GetThisPropertyAssignmentName(i); ASSERT(name->IsSymbol()); diff --git a/deps/v8/src/heap.h b/deps/v8/src/heap.h index ec1e21af5b..ac6f5be511 100644 --- a/deps/v8/src/heap.h +++ b/deps/v8/src/heap.h @@ -111,6 +111,7 @@ namespace internal { V(Object, nan_value, NanValue) \ V(Object, undefined_value, UndefinedValue) \ V(Object, no_interceptor_result_sentinel, NoInterceptorResultSentinel) \ + V(Object, termination_exception, TerminationException) \ V(Object, minus_zero_value, MinusZeroValue) \ V(Object, null_value, NullValue) \ V(Object, true_value, TrueValue) \ @@ -626,8 +627,9 @@ class Heap : public AllStatic { // Returns whether required_space bytes are available after the collection. static bool CollectGarbage(int required_space, AllocationSpace space); - // Performs a full garbage collection. - static void CollectAllGarbage(); + // Performs a full garbage collection. Force compaction if the + // parameter is true. + static void CollectAllGarbage(bool force_compaction = false); // Performs a full garbage collection if a context has been disposed // since the last time the check was performed. @@ -1386,6 +1388,20 @@ class AssertNoAllocation { bool old_state_; }; +class DisableAssertNoAllocation { + public: + DisableAssertNoAllocation() { + old_state_ = Heap::allow_allocation(true); + } + + ~DisableAssertNoAllocation() { + Heap::allow_allocation(old_state_); + } + + private: + bool old_state_; +}; + #else // ndef DEBUG class AssertNoAllocation { @@ -1394,6 +1410,12 @@ class AssertNoAllocation { ~AssertNoAllocation() { } }; +class DisableAssertNoAllocation { + public: + DisableAssertNoAllocation() { } + ~DisableAssertNoAllocation() { } +}; + #endif #ifdef ENABLE_LOGGING_AND_PROFILING diff --git a/deps/v8/src/ia32/codegen-ia32.cc b/deps/v8/src/ia32/codegen-ia32.cc index b8b3db64bd..bf1f81b137 100644 --- a/deps/v8/src/ia32/codegen-ia32.cc +++ b/deps/v8/src/ia32/codegen-ia32.cc @@ -7505,6 +7505,7 @@ void CEntryStub::GenerateThrowTOS(MacroAssembler* masm) { void CEntryStub::GenerateCore(MacroAssembler* masm, Label* throw_normal_exception, + Label* throw_termination_exception, Label* throw_out_of_memory_exception, StackFrame::Type frame_type, bool do_gc, @@ -7568,10 +7569,9 @@ void CEntryStub::GenerateCore(MacroAssembler* masm, __ test(eax, Immediate(((1 << kFailureTypeTagSize) - 1) << kFailureTagSize)); __ j(zero, &retry, taken); - Label continue_exception; - // If the returned failure is EXCEPTION then promote Top::pending_exception(). - __ cmp(eax, reinterpret_cast(Failure::Exception())); - __ j(not_equal, &continue_exception); + // Special handling of out of memory exceptions. + __ cmp(eax, reinterpret_cast(Failure::OutOfMemoryException())); + __ j(equal, throw_out_of_memory_exception); // Retrieve the pending exception and clear the variable. ExternalReference pending_exception_address(Top::k_pending_exception_address); @@ -7580,10 +7580,10 @@ void CEntryStub::GenerateCore(MacroAssembler* masm, Operand::StaticVariable(ExternalReference::the_hole_value_location())); __ mov(Operand::StaticVariable(pending_exception_address), edx); - __ bind(&continue_exception); - // Special handling of out of memory exception. - __ cmp(eax, reinterpret_cast(Failure::OutOfMemoryException())); - __ j(equal, throw_out_of_memory_exception); + // Special handling of termination exceptions which are uncatchable + // by javascript code. + __ cmp(eax, Factory::termination_exception()); + __ j(equal, throw_termination_exception); // Handle normal exception. __ jmp(throw_normal_exception); @@ -7593,7 +7593,8 @@ void CEntryStub::GenerateCore(MacroAssembler* masm, } -void CEntryStub::GenerateThrowOutOfMemory(MacroAssembler* masm) { +void CEntryStub::GenerateThrowUncatchable(MacroAssembler* masm, + UncatchableExceptionType type) { // Adjust this code if not the case. ASSERT(StackHandlerConstants::kSize == 4 * kPointerSize); @@ -7618,17 +7619,19 @@ void CEntryStub::GenerateThrowOutOfMemory(MacroAssembler* masm) { ASSERT(StackHandlerConstants::kNextOffset == 0); __ pop(Operand::StaticVariable(handler_address)); - // Set external caught exception to false. - ExternalReference external_caught(Top::k_external_caught_exception_address); - __ mov(eax, false); - __ mov(Operand::StaticVariable(external_caught), eax); + if (type == OUT_OF_MEMORY) { + // Set external caught exception to false. + ExternalReference external_caught(Top::k_external_caught_exception_address); + __ mov(eax, false); + __ mov(Operand::StaticVariable(external_caught), eax); - // Set pending exception and eax to out of memory exception. - ExternalReference pending_exception(Top::k_pending_exception_address); - __ mov(eax, reinterpret_cast(Failure::OutOfMemoryException())); - __ mov(Operand::StaticVariable(pending_exception), eax); + // Set pending exception and eax to out of memory exception. + ExternalReference pending_exception(Top::k_pending_exception_address); + __ mov(eax, reinterpret_cast(Failure::OutOfMemoryException())); + __ mov(Operand::StaticVariable(pending_exception), eax); + } - // Clear the context pointer; + // Clear the context pointer. __ xor_(esi, Operand(esi)); // Restore fp from handler and discard handler state. @@ -7667,11 +7670,14 @@ void CEntryStub::GenerateBody(MacroAssembler* masm, bool is_debug_break) { // edi: number of arguments including receiver (C callee-saved) // esi: argv pointer (C callee-saved) - Label throw_out_of_memory_exception; Label throw_normal_exception; + Label throw_termination_exception; + Label throw_out_of_memory_exception; // Call into the runtime system. - GenerateCore(masm, &throw_normal_exception, + GenerateCore(masm, + &throw_normal_exception, + &throw_termination_exception, &throw_out_of_memory_exception, frame_type, false, @@ -7680,6 +7686,7 @@ void CEntryStub::GenerateBody(MacroAssembler* masm, bool is_debug_break) { // Do space-specific GC and retry runtime call. GenerateCore(masm, &throw_normal_exception, + &throw_termination_exception, &throw_out_of_memory_exception, frame_type, true, @@ -7690,14 +7697,17 @@ void CEntryStub::GenerateBody(MacroAssembler* masm, bool is_debug_break) { __ mov(eax, Immediate(reinterpret_cast(failure))); GenerateCore(masm, &throw_normal_exception, + &throw_termination_exception, &throw_out_of_memory_exception, frame_type, true, true); __ bind(&throw_out_of_memory_exception); - GenerateThrowOutOfMemory(masm); - // control flow for generated will not return. + GenerateThrowUncatchable(masm, OUT_OF_MEMORY); + + __ bind(&throw_termination_exception); + GenerateThrowUncatchable(masm, TERMINATION); __ bind(&throw_normal_exception); GenerateThrowTOS(masm); diff --git a/deps/v8/src/mark-compact.cc b/deps/v8/src/mark-compact.cc index 6e823b3d97..10e81ac6a9 100644 --- a/deps/v8/src/mark-compact.cc +++ b/deps/v8/src/mark-compact.cc @@ -39,6 +39,7 @@ namespace internal { // ------------------------------------------------------------------------- // MarkCompactCollector +bool MarkCompactCollector::force_compaction_ = false; bool MarkCompactCollector::compacting_collection_ = false; int MarkCompactCollector::previous_marked_count_ = 0; @@ -110,7 +111,7 @@ void MarkCompactCollector::Prepare(GCTracer* tracer) { #endif ASSERT(!FLAG_always_compact || !FLAG_never_compact); - compacting_collection_ = FLAG_always_compact; + compacting_collection_ = FLAG_always_compact || force_compaction_; // We compact the old generation if it gets too fragmented (ie, we could // recover an expected amount of space by reclaiming the waste and free diff --git a/deps/v8/src/mark-compact.h b/deps/v8/src/mark-compact.h index bd9e4a0282..0bd212e0c1 100644 --- a/deps/v8/src/mark-compact.h +++ b/deps/v8/src/mark-compact.h @@ -75,6 +75,12 @@ class MarkCompactCollector: public AllStatic { // Type of functions to process non-live objects. typedef void (*ProcessNonLiveFunction)(HeapObject* object); + // Set the global force_compaction flag, it must be called before Prepare + // to take effect. + static void SetForceCompaction(bool value) { + force_compaction_ = value; + } + // Prepares for GC by resetting relocation info in old and map spaces and // choosing spaces to compact. static void Prepare(GCTracer* tracer); @@ -117,6 +123,10 @@ class MarkCompactCollector: public AllStatic { // The current stage of the collector. static CollectorState state_; #endif + + // Global flag that forces a compaction. + static bool force_compaction_; + // Global flag indicating whether spaces were compacted on the last GC. static bool compacting_collection_; diff --git a/deps/v8/src/messages.cc b/deps/v8/src/messages.cc index a3fffcb50a..e16b1b2495 100644 --- a/deps/v8/src/messages.cc +++ b/deps/v8/src/messages.cc @@ -147,14 +147,12 @@ Handle MessageHandler::GetMessage(Handle data) { Handle fmt_str = Factory::LookupAsciiSymbol("FormatMessage"); Handle fun = Handle( - JSFunction::cast( - Top::builtins()->GetProperty(*fmt_str))); + JSFunction::cast(Top::builtins()->GetProperty(*fmt_str))); Object** argv[1] = { data.location() }; bool caught_exception; Handle result = - Execution::TryCall(fun, Top::builtins(), 1, argv, - &caught_exception); + Execution::TryCall(fun, Top::builtins(), 1, argv, &caught_exception); if (caught_exception || !result->IsString()) { return Factory::LookupAsciiSymbol(""); diff --git a/deps/v8/src/objects-debug.cc b/deps/v8/src/objects-debug.cc index e97b207bac..f713171174 100644 --- a/deps/v8/src/objects-debug.cc +++ b/deps/v8/src/objects-debug.cc @@ -705,7 +705,8 @@ void Oddball::OddballVerify() { } else { ASSERT(number->IsSmi()); int value = Smi::cast(number)->value(); - ASSERT(value == 0 || value == 1 || value == -1 || value == -2); + ASSERT(value == 0 || value == 1 || value == -1 || + value == -2 || value == -3); } } diff --git a/deps/v8/src/objects-inl.h b/deps/v8/src/objects-inl.h index 7b750b61ea..91aae2f420 100644 --- a/deps/v8/src/objects-inl.h +++ b/deps/v8/src/objects-inl.h @@ -788,6 +788,7 @@ Failure* Failure::Exception() { return Construct(EXCEPTION); } + Failure* Failure::OutOfMemoryException() { return Construct(OUT_OF_MEMORY_EXCEPTION); } diff --git a/deps/v8/src/scanner.cc b/deps/v8/src/scanner.cc index 720dc3536c..3dae414f9d 100644 --- a/deps/v8/src/scanner.cc +++ b/deps/v8/src/scanner.cc @@ -183,7 +183,8 @@ uc32 TwoByteStringUTF16Buffer::Advance() { void TwoByteStringUTF16Buffer::PushBack(uc32 ch) { pos_--; - ASSERT(pos_ >= 0 && raw_data_[pos_] == ch); + ASSERT(pos_ >= Scanner::kCharacterLookaheadBufferSize); + ASSERT(raw_data_[pos_ - Scanner::kCharacterLookaheadBufferSize] == ch); } diff --git a/deps/v8/src/scanner.h b/deps/v8/src/scanner.h index 340da86ed7..a201d0e976 100644 --- a/deps/v8/src/scanner.h +++ b/deps/v8/src/scanner.h @@ -212,6 +212,8 @@ class Scanner { static unibrow::Predicate kIsLineTerminator; static unibrow::Predicate kIsWhiteSpace; + static const int kCharacterLookaheadBufferSize = 1; + private: CharacterStreamUTF16Buffer char_stream_buffer_; TwoByteStringUTF16Buffer two_byte_string_buffer_; @@ -242,8 +244,6 @@ class Scanner { bool has_line_terminator_before_next_; bool is_pre_parsing_; - static const int kCharacterLookaheadBufferSize = 1; - // Literal buffer support void StartLiteral(); void AddChar(uc32 ch); diff --git a/deps/v8/src/spaces.cc b/deps/v8/src/spaces.cc index 9227a87f3c..337f0143bc 100644 --- a/deps/v8/src/spaces.cc +++ b/deps/v8/src/spaces.cc @@ -1079,9 +1079,9 @@ void SemiSpace::TearDown() { bool SemiSpace::Grow() { - // Commit 50% extra space but only up to maximum capacity. + // Double the semispace size but only up to maximum capacity. int maximum_extra = maximum_capacity_ - capacity_; - int extra = Min(RoundUp(capacity_ / 2, OS::AllocateAlignment()), + int extra = Min(RoundUp(capacity_, OS::AllocateAlignment()), maximum_extra); if (!MemoryAllocator::CommitBlock(high(), extra, executable())) { return false; diff --git a/deps/v8/src/third_party/dtoa/dtoa.c b/deps/v8/src/third_party/dtoa/dtoa.c index 5caa96d169..fadc6d1e45 100644 --- a/deps/v8/src/third_party/dtoa/dtoa.c +++ b/deps/v8/src/third_party/dtoa/dtoa.c @@ -1535,7 +1535,7 @@ strtod double aadj, aadj1, adj, rv, rv0; Long L; ULong y, z; - Bigint *bb, *bb1, *bd, *bd0, *bs, *delta; + Bigint *bb = NULL, *bb1, *bd = NULL, *bd0, *bs = NULL, *delta = NULL; #ifdef SET_INEXACT int inexact, oldinexact; #endif diff --git a/deps/v8/src/top.cc b/deps/v8/src/top.cc index 96d4a01e71..550703aa16 100644 --- a/deps/v8/src/top.cc +++ b/deps/v8/src/top.cc @@ -98,6 +98,7 @@ void Top::InitializeThreadLocal() { thread_local_.stack_is_cooked_ = false; thread_local_.try_catch_handler_ = NULL; thread_local_.context_ = NULL; + thread_local_.thread_id_ = ThreadManager::kInvalidId; thread_local_.external_caught_exception_ = false; thread_local_.failed_access_check_callback_ = NULL; clear_pending_exception(); @@ -598,6 +599,12 @@ Failure* Top::StackOverflow() { } +Failure* Top::TerminateExecution() { + DoThrow(Heap::termination_exception(), NULL, NULL); + return Failure::Exception(); +} + + Failure* Top::Throw(Object* exception, MessageLocation* location) { DoThrow(exception, location, NULL); return Failure::Exception(); @@ -694,7 +701,8 @@ void Top::ReportUncaughtException(Handle exception, } -bool Top::ShouldReportException(bool* is_caught_externally) { +bool Top::ShouldReturnException(bool* is_caught_externally, + bool catchable_by_javascript) { // Find the top-most try-catch handler. StackHandler* handler = StackHandler::FromAddress(Top::handler(Top::GetCurrentThread())); @@ -712,7 +720,8 @@ bool Top::ShouldReportException(bool* is_caught_externally) { // // See comments in RegisterTryCatchHandler for details. *is_caught_externally = try_catch != NULL && - (handler == NULL || handler == try_catch->js_handler_); + (handler == NULL || handler == try_catch->js_handler_ || + !catchable_by_javascript); if (*is_caught_externally) { // Only report the exception if the external handler is verbose. @@ -735,12 +744,17 @@ void Top::DoThrow(Object* exception, // Determine reporting and whether the exception is caught externally. bool is_caught_externally = false; bool is_out_of_memory = exception == Failure::OutOfMemoryException(); - bool should_return_exception = ShouldReportException(&is_caught_externally); - bool report_exception = !is_out_of_memory && should_return_exception; + bool is_termination_exception = exception == Heap::termination_exception(); + bool catchable_by_javascript = !is_termination_exception && !is_out_of_memory; + bool should_return_exception = + ShouldReturnException(&is_caught_externally, catchable_by_javascript); + bool report_exception = catchable_by_javascript && should_return_exception; #ifdef ENABLE_DEBUGGER_SUPPORT // Notify debugger of exception. - Debugger::OnException(exception_handle, report_exception); + if (catchable_by_javascript) { + Debugger::OnException(exception_handle, report_exception); + } #endif // Generate the message. @@ -791,14 +805,21 @@ void Top::ReportPendingMessages() { // the global context. Note: We have to mark the global context here // since the GenerateThrowOutOfMemory stub cannot make a RuntimeCall to // set it. + bool external_caught = thread_local_.external_caught_exception_; HandleScope scope; if (thread_local_.pending_exception_ == Failure::OutOfMemoryException()) { context()->mark_out_of_memory(); + } else if (thread_local_.pending_exception_ == + Heap::termination_exception()) { + if (external_caught) { + thread_local_.try_catch_handler_->can_continue_ = false; + thread_local_.try_catch_handler_->exception_ = Heap::null_value(); + } } else { Handle exception(pending_exception()); - bool external_caught = thread_local_.external_caught_exception_; thread_local_.external_caught_exception_ = false; if (external_caught) { + thread_local_.try_catch_handler_->can_continue_ = true; thread_local_.try_catch_handler_->exception_ = thread_local_.pending_exception_; if (!thread_local_.pending_message_obj_->IsTheHole()) { @@ -834,16 +855,30 @@ void Top::TraceException(bool flag) { } -bool Top::optional_reschedule_exception(bool is_bottom_call) { +bool Top::OptionalRescheduleException(bool is_bottom_call, + bool force_clear_catchable) { // Allways reschedule out of memory exceptions. if (!is_out_of_memory()) { - // Never reschedule the exception if this is the bottom call. - bool clear_exception = is_bottom_call; + bool is_termination_exception = + pending_exception() == Heap::termination_exception(); + + // Do not reschedule the exception if this is the bottom call or + // if we are asked to clear catchable exceptions. Termination + // exceptions are not catchable and are only cleared if this is + // the bottom call. + bool clear_exception = is_bottom_call || + (force_clear_catchable && !is_termination_exception); - // If the exception is externally caught, clear it if there are no - // JavaScript frames on the way to the C++ frame that has the - // external handler. - if (thread_local_.external_caught_exception_) { + if (is_termination_exception) { + thread_local_.external_caught_exception_ = false; + if (is_bottom_call) { + clear_pending_exception(); + return false; + } + } else if (thread_local_.external_caught_exception_) { + // If the exception is externally caught, clear it if there are no + // JavaScript frames on the way to the C++ frame that has the + // external handler. ASSERT(thread_local_.try_catch_handler_ != NULL); Address external_handler_address = reinterpret_cast
(thread_local_.try_catch_handler_); diff --git a/deps/v8/src/top.h b/deps/v8/src/top.h index 25242f7ccf..d4d73c21f2 100644 --- a/deps/v8/src/top.h +++ b/deps/v8/src/top.h @@ -46,6 +46,7 @@ class ThreadLocalTop BASE_EMBEDDED { // The context where the current execution method is created and for variable // lookups. Context* context_; + int thread_id_; Object* pending_exception_; bool has_pending_message_; const char* pending_message_; @@ -118,6 +119,10 @@ class Top { thread_local_.save_context_ = save; } + // Access to current thread id. + static int thread_id() { return thread_local_.thread_id_; } + static void set_thread_id(int id) { thread_local_.thread_id_ = id; } + // Interface to pending exception. static Object* pending_exception() { ASSERT(has_pending_exception()); @@ -152,7 +157,8 @@ class Top { // exceptions. If an exception was thrown and not handled by an external // handler the exception is scheduled to be rethrown when we return to running // JavaScript code. If an exception is scheduled true is returned. - static bool optional_reschedule_exception(bool is_bottom_call); + static bool OptionalRescheduleException(bool is_bottom_call, + bool force_clear_catchable); static bool* external_caught_exception_address() { return &thread_local_.external_caught_exception_; @@ -246,7 +252,8 @@ class Top { static void DoThrow(Object* exception, MessageLocation* location, const char* message); - static bool ShouldReportException(bool* is_caught_externally); + static bool ShouldReturnException(bool* is_caught_externally, + bool catchable_by_javascript); static void ReportUncaughtException(Handle exception, MessageLocation* location, Handle stack_trace); @@ -260,6 +267,7 @@ class Top { // Out of resource exception helpers. static Failure* StackOverflow(); + static Failure* TerminateExecution(); // Administration static void Initialize(); diff --git a/deps/v8/src/v8.cc b/deps/v8/src/v8.cc index 00e0e6e6d5..faec986d38 100644 --- a/deps/v8/src/v8.cc +++ b/deps/v8/src/v8.cc @@ -156,6 +156,7 @@ uint32_t V8::Random() { return (hi << 16) + (lo & 0xFFFF); } + void V8::IdleNotification(bool is_high_priority) { if (!FLAG_use_idle_notification) return; // Ignore high priority instances of V8. diff --git a/deps/v8/src/v8threads.cc b/deps/v8/src/v8threads.cc index c5fc9fa7e6..8e0a8be5a5 100644 --- a/deps/v8/src/v8threads.cc +++ b/deps/v8/src/v8threads.cc @@ -151,6 +151,10 @@ bool ThreadManager::RestoreThread() { from = RegExpStack::RestoreStack(from); from = Bootstrapper::RestoreState(from); Thread::SetThreadLocal(thread_state_key, NULL); + if (state->terminate_on_restore()) { + StackGuard::TerminateExecution(); + state->set_terminate_on_restore(false); + } state->set_id(kInvalidId); state->Unlink(); state->LinkInto(ThreadState::FREE_LIST); @@ -188,6 +192,7 @@ ThreadState* ThreadState::in_use_anchor_ = new ThreadState(); ThreadState::ThreadState() : id_(ThreadManager::kInvalidId), + terminate_on_restore_(false), next_(this), previous_(this) { } @@ -317,7 +322,21 @@ int ThreadManager::CurrentId() { void ThreadManager::AssignId() { if (!Thread::HasThreadLocal(thread_id_key)) { - Thread::SetThreadLocalInt(thread_id_key, next_id_++); + ASSERT(Locker::IsLocked()); + int thread_id = next_id_++; + Thread::SetThreadLocalInt(thread_id_key, thread_id); + Top::set_thread_id(thread_id); + } +} + + +void ThreadManager::TerminateExecution(int thread_id) { + for (ThreadState* state = ThreadState::FirstInUse(); + state != NULL; + state = state->Next()) { + if (thread_id == state->id()) { + state->set_terminate_on_restore(true); + } } } diff --git a/deps/v8/src/v8threads.h b/deps/v8/src/v8threads.h index 83f69f060e..3f81f5706f 100644 --- a/deps/v8/src/v8threads.h +++ b/deps/v8/src/v8threads.h @@ -50,6 +50,12 @@ class ThreadState { void set_id(int id) { id_ = id; } int id() { return id_; } + // Should the thread be terminated when it is restored? + bool terminate_on_restore() { return terminate_on_restore_; } + void set_terminate_on_restore(bool terminate_on_restore) { + terminate_on_restore_ = terminate_on_restore; + } + // Get data area for archiving a thread. char* data() { return data_; } private: @@ -58,6 +64,7 @@ class ThreadState { void AllocateSpace(); int id_; + bool terminate_on_restore_; char* data_; ThreadState* next_; ThreadState* previous_; @@ -88,6 +95,8 @@ class ThreadManager : public AllStatic { static int CurrentId(); static void AssignId(); + static void TerminateExecution(int thread_id); + static const int kInvalidId = -1; private: static void EagerlyArchiveThread(); diff --git a/deps/v8/src/version.cc b/deps/v8/src/version.cc index 26b009c78d..69b7510a6d 100644 --- a/deps/v8/src/version.cc +++ b/deps/v8/src/version.cc @@ -34,7 +34,7 @@ // cannot be changed without changing the SCons build script. #define MAJOR_VERSION 1 #define MINOR_VERSION 3 -#define BUILD_NUMBER 5 +#define BUILD_NUMBER 6 #define PATCH_LEVEL 0 #define CANDIDATE_VERSION false diff --git a/deps/v8/src/x64/codegen-x64.cc b/deps/v8/src/x64/codegen-x64.cc index 7fe6ebdb8b..462b9600ee 100644 --- a/deps/v8/src/x64/codegen-x64.cc +++ b/deps/v8/src/x64/codegen-x64.cc @@ -6747,6 +6747,7 @@ void CEntryStub::GenerateThrowTOS(MacroAssembler* masm) { void CEntryStub::GenerateCore(MacroAssembler* masm, Label* throw_normal_exception, + Label* throw_termination_exception, Label* throw_out_of_memory_exception, StackFrame::Type frame_type, bool do_gc, @@ -6819,11 +6820,10 @@ void CEntryStub::GenerateCore(MacroAssembler* masm, __ testl(rax, Immediate(((1 << kFailureTypeTagSize) - 1) << kFailureTagSize)); __ j(zero, &retry); - Label continue_exception; - // If the returned failure is EXCEPTION then promote Top::pending_exception(). - __ movq(kScratchRegister, Failure::Exception(), RelocInfo::NONE); + // Special handling of out of memory exceptions. + __ movq(kScratchRegister, Failure::OutOfMemoryException(), RelocInfo::NONE); __ cmpq(rax, kScratchRegister); - __ j(not_equal, &continue_exception); + __ j(equal, throw_out_of_memory_exception); // Retrieve the pending exception and clear the variable. ExternalReference pending_exception_address(Top::k_pending_exception_address); @@ -6833,11 +6833,10 @@ void CEntryStub::GenerateCore(MacroAssembler* masm, __ movq(rdx, Operand(rdx, 0)); __ movq(Operand(kScratchRegister, 0), rdx); - __ bind(&continue_exception); - // Special handling of out of memory exception. - __ movq(kScratchRegister, Failure::OutOfMemoryException(), RelocInfo::NONE); - __ cmpq(rax, kScratchRegister); - __ j(equal, throw_out_of_memory_exception); + // Special handling of termination exceptions which are uncatchable + // by javascript code. + __ Cmp(rax, Factory::termination_exception()); + __ j(equal, throw_termination_exception); // Handle normal exception. __ jmp(throw_normal_exception); @@ -6847,7 +6846,8 @@ void CEntryStub::GenerateCore(MacroAssembler* masm, } -void CEntryStub::GenerateThrowOutOfMemory(MacroAssembler* masm) { +void CEntryStub::GenerateThrowUncatchable(MacroAssembler* masm, + UncatchableExceptionType type) { // Fetch top stack handler. ExternalReference handler_address(Top::k_handler_address); __ movq(kScratchRegister, handler_address); @@ -6857,30 +6857,32 @@ void CEntryStub::GenerateThrowOutOfMemory(MacroAssembler* masm) { Label loop, done; __ bind(&loop); // Load the type of the current stack handler. - __ cmpq(Operand(rsp, StackHandlerConstants::kStateOffset), - Immediate(StackHandler::ENTRY)); + const int kStateOffset = StackHandlerConstants::kStateOffset; + __ cmpq(Operand(rsp, kStateOffset), Immediate(StackHandler::ENTRY)); __ j(equal, &done); // Fetch the next handler in the list. - ASSERT(StackHandlerConstants::kNextOffset == 0); - __ pop(rsp); + const int kNextOffset = StackHandlerConstants::kNextOffset; + __ movq(rsp, Operand(rsp, kNextOffset)); __ jmp(&loop); __ bind(&done); // Set the top handler address to next handler past the current ENTRY handler. - __ pop(rax); - __ store_rax(handler_address); + __ movq(kScratchRegister, handler_address); + __ pop(Operand(kScratchRegister, 0)); - // Set external caught exception to false. - __ movq(rax, Immediate(false)); - ExternalReference external_caught(Top::k_external_caught_exception_address); - __ store_rax(external_caught); + if (type == OUT_OF_MEMORY) { + // Set external caught exception to false. + ExternalReference external_caught(Top::k_external_caught_exception_address); + __ movq(rax, Immediate(false)); + __ store_rax(external_caught); - // Set pending exception and rax to out of memory exception. - __ movq(rax, Failure::OutOfMemoryException(), RelocInfo::NONE); - ExternalReference pending_exception(Top::k_pending_exception_address); - __ store_rax(pending_exception); + // Set pending exception and rax to out of memory exception. + ExternalReference pending_exception(Top::k_pending_exception_address); + __ movq(rax, Failure::OutOfMemoryException(), RelocInfo::NONE); + __ store_rax(pending_exception); + } - // Clear the context pointer; + // Clear the context pointer. __ xor_(rsi, rsi); // Restore registers from handler. @@ -6956,12 +6958,14 @@ void CEntryStub::GenerateBody(MacroAssembler* masm, bool is_debug_break) { // r14: number of arguments including receiver (C callee-saved). // r15: argv pointer (C callee-saved). - Label throw_out_of_memory_exception; Label throw_normal_exception; + Label throw_termination_exception; + Label throw_out_of_memory_exception; // Call into the runtime system. GenerateCore(masm, &throw_normal_exception, + &throw_termination_exception, &throw_out_of_memory_exception, frame_type, false, @@ -6970,6 +6974,7 @@ void CEntryStub::GenerateBody(MacroAssembler* masm, bool is_debug_break) { // Do space-specific GC and retry runtime call. GenerateCore(masm, &throw_normal_exception, + &throw_termination_exception, &throw_out_of_memory_exception, frame_type, true, @@ -6980,14 +6985,17 @@ void CEntryStub::GenerateBody(MacroAssembler* masm, bool is_debug_break) { __ movq(rax, failure, RelocInfo::NONE); GenerateCore(masm, &throw_normal_exception, + &throw_termination_exception, &throw_out_of_memory_exception, frame_type, true, true); __ bind(&throw_out_of_memory_exception); - GenerateThrowOutOfMemory(masm); - // control flow for generated will not return. + GenerateThrowUncatchable(masm, OUT_OF_MEMORY); + + __ bind(&throw_termination_exception); + GenerateThrowUncatchable(masm, TERMINATION); __ bind(&throw_normal_exception); GenerateThrowTOS(masm); diff --git a/deps/v8/test/cctest/SConscript b/deps/v8/test/cctest/SConscript index 112ecd6faf..fc4e01a425 100644 --- a/deps/v8/test/cctest/SConscript +++ b/deps/v8/test/cctest/SConscript @@ -56,6 +56,7 @@ SOURCES = { 'test-spaces.cc', 'test-strings.cc', 'test-threads.cc', + 'test-thread-termination.cc', 'test-utils.cc', 'test-version.cc' ], diff --git a/deps/v8/test/cctest/test-api.cc b/deps/v8/test/cctest/test-api.cc index e1caba3c83..7b7e1a3be6 100644 --- a/deps/v8/test/cctest/test-api.cc +++ b/deps/v8/test/cctest/test-api.cc @@ -2843,7 +2843,7 @@ TEST(ErrorReporting) { static const char* js_code_causing_huge_string_flattening = "var str = 'X';" - "for (var i = 0; i < 29; i++) {" + "for (var i = 0; i < 30; i++) {" " str = str + str;" "}" "str.match(/X/);"; @@ -6217,6 +6217,58 @@ TEST(DontLeakGlobalObjects) { } +v8::Persistent some_object; +v8::Persistent bad_handle; + +void NewPersistentHandleCallback(v8::Persistent, void*) { + v8::HandleScope scope; + bad_handle = v8::Persistent::New(some_object); +} + + +THREADED_TEST(NewPersistentHandleFromWeakCallback) { + LocalContext context; + + v8::Persistent handle1, handle2; + { + v8::HandleScope scope; + some_object = v8::Persistent::New(v8::Object::New()); + handle1 = v8::Persistent::New(v8::Object::New()); + handle2 = v8::Persistent::New(v8::Object::New()); + } + // Note: order is implementation dependent alas: currently + // global handle nodes are processed by PostGarbageCollectionProcessing + // in reverse allocation order, so if second allocated handle is deleted, + // weak callback of the first handle would be able to 'reallocate' it. + handle1.MakeWeak(NULL, NewPersistentHandleCallback); + handle2.Dispose(); + i::Heap::CollectAllGarbage(); +} + + +v8::Persistent to_be_disposed; + +void DisposeAndForceGcCallback(v8::Persistent handle, void*) { + to_be_disposed.Dispose(); + i::Heap::CollectAllGarbage(); +} + + +THREADED_TEST(DoNotUseDeletedNodesInSecondLevelGc) { + LocalContext context; + + v8::Persistent handle1, handle2; + { + v8::HandleScope scope; + handle1 = v8::Persistent::New(v8::Object::New()); + handle2 = v8::Persistent::New(v8::Object::New()); + } + handle1.MakeWeak(NULL, DisposeAndForceGcCallback); + to_be_disposed = handle2; + i::Heap::CollectAllGarbage(); +} + + THREADED_TEST(CheckForCrossContextObjectLiterals) { v8::V8::Initialize(); @@ -7121,6 +7173,30 @@ THREADED_TEST(MorphCompositeStringTest) { } +TEST(CompileExternalTwoByteSource) { + v8::HandleScope scope; + LocalContext context; + + // This is a very short list of sources, which currently is to check for a + // regression caused by r2703. + const char* ascii_sources[] = { + "0.5", + "-0.5", // This mainly testes PushBack in the Scanner. + "--0.5", // This mainly testes PushBack in the Scanner. + NULL + }; + + // Compile the sources as external two byte strings. + for (int i = 0; ascii_sources[i] != NULL; i++) { + uint16_t* two_byte_string = AsciiToTwoByteString(ascii_sources[i]); + UC16VectorResource uc16_resource( + i::Vector(two_byte_string, strlen(ascii_sources[i]))); + v8::Local source = v8::String::NewExternal(&uc16_resource); + v8::Script::Compile(source); + } +} + + class RegExpStringModificationTest { public: RegExpStringModificationTest() diff --git a/deps/v8/test/cctest/test-thread-termination.cc b/deps/v8/test/cctest/test-thread-termination.cc new file mode 100644 index 0000000000..323be1d77f --- /dev/null +++ b/deps/v8/test/cctest/test-thread-termination.cc @@ -0,0 +1,195 @@ +// Copyright 2009 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. + +#include "v8.h" +#include "platform.h" +#include "cctest.h" + + +v8::internal::Semaphore* semaphore = NULL; + + +v8::Handle Signal(const v8::Arguments& args) { + semaphore->Signal(); + return v8::Undefined(); +} + + +v8::Handle TerminateCurrentThread(const v8::Arguments& args) { + v8::V8::TerminateExecution(); + return v8::Undefined(); +} + + +v8::Handle Fail(const v8::Arguments& args) { + CHECK(false); + return v8::Undefined(); +} + + +v8::Handle Loop(const v8::Arguments& args) { + v8::Handle source = + v8::String::New("try { doloop(); fail(); } catch(e) { fail(); }"); + v8::Script::Compile(source)->Run(); + return v8::Undefined(); +} + + +v8::Handle DoLoop(const v8::Arguments& args) { + v8::TryCatch try_catch; + v8::Script::Compile(v8::String::New("function f() {" + " var term = true;" + " try {" + " while(true) {" + " if (term) terminate();" + " term = false;" + " }" + " fail();" + " } catch(e) {" + " fail();" + " }" + "}" + "f()"))->Run(); + CHECK(try_catch.HasCaught()); + CHECK(try_catch.Exception()->IsNull()); + CHECK(try_catch.Message().IsEmpty()); + CHECK(!try_catch.CanContinue()); + return v8::Undefined(); +} + + +v8::Handle CreateGlobalTemplate( + v8::InvocationCallback terminate) { + v8::Handle global = v8::ObjectTemplate::New(); + global->Set(v8::String::New("terminate"), + v8::FunctionTemplate::New(terminate)); + global->Set(v8::String::New("fail"), v8::FunctionTemplate::New(Fail)); + global->Set(v8::String::New("loop"), v8::FunctionTemplate::New(Loop)); + global->Set(v8::String::New("doloop"), v8::FunctionTemplate::New(DoLoop)); + return global; +} + + +// Test that a single thread of JavaScript execution can terminate +// itself. +TEST(TerminateOnlyV8ThreadFromThreadItself) { + v8::HandleScope scope; + v8::Handle global = + CreateGlobalTemplate(TerminateCurrentThread); + v8::Persistent context = v8::Context::New(NULL, global); + v8::Context::Scope context_scope(context); + // Run a loop that will be infinite if thread termination does not work. + v8::Handle source = + v8::String::New("try { loop(); fail(); } catch(e) { fail(); }"); + v8::Script::Compile(source)->Run(); + // Test that we can run the code again after thread termination. + v8::Script::Compile(source)->Run(); + context.Dispose(); +} + + +class TerminatorThread : public v8::internal::Thread { + void Run() { + semaphore->Wait(); + v8::V8::TerminateExecution(); + } +}; + + +// Test that a single thread of JavaScript execution can be terminated +// from the side by another thread. +TEST(TerminateOnlyV8ThreadFromOtherThread) { + semaphore = v8::internal::OS::CreateSemaphore(0); + TerminatorThread thread; + thread.Start(); + + v8::HandleScope scope; + v8::Handle global = CreateGlobalTemplate(Signal); + v8::Persistent context = v8::Context::New(NULL, global); + v8::Context::Scope context_scope(context); + // Run a loop that will be infinite if thread termination does not work. + v8::Handle source = + v8::String::New("try { loop(); fail(); } catch(e) { fail(); }"); + v8::Script::Compile(source)->Run(); + + thread.Join(); + delete semaphore; + semaphore = NULL; + context.Dispose(); +} + + +class LoopingThread : public v8::internal::Thread { + public: + void Run() { + v8::Locker locker; + v8::HandleScope scope; + v8_thread_id_ = v8::V8::GetCurrentThreadId(); + v8::Handle global = CreateGlobalTemplate(Signal); + v8::Persistent context = v8::Context::New(NULL, global); + v8::Context::Scope context_scope(context); + // Run a loop that will be infinite if thread termination does not work. + v8::Handle source = + v8::String::New("try { loop(); fail(); } catch(e) { fail(); }"); + v8::Script::Compile(source)->Run(); + context.Dispose(); + } + + int GetV8ThreadId() { return v8_thread_id_; } + + private: + int v8_thread_id_; +}; + + +// Test that multiple threads using V8 can be terminated from another +// thread when using Lockers and preemption. +TEST(TerminateMultipleV8Threads) { + { + v8::Locker locker; + v8::V8::Initialize(); + v8::Locker::StartPreemption(1); + semaphore = v8::internal::OS::CreateSemaphore(0); + } + LoopingThread thread1; + thread1.Start(); + LoopingThread thread2; + thread2.Start(); + // Wait until both threads have signaled the semaphore. + semaphore->Wait(); + semaphore->Wait(); + { + v8::Locker locker; + v8::V8::TerminateExecution(thread1.GetV8ThreadId()); + v8::V8::TerminateExecution(thread2.GetV8ThreadId()); + } + thread1.Join(); + thread2.Join(); + + delete semaphore; + semaphore = NULL; +} diff --git a/deps/v8/test/cctest/test-threads.cc b/deps/v8/test/cctest/test-threads.cc index c0d55f250d..f70c4e8c15 100644 --- a/deps/v8/test/cctest/test-threads.cc +++ b/deps/v8/test/cctest/test-threads.cc @@ -50,5 +50,3 @@ TEST(Preemption) { script->Run(); } - - diff --git a/deps/v8/test/mozilla/mozilla.status b/deps/v8/test/mozilla/mozilla.status index a1551dcdd4..399c9c604b 100644 --- a/deps/v8/test/mozilla/mozilla.status +++ b/deps/v8/test/mozilla/mozilla.status @@ -624,7 +624,6 @@ js1_5/extensions/regress-333541: FAIL_OK js1_5/extensions/regress-335700: FAIL_OK js1_5/extensions/regress-336409-1: FAIL_OK js1_5/extensions/regress-336409-2: FAIL_OK -js1_5/extensions/regress-336410-1: FAIL_OK js1_5/extensions/regress-336410-2: FAIL_OK js1_5/extensions/regress-341956-01: FAIL_OK js1_5/extensions/regress-341956-02: FAIL_OK @@ -706,6 +705,11 @@ js1_5/extensions/toLocaleFormat-02: FAIL_OK js1_5/extensions/regress-330569: TIMEOUT js1_5/extensions/regress-351448: TIMEOUT js1_5/extensions/regress-342960: FAIL_OK || TIMEOUT if $mode == debug +# In the 64-bit version, this test takes longer to run out of memory +# than it does in the 32-bit version when attempting to generate a huge +# error message in debug mode. +js1_5/extensions/regress-336410-1: FAIL_OK || TIMEOUT if ($mode == debug && $arch == x64) + ##################### DECOMPILATION TESTS ##################### diff --git a/deps/v8/tools/gyp/v8.gyp b/deps/v8/tools/gyp/v8.gyp index b0c33318fe..c57419879f 100644 --- a/deps/v8/tools/gyp/v8.gyp +++ b/deps/v8/tools/gyp/v8.gyp @@ -97,9 +97,15 @@ ], }], ], - 'cflags_cc': [ - '-fno-rtti', - ], + }], + ['OS=="mac"', { + 'xcode_settings': { + 'GCC_OPTIMIZATION_LEVEL': '3', # -O3 + 'GCC_STRICT_ALIASING': 'YES', # -fstrict-aliasing. Mainline gcc + # enables this at -O2 and above, + # but Apple gcc does not unless it + # is specified explicitly. + }, }], ['OS=="win"', { 'msvs_configuration_attributes': { @@ -128,10 +134,6 @@ ], }, }, - 'xcode_settings': { - 'GCC_ENABLE_CPP_EXCEPTIONS': 'NO', - 'GCC_ENABLE_CPP_RTTI': 'NO', - }, }, 'targets': [ { @@ -387,7 +389,7 @@ '../../src/arm/assembler-arm.cc', '../../src/arm/assembler-arm.h', '../../src/arm/builtins-arm.cc', - '../../src/arm/cfg-arm.cc', + '../../src/arm/cfg-arm.cc', '../../src/arm/codegen-arm.cc', '../../src/arm/codegen-arm.h', '../../src/arm/constants-arm.h', @@ -418,7 +420,7 @@ '../../src/ia32/assembler-ia32.cc', '../../src/ia32/assembler-ia32.h', '../../src/ia32/builtins-ia32.cc', - '../../src/ia32/cfg-ia32.cc', + '../../src/ia32/cfg-ia32.cc', '../../src/ia32/codegen-ia32.cc', '../../src/ia32/codegen-ia32.h', '../../src/ia32/cpu-ia32.cc', @@ -451,7 +453,7 @@ '../../src/x64/assembler-x64.cc', '../../src/x64/assembler-x64.h', '../../src/x64/builtins-x64.cc', - '../../src/x64/cfg-x64.cc', + '../../src/x64/cfg-x64.cc', '../../src/x64/codegen-x64.cc', '../../src/x64/codegen-x64.h', '../../src/x64/cpu-x64.cc',