Commit a2be3b6f authored by kasperl@chromium.org's avatar kasperl@chromium.org

Make sure that allocations through CALL_HEAP_FUNCTION

and runtime calls from JavaScript will always succeed
eventually if we have enough memory.
Review URL: http://codereview.chromium.org/8700

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@646 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 24cb757a
......@@ -564,6 +564,10 @@ ExternalReference ExternalReference::new_space_allocation_top_address() {
return ExternalReference(Heap::NewSpaceAllocationTopAddress());
}
ExternalReference ExternalReference::heap_always_allocate_scope_depth() {
return ExternalReference(Heap::always_allocate_scope_depth_address());
}
ExternalReference ExternalReference::new_space_allocation_limit_address() {
return ExternalReference(Heap::NewSpaceAllocationLimitAddress());
}
......
......@@ -429,6 +429,7 @@ class ExternalReference BASE_EMBEDDED {
// Static variable Heap::NewSpaceStart()
static ExternalReference new_space_start();
static ExternalReference heap_always_allocate_scope_depth();
// Used for fast allocation in generated code.
static ExternalReference new_space_allocation_top_address();
......
......@@ -3812,7 +3812,8 @@ void CEntryStub::GenerateCore(MacroAssembler* masm,
Label* throw_normal_exception,
Label* throw_out_of_memory_exception,
StackFrame::Type frame_type,
bool do_gc) {
bool do_gc,
bool always_allocate) {
// r0: result parameter for PerformGC, if any
// r4: number of arguments including receiver (C callee-saved)
// r5: pointer to builtin function (C callee-saved)
......@@ -3823,6 +3824,15 @@ void CEntryStub::GenerateCore(MacroAssembler* masm,
__ Call(FUNCTION_ADDR(Runtime::PerformGC), RelocInfo::RUNTIME_ENTRY);
}
ExternalReference scope_depth =
ExternalReference::heap_always_allocate_scope_depth();
if (always_allocate) {
__ mov(r0, Operand(scope_depth));
__ ldr(r1, MemOperand(r0));
__ add(r1, r1, Operand(1));
__ str(r1, MemOperand(r0));
}
// Call C built-in.
// r0 = argc, r1 = argv
__ mov(r0, Operand(r4));
......@@ -3843,7 +3853,15 @@ void CEntryStub::GenerateCore(MacroAssembler* masm,
#else /* !defined(__arm__) */
__ mov(pc, Operand(r5));
#endif /* !defined(__arm__) */
// result is in r0 or r0:r1 - do not destroy these registers!
if (always_allocate) {
// It's okay to clobber r2 and r3 here. Don't mess with r0 and r1
// though (contain the result).
__ mov(r2, Operand(scope_depth));
__ ldr(r3, MemOperand(r2));
__ sub(r3, r3, Operand(1));
__ str(r3, MemOperand(r2));
}
// check for failure result
Label failure_returned;
......@@ -3929,14 +3947,16 @@ void CEntryStub::GenerateBody(MacroAssembler* masm, bool is_debug_break) {
GenerateCore(masm, &throw_normal_exception,
&throw_out_of_memory_exception,
frame_type,
FLAG_gc_greedy);
FLAG_gc_greedy,
false);
// Do space-specific GC and retry runtime call.
GenerateCore(masm,
&throw_normal_exception,
&throw_out_of_memory_exception,
frame_type,
true);
true,
false);
// Do full GC and retry runtime call one final time.
Failure* failure = Failure::InternalError();
......@@ -3945,6 +3965,7 @@ void CEntryStub::GenerateBody(MacroAssembler* masm, bool is_debug_break) {
&throw_normal_exception,
&throw_out_of_memory_exception,
frame_type,
true,
true);
__ bind(&throw_out_of_memory_exception);
......
......@@ -4809,7 +4809,8 @@ void CEntryStub::GenerateCore(MacroAssembler* masm,
Label* throw_normal_exception,
Label* throw_out_of_memory_exception,
StackFrame::Type frame_type,
bool do_gc) {
bool do_gc,
bool always_allocate_scope) {
// eax: result parameter for PerformGC, if any
// ebx: pointer to C function (C callee-saved)
// ebp: frame pointer (restored after C call)
......@@ -4822,12 +4823,22 @@ void CEntryStub::GenerateCore(MacroAssembler* masm,
__ call(FUNCTION_ADDR(Runtime::PerformGC), RelocInfo::RUNTIME_ENTRY);
}
ExternalReference scope_depth =
ExternalReference::heap_always_allocate_scope_depth();
if (always_allocate_scope) {
__ inc(Operand::StaticVariable(scope_depth));
}
// Call C function.
__ mov(Operand(esp, 0 * kPointerSize), edi); // argc.
__ mov(Operand(esp, 1 * kPointerSize), esi); // argv.
__ call(Operand(ebx));
// Result is in eax or edx:eax - do not destroy these registers!
if (always_allocate_scope) {
__ dec(Operand::StaticVariable(scope_depth));
}
// Check for failure result.
Label failure_returned;
ASSERT(((kFailureTag + 1) & kFailureTagMask) == 0);
......@@ -4963,14 +4974,16 @@ void CEntryStub::GenerateBody(MacroAssembler* masm, bool is_debug_break) {
GenerateCore(masm, &throw_normal_exception,
&throw_out_of_memory_exception,
frame_type,
FLAG_gc_greedy);
FLAG_gc_greedy,
false);
// Do space-specific GC and retry runtime call.
GenerateCore(masm,
&throw_normal_exception,
&throw_out_of_memory_exception,
frame_type,
true);
true,
false);
// Do full GC and retry runtime call one final time.
Failure* failure = Failure::InternalError();
......@@ -4979,6 +4992,7 @@ void CEntryStub::GenerateBody(MacroAssembler* masm, bool is_debug_break) {
&throw_normal_exception,
&throw_out_of_memory_exception,
frame_type,
true,
true);
__ bind(&throw_out_of_memory_exception);
......
......@@ -196,7 +196,8 @@ class CEntryStub : public CodeStub {
Label* throw_normal_exception,
Label* throw_out_of_memory_exception,
StackFrame::Type frame_type,
bool do_gc);
bool do_gc,
bool always_allocate_scope);
void GenerateThrowTOS(MacroAssembler* masm);
void GenerateThrowOutOfMemory(MacroAssembler* masm);
......
This diff is collapsed.
......@@ -96,6 +96,8 @@ Heap::HeapState Heap::gc_state_ = NOT_IN_GC;
int Heap::mc_count_ = 0;
int Heap::gc_count_ = 0;
int Heap::always_allocate_scope_depth_ = 0;
#ifdef DEBUG
bool Heap::allocation_allowed_ = true;
......@@ -1025,7 +1027,7 @@ Object* Heap::AllocateHeapNumber(double value, PretenureFlag pretenure) {
// spaces.
STATIC_ASSERT(HeapNumber::kSize <= Page::kMaxHeapObjectSize);
AllocationSpace space = (pretenure == TENURED) ? OLD_DATA_SPACE : NEW_SPACE;
Object* result = AllocateRaw(HeapNumber::kSize, space);
Object* result = AllocateRaw(HeapNumber::kSize, space, OLD_DATA_SPACE);
if (result->IsFailure()) return result;
HeapObject::cast(result)->set_map(heap_number_map());
......@@ -1035,6 +1037,8 @@ Object* Heap::AllocateHeapNumber(double value, PretenureFlag pretenure) {
Object* Heap::AllocateHeapNumber(double value) {
// Use general version, if we're forced to always allocate.
if (always_allocate()) return AllocateHeapNumber(value, NOT_TENURED);
// This version of AllocateHeapNumber is optimized for
// allocation in new space.
STATIC_ASSERT(HeapNumber::kSize <= Page::kMaxHeapObjectSize);
......@@ -1531,7 +1535,7 @@ Object* Heap::AllocateByteArray(int length) {
AllocationSpace space =
size > MaxHeapObjectSize() ? LO_SPACE : NEW_SPACE;
Object* result = AllocateRaw(size, space);
Object* result = AllocateRaw(size, space, OLD_DATA_SPACE);
if (result->IsFailure()) return result;
......@@ -1604,7 +1608,9 @@ Object* Heap::CopyCode(Code* code) {
Object* Heap::Allocate(Map* map, AllocationSpace space) {
ASSERT(gc_state_ == NOT_IN_GC);
ASSERT(map->instance_type() != MAP_TYPE);
Object* result = AllocateRaw(map->instance_size(), space);
Object* result = AllocateRaw(map->instance_size(),
space,
TargetSpaceId(map->instance_type()));
if (result->IsFailure()) return result;
HeapObject::cast(result)->set_map(map);
return result;
......@@ -1664,19 +1670,19 @@ Object* Heap::AllocateArgumentsObject(Object* callee, int length) {
// Make the clone.
Map* map = boilerplate->map();
int object_size = map->instance_size();
Object* result = new_space_.AllocateRaw(object_size);
Object* result = AllocateRaw(object_size, NEW_SPACE, OLD_POINTER_SPACE);
if (result->IsFailure()) return result;
ASSERT(Heap::InNewSpace(result));
// Copy the content.
// Copy the content. The arguments boilerplate doesn't have any
// fields that point to new space so it's safe to skip the write
// barrier here.
CopyBlock(reinterpret_cast<Object**>(HeapObject::cast(result)->address()),
reinterpret_cast<Object**>(boilerplate->address()),
object_size);
// Set the two properties.
JSObject::cast(result)->InObjectPropertyAtPut(arguments_callee_index,
callee,
SKIP_WRITE_BARRIER);
callee);
JSObject::cast(result)->InObjectPropertyAtPut(arguments_length_index,
Smi::FromInt(length),
SKIP_WRITE_BARRIER);
......@@ -1784,14 +1790,33 @@ Object* Heap::CopyJSObject(JSObject* source) {
// Make the clone.
Map* map = source->map();
int object_size = map->instance_size();
Object* clone = new_space_.AllocateRaw(object_size);
if (clone->IsFailure()) return clone;
ASSERT(Heap::InNewSpace(clone));
// Copy the content.
CopyBlock(reinterpret_cast<Object**>(HeapObject::cast(clone)->address()),
reinterpret_cast<Object**>(source->address()),
object_size);
Object* clone;
// If we're forced to always allocate, we use the general allocation
// functions which may leave us with an object in old space.
if (always_allocate()) {
clone = AllocateRaw(object_size, NEW_SPACE, OLD_POINTER_SPACE);
if (clone->IsFailure()) return clone;
Address clone_address = HeapObject::cast(clone)->address();
CopyBlock(reinterpret_cast<Object**>(clone_address),
reinterpret_cast<Object**>(source->address()),
object_size);
// Update write barrier for all fields that lie beyond the header.
for (int offset = JSObject::kHeaderSize;
offset < object_size;
offset += kPointerSize) {
RecordWrite(clone_address, offset);
}
} else {
clone = new_space_.AllocateRaw(object_size);
if (clone->IsFailure()) return clone;
ASSERT(Heap::InNewSpace(clone));
// Since we know the clone is allocated in new space, we can copy
// the contents without worring about updating the write barrier.
CopyBlock(reinterpret_cast<Object**>(HeapObject::cast(clone)->address()),
reinterpret_cast<Object**>(source->address()),
object_size);
}
FixedArray* elements = FixedArray::cast(source->elements());
FixedArray* properties = FixedArray::cast(source->properties());
......@@ -2013,7 +2038,7 @@ Object* Heap::AllocateSymbol(unibrow::CharacterStream* buffer,
// Allocate string.
AllocationSpace space =
(size > MaxHeapObjectSize()) ? LO_SPACE : OLD_DATA_SPACE;
Object* result = AllocateRaw(size, space);
Object* result = AllocateRaw(size, space, OLD_DATA_SPACE);
if (result->IsFailure()) return result;
reinterpret_cast<HeapObject*>(result)->set_map(map);
......@@ -2039,7 +2064,7 @@ Object* Heap::AllocateRawAsciiString(int length, PretenureFlag pretenure) {
// Use AllocateRaw rather than Allocate because the object's size cannot be
// determined from the map.
Object* result = AllocateRaw(size, space);
Object* result = AllocateRaw(size, space, OLD_DATA_SPACE);
if (result->IsFailure()) return result;
// Determine the map based on the string's length.
......@@ -2069,7 +2094,7 @@ Object* Heap::AllocateRawTwoByteString(int length, PretenureFlag pretenure) {
// Use AllocateRaw rather than Allocate because the object's size cannot be
// determined from the map.
Object* result = AllocateRaw(size, space);
Object* result = AllocateRaw(size, space, OLD_DATA_SPACE);
if (result->IsFailure()) return result;
// Determine the map based on the string's length.
......@@ -2092,7 +2117,7 @@ Object* Heap::AllocateRawTwoByteString(int length, PretenureFlag pretenure) {
Object* Heap::AllocateEmptyFixedArray() {
int size = FixedArray::SizeFor(0);
Object* result = AllocateRaw(size, OLD_DATA_SPACE);
Object* result = AllocateRaw(size, OLD_DATA_SPACE, OLD_DATA_SPACE);
if (result->IsFailure()) return result;
// Initialize the object.
reinterpret_cast<Array*>(result)->set_map(fixed_array_map());
......@@ -2102,6 +2127,8 @@ Object* Heap::AllocateEmptyFixedArray() {
Object* Heap::AllocateRawFixedArray(int length) {
// Use the general function if we're forced to always allocate.
if (always_allocate()) return AllocateFixedArray(length, NOT_TENURED);
// Allocate the raw data for a fixed array.
int size = FixedArray::SizeFor(length);
return (size > MaxHeapObjectSize())
......@@ -2159,7 +2186,7 @@ Object* Heap::AllocateFixedArray(int length, PretenureFlag pretenure) {
} else {
AllocationSpace space =
(pretenure == TENURED) ? OLD_POINTER_SPACE : NEW_SPACE;
result = AllocateRaw(size, space);
result = AllocateRaw(size, space, OLD_POINTER_SPACE);
}
if (result->IsFailure()) return result;
......
......@@ -260,6 +260,11 @@ class Heap : public AllStatic {
static MapSpace* map_space() { return map_space_; }
static LargeObjectSpace* lo_space() { return lo_space_; }
static bool always_allocate() { return always_allocate_scope_depth_ != 0; }
static Address always_allocate_scope_depth_address() {
return reinterpret_cast<Address>(&always_allocate_scope_depth_);
}
static Address* NewSpaceAllocationTopAddress() {
return new_space_.allocation_top_address();
}
......@@ -523,7 +528,8 @@ class Heap : public AllStatic {
// failed.
// Please note this function does not perform a garbage collection.
static inline Object* AllocateRaw(int size_in_bytes,
AllocationSpace space);
AllocationSpace space,
AllocationSpace retry_space);
// Makes a new native code object
// Returns Failure::RetryAfterGC(requested_bytes, space) if the allocation
......@@ -631,6 +637,7 @@ class Heap : public AllStatic {
// Finds out which space an object should get promoted to based on its type.
static inline OldSpace* TargetSpace(HeapObject* object);
static inline AllocationSpace TargetSpaceId(InstanceType type);
// Sets the stub_cache_ (only used when expanding the dictionary).
static void set_code_stubs(Dictionary* value) { code_stubs_ = value; }
......@@ -767,6 +774,8 @@ class Heap : public AllStatic {
static int new_space_growth_limit_;
static int scavenge_count_;
static int always_allocate_scope_depth_;
static const int kMaxMapSpaceSize = 8*MB;
static NewSpace new_space_;
......@@ -925,6 +934,25 @@ class Heap : public AllStatic {
friend class Factory;
friend class DisallowAllocationFailure;
friend class AlwaysAllocateScope;
};
class AlwaysAllocateScope {
public:
AlwaysAllocateScope() {
// We shouldn't hit any nested scopes, because that requires
// non-handle code to call handle code. The code still works but
// performance will degrade, so we want to catch this situation
// in debug mode.
ASSERT(Heap::always_allocate_scope_depth_ == 0);
Heap::always_allocate_scope_depth_++;
}
~AlwaysAllocateScope() {
Heap::always_allocate_scope_depth_--;
ASSERT(Heap::always_allocate_scope_depth_ == 0);
}
};
......
......@@ -5787,6 +5787,7 @@ void Runtime::PerformGC(Object* result) {
} else {
// Handle last resort GC and make sure to allow future allocations
// to grow the heap without causing GCs (if possible).
Counters::gc_last_resort_from_js.Increment();
Heap::CollectAllGarbage();
}
}
......
......@@ -593,17 +593,21 @@ ExternalReferenceTable::ExternalReferenceTable() : refs_(64) {
UNCLASSIFIED,
5,
"Heap::NewSpaceStart()");
Add(ExternalReference::new_space_allocation_limit_address().address(),
Add(ExternalReference::heap_always_allocate_scope_depth().address(),
UNCLASSIFIED,
6,
"Heap::always_allocate_scope_depth()");
Add(ExternalReference::new_space_allocation_limit_address().address(),
UNCLASSIFIED,
7,
"Heap::NewSpaceAllocationLimitAddress()");
Add(ExternalReference::new_space_allocation_top_address().address(),
UNCLASSIFIED,
7,
8,
"Heap::NewSpaceAllocationTopAddress()");
Add(ExternalReference::debug_step_in_fp_address().address(),
UNCLASSIFIED,
8,
9,
"Debug::step_in_fp_addr()");
}
......@@ -1401,7 +1405,10 @@ Object* Deserializer::GetObject() {
} else if (IsLargeFixedArray(a)) {
o = Heap::lo_space()->AllocateRawFixedArray(size);
} else {
o = Heap::AllocateRaw(size, space);
AllocationSpace retry_space = (space == NEW_SPACE)
? Heap::TargetSpaceId(type)
: space;
o = Heap::AllocateRaw(size, space, retry_space);
}
ASSERT(!o->IsFailure());
// Check that the simulation of heap allocation was correct.
......
......@@ -1533,7 +1533,7 @@ HeapObject* OldSpace::SlowAllocateRaw(int size_in_bytes) {
// Free list allocation failed and there is no next page. Fail if we have
// hit the old generation size limit that should cause a garbage
// collection.
if (Heap::OldGenerationAllocationLimitReached()) {
if (!Heap::always_allocate() && Heap::OldGenerationAllocationLimitReached()) {
return NULL;
}
......@@ -2018,7 +2018,7 @@ HeapObject* MapSpace::SlowAllocateRaw(int size_in_bytes) {
// Free list allocation failed and there is no next page. Fail if we have
// hit the old generation size limit that should cause a garbage
// collection.
if (Heap::OldGenerationAllocationLimitReached()) {
if (!Heap::always_allocate() && Heap::OldGenerationAllocationLimitReached()) {
return NULL;
}
......@@ -2251,7 +2251,7 @@ Object* LargeObjectSpace::AllocateRawInternal(int requested_size,
// Check if we want to force a GC before growing the old space further.
// If so, fail the allocation.
if (Heap::OldGenerationAllocationLimitReached()) {
if (!Heap::always_allocate() && Heap::OldGenerationAllocationLimitReached()) {
return Failure::RetryAfterGC(requested_size, identity());
}
......
......@@ -101,6 +101,8 @@ namespace v8 { namespace internal {
V8.GCCompactorCausedByOldspaceExhaustion) \
SC(gc_compactor_caused_by_weak_handles, \
V8.GCCompactorCausedByWeakHandles) \
SC(gc_last_resort_from_js, V8.GCLastResortFromJS) \
SC(gc_last_resort_from_handles, V8.GCLastResortFromHandles) \
/* How is the generic keyed-load stub used? */ \
SC(keyed_load_generic_smi, V8.KeyedLoadGenericSmi) \
SC(keyed_load_generic_symbol, V8.KeyedLoadGenericSymbol) \
......
......@@ -38,7 +38,7 @@ SOURCES = {
'test-ast.cc', 'test-heap.cc', 'test-utils.cc', 'test-compiler.cc',
'test-spaces.cc', 'test-mark-compact.cc', 'test-lock.cc',
'test-conversions.cc', 'test-strings.cc', 'test-serialize.cc',
'test-decls.cc'
'test-decls.cc', 'test-alloc.cc'
],
'arch:arm': ['test-assembler-arm.cc', 'test-disasm-arm.cc'],
'arch:ia32': ['test-assembler-ia32.cc', 'test-disasm-ia32.cc'],
......
// Copyright 2007-2008 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 "top.h"
#include "cctest.h"
using namespace v8::internal;
static Object* AllocateAfterFailures() {
static int attempts = 0;
if (++attempts < 3) return Failure::RetryAfterGC(0);
// New space.
NewSpace* new_space = Heap::new_space();
static const int kNewSpaceFillerSize = ByteArray::SizeFor(0);
while (new_space->Available() > kNewSpaceFillerSize) {
CHECK(!Heap::AllocateByteArray(0)->IsFailure());
}
CHECK(!Heap::AllocateByteArray(100)->IsFailure());
CHECK(!Heap::AllocateFixedArray(100, NOT_TENURED)->IsFailure());
// Make sure we can allocate through optimized allocation functions
// for specific kinds.
CHECK(!Heap::AllocateFixedArray(100)->IsFailure());
CHECK(!Heap::AllocateHeapNumber(0.42)->IsFailure());
CHECK(!Heap::AllocateArgumentsObject(Smi::FromInt(87), 10)->IsFailure());
Object* object = Heap::AllocateJSObject(*Top::object_function());
CHECK(!Heap::CopyJSObject(JSObject::cast(object))->IsFailure());
// Old data space.
OldSpace* old_data_space = Heap::old_data_space();
static const int kOldDataSpaceFillerSize = SeqAsciiString::SizeFor(0);
while (old_data_space->Available() > kOldDataSpaceFillerSize) {
CHECK(!Heap::AllocateRawAsciiString(0, TENURED)->IsFailure());
}
CHECK(!Heap::AllocateRawAsciiString(100, TENURED)->IsFailure());
// Large object space.
while (!Heap::OldGenerationAllocationLimitReached()) {
CHECK(!Heap::AllocateFixedArray(10000, TENURED)->IsFailure());
}
CHECK(!Heap::AllocateFixedArray(10000, TENURED)->IsFailure());
// Map space.
MapSpace* map_space = Heap::map_space();
static const int kMapSpaceFillerSize = Map::kSize;
InstanceType instance_type = JS_OBJECT_TYPE;
int instance_size = JSObject::kHeaderSize;
while (map_space->Available() > kMapSpaceFillerSize) {
CHECK(!Heap::AllocateMap(instance_type, instance_size)->IsFailure());
}
CHECK(!Heap::AllocateMap(instance_type, instance_size)->IsFailure());
// Test that we can allocate in old pointer space and code space.
CHECK(!Heap::AllocateFixedArray(100, TENURED)->IsFailure());
CHECK(!Heap::CopyCode(Builtins::builtin(Builtins::Illegal))->IsFailure());
// Return success.
return Smi::FromInt(42);
}
static Handle<Object> Test() {
CALL_HEAP_FUNCTION(AllocateAfterFailures(), Object);
}
TEST(Stress) {
v8::Persistent<v8::Context> env = v8::Context::New();
v8::HandleScope scope;
env->Enter();
Handle<Object> o = Test();
CHECK(o->IsSmi() && Smi::cast(*o)->value() == 42);
env->Exit();
}
......@@ -143,6 +143,10 @@
RelativePath="..\..\test\cctest\cctest.cc"
>
</File>
<File
RelativePath="..\..\test\cctest\test-alloc.cc"
>
</File>
<File
RelativePath="..\..\test\cctest\test-api.cc"
>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment