static void SetArrayBufferAllocator(ArrayBuffer::Allocator* allocator);
/**
+ * Ignore out-of-memory exceptions.
+ *
+ * V8 running out of memory is treated as a fatal error by default.
+ * This means that the fatal error handler is called and that V8 is
+ * terminated.
+ *
+ * IgnoreOutOfMemoryException can be used to not treat an
+ * out-of-memory situation as a fatal error. This way, the contexts
+ * that did not cause the out of memory problem might be able to
+ * continue execution.
+ */
+ static void IgnoreOutOfMemoryException();
+
+ /**
* Check if V8 is dead and therefore unusable. This is the case after
* fatal errors such as out-of-memory situations.
*/
*/
void Exit();
+ /** Returns true if the context has experienced an out of memory situation. */
+ bool HasOutOfMemoryException();
+
/** Returns an isolate associated with a current context. */
v8::Isolate* GetIsolate();
(isolate)->handle_scope_implementer(); \
handle_scope_implementer->DecrementCallDepth(); \
if (has_pending_exception) { \
+ if (handle_scope_implementer->CallDepthIsZero() && \
+ (isolate)->is_out_of_memory()) { \
+ if (!(isolate)->ignore_out_of_memory()) \
+ i::V8::FatalProcessOutOfMemory(NULL); \
+ } \
bool call_depth_is_zero = handle_scope_implementer->CallDepthIsZero(); \
(isolate)->OptionalRescheduleException(call_depth_is_zero); \
do_callback \
}
+bool Context::HasOutOfMemoryException() {
+ i::Handle<i::Context> env = Utils::OpenHandle(this);
+ return env->has_out_of_memory();
+}
+
+
v8::Isolate* Context::GetIsolate() {
i::Handle<i::Context> env = Utils::OpenHandle(this);
return reinterpret_cast<Isolate*>(env->GetIsolate());
}
+void V8::IgnoreOutOfMemoryException() {
+ EnterIsolateIfNeeded()->set_ignore_out_of_memory(true);
+}
+
+
bool V8::AddMessageListener(MessageCallback that, Handle<Value> data) {
i::Isolate* isolate = i::Isolate::Current();
EnsureInitializedForIsolate(isolate, "v8::V8::AddMessageListener()");
}
+static void JumpIfOOM(MacroAssembler* masm,
+ Register value,
+ Register scratch,
+ Label* oom_label) {
+ STATIC_ASSERT(Failure::OUT_OF_MEMORY_EXCEPTION == 3);
+ STATIC_ASSERT(kFailureTag == 3);
+ __ and_(scratch, value, Operand(0xf));
+ __ cmp(scratch, Operand(0xf));
+ __ b(eq, oom_label);
+}
+
+
void CEntryStub::GenerateCore(MacroAssembler* masm,
Label* throw_normal_exception,
Label* throw_termination_exception,
+ Label* throw_out_of_memory_exception,
bool do_gc,
bool always_allocate) {
// r0: result parameter for PerformGC, if any
__ tst(r0, Operand(((1 << kFailureTypeTagSize) - 1) << kFailureTagSize));
__ b(eq, &retry);
+ // Special handling of out of memory exceptions.
+ JumpIfOOM(masm, r0, ip, throw_out_of_memory_exception);
+
// Retrieve the pending exception.
__ mov(ip, Operand(ExternalReference(Isolate::kPendingExceptionAddress,
isolate)));
__ ldr(r0, MemOperand(ip));
+ // See if we just retrieved an OOM exception.
+ JumpIfOOM(masm, r0, ip, throw_out_of_memory_exception);
+
// Clear the pending exception.
__ LoadRoot(r3, Heap::kTheHoleValueRootIndex);
__ mov(ip, Operand(ExternalReference(Isolate::kPendingExceptionAddress,
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,
false,
false);
GenerateCore(masm,
&throw_normal_exception,
&throw_termination_exception,
+ &throw_out_of_memory_exception,
true,
false);
GenerateCore(masm,
&throw_normal_exception,
&throw_termination_exception,
+ &throw_out_of_memory_exception,
true,
true);
+ __ bind(&throw_out_of_memory_exception);
+ // Set external caught exception to false.
+ Isolate* isolate = masm->isolate();
+ ExternalReference external_caught(Isolate::kExternalCaughtExceptionAddress,
+ isolate);
+ __ mov(r0, Operand(false, RelocInfo::NONE32));
+ __ mov(r2, Operand(external_caught));
+ __ str(r0, MemOperand(r2));
+
+ // Set pending exception and r0 to out of memory exception.
+ Label already_have_failure;
+ JumpIfOOM(masm, r0, ip, &already_have_failure);
+ Failure* out_of_memory = Failure::OutOfMemoryException(0x1);
+ __ mov(r0, Operand(reinterpret_cast<int32_t>(out_of_memory)));
+ __ bind(&already_have_failure);
+ __ mov(r2, Operand(ExternalReference(Isolate::kPendingExceptionAddress,
+ isolate)));
+ __ str(r0, MemOperand(r2));
+ // Fall through to the next label.
+
__ bind(&throw_termination_exception);
__ ThrowUncatchable(r0);
}
+static void JumpIfOOM(MacroAssembler* masm,
+ Register value,
+ Register scratch,
+ Label* oom_label) {
+ STATIC_ASSERT(Failure::OUT_OF_MEMORY_EXCEPTION == 3);
+ STATIC_ASSERT(kFailureTag == 3);
+ __ And(scratch, value, 0xf);
+ __ Cmp(scratch, 0xf);
+ __ B(eq, oom_label);
+}
+
+
bool CEntryStub::NeedsImmovableCode() {
// CEntryStub stores the return address on the stack before calling into
// C++ code. In some cases, the VM accesses this address, but it is not used
void CEntryStub::GenerateCore(MacroAssembler* masm,
Label* throw_normal,
Label* throw_termination,
+ Label* throw_out_of_memory,
bool do_gc,
bool always_allocate) {
// x0 : Result parameter for PerformGC, if do_gc is true.
__ Tst(result, kFailureTypeTagMask << kFailureTagSize);
__ B(eq, &retry); // RETRY_AFTER_GC
+ // Special handling of out-of-memory exceptions: Pass the failure result,
+ // rather than the exception descriptor.
+ JumpIfOOM(masm, result, x10, throw_out_of_memory);
+
// Retrieve the pending exception.
const Register& exception = result;
const Register& exception_address = x11;
isolate)));
__ Ldr(exception, MemOperand(exception_address));
+ // See if we just retrieved an OOM exception.
+ JumpIfOOM(masm, exception, x10, throw_out_of_memory);
+
// Clear the pending exception.
__ Mov(x10, Operand(isolate->factory()->the_hole_value()));
__ Str(x10, MemOperand(exception_address));
Label throw_normal;
Label throw_termination;
+ Label throw_out_of_memory;
// Call the runtime function.
GenerateCore(masm,
&throw_normal,
&throw_termination,
+ &throw_out_of_memory,
false,
false);
GenerateCore(masm,
&throw_normal,
&throw_termination,
+ &throw_out_of_memory,
true,
false);
GenerateCore(masm,
&throw_normal,
&throw_termination,
+ &throw_out_of_memory,
true,
true);
// If we throw an exception, we can end up re-entering CEntryStub before we
// pop the exit frame, so need to ensure that x21-x23 contain GC-safe values
// here.
+ __ Bind(&throw_out_of_memory);
+ ASM_LOCATION("Throw out of memory");
+ __ Mov(argv, 0);
+ __ Mov(argc, 0);
+ __ Mov(target, 0);
+ // Set external caught exception to false.
+ Isolate* isolate = masm->isolate();
+ __ Mov(x2, Operand(ExternalReference(Isolate::kExternalCaughtExceptionAddress,
+ isolate)));
+ __ Str(xzr, MemOperand(x2));
+
+ // Set pending exception and x0 to out of memory exception.
+ Label already_have_failure;
+ JumpIfOOM(masm, x0, x10, &already_have_failure);
+ Failure* out_of_memory = Failure::OutOfMemoryException(0x1);
+ __ Mov(x0, Operand(reinterpret_cast<uint64_t>(out_of_memory)));
+ __ Bind(&already_have_failure);
+ __ Mov(x2, Operand(ExternalReference(Isolate::kPendingExceptionAddress,
+ isolate)));
+ __ Str(x0, MemOperand(x2));
+ // Fall through to the next label.
__ Bind(&throw_termination);
ASM_LOCATION("Throw termination");
delegate->shared()->DontAdaptArguments();
}
+ // Initialize the out of memory slot.
+ native_context()->set_out_of_memory(heap->false_value());
+
// Initialize the embedder data slot.
Handle<FixedArray> embedder_data = factory->NewFixedArray(3);
native_context()->set_embedder_data(*embedder_data);
void GenerateCore(MacroAssembler* masm,
Label* throw_normal_exception,
Label* throw_termination_exception,
+ Label* throw_out_of_memory_exception,
bool do_gc,
bool always_allocate_scope);
V(SCRIPT_FUNCTION_INDEX, JSFunction, script_function) \
V(OPAQUE_REFERENCE_FUNCTION_INDEX, JSFunction, opaque_reference_function) \
V(CONTEXT_EXTENSION_FUNCTION_INDEX, JSFunction, context_extension_function) \
+ V(OUT_OF_MEMORY_INDEX, Object, out_of_memory) \
V(MAP_CACHE_INDEX, Object, map_cache) \
V(EMBEDDER_DATA_INDEX, FixedArray, embedder_data) \
V(ALLOW_CODE_GEN_FROM_STRINGS_INDEX, Object, allow_code_gen_from_strings) \
return map == map->GetHeap()->global_context_map();
}
+ // Tells whether the native context is marked with out of memory.
+ inline bool has_out_of_memory();
+
+ // Mark the native context with out of memory.
+ inline void mark_out_of_memory();
+
// A native context holds a list of all functions with optimized code.
void AddOptimizedFunction(JSFunction* function);
void RemoveOptimizedFunction(JSFunction* function);
// that no GC is triggered, allocate HeapNumbers from old space if they
// can't be taken from new space.
if (!maybe_value->ToObject(&value)) {
- ASSERT(maybe_value->IsRetryAfterGC());
+ ASSERT(maybe_value->IsRetryAfterGC() || maybe_value->IsOutOfMemory());
Heap* heap = from->GetHeap();
MaybeObject* maybe_value_object =
heap->AllocateHeapNumber(from->get_scalar(i + from_start),
ASSERT(*has_pending_exception == isolate->has_pending_exception());
if (*has_pending_exception) {
isolate->ReportPendingMessages();
+ if (isolate->pending_exception()->IsOutOfMemory()) {
+ if (!isolate->ignore_out_of_memory()) {
+ V8::FatalProcessOutOfMemory("JS", true);
+ }
+ }
#ifdef ENABLE_DEBUGGER_SUPPORT
// Reset stepping state when script exits with uncaught exception.
if (isolate->debugger()->IsDebuggerActive()) {
ASSERT(catcher.HasCaught());
ASSERT(isolate->has_pending_exception());
ASSERT(isolate->external_caught_exception());
+ if (isolate->is_out_of_memory() && !isolate->ignore_out_of_memory()) {
+ V8::FatalProcessOutOfMemory("OOM during Execution::TryCall");
+ }
if (isolate->pending_exception() ==
isolate->heap()->termination_exception()) {
result = isolate->factory()->termination_exception();
MaybeObject* Heap::AllocateOneByteInternalizedString(Vector<const uint8_t> str,
uint32_t hash_field) {
if (str.length() > String::kMaxLength) {
- v8::internal::Heap::FatalProcessOutOfMemory("invalid string length", true);
+ return Failure::OutOfMemoryException(0x2);
}
// Compute map and object size.
Map* map = ascii_internalized_string_map();
MaybeObject* Heap::AllocateTwoByteInternalizedString(Vector<const uc16> str,
uint32_t hash_field) {
if (str.length() > String::kMaxLength) {
- v8::internal::Heap::FatalProcessOutOfMemory("invalid string length", true);
+ return Failure::OutOfMemoryException(0x3);
}
// Compute map and object size.
Map* map = internalized_string_map();
// Warning: Do not use the identifiers __object__, __maybe_object__ or
// __scope__ in a call to this macro.
-#define CALL_AND_RETRY(ISOLATE, FUNCTION_CALL, RETURN_VALUE, RETURN_EMPTY) \
+#define CALL_AND_RETRY(ISOLATE, FUNCTION_CALL, RETURN_VALUE, RETURN_EMPTY, OOM)\
do { \
GC_GREEDY_CHECK(ISOLATE); \
MaybeObject* __maybe_object__ = FUNCTION_CALL; \
Object* __object__ = NULL; \
if (__maybe_object__->ToObject(&__object__)) RETURN_VALUE; \
+ if (__maybe_object__->IsOutOfMemory()) { \
+ OOM; \
+ } \
if (!__maybe_object__->IsRetryAfterGC()) RETURN_EMPTY; \
(ISOLATE)->heap()->CollectGarbage(Failure::cast(__maybe_object__)-> \
allocation_space(), \
"allocation failure"); \
__maybe_object__ = FUNCTION_CALL; \
if (__maybe_object__->ToObject(&__object__)) RETURN_VALUE; \
+ if (__maybe_object__->IsOutOfMemory()) { \
+ OOM; \
+ } \
if (!__maybe_object__->IsRetryAfterGC()) RETURN_EMPTY; \
(ISOLATE)->counters()->gc_last_resort_from_handles()->Increment(); \
(ISOLATE)->heap()->CollectAllAvailableGarbage("last resort gc"); \
__maybe_object__ = FUNCTION_CALL; \
} \
if (__maybe_object__->ToObject(&__object__)) RETURN_VALUE; \
+ if (__maybe_object__->IsOutOfMemory()) { \
+ OOM; \
+ } \
if (__maybe_object__->IsRetryAfterGC()) { \
/* TODO(1181417): Fix this. */ \
v8::internal::Heap::FatalProcessOutOfMemory("CALL_AND_RETRY_LAST", true);\
ISOLATE, \
FUNCTION_CALL, \
RETURN_VALUE, \
- RETURN_EMPTY)
+ RETURN_EMPTY, \
+ v8::internal::Heap::FatalProcessOutOfMemory("CALL_AND_RETRY", true))
#define CALL_HEAP_FUNCTION(ISOLATE, FUNCTION_CALL, TYPE) \
CALL_AND_RETRY_OR_DIE(ISOLATE, \
CALL_AND_RETRY(ISOLATE, \
FUNCTION_CALL, \
return __object__, \
+ return __maybe_object__, \
return __maybe_object__)
const ExternalAsciiString::Resource* resource) {
size_t length = resource->length();
if (length > static_cast<size_t>(String::kMaxLength)) {
- v8::internal::Heap::FatalProcessOutOfMemory("invalid string length", true);
+ isolate()->context()->mark_out_of_memory();
+ return Failure::OutOfMemoryException(0x5);
}
Map* map = external_ascii_string_map();
const ExternalTwoByteString::Resource* resource) {
size_t length = resource->length();
if (length > static_cast<size_t>(String::kMaxLength)) {
- v8::internal::Heap::FatalProcessOutOfMemory("invalid string length", true);
+ isolate()->context()->mark_out_of_memory();
+ return Failure::OutOfMemoryException(0x6);
}
// For small strings we check whether the resource contains only
MaybeObject* Heap::AllocateByteArray(int length, PretenureFlag pretenure) {
if (length < 0 || length > ByteArray::kMaxLength) {
- v8::internal::Heap::FatalProcessOutOfMemory("invalid array length", true);
+ return Failure::OutOfMemoryException(0x7);
}
int size = ByteArray::SizeFor(length);
AllocationSpace space = SelectSpace(size, OLD_DATA_SPACE, pretenure);
Map* map;
if (chars > String::kMaxLength) {
- v8::internal::Heap::FatalProcessOutOfMemory("invalid string length", true);
+ return Failure::OutOfMemoryException(0x9);
}
if (is_one_byte) {
map = ascii_internalized_string_map();
MaybeObject* Heap::AllocateRawOneByteString(int length,
PretenureFlag pretenure) {
if (length < 0 || length > String::kMaxLength) {
- v8::internal::Heap::FatalProcessOutOfMemory("invalid string length", true);
+ return Failure::OutOfMemoryException(0xb);
}
int size = SeqOneByteString::SizeFor(length);
ASSERT(size <= SeqOneByteString::kMaxSize);
MaybeObject* Heap::AllocateRawTwoByteString(int length,
PretenureFlag pretenure) {
if (length < 0 || length > String::kMaxLength) {
- v8::internal::Heap::FatalProcessOutOfMemory("invalid string length", true);
+ return Failure::OutOfMemoryException(0xc);
}
int size = SeqTwoByteString::SizeFor(length);
ASSERT(size <= SeqTwoByteString::kMaxSize);
MaybeObject* Heap::AllocateRawFixedArray(int length, PretenureFlag pretenure) {
if (length < 0 || length > FixedArray::kMaxLength) {
- v8::internal::Heap::FatalProcessOutOfMemory("invalid array length", true);
+ return Failure::OutOfMemoryException(0xe);
}
int size = FixedArray::SizeFor(length);
AllocationSpace space = SelectSpace(size, OLD_POINTER_SPACE, pretenure);
MaybeObject* Heap::AllocateRawFixedDoubleArray(int length,
PretenureFlag pretenure) {
if (length < 0 || length > FixedDoubleArray::kMaxLength) {
- v8::internal::Heap::FatalProcessOutOfMemory("invalid array length", true);
+ return Failure::OutOfMemoryException(0xf);
}
int size = FixedDoubleArray::SizeFor(length);
#ifndef V8_HOST_ARCH_64_BIT
}
+static void JumpIfOOM(MacroAssembler* masm,
+ Register value,
+ Register scratch,
+ Label* oom_label) {
+ __ mov(scratch, value);
+ STATIC_ASSERT(Failure::OUT_OF_MEMORY_EXCEPTION == 3);
+ STATIC_ASSERT(kFailureTag == 3);
+ __ and_(scratch, 0xf);
+ __ cmp(scratch, 0xf);
+ __ j(equal, oom_label);
+}
+
+
void CEntryStub::GenerateCore(MacroAssembler* masm,
Label* throw_normal_exception,
Label* throw_termination_exception,
+ Label* throw_out_of_memory_exception,
bool do_gc,
bool always_allocate_scope) {
// eax: result parameter for PerformGC, if any
__ test(eax, Immediate(((1 << kFailureTypeTagSize) - 1) << kFailureTagSize));
__ j(zero, &retry, Label::kNear);
+ // Special handling of out of memory exceptions.
+ JumpIfOOM(masm, eax, ecx, throw_out_of_memory_exception);
+
// Retrieve the pending exception.
__ mov(eax, Operand::StaticVariable(pending_exception_address));
+ // See if we just retrieved an OOM exception.
+ JumpIfOOM(masm, eax, ecx, throw_out_of_memory_exception);
+
// Clear the pending exception.
__ mov(edx, Immediate(masm->isolate()->factory()->the_hole_value()));
__ mov(Operand::StaticVariable(pending_exception_address), edx);
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,
false,
false);
GenerateCore(masm,
&throw_normal_exception,
&throw_termination_exception,
+ &throw_out_of_memory_exception,
true,
false);
GenerateCore(masm,
&throw_normal_exception,
&throw_termination_exception,
+ &throw_out_of_memory_exception,
true,
true);
+ __ bind(&throw_out_of_memory_exception);
+ // Set external caught exception to false.
+ Isolate* isolate = masm->isolate();
+ ExternalReference external_caught(Isolate::kExternalCaughtExceptionAddress,
+ isolate);
+ __ mov(Operand::StaticVariable(external_caught), Immediate(false));
+
+ // Set pending exception and eax to out of memory exception.
+ ExternalReference pending_exception(Isolate::kPendingExceptionAddress,
+ isolate);
+ Label already_have_failure;
+ JumpIfOOM(masm, eax, ecx, &already_have_failure);
+ __ mov(eax, reinterpret_cast<int32_t>(Failure::OutOfMemoryException(0x1)));
+ __ bind(&already_have_failure);
+ __ mov(Operand::StaticVariable(pending_exception), eax);
+ // Fall through to the next label.
+
__ bind(&throw_termination_exception);
__ ThrowUncatchable(eax);
ThreadLocalTop::ThreadLocalTop() {
InitializeInternal();
+ // This flag may be set using v8::V8::IgnoreOutOfMemoryException()
+ // before an isolate is initialized. The initialize methods below do
+ // not touch it to preserve its value.
+ ignore_out_of_memory_ = false;
}
ASSERT(has_pending_exception());
PropagatePendingExceptionToExternalTryCatch();
+ // If the pending exception is OutOfMemoryException set out_of_memory in
+ // the native context. Note: We have to mark the native context here
+ // since the GenerateThrowOutOfMemory stub cannot make a RuntimeCall to
+ // set it.
HandleScope scope(this);
- if (thread_local_top_.pending_exception_ ==
+ if (thread_local_top_.pending_exception_->IsOutOfMemory()) {
+ context()->mark_out_of_memory();
+ } else if (thread_local_top_.pending_exception_ ==
heap()->termination_exception()) {
// Do nothing: if needed, the exception has been already propagated to
// v8::TryCatch.
MessageLocation Isolate::GetMessageLocation() {
ASSERT(has_pending_exception());
- if (thread_local_top_.pending_exception_ != heap()->termination_exception() &&
+ if (!thread_local_top_.pending_exception_->IsOutOfMemory() &&
+ thread_local_top_.pending_exception_ != heap()->termination_exception() &&
thread_local_top_.has_pending_message_ &&
!thread_local_top_.pending_message_obj_->IsTheHole() &&
!thread_local_top_.pending_message_obj_->IsTheHole()) {
ASSERT(has_pending_exception());
PropagatePendingExceptionToExternalTryCatch();
- bool is_termination_exception =
- pending_exception() == heap_.termination_exception();
+ // Always reschedule out of memory exceptions.
+ if (!is_out_of_memory()) {
+ bool is_termination_exception =
+ pending_exception() == heap_.termination_exception();
- // Do not reschedule the exception if this is the bottom call.
- bool clear_exception = is_bottom_call;
+ // Do not reschedule the exception if this is the bottom call.
+ bool clear_exception = is_bottom_call;
- if (is_termination_exception) {
- if (is_bottom_call) {
+ if (is_termination_exception) {
+ if (is_bottom_call) {
+ thread_local_top()->external_caught_exception_ = false;
+ clear_pending_exception();
+ return false;
+ }
+ } else if (thread_local_top()->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_top()->try_catch_handler_address() != NULL);
+ Address external_handler_address =
+ thread_local_top()->try_catch_handler_address();
+ JavaScriptFrameIterator it(this);
+ if (it.done() || (it.frame()->sp() > external_handler_address)) {
+ clear_exception = true;
+ }
+ }
+
+ // Clear the exception if needed.
+ if (clear_exception) {
thread_local_top()->external_caught_exception_ = false;
clear_pending_exception();
return false;
}
- } else if (thread_local_top()->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_top()->try_catch_handler_address() != NULL);
- Address external_handler_address =
- thread_local_top()->try_catch_handler_address();
- JavaScriptFrameIterator it(this);
- if (it.done() || (it.frame()->sp() > external_handler_address)) {
- clear_exception = true;
- }
- }
-
- // Clear the exception if needed.
- if (clear_exception) {
- thread_local_top()->external_caught_exception_ = false;
- clear_pending_exception();
- return false;
}
// Reschedule the exception.
}
+bool Isolate::is_out_of_memory() {
+ if (has_pending_exception()) {
+ MaybeObject* e = pending_exception();
+ if (e->IsFailure() && Failure::cast(e)->IsOutOfMemoryException()) {
+ return true;
+ }
+ }
+ if (has_scheduled_exception()) {
+ MaybeObject* e = scheduled_exception();
+ if (e->IsFailure() && Failure::cast(e)->IsOutOfMemoryException()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+
Handle<Context> Isolate::native_context() {
return Handle<Context>(context()->global_object()->native_context());
}
if (!external_caught) return;
- if (thread_local_top_.pending_exception_ ==
+ if (thread_local_top_.pending_exception_->IsOutOfMemory()) {
+ // Do not propagate OOM exception: we should kill VM asap.
+ } else if (thread_local_top_.pending_exception_ ==
heap()->termination_exception()) {
try_catch_handler()->can_continue_ = false;
try_catch_handler()->has_terminated_ = true;
// Head of the list of live LookupResults.
LookupResult* top_lookup_result_;
+ // Whether out of memory exceptions should be ignored.
+ bool ignore_out_of_memory_;
+
private:
void InitializeInternal();
bool IsExternallyCaught();
bool is_catchable_by_javascript(MaybeObject* exception) {
- return exception != heap()->termination_exception();
+ return (!exception->IsOutOfMemory()) &&
+ (exception != heap()->termination_exception());
}
// Serializer.
int frame_limit,
StackTrace::StackTraceOptions options);
+ // Tells whether the current context has experienced an out of memory
+ // exception.
+ bool is_out_of_memory();
+
+ THREAD_LOCAL_TOP_ACCESSOR(bool, ignore_out_of_memory)
+
void PrintCurrentStackTrace(FILE* out);
void PrintStack(StringStream* accumulator);
void PrintStack(FILE* out);
};
+// Tells whether the native context is marked with out of memory.
+inline bool Context::has_out_of_memory() {
+ return native_context()->out_of_memory()->IsTrue();
+}
+
+
+// Mark the native context with out of memory.
+inline void Context::mark_out_of_memory() {
+ native_context()->set_out_of_memory(GetIsolate()->heap()->true_value());
+}
+
class CodeTracer V8_FINAL : public Malloced {
public:
explicit CodeTracer(int isolate_id)
}
+static void JumpIfOOM(MacroAssembler* masm,
+ Register value,
+ Register scratch,
+ Label* oom_label) {
+ STATIC_ASSERT(Failure::OUT_OF_MEMORY_EXCEPTION == 3);
+ STATIC_ASSERT(kFailureTag == 3);
+ __ andi(scratch, value, 0xf);
+ __ Branch(oom_label, eq, scratch, Operand(0xf));
+}
+
+
void CEntryStub::GenerateCore(MacroAssembler* masm,
Label* throw_normal_exception,
Label* throw_termination_exception,
+ Label* throw_out_of_memory_exception,
bool do_gc,
bool always_allocate) {
// v0: result parameter for PerformGC, if any
__ andi(t0, v0, ((1 << kFailureTypeTagSize) - 1) << kFailureTagSize);
__ Branch(&retry, eq, t0, Operand(zero_reg));
+ // Special handling of out of memory exceptions.
+ JumpIfOOM(masm, v0, t0, throw_out_of_memory_exception);
+
// Retrieve the pending exception.
__ li(t0, Operand(ExternalReference(Isolate::kPendingExceptionAddress,
isolate)));
__ lw(v0, MemOperand(t0));
+ // See if we just retrieved an OOM exception.
+ JumpIfOOM(masm, v0, t0, throw_out_of_memory_exception);
+
// Clear the pending exception.
__ li(a3, Operand(isolate->factory()->the_hole_value()));
__ li(t0, Operand(ExternalReference(Isolate::kPendingExceptionAddress,
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,
false,
false);
GenerateCore(masm,
&throw_normal_exception,
&throw_termination_exception,
+ &throw_out_of_memory_exception,
true,
false);
GenerateCore(masm,
&throw_normal_exception,
&throw_termination_exception,
+ &throw_out_of_memory_exception,
true,
true);
+ __ bind(&throw_out_of_memory_exception);
+ // Set external caught exception to false.
+ Isolate* isolate = masm->isolate();
+ ExternalReference external_caught(Isolate::kExternalCaughtExceptionAddress,
+ isolate);
+ __ li(a0, Operand(false, RelocInfo::NONE32));
+ __ li(a2, Operand(external_caught));
+ __ sw(a0, MemOperand(a2));
+
+ // Set pending exception and v0 to out of memory exception.
+ Label already_have_failure;
+ JumpIfOOM(masm, v0, t0, &already_have_failure);
+ Failure* out_of_memory = Failure::OutOfMemoryException(0x1);
+ __ li(v0, Operand(reinterpret_cast<int32_t>(out_of_memory)));
+ __ bind(&already_have_failure);
+ __ li(a2, Operand(ExternalReference(Isolate::kPendingExceptionAddress,
+ isolate)));
+ __ sw(v0, MemOperand(a2));
+ // Fall through to the next label.
+
__ bind(&throw_termination_exception);
__ ThrowUncatchable(v0);
}
+bool MaybeObject::IsOutOfMemory() {
+ return HAS_FAILURE_TAG(this)
+ && Failure::cast(this)->IsOutOfMemoryException();
+}
+
+
bool MaybeObject::IsException() {
return this == Failure::Exception();
}
}
+bool Failure::IsOutOfMemoryException() const {
+ return type() == OUT_OF_MEMORY_EXCEPTION;
+}
+
+
AllocationSpace Failure::allocation_space() const {
ASSERT_EQ(RETRY_AFTER_GC, type());
return static_cast<AllocationSpace>((value() >> kFailureTypeTagSize)
}
+Failure* Failure::OutOfMemoryException(intptr_t value) {
+ return Construct(OUT_OF_MEMORY_EXCEPTION, value);
+}
+
+
intptr_t Failure::value() const {
return static_cast<intptr_t>(
reinterpret_cast<uintptr_t>(this) >> kFailureTagSize);
? at_least_space_for
: ComputeCapacity(at_least_space_for);
if (capacity > HashTable::kMaxCapacity) {
- v8::internal::Heap::FatalProcessOutOfMemory("invalid table size", true);
+ return Failure::OutOfMemoryException(0x10);
}
Object* obj;
public:
inline bool IsFailure();
inline bool IsRetryAfterGC();
+ inline bool IsOutOfMemory();
inline bool IsException();
INLINE(bool IsTheHole());
INLINE(bool IsUninitialized());
inline AllocationSpace allocation_space() const;
inline bool IsInternalError() const;
+ inline bool IsOutOfMemoryException() const;
static inline Failure* RetryAfterGC(AllocationSpace space);
static inline Failure* RetryAfterGC(); // NEW_SPACE
static inline Failure* Exception();
static inline Failure* InternalError();
+ // TODO(jkummerow): The value is temporary instrumentation. Remove it
+ // when it has served its purpose.
+ static inline Failure* OutOfMemoryException(intptr_t value);
// Casting.
static inline Failure* cast(MaybeObject* object);
static_cast<int64_t>(pattern_len)) *
static_cast<int64_t>(matches) +
static_cast<int64_t>(subject_len);
- if (result_len_64 > INT_MAX) {
- v8::internal::Heap::FatalProcessOutOfMemory("invalid string length", true);
- }
+ if (result_len_64 > INT_MAX) return Failure::OutOfMemoryException(0x11);
int result_len = static_cast<int>(result_len_64);
int subject_pos = 0;
}
+static void JumpIfOOM(MacroAssembler* masm,
+ Register value,
+ Register scratch,
+ Label* oom_label) {
+ __ movp(scratch, value);
+ STATIC_ASSERT(Failure::OUT_OF_MEMORY_EXCEPTION == 3);
+ STATIC_ASSERT(kFailureTag == 3);
+ __ and_(scratch, Immediate(0xf));
+ __ cmpq(scratch, Immediate(0xf));
+ __ j(equal, oom_label);
+}
+
+
void CEntryStub::GenerateCore(MacroAssembler* masm,
Label* throw_normal_exception,
Label* throw_termination_exception,
+ Label* throw_out_of_memory_exception,
bool do_gc,
bool always_allocate_scope) {
// rax: result parameter for PerformGC, if any.
__ testl(rax, Immediate(((1 << kFailureTypeTagSize) - 1) << kFailureTagSize));
__ j(zero, &retry, Label::kNear);
+ // Special handling of out of memory exceptions.
+ JumpIfOOM(masm, rax, kScratchRegister, throw_out_of_memory_exception);
+
// Retrieve the pending exception.
ExternalReference pending_exception_address(
Isolate::kPendingExceptionAddress, masm->isolate());
masm->ExternalOperand(pending_exception_address);
__ movp(rax, pending_exception_operand);
+ // See if we just retrieved an OOM exception.
+ JumpIfOOM(masm, rax, kScratchRegister, throw_out_of_memory_exception);
+
// Clear the pending exception.
pending_exception_operand =
masm->ExternalOperand(pending_exception_address);
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,
false,
false);
GenerateCore(masm,
&throw_normal_exception,
&throw_termination_exception,
+ &throw_out_of_memory_exception,
true,
false);
GenerateCore(masm,
&throw_normal_exception,
&throw_termination_exception,
+ &throw_out_of_memory_exception,
true,
true);
+ __ bind(&throw_out_of_memory_exception);
+ // Set external caught exception to false.
+ Isolate* isolate = masm->isolate();
+ ExternalReference external_caught(Isolate::kExternalCaughtExceptionAddress,
+ isolate);
+ __ Set(rax, static_cast<int64_t>(false));
+ __ Store(external_caught, rax);
+
+ // Set pending exception and rax to out of memory exception.
+ ExternalReference pending_exception(Isolate::kPendingExceptionAddress,
+ isolate);
+ Label already_have_failure;
+ JumpIfOOM(masm, rax, kScratchRegister, &already_have_failure);
+ __ Move(rax, Failure::OutOfMemoryException(0x1), Assembler::RelocInfoNone());
+ __ bind(&already_have_failure);
+ __ Store(pending_exception, rax);
+ // Fall through to the next label.
+
__ bind(&throw_termination_exception);
__ ThrowUncatchable(rax);
class InitDefaultIsolateThread : public v8::internal::Thread {
public:
enum TestCase {
+ IgnoreOOM,
SetResourceConstraints,
SetFatalHandler,
SetCounterFunction,
v8::Isolate* isolate = v8::Isolate::New();
isolate->Enter();
switch (testCase_) {
- case SetResourceConstraints: {
- static const int K = 1024;
- v8::ResourceConstraints constraints;
- constraints.set_max_young_space_size(256 * K);
- constraints.set_max_old_space_size(4 * K * K);
- v8::SetResourceConstraints(CcTest::isolate(), &constraints);
- break;
- }
+ case IgnoreOOM:
+ v8::V8::IgnoreOutOfMemoryException();
+ break;
- case SetFatalHandler:
- v8::V8::SetFatalErrorHandler(NULL);
- break;
+ case SetResourceConstraints: {
+ static const int K = 1024;
+ v8::ResourceConstraints constraints;
+ constraints.set_max_young_space_size(256 * K);
+ constraints.set_max_old_space_size(4 * K * K);
+ v8::SetResourceConstraints(CcTest::isolate(), &constraints);
+ break;
+ }
- case SetCounterFunction:
- v8::V8::SetCounterFunction(NULL);
- break;
+ case SetFatalHandler:
+ v8::V8::SetFatalErrorHandler(NULL);
+ break;
- case SetCreateHistogramFunction:
- v8::V8::SetCreateHistogramFunction(NULL);
- break;
+ case SetCounterFunction:
+ v8::V8::SetCounterFunction(NULL);
+ break;
- case SetAddHistogramSampleFunction:
- v8::V8::SetAddHistogramSampleFunction(NULL);
- break;
+ case SetCreateHistogramFunction:
+ v8::V8::SetCreateHistogramFunction(NULL);
+ break;
+
+ case SetAddHistogramSampleFunction:
+ v8::V8::SetAddHistogramSampleFunction(NULL);
+ break;
}
isolate->Exit();
isolate->Dispose();
TEST(InitializeDefaultIsolateOnSecondaryThread1) {
- InitializeTestHelper(InitDefaultIsolateThread::SetResourceConstraints);
+ InitializeTestHelper(InitDefaultIsolateThread::IgnoreOOM);
}
TEST(InitializeDefaultIsolateOnSecondaryThread2) {
- InitializeTestHelper(InitDefaultIsolateThread::SetFatalHandler);
+ InitializeTestHelper(InitDefaultIsolateThread::SetResourceConstraints);
}
TEST(InitializeDefaultIsolateOnSecondaryThread3) {
- InitializeTestHelper(InitDefaultIsolateThread::SetCounterFunction);
+ InitializeTestHelper(InitDefaultIsolateThread::SetFatalHandler);
}
TEST(InitializeDefaultIsolateOnSecondaryThread4) {
- InitializeTestHelper(InitDefaultIsolateThread::SetCreateHistogramFunction);
+ InitializeTestHelper(InitDefaultIsolateThread::SetCounterFunction);
}
TEST(InitializeDefaultIsolateOnSecondaryThread5) {
+ InitializeTestHelper(InitDefaultIsolateThread::SetCreateHistogramFunction);
+}
+
+
+TEST(InitializeDefaultIsolateOnSecondaryThread6) {
InitializeTestHelper(InitDefaultIsolateThread::SetAddHistogramSampleFunction);
}
}
+TEST(RegExpOverflow) {
+ // Result string has the length 2^32, causing a 32-bit integer overflow.
+ CcTest::InitializeVM();
+ v8::HandleScope scope(CcTest::isolate());
+ LocalContext context;
+ v8::V8::IgnoreOutOfMemoryException();
+ v8::Local<v8::Value> result = CompileRun(
+ "var a = 'a'; "
+ "for (var i = 0; i < 16; i++) { "
+ " a += a; "
+ "} "
+ "a.replace(/a/g, a); ");
+ CHECK(result.IsEmpty());
+ CHECK(context->HasOutOfMemoryException());
+}
+
+
TEST(StringReplaceAtomTwoByteResult) {
CcTest::InitializeVM();
v8::HandleScope scope(CcTest::isolate());