diff options
author | Bert Belder <bertbelder@gmail.com> | 2012-06-13 15:34:45 +0200 |
---|---|---|
committer | Bert Belder <bertbelder@gmail.com> | 2012-06-14 01:37:13 +0200 |
commit | 50464cd4f49e40f4fe792ff46a81052319a222e9 (patch) | |
tree | 1fe524b2e6c0eb3c459142cd27539f88e1a3f63c /deps/v8/test/cctest | |
parent | 09be360a0fee2c7619bae8c4248f9ed3d79d1b30 (diff) | |
download | node-new-50464cd4f49e40f4fe792ff46a81052319a222e9.tar.gz |
v8: upgrade to v3.11.10
Diffstat (limited to 'deps/v8/test/cctest')
22 files changed, 1508 insertions, 459 deletions
diff --git a/deps/v8/test/cctest/cctest.status b/deps/v8/test/cctest/cctest.status index af28be19d8..fc111ab94b 100644 --- a/deps/v8/test/cctest/cctest.status +++ b/deps/v8/test/cctest/cctest.status @@ -27,6 +27,7 @@ prefix cctest +# All tests prefixed with 'Bug' are expected to fail. test-api/Bug*: FAIL ############################################################################## @@ -75,17 +76,3 @@ test-serialize/DeserializeFromSecondSerializationAndRunScript2: SKIP test-serialize/DeserializeAndRunScript2: SKIP test-serialize/DeserializeFromSecondSerialization: SKIP -############################################################################## -[ $arch == arm && $crankshaft ] - -# Tests that time out with crankshaft. -test-debug/ThreadedDebugging: SKIP -test-debug/DebugBreakLoop: SKIP - - -############################################################################## -[ $arch == mips && $crankshaft ] - -# Tests that time out with crankshaft. -test-debug/ThreadedDebugging: SKIP -test-debug/DebugBreakLoop: SKIP diff --git a/deps/v8/test/cctest/test-accessors.cc b/deps/v8/test/cctest/test-accessors.cc index b1900f9ed3..0b342ff3d9 100644 --- a/deps/v8/test/cctest/test-accessors.cc +++ b/deps/v8/test/cctest/test-accessors.cc @@ -1,4 +1,4 @@ -// Copyright 2009 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -116,6 +116,8 @@ static v8::Handle<v8::Object> x_holder; static v8::Handle<Value> XGetter(Local<String> name, const AccessorInfo& info) { ApiTestFuzzer::Fuzz(); + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + CHECK_EQ(isolate, info.GetIsolate()); CHECK_EQ(x_receiver, info.This()); CHECK_EQ(x_holder, info.Holder()); return v8_num(x_register); @@ -125,6 +127,8 @@ static v8::Handle<Value> XGetter(Local<String> name, const AccessorInfo& info) { static void XSetter(Local<String> name, Local<Value> value, const AccessorInfo& info) { + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + CHECK_EQ(isolate, info.GetIsolate()); CHECK_EQ(x_holder, info.This()); CHECK_EQ(x_holder, info.Holder()); x_register = value->Int32Value(); @@ -236,12 +240,15 @@ THREADED_TEST(HandleScopePop) { static v8::Handle<Value> CheckAccessorArgsCorrect(Local<String> name, const AccessorInfo& info) { + CHECK(info.GetIsolate() == v8::Isolate::GetCurrent()); CHECK(info.This() == info.Holder()); CHECK(info.Data()->Equals(v8::String::New("data"))); ApiTestFuzzer::Fuzz(); + CHECK(info.GetIsolate() == v8::Isolate::GetCurrent()); CHECK(info.This() == info.Holder()); CHECK(info.Data()->Equals(v8::String::New("data"))); HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); + CHECK(info.GetIsolate() == v8::Isolate::GetCurrent()); CHECK(info.This() == info.Holder()); CHECK(info.Data()->Equals(v8::String::New("data"))); return v8::Integer::New(17); diff --git a/deps/v8/test/cctest/test-alloc.cc b/deps/v8/test/cctest/test-alloc.cc index 769fe7be29..e195d14923 100644 --- a/deps/v8/test/cctest/test-alloc.cc +++ b/deps/v8/test/cctest/test-alloc.cc @@ -34,6 +34,15 @@ using namespace v8::internal; +static inline void SimulateFullSpace(PagedSpace* space) { + int old_linear_size = static_cast<int>(space->limit() - space->top()); + space->Free(space->top(), old_linear_size); + space->SetTop(space->limit(), space->limit()); + space->ResetFreeList(); + space->ClearStats(); +} + + static MaybeObject* AllocateAfterFailures() { static int attempts = 0; if (++attempts < 3) return Failure::RetryAfterGC(); @@ -65,24 +74,12 @@ static MaybeObject* AllocateAfterFailures() { CHECK(!heap->CopyJSObject(JSObject::cast(object))->IsFailure()); // Old data space. - OldSpace* old_data_space = heap->old_data_space(); - static const int kOldDataSpaceFillerSize = ByteArray::SizeFor(0); - while (old_data_space->Available() > kOldDataSpaceFillerSize) { - CHECK(!heap->AllocateByteArray(0, TENURED)->IsFailure()); - } + SimulateFullSpace(heap->old_data_space()); CHECK(!heap->AllocateRawAsciiString(100, TENURED)->IsFailure()); // Old pointer space. - OldSpace* old_pointer_space = heap->old_pointer_space(); - static const int kOldPointerSpaceFillerLength = 10000; - static const int kOldPointerSpaceFillerSize = FixedArray::SizeFor( - kOldPointerSpaceFillerLength); - while (old_pointer_space->Available() > kOldPointerSpaceFillerSize) { - CHECK(!heap->AllocateFixedArray(kOldPointerSpaceFillerLength, TENURED)-> - IsFailure()); - } - CHECK(!heap->AllocateFixedArray(kOldPointerSpaceFillerLength, TENURED)-> - IsFailure()); + SimulateFullSpace(heap->old_pointer_space()); + CHECK(!heap->AllocateFixedArray(10000, TENURED)->IsFailure()); // Large object space. static const int kLargeObjectSpaceFillerLength = 300000; @@ -97,14 +94,9 @@ static MaybeObject* AllocateAfterFailures() { IsFailure()); // Map space. - MapSpace* map_space = heap->map_space(); - static const int kMapSpaceFillerSize = Map::kSize; - InstanceType instance_type = JS_OBJECT_TYPE; + SimulateFullSpace(heap->map_space()); 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()); + CHECK(!heap->AllocateMap(JS_OBJECT_TYPE, instance_size)->IsFailure()); // Test that we can allocate in old pointer space and code space. CHECK(!heap->AllocateFixedArray(100, TENURED)->IsFailure()); diff --git a/deps/v8/test/cctest/test-api.cc b/deps/v8/test/cctest/test-api.cc index f4ab9ad0df..ed31f6ffae 100644 --- a/deps/v8/test/cctest/test-api.cc +++ b/deps/v8/test/cctest/test-api.cc @@ -7662,7 +7662,7 @@ THREADED_TEST(ShadowObject) { value = Script::Compile(v8_str("f()"))->Run(); CHECK_EQ(42, value->Int32Value()); - Script::Compile(v8_str("y = 42"))->Run(); + Script::Compile(v8_str("y = 43"))->Run(); CHECK_EQ(1, shadow_y_setter_call_count); value = Script::Compile(v8_str("y"))->Run(); CHECK_EQ(1, shadow_y_getter_call_count); @@ -8608,6 +8608,8 @@ static void CheckInterceptorLoadIC(NamedPropertyGetter getter, static v8::Handle<Value> InterceptorLoadICGetter(Local<String> name, const AccessorInfo& info) { ApiTestFuzzer::Fuzz(); + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + CHECK_EQ(isolate, info.GetIsolate()); CHECK_EQ(v8_str("data"), info.Data()); CHECK_EQ(v8_str("x"), name); return v8::Integer::New(42); @@ -9334,6 +9336,8 @@ static v8::Handle<Value> InterceptorCallICFastApi(Local<String> name, static v8::Handle<Value> FastApiCallback_TrivialSignature( const v8::Arguments& args) { ApiTestFuzzer::Fuzz(); + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + CHECK_EQ(isolate, args.GetIsolate()); CHECK_EQ(args.This(), args.Holder()); CHECK(args.Data()->Equals(v8_str("method_data"))); return v8::Integer::New(args[0]->Int32Value() + 1); @@ -9342,6 +9346,8 @@ static v8::Handle<Value> FastApiCallback_TrivialSignature( static v8::Handle<Value> FastApiCallback_SimpleSignature( const v8::Arguments& args) { ApiTestFuzzer::Fuzz(); + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + CHECK_EQ(isolate, args.GetIsolate()); CHECK_EQ(args.This()->GetPrototype(), args.Holder()); CHECK(args.Data()->Equals(v8_str("method_data"))); // Note, we're using HasRealNamedProperty instead of Has to avoid @@ -10254,6 +10260,7 @@ static v8::Handle<Value> ChildGetter(Local<String> name, THREADED_TEST(Overriding) { + i::FLAG_es5_readonly = true; v8::HandleScope scope; LocalContext context; @@ -10300,11 +10307,11 @@ THREADED_TEST(Overriding) { value = v8_compile("o.g")->Run(); CHECK_EQ(42, value->Int32Value()); - // Check 'h' can be shadowed. + // Check that 'h' cannot be shadowed. value = v8_compile("o.h = 3; o.h")->Run(); - CHECK_EQ(3, value->Int32Value()); + CHECK_EQ(1, value->Int32Value()); - // Check 'i' is cannot be shadowed or changed. + // Check that 'i' cannot be shadowed or changed. value = v8_compile("o.i = 3; o.i")->Run(); CHECK_EQ(42, value->Int32Value()); } @@ -10865,13 +10872,18 @@ THREADED_TEST(NestedHandleScopeAndContexts) { } +static int64_t cast(intptr_t x) { return static_cast<int64_t>(x); } + + THREADED_TEST(ExternalAllocatedMemory) { v8::HandleScope outer; v8::Persistent<Context> env(Context::New()); CHECK(!env.IsEmpty()); - const int kSize = 1024*1024; - CHECK_EQ(v8::V8::AdjustAmountOfExternalAllocatedMemory(kSize), kSize); - CHECK_EQ(v8::V8::AdjustAmountOfExternalAllocatedMemory(-kSize), 0); + const intptr_t kSize = 1024*1024; + CHECK_EQ(cast(v8::V8::AdjustAmountOfExternalAllocatedMemory(kSize)), + cast(kSize)); + CHECK_EQ(cast(v8::V8::AdjustAmountOfExternalAllocatedMemory(-kSize)), + cast(0)); } @@ -12137,9 +12149,10 @@ TEST(RegExpStringModification) { } -// Test that we can set a property on the global object even if there +// Test that we cannot set a property on the global object if there // is a read-only property in the prototype chain. TEST(ReadOnlyPropertyInGlobalProto) { + i::FLAG_es5_readonly = true; v8::HandleScope scope; v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(); LocalContext context(0, templ); @@ -12151,12 +12164,13 @@ TEST(ReadOnlyPropertyInGlobalProto) { // Check without 'eval' or 'with'. v8::Handle<v8::Value> res = CompileRun("function f() { x = 42; return x; }; f()"); + CHECK_EQ(v8::Integer::New(0), res); // Check with 'eval'. - res = CompileRun("function f() { eval('1'); y = 42; return y; }; f()"); - CHECK_EQ(v8::Integer::New(42), res); + res = CompileRun("function f() { eval('1'); y = 43; return y; }; f()"); + CHECK_EQ(v8::Integer::New(0), res); // Check with 'with'. - res = CompileRun("function f() { with (this) { y = 42 }; return y; }; f()"); - CHECK_EQ(v8::Integer::New(42), res); + res = CompileRun("function f() { with (this) { y = 44 }; return y; }; f()"); + CHECK_EQ(v8::Integer::New(0), res); } static int force_set_set_count = 0; @@ -12365,6 +12379,46 @@ THREADED_TEST(ForceDeleteIC) { } +TEST(InlinedFunctionAcrossContexts) { + i::FLAG_allow_natives_syntax = true; + v8::HandleScope outer_scope; + v8::Persistent<v8::Context> ctx1 = v8::Context::New(); + v8::Persistent<v8::Context> ctx2 = v8::Context::New(); + ctx1->Enter(); + + { + v8::HandleScope inner_scope; + CompileRun("var G = 42; function foo() { return G; }"); + v8::Local<v8::Value> foo = ctx1->Global()->Get(v8_str("foo")); + ctx2->Enter(); + ctx2->Global()->Set(v8_str("o"), foo); + v8::Local<v8::Value> res = CompileRun( + "function f() { return o(); }" + "for (var i = 0; i < 10; ++i) f();" + "%OptimizeFunctionOnNextCall(f);" + "f();"); + CHECK_EQ(42, res->Int32Value()); + ctx2->Exit(); + v8::Handle<v8::String> G_property = v8::String::New("G"); + CHECK(ctx1->Global()->ForceDelete(G_property)); + ctx2->Enter(); + ExpectString( + "(function() {" + " try {" + " return f();" + " } catch(e) {" + " return e.toString();" + " }" + " })()", + "ReferenceError: G is not defined"); + ctx2->Exit(); + ctx1->Exit(); + ctx1.Dispose(); + } + ctx2.Dispose(); +} + + v8::Persistent<Context> calling_context0; v8::Persistent<Context> calling_context1; v8::Persistent<Context> calling_context2; @@ -12430,19 +12484,16 @@ THREADED_TEST(GetCallingContext) { // Check that a variable declaration with no explicit initialization -// value does not shadow an existing property in the prototype chain. -// -// This is consistent with Firefox and Safari. -// -// See http://crbug.com/12548. +// value does shadow an existing property in the prototype chain. THREADED_TEST(InitGlobalVarInProtoChain) { + i::FLAG_es52_globals = true; v8::HandleScope scope; LocalContext context; // Introduce a variable in the prototype chain. CompileRun("__proto__.x = 42"); - v8::Handle<v8::Value> result = CompileRun("var x; x"); + v8::Handle<v8::Value> result = CompileRun("var x = 43; x"); CHECK(!result->IsUndefined()); - CHECK_EQ(42, result->Int32Value()); + CHECK_EQ(43, result->Int32Value()); } @@ -13947,75 +13998,104 @@ TEST(SourceURLInStackTrace) { } +static void CreateGarbageInOldSpace() { + v8::HandleScope scope; + i::AlwaysAllocateScope always_allocate; + for (int i = 0; i < 1000; i++) { + FACTORY->NewFixedArray(1000, i::TENURED); + } +} + // Test that idle notification can be handled and eventually returns true. -// This just checks the contract of the IdleNotification() function, -// and does not verify that it does reasonable work. -THREADED_TEST(IdleNotification) { +TEST(IdleNotification) { + const intptr_t MB = 1024 * 1024; v8::HandleScope scope; LocalContext env; - { - // Create garbage in old-space to generate work for idle notification. - i::AlwaysAllocateScope always_allocate; - for (int i = 0; i < 100; i++) { - FACTORY->NewFixedArray(1000, i::TENURED); - } + intptr_t initial_size = HEAP->SizeOfObjects(); + CreateGarbageInOldSpace(); + intptr_t size_with_garbage = HEAP->SizeOfObjects(); + CHECK_GT(size_with_garbage, initial_size + MB); + bool finished = false; + for (int i = 0; i < 200 && !finished; i++) { + finished = v8::V8::IdleNotification(); } - bool finshed_idle_work = false; - for (int i = 0; i < 100 && !finshed_idle_work; i++) { - finshed_idle_work = v8::V8::IdleNotification(); - } - CHECK(finshed_idle_work); + intptr_t final_size = HEAP->SizeOfObjects(); + CHECK(finished); + CHECK_LT(final_size, initial_size + 1); } -// Test that idle notification can be handled and eventually returns true. -// This just checks the contract of the IdleNotification() function, -// and does not verify that it does reasonable work. + +// Test that idle notification can be handled and eventually collects garbage. TEST(IdleNotificationWithSmallHint) { + const intptr_t MB = 1024 * 1024; + const int IdlePauseInMs = 900; v8::HandleScope scope; LocalContext env; - { - // Create garbage in old-space to generate work for idle notification. - i::AlwaysAllocateScope always_allocate; - for (int i = 0; i < 100; i++) { - FACTORY->NewFixedArray(1000, i::TENURED); - } + intptr_t initial_size = HEAP->SizeOfObjects(); + CreateGarbageInOldSpace(); + intptr_t size_with_garbage = HEAP->SizeOfObjects(); + CHECK_GT(size_with_garbage, initial_size + MB); + bool finished = false; + for (int i = 0; i < 200 && !finished; i++) { + finished = v8::V8::IdleNotification(IdlePauseInMs); } - intptr_t old_size = HEAP->SizeOfObjects(); - bool finshed_idle_work = false; - bool no_idle_work = v8::V8::IdleNotification(10); - for (int i = 0; i < 200 && !finshed_idle_work; i++) { - finshed_idle_work = v8::V8::IdleNotification(10); - } - intptr_t new_size = HEAP->SizeOfObjects(); - CHECK(finshed_idle_work); - CHECK(no_idle_work || new_size < old_size); + intptr_t final_size = HEAP->SizeOfObjects(); + CHECK(finished); + CHECK_LT(final_size, initial_size + 1); } -// This just checks the contract of the IdleNotification() function, -// and does not verify that it does reasonable work. +// Test that idle notification can be handled and eventually collects garbage. TEST(IdleNotificationWithLargeHint) { + const intptr_t MB = 1024 * 1024; + const int IdlePauseInMs = 900; v8::HandleScope scope; LocalContext env; - { - // Create garbage in old-space to generate work for idle notification. - i::AlwaysAllocateScope always_allocate; - for (int i = 0; i < 100; i++) { - FACTORY->NewFixedArray(1000, i::TENURED); - } - } - intptr_t old_size = HEAP->SizeOfObjects(); - bool finshed_idle_work = false; - bool no_idle_work = v8::V8::IdleNotification(900); - for (int i = 0; i < 200 && !finshed_idle_work; i++) { - finshed_idle_work = v8::V8::IdleNotification(900); + intptr_t initial_size = HEAP->SizeOfObjects(); + CreateGarbageInOldSpace(); + intptr_t size_with_garbage = HEAP->SizeOfObjects(); + CHECK_GT(size_with_garbage, initial_size + MB); + bool finished = false; + for (int i = 0; i < 200 && !finished; i++) { + finished = v8::V8::IdleNotification(IdlePauseInMs); } - intptr_t new_size = HEAP->SizeOfObjects(); - CHECK(finshed_idle_work); - CHECK(no_idle_work || new_size < old_size); + intptr_t final_size = HEAP->SizeOfObjects(); + CHECK(finished); + CHECK_LT(final_size, initial_size + 1); } +TEST(Regress2107) { + const intptr_t MB = 1024 * 1024; + const int kShortIdlePauseInMs = 100; + const int kLongIdlePauseInMs = 1000; + v8::HandleScope scope; + LocalContext env; + intptr_t initial_size = HEAP->SizeOfObjects(); + // Send idle notification to start a round of incremental GCs. + v8::V8::IdleNotification(kShortIdlePauseInMs); + // Emulate 7 page reloads. + for (int i = 0; i < 7; i++) { + v8::Persistent<v8::Context> ctx = v8::Context::New(); + ctx->Enter(); + CreateGarbageInOldSpace(); + ctx->Exit(); + ctx.Dispose(); + v8::V8::ContextDisposedNotification(); + v8::V8::IdleNotification(kLongIdlePauseInMs); + } + // Create garbage and check that idle notification still collects it. + CreateGarbageInOldSpace(); + intptr_t size_with_garbage = HEAP->SizeOfObjects(); + CHECK_GT(size_with_garbage, initial_size + MB); + bool finished = false; + for (int i = 0; i < 200 && !finished; i++) { + finished = v8::V8::IdleNotification(kShortIdlePauseInMs); + } + intptr_t final_size = HEAP->SizeOfObjects(); + CHECK_LT(final_size, initial_size + 1); +} + static uint32_t* stack_limit; static v8::Handle<Value> GetStackLimitCallback(const v8::Arguments& args) { @@ -16425,3 +16505,309 @@ TEST(PrimaryStubCache) { StubCacheHelper(false); } + +static int fatal_error_callback_counter = 0; +static void CountingErrorCallback(const char* location, const char* message) { + printf("CountingErrorCallback(\"%s\", \"%s\")\n", location, message); + fatal_error_callback_counter++; +} + + +TEST(StaticGetters) { + v8::HandleScope scope; + LocalContext context; + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + i::Handle<i::Object> undefined_value = FACTORY->undefined_value(); + CHECK(*v8::Utils::OpenHandle(*v8::Undefined()) == *undefined_value); + CHECK(*v8::Utils::OpenHandle(*v8::Undefined(isolate)) == *undefined_value); + i::Handle<i::Object> null_value = FACTORY->null_value(); + CHECK(*v8::Utils::OpenHandle(*v8::Null()) == *null_value); + CHECK(*v8::Utils::OpenHandle(*v8::Null(isolate)) == *null_value); + i::Handle<i::Object> true_value = FACTORY->true_value(); + CHECK(*v8::Utils::OpenHandle(*v8::True()) == *true_value); + CHECK(*v8::Utils::OpenHandle(*v8::True(isolate)) == *true_value); + i::Handle<i::Object> false_value = FACTORY->false_value(); + CHECK(*v8::Utils::OpenHandle(*v8::False()) == *false_value); + CHECK(*v8::Utils::OpenHandle(*v8::False(isolate)) == *false_value); + + // Test after-death behavior. + CHECK(i::Internals::IsInitialized(isolate)); + CHECK_EQ(0, fatal_error_callback_counter); + v8::V8::SetFatalErrorHandler(CountingErrorCallback); + v8::Utils::ReportApiFailure("StaticGetters()", "Kill V8"); + i::Isolate::Current()->TearDown(); + CHECK(!i::Internals::IsInitialized(isolate)); + CHECK_EQ(1, fatal_error_callback_counter); + CHECK(v8::Undefined().IsEmpty()); + CHECK_EQ(2, fatal_error_callback_counter); + CHECK(v8::Undefined(isolate).IsEmpty()); + CHECK_EQ(3, fatal_error_callback_counter); + CHECK(v8::Null().IsEmpty()); + CHECK_EQ(4, fatal_error_callback_counter); + CHECK(v8::Null(isolate).IsEmpty()); + CHECK_EQ(5, fatal_error_callback_counter); + CHECK(v8::True().IsEmpty()); + CHECK_EQ(6, fatal_error_callback_counter); + CHECK(v8::True(isolate).IsEmpty()); + CHECK_EQ(7, fatal_error_callback_counter); + CHECK(v8::False().IsEmpty()); + CHECK_EQ(8, fatal_error_callback_counter); + CHECK(v8::False(isolate).IsEmpty()); + CHECK_EQ(9, fatal_error_callback_counter); +} + + +TEST(IsolateEmbedderData) { + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + CHECK_EQ(NULL, isolate->GetData()); + CHECK_EQ(NULL, ISOLATE->GetData()); + static void* data1 = reinterpret_cast<void*>(0xacce55ed); + isolate->SetData(data1); + CHECK_EQ(data1, isolate->GetData()); + CHECK_EQ(data1, ISOLATE->GetData()); + static void* data2 = reinterpret_cast<void*>(0xdecea5ed); + ISOLATE->SetData(data2); + CHECK_EQ(data2, isolate->GetData()); + CHECK_EQ(data2, ISOLATE->GetData()); + ISOLATE->TearDown(); + CHECK_EQ(data2, isolate->GetData()); + CHECK_EQ(data2, ISOLATE->GetData()); +} + + +TEST(StringEmpty) { + v8::HandleScope scope; + LocalContext context; + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + i::Handle<i::Object> empty_string = FACTORY->empty_symbol(); + CHECK(*v8::Utils::OpenHandle(*v8::String::Empty()) == *empty_string); + CHECK(*v8::Utils::OpenHandle(*v8::String::Empty(isolate)) == *empty_string); + + // Test after-death behavior. + CHECK(i::Internals::IsInitialized(isolate)); + CHECK_EQ(0, fatal_error_callback_counter); + v8::V8::SetFatalErrorHandler(CountingErrorCallback); + v8::Utils::ReportApiFailure("StringEmpty()", "Kill V8"); + i::Isolate::Current()->TearDown(); + CHECK(!i::Internals::IsInitialized(isolate)); + CHECK_EQ(1, fatal_error_callback_counter); + CHECK(v8::String::Empty().IsEmpty()); + CHECK_EQ(2, fatal_error_callback_counter); + CHECK(v8::String::Empty(isolate).IsEmpty()); + CHECK_EQ(3, fatal_error_callback_counter); +} + + +static int instance_checked_getter_count = 0; +static Handle<Value> InstanceCheckedGetter(Local<String> name, + const AccessorInfo& info) { + CHECK_EQ(name, v8_str("foo")); + instance_checked_getter_count++; + return v8_num(11); +} + + +static int instance_checked_setter_count = 0; +static void InstanceCheckedSetter(Local<String> name, + Local<Value> value, + const AccessorInfo& info) { + CHECK_EQ(name, v8_str("foo")); + CHECK_EQ(value, v8_num(23)); + instance_checked_setter_count++; +} + + +static void CheckInstanceCheckedResult(int getters, + int setters, + bool expects_callbacks, + TryCatch* try_catch) { + if (expects_callbacks) { + CHECK(!try_catch->HasCaught()); + CHECK_EQ(getters, instance_checked_getter_count); + CHECK_EQ(setters, instance_checked_setter_count); + } else { + CHECK(try_catch->HasCaught()); + CHECK_EQ(0, instance_checked_getter_count); + CHECK_EQ(0, instance_checked_setter_count); + } + try_catch->Reset(); +} + + +static void CheckInstanceCheckedAccessors(bool expects_callbacks) { + instance_checked_getter_count = 0; + instance_checked_setter_count = 0; + TryCatch try_catch; + + // Test path through generic runtime code. + CompileRun("obj.foo"); + CheckInstanceCheckedResult(1, 0, expects_callbacks, &try_catch); + CompileRun("obj.foo = 23"); + CheckInstanceCheckedResult(1, 1, expects_callbacks, &try_catch); + + // Test path through generated LoadIC and StoredIC. + CompileRun("function test_get(o) { o.foo; }" + "test_get(obj);"); + CheckInstanceCheckedResult(2, 1, expects_callbacks, &try_catch); + CompileRun("test_get(obj);"); + CheckInstanceCheckedResult(3, 1, expects_callbacks, &try_catch); + CompileRun("test_get(obj);"); + CheckInstanceCheckedResult(4, 1, expects_callbacks, &try_catch); + CompileRun("function test_set(o) { o.foo = 23; }" + "test_set(obj);"); + CheckInstanceCheckedResult(4, 2, expects_callbacks, &try_catch); + CompileRun("test_set(obj);"); + CheckInstanceCheckedResult(4, 3, expects_callbacks, &try_catch); + CompileRun("test_set(obj);"); + CheckInstanceCheckedResult(4, 4, expects_callbacks, &try_catch); + + // Test path through optimized code. + CompileRun("%OptimizeFunctionOnNextCall(test_get);" + "test_get(obj);"); + CheckInstanceCheckedResult(5, 4, expects_callbacks, &try_catch); + CompileRun("%OptimizeFunctionOnNextCall(test_set);" + "test_set(obj);"); + CheckInstanceCheckedResult(5, 5, expects_callbacks, &try_catch); + + // Cleanup so that closures start out fresh in next check. + CompileRun("%DeoptimizeFunction(test_get);" + "%ClearFunctionTypeFeedback(test_get);" + "%DeoptimizeFunction(test_set);" + "%ClearFunctionTypeFeedback(test_set);"); +} + + +THREADED_TEST(InstanceCheckOnInstanceAccessor) { + v8::internal::FLAG_allow_natives_syntax = true; + v8::HandleScope scope; + LocalContext context; + + Local<FunctionTemplate> templ = FunctionTemplate::New(); + Local<ObjectTemplate> inst = templ->InstanceTemplate(); + inst->SetAccessor(v8_str("foo"), + InstanceCheckedGetter, InstanceCheckedSetter, + Handle<Value>(), + v8::DEFAULT, + v8::None, + v8::AccessorSignature::New(templ)); + context->Global()->Set(v8_str("f"), templ->GetFunction()); + + printf("Testing positive ...\n"); + CompileRun("var obj = new f();"); + CHECK(templ->HasInstance(context->Global()->Get(v8_str("obj")))); + CheckInstanceCheckedAccessors(true); + + printf("Testing negative ...\n"); + CompileRun("var obj = {};" + "obj.__proto__ = new f();"); + CHECK(!templ->HasInstance(context->Global()->Get(v8_str("obj")))); + CheckInstanceCheckedAccessors(false); +} + + +THREADED_TEST(InstanceCheckOnInstanceAccessorWithInterceptor) { + v8::internal::FLAG_allow_natives_syntax = true; + v8::HandleScope scope; + LocalContext context; + + Local<FunctionTemplate> templ = FunctionTemplate::New(); + Local<ObjectTemplate> inst = templ->InstanceTemplate(); + AddInterceptor(templ, EmptyInterceptorGetter, EmptyInterceptorSetter); + inst->SetAccessor(v8_str("foo"), + InstanceCheckedGetter, InstanceCheckedSetter, + Handle<Value>(), + v8::DEFAULT, + v8::None, + v8::AccessorSignature::New(templ)); + context->Global()->Set(v8_str("f"), templ->GetFunction()); + + printf("Testing positive ...\n"); + CompileRun("var obj = new f();"); + CHECK(templ->HasInstance(context->Global()->Get(v8_str("obj")))); + CheckInstanceCheckedAccessors(true); + + printf("Testing negative ...\n"); + CompileRun("var obj = {};" + "obj.__proto__ = new f();"); + CHECK(!templ->HasInstance(context->Global()->Get(v8_str("obj")))); + CheckInstanceCheckedAccessors(false); +} + + +THREADED_TEST(InstanceCheckOnPrototypeAccessor) { + v8::internal::FLAG_allow_natives_syntax = true; + v8::HandleScope scope; + LocalContext context; + + Local<FunctionTemplate> templ = FunctionTemplate::New(); + Local<ObjectTemplate> proto = templ->PrototypeTemplate(); + proto->SetAccessor(v8_str("foo"), + InstanceCheckedGetter, InstanceCheckedSetter, + Handle<Value>(), + v8::DEFAULT, + v8::None, + v8::AccessorSignature::New(templ)); + context->Global()->Set(v8_str("f"), templ->GetFunction()); + + printf("Testing positive ...\n"); + CompileRun("var obj = new f();"); + CHECK(templ->HasInstance(context->Global()->Get(v8_str("obj")))); + CheckInstanceCheckedAccessors(true); + + printf("Testing negative ...\n"); + CompileRun("var obj = {};" + "obj.__proto__ = new f();"); + CHECK(!templ->HasInstance(context->Global()->Get(v8_str("obj")))); + CheckInstanceCheckedAccessors(false); + + printf("Testing positive with modified prototype chain ...\n"); + CompileRun("var obj = new f();" + "var pro = {};" + "pro.__proto__ = obj.__proto__;" + "obj.__proto__ = pro;"); + CHECK(templ->HasInstance(context->Global()->Get(v8_str("obj")))); + CheckInstanceCheckedAccessors(true); +} + + +TEST(TryFinallyMessage) { + v8::HandleScope scope; + LocalContext context; + { + // Test that the original error message is not lost if there is a + // recursive call into Javascript is done in the finally block, e.g. to + // initialize an IC. (crbug.com/129171) + TryCatch try_catch; + const char* trigger_ic = + "try { \n" + " throw new Error('test'); \n" + "} finally { \n" + " var x = 0; \n" + " x++; \n" // Trigger an IC initialization here. + "} \n"; + CompileRun(trigger_ic); + CHECK(try_catch.HasCaught()); + Local<Message> message = try_catch.Message(); + CHECK(!message.IsEmpty()); + CHECK_EQ(2, message->GetLineNumber()); + } + + { + // Test that the original exception message is indeed overwritten if + // a new error is thrown in the finally block. + TryCatch try_catch; + const char* throw_again = + "try { \n" + " throw new Error('test'); \n" + "} finally { \n" + " var x = 0; \n" + " x++; \n" + " throw new Error('again'); \n" // This is the new uncaught error. + "} \n"; + CompileRun(throw_again); + CHECK(try_catch.HasCaught()); + Local<Message> message = try_catch.Message(); + CHECK(!message.IsEmpty()); + CHECK_EQ(6, message->GetLineNumber()); + } +} diff --git a/deps/v8/test/cctest/test-dataflow.cc b/deps/v8/test/cctest/test-dataflow.cc index a63008d210..005d440d13 100644 --- a/deps/v8/test/cctest/test-dataflow.cc +++ b/deps/v8/test/cctest/test-dataflow.cc @@ -37,7 +37,7 @@ using namespace v8::internal; TEST(BitVector) { v8::internal::V8::Initialize(NULL); ZoneScope zone_scope(Isolate::Current(), DELETE_ON_EXIT); - Zone* zone = ZONE; + Zone* zone = Isolate::Current()->zone(); { BitVector v(15, zone); v.Add(1); diff --git a/deps/v8/test/cctest/test-debug.cc b/deps/v8/test/cctest/test-debug.cc index ffa845813f..e40f406c3c 100644 --- a/deps/v8/test/cctest/test-debug.cc +++ b/deps/v8/test/cctest/test-debug.cc @@ -5013,7 +5013,10 @@ static void ThreadedMessageHandler(const v8::Debug::Message& message) { if (IsBreakEventMessage(print_buffer)) { // Check that we are inside the while loop. int source_line = GetSourceLineFromBreakEventMessage(print_buffer); - CHECK(8 <= source_line && source_line <= 13); + // TODO(2047): This should really be 8 <= source_line <= 13; but we + // currently have an off-by-one error when calculating the source + // position corresponding to the program counter at the debug break. + CHECK(7 <= source_line && source_line <= 13); threaded_debugging_barriers.barrier_2.Wait(); } } diff --git a/deps/v8/test/cctest/test-decls.cc b/deps/v8/test/cctest/test-decls.cc index aa733c70bc..e6bdc9f505 100644 --- a/deps/v8/test/cctest/test-decls.cc +++ b/deps/v8/test/cctest/test-decls.cc @@ -521,6 +521,7 @@ class ExistsInPrototypeContext: public DeclarationContext { TEST(ExistsInPrototype) { + i::FLAG_es52_globals = true; HandleScope scope; // Sanity check to make sure that the holder of the interceptor @@ -535,17 +536,17 @@ TEST(ExistsInPrototype) { { ExistsInPrototypeContext context; context.Check("var x; x", - 1, // get + 0, // get 0, - 1, // declaration - EXPECT_EXCEPTION); + 0, // declaration + EXPECT_RESULT, Undefined()); } { ExistsInPrototypeContext context; context.Check("var x = 0; x", 0, 0, - 1, // declaration + 0, // declaration EXPECT_RESULT, Number::New(0)); } @@ -553,7 +554,7 @@ TEST(ExistsInPrototype) { context.Check("const x; x", 0, 0, - 1, // declaration + 0, // declaration EXPECT_RESULT, Undefined()); } @@ -561,7 +562,7 @@ TEST(ExistsInPrototype) { context.Check("const x = 0; x", 0, 0, - 1, // declaration + 0, // declaration EXPECT_RESULT, Number::New(0)); } } @@ -583,13 +584,14 @@ class AbsentInPrototypeContext: public DeclarationContext { TEST(AbsentInPrototype) { + i::FLAG_es52_globals = true; HandleScope scope; { AbsentInPrototypeContext context; context.Check("if (false) { var x = 0; }; x", 0, 0, - 1, // declaration + 0, // declaration EXPECT_RESULT, Undefined()); } } diff --git a/deps/v8/test/cctest/test-disasm-arm.cc b/deps/v8/test/cctest/test-disasm-arm.cc index 0e9432d95d..3a2d9e8361 100644 --- a/deps/v8/test/cctest/test-disasm-arm.cc +++ b/deps/v8/test/cctest/test-disasm-arm.cc @@ -92,6 +92,10 @@ bool DisassembleAndCompare(byte* pc, const char* compare_string) { if (!DisassembleAndCompare(progcounter, compare_string)) failure = true; \ } +// Force emission of any pending literals into a pool. +#define EMIT_PENDING_LITERALS() \ + assm.CheckConstPool(true, false) + // Verify that all invocations of the COMPARE macro passed successfully. // Exit with a failure if at least one of the tests failed. @@ -280,6 +284,10 @@ TEST(Type0) { // is pretty strange anyway. COMPARE(mov(r5, Operand(0x01234), SetCC, ne), "159fc000 ldrne ip, [pc, #+0]"); + // Emit a literal pool now, otherwise this could be dumped later, in the + // middle of a different test. + EMIT_PENDING_LITERALS(); + // We only disassemble one instruction so the eor instruction is not here. // The eor does the setcc so we get a movw here. COMPARE(eor(r5, r4, Operand(0x1234), SetCC, ne), diff --git a/deps/v8/test/cctest/test-disasm-x64.cc b/deps/v8/test/cctest/test-disasm-x64.cc index da85eb933e..c6332e249b 100644 --- a/deps/v8/test/cctest/test-disasm-x64.cc +++ b/deps/v8/test/cctest/test-disasm-x64.cc @@ -264,6 +264,7 @@ TEST(DisasmX64) { ExternalReference after_break_target = ExternalReference(Debug_Address::AfterBreakTarget(), assm.isolate()); + USE(after_break_target); #endif // ENABLE_DEBUGGER_SUPPORT __ jmp(ic, RelocInfo::CODE_TARGET); __ nop(); diff --git a/deps/v8/test/cctest/test-double.cc b/deps/v8/test/cctest/test-double.cc index 3594a4fe32..6ef42c64dd 100644 --- a/deps/v8/test/cctest/test-double.cc +++ b/deps/v8/test/cctest/test-double.cc @@ -112,21 +112,6 @@ TEST(IsInfinite) { } -TEST(IsNan) { - CHECK(Double(OS::nan_value()).IsNan()); - uint64_t other_nan = V8_2PART_UINT64_C(0xFFFFFFFF, 00000001); - CHECK(Double(other_nan).IsNan()); - CHECK(!Double(V8_INFINITY).IsNan()); - CHECK(!Double(-V8_INFINITY).IsNan()); - CHECK(!Double(0.0).IsNan()); - CHECK(!Double(-0.0).IsNan()); - CHECK(!Double(1.0).IsNan()); - CHECK(!Double(-1.0).IsNan()); - uint64_t min_double64 = V8_2PART_UINT64_C(0x00000000, 00000001); - CHECK(!Double(min_double64).IsNan()); -} - - TEST(Sign) { CHECK_EQ(1, Double(1.0).Sign()); CHECK_EQ(1, Double(V8_INFINITY).Sign()); diff --git a/deps/v8/test/cctest/test-func-name-inference.cc b/deps/v8/test/cctest/test-func-name-inference.cc index 8f405b726e..762cc9f0fa 100644 --- a/deps/v8/test/cctest/test-func-name-inference.cc +++ b/deps/v8/test/cctest/test-func-name-inference.cc @@ -400,3 +400,41 @@ TEST(AssignmentAndCall) { // See MultipleAssignments test. CheckFunctionName(script, "return 2", "Enclosing.Bar"); } + + +TEST(MethodAssignmentInAnonymousFunctionCall) { + InitializeVM(); + v8::HandleScope scope; + + v8::Handle<v8::Script> script = Compile( + "(function () {\n" + " var EventSource = function () { };\n" + " EventSource.prototype.addListener = function () {\n" + " return 2012;\n" + " };\n" + " this.PublicEventSource = EventSource;\n" + "})();"); + CheckFunctionName(script, "return 2012", "EventSource.addListener"); +} + + +TEST(ReturnAnonymousFunction) { + InitializeVM(); + v8::HandleScope scope; + + v8::Handle<v8::Script> script = Compile( + "(function() {\n" + " function wrapCode() {\n" + " return function () {\n" + " return 2012;\n" + " };\n" + " };\n" + " var foo = 10;\n" + " function f() {\n" + " return wrapCode();\n" + " }\n" + " this.ref = f;\n" + "})()"); + script->Run(); + CheckFunctionName(script, "return 2012", ""); +} diff --git a/deps/v8/test/cctest/test-heap-profiler.cc b/deps/v8/test/cctest/test-heap-profiler.cc index a56f250c2a..9d2755ddc8 100644 --- a/deps/v8/test/cctest/test-heap-profiler.cc +++ b/deps/v8/test/cctest/test-heap-profiler.cc @@ -2,11 +2,15 @@ // // Tests for heap profiler +#include <ctype.h> + #include "v8.h" #include "cctest.h" +#include "hashmap.h" #include "heap-profiler.h" #include "snapshot.h" +#include "debug.h" #include "utils-inl.h" #include "../include/v8-profiler.h" @@ -24,22 +28,30 @@ class NamedEntriesDetector { if (strcmp(entry->name(), "C2") == 0) has_C2 = true; } + static bool AddressesMatch(void* key1, void* key2) { + return key1 == key2; + } + void CheckAllReachables(i::HeapEntry* root) { + i::HashMap visited(AddressesMatch); i::List<i::HeapEntry*> list(10); list.Add(root); - root->paint(); CheckEntry(root); while (!list.is_empty()) { i::HeapEntry* entry = list.RemoveLast(); - i::Vector<i::HeapGraphEdge> children = entry->children(); + i::Vector<i::HeapGraphEdge*> children = entry->children(); for (int i = 0; i < children.length(); ++i) { - if (children[i].type() == i::HeapGraphEdge::kShortcut) continue; - i::HeapEntry* child = children[i].to(); - if (!child->painted()) { - list.Add(child); - child->paint(); - CheckEntry(child); - } + if (children[i]->type() == i::HeapGraphEdge::kShortcut) continue; + i::HeapEntry* child = children[i]->to(); + i::HashMap::Entry* entry = visited.Lookup( + reinterpret_cast<void*>(child), + static_cast<uint32_t>(reinterpret_cast<uintptr_t>(child)), + true); + if (entry->value) + continue; + entry->value = reinterpret_cast<void*>(1); + list.Add(child); + CheckEntry(child); } } } @@ -102,24 +114,19 @@ TEST(HeapSnapshot) { "var c2 = new C2(a2);"); const v8::HeapSnapshot* snapshot_env2 = v8::HeapProfiler::TakeSnapshot(v8_str("env2")); - i::HeapSnapshot* i_snapshot_env2 = - const_cast<i::HeapSnapshot*>( - reinterpret_cast<const i::HeapSnapshot*>(snapshot_env2)); const v8::HeapGraphNode* global_env2 = GetGlobalObject(snapshot_env2); // Verify, that JS global object of env2 has '..2' properties. const v8::HeapGraphNode* a2_node = - GetProperty(global_env2, v8::HeapGraphEdge::kShortcut, "a2"); + GetProperty(global_env2, v8::HeapGraphEdge::kProperty, "a2"); CHECK_NE(NULL, a2_node); CHECK_NE( - NULL, GetProperty(global_env2, v8::HeapGraphEdge::kShortcut, "b2_1")); + NULL, GetProperty(global_env2, v8::HeapGraphEdge::kProperty, "b2_1")); CHECK_NE( - NULL, GetProperty(global_env2, v8::HeapGraphEdge::kShortcut, "b2_2")); - CHECK_NE(NULL, GetProperty(global_env2, v8::HeapGraphEdge::kShortcut, "c2")); + NULL, GetProperty(global_env2, v8::HeapGraphEdge::kProperty, "b2_2")); + CHECK_NE(NULL, GetProperty(global_env2, v8::HeapGraphEdge::kProperty, "c2")); - // Paint all nodes reachable from global object. NamedEntriesDetector det; - i_snapshot_env2->ClearPaint(); det.CheckAllReachables(const_cast<i::HeapEntry*>( reinterpret_cast<const i::HeapEntry*>(global_env2))); CHECK(det.has_A2); @@ -137,12 +144,13 @@ TEST(HeapSnapshotObjectSizes) { CompileRun( "function X(a, b) { this.a = a; this.b = b; }\n" "x = new X(new X(), new X());\n" + "dummy = new X();\n" "(function() { x.a.a = x.b; })();"); const v8::HeapSnapshot* snapshot = v8::HeapProfiler::TakeSnapshot(v8_str("sizes")); const v8::HeapGraphNode* global = GetGlobalObject(snapshot); const v8::HeapGraphNode* x = - GetProperty(global, v8::HeapGraphEdge::kShortcut, "x"); + GetProperty(global, v8::HeapGraphEdge::kProperty, "x"); CHECK_NE(NULL, x); const v8::HeapGraphNode* x1 = GetProperty(x, v8::HeapGraphEdge::kProperty, "a"); @@ -152,9 +160,9 @@ TEST(HeapSnapshotObjectSizes) { CHECK_NE(NULL, x2); // Test sizes. - CHECK_EQ(x->GetSelfSize() * 3, x->GetRetainedSize()); - CHECK_EQ(x1->GetSelfSize(), x1->GetRetainedSize()); - CHECK_EQ(x2->GetSelfSize(), x2->GetRetainedSize()); + CHECK_NE(0, x->GetSelfSize()); + CHECK_NE(0, x1->GetSelfSize()); + CHECK_NE(0, x2->GetSelfSize()); } @@ -169,7 +177,7 @@ TEST(BoundFunctionInSnapshot) { v8::HeapProfiler::TakeSnapshot(v8_str("sizes")); const v8::HeapGraphNode* global = GetGlobalObject(snapshot); const v8::HeapGraphNode* f = - GetProperty(global, v8::HeapGraphEdge::kShortcut, "boundFunction"); + GetProperty(global, v8::HeapGraphEdge::kProperty, "boundFunction"); CHECK(f); CHECK_EQ(v8::String::New("native_bind"), f->GetName()); const v8::HeapGraphNode* bindings = @@ -233,15 +241,15 @@ TEST(HeapSnapshotCodeObjects) { const v8::HeapGraphNode* global = GetGlobalObject(snapshot); const v8::HeapGraphNode* compiled = - GetProperty(global, v8::HeapGraphEdge::kShortcut, "compiled"); + GetProperty(global, v8::HeapGraphEdge::kProperty, "compiled"); CHECK_NE(NULL, compiled); CHECK_EQ(v8::HeapGraphNode::kClosure, compiled->GetType()); const v8::HeapGraphNode* lazy = - GetProperty(global, v8::HeapGraphEdge::kShortcut, "lazy"); + GetProperty(global, v8::HeapGraphEdge::kProperty, "lazy"); CHECK_NE(NULL, lazy); CHECK_EQ(v8::HeapGraphNode::kClosure, lazy->GetType()); const v8::HeapGraphNode* anonymous = - GetProperty(global, v8::HeapGraphEdge::kShortcut, "anonymous"); + GetProperty(global, v8::HeapGraphEdge::kProperty, "anonymous"); CHECK_NE(NULL, anonymous); CHECK_EQ(v8::HeapGraphNode::kClosure, anonymous->GetType()); v8::String::AsciiValue anonymous_name(anonymous->GetName()); @@ -293,9 +301,9 @@ TEST(HeapSnapshotHeapNumbers) { const v8::HeapSnapshot* snapshot = v8::HeapProfiler::TakeSnapshot(v8_str("numbers")); const v8::HeapGraphNode* global = GetGlobalObject(snapshot); - CHECK_EQ(NULL, GetProperty(global, v8::HeapGraphEdge::kShortcut, "a")); + CHECK_EQ(NULL, GetProperty(global, v8::HeapGraphEdge::kProperty, "a")); const v8::HeapGraphNode* b = - GetProperty(global, v8::HeapGraphEdge::kShortcut, "b"); + GetProperty(global, v8::HeapGraphEdge::kProperty, "b"); CHECK_NE(NULL, b); CHECK_EQ(v8::HeapGraphNode::kHeapNumber, b->GetType()); } @@ -313,10 +321,10 @@ TEST(HeapSnapshotSlicedString) { v8::HeapProfiler::TakeSnapshot(v8_str("strings")); const v8::HeapGraphNode* global = GetGlobalObject(snapshot); const v8::HeapGraphNode* parent_string = - GetProperty(global, v8::HeapGraphEdge::kShortcut, "parent_string"); + GetProperty(global, v8::HeapGraphEdge::kProperty, "parent_string"); CHECK_NE(NULL, parent_string); const v8::HeapGraphNode* child_string = - GetProperty(global, v8::HeapGraphEdge::kShortcut, "child_string"); + GetProperty(global, v8::HeapGraphEdge::kProperty, "child_string"); CHECK_NE(NULL, child_string); const v8::HeapGraphNode* parent = GetProperty(child_string, v8::HeapGraphEdge::kInternal, "parent"); @@ -344,12 +352,12 @@ TEST(HeapSnapshotInternalReferences) { } -// Trying to introduce a check helper for uint64_t causes many +// Trying to introduce a check helper for uint32_t causes many // overloading ambiguities, so it seems easier just to cast // them to a signed type. -#define CHECK_EQ_UINT64_T(a, b) \ - CHECK_EQ(static_cast<int64_t>(a), static_cast<int64_t>(b)) -#define CHECK_NE_UINT64_T(a, b) \ +#define CHECK_EQ_SNAPSHOT_OBJECT_ID(a, b) \ + CHECK_EQ(static_cast<int32_t>(a), static_cast<int32_t>(b)) +#define CHECK_NE_SNAPSHOT_OBJECT_ID(a, b) \ CHECK((a) != (b)) // NOLINT TEST(HeapEntryIdsAndArrayShift) { @@ -378,31 +386,24 @@ TEST(HeapEntryIdsAndArrayShift) { const v8::HeapGraphNode* global1 = GetGlobalObject(snapshot1); const v8::HeapGraphNode* global2 = GetGlobalObject(snapshot2); - CHECK_NE_UINT64_T(0, global1->GetId()); - CHECK_EQ_UINT64_T(global1->GetId(), global2->GetId()); + CHECK_NE_SNAPSHOT_OBJECT_ID(0, global1->GetId()); + CHECK_EQ_SNAPSHOT_OBJECT_ID(global1->GetId(), global2->GetId()); const v8::HeapGraphNode* a1 = GetProperty(global1, v8::HeapGraphEdge::kProperty, "a"); CHECK_NE(NULL, a1); - const v8::HeapGraphNode* e1 = - GetProperty(a1, v8::HeapGraphEdge::kHidden, "1"); - CHECK_NE(NULL, e1); const v8::HeapGraphNode* k1 = - GetProperty(e1, v8::HeapGraphEdge::kInternal, "elements"); + GetProperty(a1, v8::HeapGraphEdge::kInternal, "elements"); CHECK_NE(NULL, k1); const v8::HeapGraphNode* a2 = GetProperty(global2, v8::HeapGraphEdge::kProperty, "a"); CHECK_NE(NULL, a2); - const v8::HeapGraphNode* e2 = - GetProperty(a2, v8::HeapGraphEdge::kHidden, "1"); - CHECK_NE(NULL, e2); const v8::HeapGraphNode* k2 = - GetProperty(e2, v8::HeapGraphEdge::kInternal, "elements"); + GetProperty(a2, v8::HeapGraphEdge::kInternal, "elements"); CHECK_NE(NULL, k2); - CHECK_EQ_UINT64_T(a1->GetId(), a2->GetId()); - CHECK_EQ_UINT64_T(e1->GetId(), e2->GetId()); - CHECK_EQ_UINT64_T(k1->GetId(), k2->GetId()); + CHECK_EQ_SNAPSHOT_OBJECT_ID(a1->GetId(), a2->GetId()); + CHECK_EQ_SNAPSHOT_OBJECT_ID(k1->GetId(), k2->GetId()); } TEST(HeapEntryIdsAndGC) { @@ -414,50 +415,56 @@ TEST(HeapEntryIdsAndGC) { "function B(x) { this.x = x; }\n" "var a = new A();\n" "var b = new B(a);"); + v8::Local<v8::String> s1_str = v8_str("s1"); + v8::Local<v8::String> s2_str = v8_str("s2"); const v8::HeapSnapshot* snapshot1 = - v8::HeapProfiler::TakeSnapshot(v8_str("s1")); + v8::HeapProfiler::TakeSnapshot(s1_str); HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); const v8::HeapSnapshot* snapshot2 = - v8::HeapProfiler::TakeSnapshot(v8_str("s2")); + v8::HeapProfiler::TakeSnapshot(s2_str); + + CHECK_GT(snapshot1->GetMaxSnapshotJSObjectId(), 7000); + CHECK(snapshot1->GetMaxSnapshotJSObjectId() <= + snapshot2->GetMaxSnapshotJSObjectId()); const v8::HeapGraphNode* global1 = GetGlobalObject(snapshot1); const v8::HeapGraphNode* global2 = GetGlobalObject(snapshot2); - CHECK_NE_UINT64_T(0, global1->GetId()); - CHECK_EQ_UINT64_T(global1->GetId(), global2->GetId()); + CHECK_NE_SNAPSHOT_OBJECT_ID(0, global1->GetId()); + CHECK_EQ_SNAPSHOT_OBJECT_ID(global1->GetId(), global2->GetId()); const v8::HeapGraphNode* A1 = GetProperty(global1, v8::HeapGraphEdge::kProperty, "A"); CHECK_NE(NULL, A1); const v8::HeapGraphNode* A2 = GetProperty(global2, v8::HeapGraphEdge::kProperty, "A"); CHECK_NE(NULL, A2); - CHECK_NE_UINT64_T(0, A1->GetId()); - CHECK_EQ_UINT64_T(A1->GetId(), A2->GetId()); + CHECK_NE_SNAPSHOT_OBJECT_ID(0, A1->GetId()); + CHECK_EQ_SNAPSHOT_OBJECT_ID(A1->GetId(), A2->GetId()); const v8::HeapGraphNode* B1 = GetProperty(global1, v8::HeapGraphEdge::kProperty, "B"); CHECK_NE(NULL, B1); const v8::HeapGraphNode* B2 = GetProperty(global2, v8::HeapGraphEdge::kProperty, "B"); CHECK_NE(NULL, B2); - CHECK_NE_UINT64_T(0, B1->GetId()); - CHECK_EQ_UINT64_T(B1->GetId(), B2->GetId()); + CHECK_NE_SNAPSHOT_OBJECT_ID(0, B1->GetId()); + CHECK_EQ_SNAPSHOT_OBJECT_ID(B1->GetId(), B2->GetId()); const v8::HeapGraphNode* a1 = GetProperty(global1, v8::HeapGraphEdge::kProperty, "a"); CHECK_NE(NULL, a1); const v8::HeapGraphNode* a2 = GetProperty(global2, v8::HeapGraphEdge::kProperty, "a"); CHECK_NE(NULL, a2); - CHECK_NE_UINT64_T(0, a1->GetId()); - CHECK_EQ_UINT64_T(a1->GetId(), a2->GetId()); + CHECK_NE_SNAPSHOT_OBJECT_ID(0, a1->GetId()); + CHECK_EQ_SNAPSHOT_OBJECT_ID(a1->GetId(), a2->GetId()); const v8::HeapGraphNode* b1 = GetProperty(global1, v8::HeapGraphEdge::kProperty, "b"); CHECK_NE(NULL, b1); const v8::HeapGraphNode* b2 = GetProperty(global2, v8::HeapGraphEdge::kProperty, "b"); CHECK_NE(NULL, b2); - CHECK_NE_UINT64_T(0, b1->GetId()); - CHECK_EQ_UINT64_T(b1->GetId(), b2->GetId()); + CHECK_NE_SNAPSHOT_OBJECT_ID(0, b1->GetId()); + CHECK_EQ_SNAPSHOT_OBJECT_ID(b1->GetId(), b2->GetId()); } @@ -474,66 +481,6 @@ TEST(HeapSnapshotRootPreservedAfterSorting) { } -TEST(HeapEntryDominator) { - // The graph looks like this: - // - // -> node1 - // a |^ - // -> node5 ba - // a v| - // node6 -> node2 - // b a |^ - // -> node4 ba - // b v| - // -> node3 - // - // The dominator for all nodes is node6. - - v8::HandleScope scope; - LocalContext env; - - CompileRun( - "function X(a, b) { this.a = a; this.b = b; }\n" - "node6 = new X(new X(new X()), new X(new X(),new X()));\n" - "(function(){\n" - "node6.a.a.b = node6.b.a; // node1 -> node2\n" - "node6.b.a.a = node6.a.a; // node2 -> node1\n" - "node6.b.a.b = node6.b.b; // node2 -> node3\n" - "node6.b.b.a = node6.b.a; // node3 -> node2\n" - "})();"); - - const v8::HeapSnapshot* snapshot = - v8::HeapProfiler::TakeSnapshot(v8_str("dominators")); - - const v8::HeapGraphNode* global = GetGlobalObject(snapshot); - CHECK_NE(NULL, global); - const v8::HeapGraphNode* node6 = - GetProperty(global, v8::HeapGraphEdge::kShortcut, "node6"); - CHECK_NE(NULL, node6); - const v8::HeapGraphNode* node5 = - GetProperty(node6, v8::HeapGraphEdge::kProperty, "a"); - CHECK_NE(NULL, node5); - const v8::HeapGraphNode* node4 = - GetProperty(node6, v8::HeapGraphEdge::kProperty, "b"); - CHECK_NE(NULL, node4); - const v8::HeapGraphNode* node3 = - GetProperty(node4, v8::HeapGraphEdge::kProperty, "b"); - CHECK_NE(NULL, node3); - const v8::HeapGraphNode* node2 = - GetProperty(node4, v8::HeapGraphEdge::kProperty, "a"); - CHECK_NE(NULL, node2); - const v8::HeapGraphNode* node1 = - GetProperty(node5, v8::HeapGraphEdge::kProperty, "a"); - CHECK_NE(NULL, node1); - - CHECK_EQ(node6, node1->GetDominatorNode()); - CHECK_EQ(node6, node2->GetDominatorNode()); - CHECK_EQ(node6, node3->GetDominatorNode()); - CHECK_EQ(node6, node4->GetDominatorNode()); - CHECK_EQ(node6, node5->GetDominatorNode()); -} - - namespace { class TestJSONStream : public v8::OutputStream { @@ -551,9 +498,14 @@ class TestJSONStream : public v8::OutputStream { memcpy(chunk.start(), buffer, chars_written); return kContinue; } + virtual WriteResult WriteUint32Chunk(uint32_t* buffer, int chars_written) { + ASSERT(false); + return kAbort; + } void WriteTo(i::Vector<char> dest) { buffer_.WriteTo(dest); } int eos_signaled() { return eos_signaled_; } int size() { return buffer_.size(); } + private: i::Collector<char> buffer_; int eos_signaled_; @@ -607,42 +559,44 @@ TEST(HeapSnapshotJSONSerialization) { env->Global()->Get(v8_str("parsed"))->ToObject(); CHECK(parsed_snapshot->Has(v8_str("snapshot"))); CHECK(parsed_snapshot->Has(v8_str("nodes"))); + CHECK(parsed_snapshot->Has(v8_str("edges"))); CHECK(parsed_snapshot->Has(v8_str("strings"))); // Get node and edge "member" offsets. v8::Local<v8::Value> meta_analysis_result = CompileRun( - "var parsed_meta = parsed.nodes[0];\n" - "var children_count_offset =" - " parsed_meta.fields.indexOf('children_count');\n" - "var children_offset =" - " parsed_meta.fields.indexOf('children');\n" - "var children_meta =" - " parsed_meta.types[children_offset];\n" - "var child_fields_count = children_meta.fields.length;\n" - "var child_type_offset =" - " children_meta.fields.indexOf('type');\n" - "var child_name_offset =" - " children_meta.fields.indexOf('name_or_index');\n" - "var child_to_node_offset =" - " children_meta.fields.indexOf('to_node');\n" + "var meta = parsed.snapshot.meta;\n" + "var edge_count_offset = meta.node_fields.indexOf('edge_count');\n" + "var node_fields_count = meta.node_fields.length;\n" + "var edge_fields_count = meta.edge_fields.length;\n" + "var edge_type_offset = meta.edge_fields.indexOf('type');\n" + "var edge_name_offset = meta.edge_fields.indexOf('name_or_index');\n" + "var edge_to_node_offset = meta.edge_fields.indexOf('to_node');\n" "var property_type =" - " children_meta.types[child_type_offset].indexOf('property');\n" + " meta.edge_types[edge_type_offset].indexOf('property');\n" "var shortcut_type =" - " children_meta.types[child_type_offset].indexOf('shortcut');"); + " meta.edge_types[edge_type_offset].indexOf('shortcut');\n" + "var node_count = parsed.nodes.length / node_fields_count;\n" + "var first_edge_indexes = parsed.first_edge_indexes = [];\n" + "for (var i = 0, first_edge_index = 0; i < node_count; ++i) {\n" + " first_edge_indexes[i] = first_edge_index;\n" + " first_edge_index += edge_fields_count *\n" + " parsed.nodes[i * node_fields_count + edge_count_offset];\n" + "}\n"); CHECK(!meta_analysis_result.IsEmpty()); // A helper function for processing encoded nodes. CompileRun( "function GetChildPosByProperty(pos, prop_name, prop_type) {\n" " var nodes = parsed.nodes;\n" + " var edges = parsed.edges;\n" " var strings = parsed.strings;\n" - " for (var i = 0,\n" - " count = nodes[pos + children_count_offset] * child_fields_count;\n" - " i < count; i += child_fields_count) {\n" - " var child_pos = pos + children_offset + i;\n" - " if (nodes[child_pos + child_type_offset] === prop_type\n" - " && strings[nodes[child_pos + child_name_offset]] === prop_name)\n" - " return nodes[child_pos + child_to_node_offset];\n" + " var node_ordinal = pos / node_fields_count;\n" + " for (var i = parsed.first_edge_indexes[node_ordinal],\n" + " count = parsed.first_edge_indexes[node_ordinal + 1];\n" + " i < count; i += edge_fields_count) {\n" + " if (edges[i + edge_type_offset] === prop_type\n" + " && strings[edges[i + edge_name_offset]] === prop_name)\n" + " return edges[i + edge_to_node_offset];\n" " }\n" " return null;\n" "}\n"); @@ -651,8 +605,8 @@ TEST(HeapSnapshotJSONSerialization) { "GetChildPosByProperty(\n" " GetChildPosByProperty(\n" " GetChildPosByProperty(" - " parsed.nodes[1 + children_offset + child_to_node_offset]," - " \"b\",shortcut_type),\n" + " parsed.edges[edge_to_node_offset]," + " \"b\", property_type),\n" " \"x\", property_type)," " \"s\", property_type)"); CHECK(!string_obj_pos_val.IsEmpty()); @@ -685,6 +639,212 @@ TEST(HeapSnapshotJSONSerializationAborting) { CHECK_EQ(0, stream.eos_signaled()); } +namespace { + +class TestStatsStream : public v8::OutputStream { + public: + TestStatsStream() + : eos_signaled_(0), + updates_written_(0), + entries_count_(0), + entries_size_(0), + intervals_count_(0), + first_interval_index_(-1) { } + TestStatsStream(const TestStatsStream& stream) + : v8::OutputStream(stream), + eos_signaled_(stream.eos_signaled_), + updates_written_(stream.updates_written_), + entries_count_(stream.entries_count_), + entries_size_(stream.entries_size_), + intervals_count_(stream.intervals_count_), + first_interval_index_(stream.first_interval_index_) { } + virtual ~TestStatsStream() {} + virtual void EndOfStream() { ++eos_signaled_; } + virtual WriteResult WriteAsciiChunk(char* buffer, int chars_written) { + ASSERT(false); + return kAbort; + } + virtual WriteResult WriteHeapStatsChunk(v8::HeapStatsUpdate* buffer, + int updates_written) { + ++intervals_count_; + ASSERT(updates_written); + updates_written_ += updates_written; + entries_count_ = 0; + if (first_interval_index_ == -1 && updates_written != 0) + first_interval_index_ = buffer[0].index; + for (int i = 0; i < updates_written; ++i) { + entries_count_ += buffer[i].count; + entries_size_ += buffer[i].size; + } + + return kContinue; + } + int eos_signaled() { return eos_signaled_; } + int updates_written() { return updates_written_; } + uint32_t entries_count() const { return entries_count_; } + uint32_t entries_size() const { return entries_size_; } + int intervals_count() const { return intervals_count_; } + int first_interval_index() const { return first_interval_index_; } + + private: + int eos_signaled_; + int updates_written_; + uint32_t entries_count_; + uint32_t entries_size_; + int intervals_count_; + int first_interval_index_; +}; + +} // namespace + +static TestStatsStream GetHeapStatsUpdate( + v8::SnapshotObjectId* object_id = NULL) { + TestStatsStream stream; + v8::SnapshotObjectId last_seen_id = + v8::HeapProfiler::PushHeapObjectsStats(&stream); + if (object_id) + *object_id = last_seen_id; + CHECK_EQ(1, stream.eos_signaled()); + return stream; +} + + +TEST(HeapSnapshotObjectsStats) { + v8::HandleScope scope; + LocalContext env; + + v8::HeapProfiler::StartHeapObjectsTracking(); + // We have to call GC 5 times. In other case the garbage will be + // the reason of flakiness. + for (int i = 0; i < 5; ++i) { + HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); + } + + v8::SnapshotObjectId initial_id; + { + // Single chunk of data expected in update. Initial data. + TestStatsStream stats_update = GetHeapStatsUpdate(&initial_id); + CHECK_EQ(1, stats_update.intervals_count()); + CHECK_EQ(1, stats_update.updates_written()); + CHECK_LT(0, stats_update.entries_size()); + CHECK_EQ(0, stats_update.first_interval_index()); + } + + // No data expected in update because nothing has happened. + v8::SnapshotObjectId same_id; + CHECK_EQ(0, GetHeapStatsUpdate(&same_id).updates_written()); + CHECK_EQ_SNAPSHOT_OBJECT_ID(initial_id, same_id); + + { + v8::SnapshotObjectId additional_string_id; + v8::HandleScope inner_scope_1; + v8_str("string1"); + { + // Single chunk of data with one new entry expected in update. + TestStatsStream stats_update = GetHeapStatsUpdate(&additional_string_id); + CHECK_LT(same_id, additional_string_id); + CHECK_EQ(1, stats_update.intervals_count()); + CHECK_EQ(1, stats_update.updates_written()); + CHECK_LT(0, stats_update.entries_size()); + CHECK_EQ(1, stats_update.entries_count()); + CHECK_EQ(2, stats_update.first_interval_index()); + } + + // No data expected in update because nothing happened. + v8::SnapshotObjectId last_id; + CHECK_EQ(0, GetHeapStatsUpdate(&last_id).updates_written()); + CHECK_EQ_SNAPSHOT_OBJECT_ID(additional_string_id, last_id); + + { + v8::HandleScope inner_scope_2; + v8_str("string2"); + + uint32_t entries_size; + { + v8::HandleScope inner_scope_3; + v8_str("string3"); + v8_str("string4"); + + { + // Single chunk of data with three new entries expected in update. + TestStatsStream stats_update = GetHeapStatsUpdate(); + CHECK_EQ(1, stats_update.intervals_count()); + CHECK_EQ(1, stats_update.updates_written()); + CHECK_LT(0, entries_size = stats_update.entries_size()); + CHECK_EQ(3, stats_update.entries_count()); + CHECK_EQ(4, stats_update.first_interval_index()); + } + } + + { + // Single chunk of data with two left entries expected in update. + TestStatsStream stats_update = GetHeapStatsUpdate(); + CHECK_EQ(1, stats_update.intervals_count()); + CHECK_EQ(1, stats_update.updates_written()); + CHECK_GT(entries_size, stats_update.entries_size()); + CHECK_EQ(1, stats_update.entries_count()); + // Two strings from forth interval were released. + CHECK_EQ(4, stats_update.first_interval_index()); + } + } + + { + // Single chunk of data with 0 left entries expected in update. + TestStatsStream stats_update = GetHeapStatsUpdate(); + CHECK_EQ(1, stats_update.intervals_count()); + CHECK_EQ(1, stats_update.updates_written()); + CHECK_EQ(0, stats_update.entries_size()); + CHECK_EQ(0, stats_update.entries_count()); + // The last string from forth interval was released. + CHECK_EQ(4, stats_update.first_interval_index()); + } + } + { + // Single chunk of data with 0 left entries expected in update. + TestStatsStream stats_update = GetHeapStatsUpdate(); + CHECK_EQ(1, stats_update.intervals_count()); + CHECK_EQ(1, stats_update.updates_written()); + CHECK_EQ(0, stats_update.entries_size()); + CHECK_EQ(0, stats_update.entries_count()); + // The only string from the second interval was released. + CHECK_EQ(2, stats_update.first_interval_index()); + } + + v8::Local<v8::Array> array = v8::Array::New(); + CHECK_EQ(0, array->Length()); + // Force array's buffer allocation. + array->Set(2, v8_num(7)); + + uint32_t entries_size; + { + // Single chunk of data with 2 entries expected in update. + TestStatsStream stats_update = GetHeapStatsUpdate(); + CHECK_EQ(1, stats_update.intervals_count()); + CHECK_EQ(1, stats_update.updates_written()); + CHECK_LT(0, entries_size = stats_update.entries_size()); + // They are the array and its buffer. + CHECK_EQ(2, stats_update.entries_count()); + CHECK_EQ(8, stats_update.first_interval_index()); + } + + for (int i = 0; i < 100; ++i) + array->Set(i, v8_num(i)); + + { + // Single chunk of data with 1 entry expected in update. + TestStatsStream stats_update = GetHeapStatsUpdate(); + CHECK_EQ(1, stats_update.intervals_count()); + // The first interval was changed because old buffer was collected. + // The second interval was changed because new buffer was allocated. + CHECK_EQ(2, stats_update.updates_written()); + CHECK_LT(entries_size, stats_update.entries_size()); + CHECK_EQ(2, stats_update.entries_count()); + CHECK_EQ(8, stats_update.first_interval_index()); + } + + v8::HeapProfiler::StopHeapObjectsTracking(); +} + static void CheckChildrenIds(const v8::HeapSnapshot* snapshot, const v8::HeapGraphNode* node, @@ -695,7 +855,7 @@ static void CheckChildrenIds(const v8::HeapSnapshot* snapshot, const v8::HeapGraphEdge* prop = node->GetChild(i); const v8::HeapGraphNode* child = snapshot->GetNodeById(prop->GetToNode()->GetId()); - CHECK_EQ_UINT64_T(prop->GetToNode()->GetId(), child->GetId()); + CHECK_EQ_SNAPSHOT_OBJECT_ID(prop->GetToNode()->GetId(), child->GetId()); CHECK_EQ(prop->GetToNode(), child); CheckChildrenIds(snapshot, child, level + 1, max_level); } @@ -715,6 +875,42 @@ TEST(HeapSnapshotGetNodeById) { } +TEST(HeapSnapshotGetSnapshotObjectId) { + v8::HandleScope scope; + LocalContext env; + CompileRun("globalObject = {};\n"); + const v8::HeapSnapshot* snapshot = + v8::HeapProfiler::TakeSnapshot(v8_str("get_snapshot_object_id")); + const v8::HeapGraphNode* global = GetGlobalObject(snapshot); + const v8::HeapGraphNode* global_object = + GetProperty(global, v8::HeapGraphEdge::kProperty, "globalObject"); + CHECK(global_object); + + v8::Local<v8::Value> globalObjectHandle = + env->Global()->Get(v8::String::New("globalObject")); + CHECK(!globalObjectHandle.IsEmpty()); + CHECK(globalObjectHandle->IsObject()); + + v8::SnapshotObjectId id = + v8::HeapProfiler::GetSnapshotObjectId(globalObjectHandle); + CHECK_NE(static_cast<int>(v8::HeapProfiler::kUnknownObjectId), + id); + CHECK_EQ(static_cast<int>(id), global_object->GetId()); +} + + +TEST(HeapSnapshotUnknownSnapshotObjectId) { + v8::HandleScope scope; + LocalContext env; + CompileRun("globalObject = {};\n"); + const v8::HeapSnapshot* snapshot = + v8::HeapProfiler::TakeSnapshot(v8_str("unknown_object_id")); + const v8::HeapGraphNode* node = + snapshot->GetNodeById(v8::HeapProfiler::kUnknownObjectId); + CHECK_EQ(NULL, node); +} + + namespace { class TestActivityControl : public v8::ActivityControl { @@ -953,9 +1149,8 @@ TEST(HeapSnapshotImplicitReferences) { v8::HeapProfiler::TakeSnapshot(v8_str("implicit_refs")); const v8::HeapGraphNode* global_object = GetGlobalObject(snapshot); - // Use kShortcut type to skip intermediate JSGlobalPropertyCell const v8::HeapGraphNode* obj0 = GetProperty( - global_object, v8::HeapGraphEdge::kShortcut, "root_object"); + global_object, v8::HeapGraphEdge::kProperty, "root_object"); CHECK(obj0); CHECK_EQ(v8::HeapGraphNode::kObject, obj0->GetType()); const v8::HeapGraphNode* obj1 = GetProperty( @@ -1128,7 +1323,7 @@ TEST(GetHeapValue) { env->Global()->GetPrototype().As<v8::Object>(); CHECK(js_global == global->GetHeapValue()); const v8::HeapGraphNode* obj = GetProperty( - global, v8::HeapGraphEdge::kShortcut, "a"); + global, v8::HeapGraphEdge::kProperty, "a"); CHECK(obj->GetHeapValue()->IsObject()); v8::Local<v8::Object> js_obj = js_global->Get(v8_str("a")).As<v8::Object>(); CHECK(js_obj == obj->GetHeapValue()); @@ -1157,7 +1352,7 @@ TEST(GetHeapValueForDeletedObject) { v8::HeapProfiler::TakeSnapshot(v8_str("snapshot")); const v8::HeapGraphNode* global = GetGlobalObject(snapshot); const v8::HeapGraphNode* obj = GetProperty( - global, v8::HeapGraphEdge::kShortcut, "a"); + global, v8::HeapGraphEdge::kProperty, "a"); const v8::HeapGraphNode* prop = GetProperty( obj, v8::HeapGraphEdge::kProperty, "p"); { @@ -1244,7 +1439,7 @@ TEST(FastCaseGetter) { const v8::HeapGraphNode* global = GetGlobalObject(snapshot); CHECK_NE(NULL, global); const v8::HeapGraphNode* obj1 = - GetProperty(global, v8::HeapGraphEdge::kShortcut, "obj1"); + GetProperty(global, v8::HeapGraphEdge::kProperty, "obj1"); CHECK_NE(NULL, obj1); const v8::HeapGraphNode* getterFunction = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "get-propWithGetter"); @@ -1326,7 +1521,7 @@ TEST(SfiAndJsFunctionWeakRefs) { const v8::HeapGraphNode* global = GetGlobalObject(snapshot); CHECK_NE(NULL, global); const v8::HeapGraphNode* fun = - GetProperty(global, v8::HeapGraphEdge::kShortcut, "fun"); + GetProperty(global, v8::HeapGraphEdge::kProperty, "fun"); CHECK(HasWeakEdge(fun)); const v8::HeapGraphNode* shared = GetProperty(fun, v8::HeapGraphEdge::kInternal, "shared"); @@ -1334,6 +1529,30 @@ TEST(SfiAndJsFunctionWeakRefs) { } +TEST(NoDebugObjectInSnapshot) { + v8::HandleScope scope; + LocalContext env; + + v8::internal::Isolate::Current()->debug()->Load(); + CompileRun("foo = {};"); + const v8::HeapSnapshot* snapshot = + v8::HeapProfiler::TakeSnapshot(v8_str("snapshot")); + const v8::HeapGraphNode* root = snapshot->GetRoot(); + int globals_count = 0; + for (int i = 0; i < root->GetChildrenCount(); ++i) { + const v8::HeapGraphEdge* edge = root->GetChild(i); + if (edge->GetType() == v8::HeapGraphEdge::kShortcut) { + ++globals_count; + const v8::HeapGraphNode* global = edge->GetToNode(); + const v8::HeapGraphNode* foo = + GetProperty(global, v8::HeapGraphEdge::kProperty, "foo"); + CHECK_NE(NULL, foo); + } + } + CHECK_EQ(1, globals_count); +} + + TEST(PersistentHandleCount) { v8::HandleScope scope; LocalContext env; @@ -1366,3 +1585,44 @@ TEST(PersistentHandleCount) { p_BBB.Dispose(); CHECK_EQ(global_handle_count, v8::HeapProfiler::GetPersistentHandleCount()); } + + +TEST(AllStrongGcRootsHaveNames) { + v8::HandleScope scope; + LocalContext env; + + CompileRun("foo = {};"); + const v8::HeapSnapshot* snapshot = + v8::HeapProfiler::TakeSnapshot(v8_str("snapshot")); + const v8::HeapGraphNode* gc_roots = GetNode( + snapshot->GetRoot(), v8::HeapGraphNode::kObject, "(GC roots)"); + CHECK_NE(NULL, gc_roots); + const v8::HeapGraphNode* strong_roots = GetNode( + gc_roots, v8::HeapGraphNode::kObject, "(Strong roots)"); + CHECK_NE(NULL, strong_roots); + for (int i = 0; i < strong_roots->GetChildrenCount(); ++i) { + const v8::HeapGraphEdge* edge = strong_roots->GetChild(i); + CHECK_EQ(v8::HeapGraphEdge::kInternal, edge->GetType()); + v8::String::AsciiValue name(edge->GetName()); + CHECK(isalpha(**name)); + } +} + + +TEST(NoRefsToNonEssentialEntries) { + v8::HandleScope scope; + LocalContext env; + CompileRun("global_object = {};\n"); + const v8::HeapSnapshot* snapshot = + v8::HeapProfiler::TakeSnapshot(v8_str("snapshot")); + const v8::HeapGraphNode* global = GetGlobalObject(snapshot); + const v8::HeapGraphNode* global_object = + GetProperty(global, v8::HeapGraphEdge::kProperty, "global_object"); + CHECK_NE(NULL, global_object); + const v8::HeapGraphNode* properties = + GetProperty(global_object, v8::HeapGraphEdge::kInternal, "properties"); + CHECK_EQ(NULL, properties); + const v8::HeapGraphNode* elements = + GetProperty(global_object, v8::HeapGraphEdge::kInternal, "elements"); + CHECK_EQ(NULL, elements); +} diff --git a/deps/v8/test/cctest/test-heap.cc b/deps/v8/test/cctest/test-heap.cc index f97bf17219..498b67db2c 100644 --- a/deps/v8/test/cctest/test-heap.cc +++ b/deps/v8/test/cctest/test-heap.cc @@ -673,7 +673,7 @@ TEST(JSArray) { array->SetElementsLength(Smi::FromInt(0))->ToObjectChecked(); CHECK_EQ(Smi::FromInt(0), array->length()); // Must be in fast mode. - CHECK(array->HasFastTypeElements()); + CHECK(array->HasFastSmiOrObjectElements()); // array[length] = name. array->SetElement(0, *name, NONE, kNonStrictMode)->ToObjectChecked(); @@ -811,7 +811,9 @@ TEST(Iteration) { // Allocate a JS array to OLD_POINTER_SPACE and NEW_SPACE objs[next_objs_index++] = FACTORY->NewJSArray(10); - objs[next_objs_index++] = FACTORY->NewJSArray(10, FAST_ELEMENTS, TENURED); + objs[next_objs_index++] = FACTORY->NewJSArray(10, + FAST_HOLEY_ELEMENTS, + TENURED); // Allocate a small string to OLD_DATA_SPACE and NEW_SPACE objs[next_objs_index++] = @@ -1214,7 +1216,9 @@ TEST(TestSizeOfObjects) { // The heap size should go back to initial size after a full GC, even // though sweeping didn't finish yet. HEAP->CollectAllGarbage(Heap::kNoGCFlags); - CHECK(!HEAP->old_pointer_space()->IsSweepingComplete()); + + // Normally sweeping would not be complete here, but no guarantees. + CHECK_EQ(initial_size, static_cast<int>(HEAP->SizeOfObjects())); // Advancing the sweeper step-wise should not change the heap size. @@ -1264,9 +1268,9 @@ static void FillUpNewSpace(NewSpace* new_space) { v8::HandleScope scope; AlwaysAllocateScope always_allocate; intptr_t available = new_space->EffectiveCapacity() - new_space->Size(); - intptr_t number_of_fillers = (available / FixedArray::SizeFor(1000)) - 10; + intptr_t number_of_fillers = (available / FixedArray::SizeFor(32)) - 1; for (intptr_t i = 0; i < number_of_fillers; i++) { - CHECK(HEAP->InNewSpace(*FACTORY->NewFixedArray(1000, NOT_TENURED))); + CHECK(HEAP->InNewSpace(*FACTORY->NewFixedArray(32, NOT_TENURED))); } } @@ -1275,6 +1279,13 @@ TEST(GrowAndShrinkNewSpace) { InitializeVM(); NewSpace* new_space = HEAP->new_space(); + if (HEAP->ReservedSemiSpaceSize() == HEAP->InitialSemiSpaceSize()) { + // The max size cannot exceed the reserved size, since semispaces must be + // always within the reserved space. We can't test new space growing and + // shrinking if the reserved size is the same as the minimum (initial) size. + return; + } + // Explicitly growing should double the space capacity. intptr_t old_capacity, new_capacity; old_capacity = new_space->Capacity(); @@ -1315,6 +1326,14 @@ TEST(GrowAndShrinkNewSpace) { TEST(CollectingAllAvailableGarbageShrinksNewSpace) { InitializeVM(); + + if (HEAP->ReservedSemiSpaceSize() == HEAP->InitialSemiSpaceSize()) { + // The max size cannot exceed the reserved size, since semispaces must be + // always within the reserved space. We can't test new space growing and + // shrinking if the reserved size is the same as the minimum (initial) size. + return; + } + v8::HandleScope scope; NewSpace* new_space = HEAP->new_space(); intptr_t old_capacity, new_capacity; @@ -1560,25 +1579,30 @@ TEST(PrototypeTransitionClearing) { *v8::Handle<v8::Object>::Cast( v8::Context::GetCurrent()->Global()->Get(v8_str("base")))); - // Verify that only dead prototype transitions are cleared. - CHECK_EQ(10, baseObject->map()->NumberOfProtoTransitions()); + // Verify that only dead prototype transitions are cleared. There is an + // extra, 11th, prototype transition on the Object map, which is the + // transition to a map with the used_for_prototype flag set (the key is + // the_hole). + CHECK_EQ(11, baseObject->map()->NumberOfProtoTransitions()); HEAP->CollectAllGarbage(Heap::kNoGCFlags); - CHECK_EQ(10 - 3, baseObject->map()->NumberOfProtoTransitions()); + const int transitions = 11 - 3; + CHECK_EQ(transitions, baseObject->map()->NumberOfProtoTransitions()); // Verify that prototype transitions array was compacted. FixedArray* trans = baseObject->map()->prototype_transitions(); - for (int i = 0; i < 10 - 3; i++) { + for (int i = 0; i < transitions; i++) { int j = Map::kProtoTransitionHeaderSize + i * Map::kProtoTransitionElementsPerEntry; CHECK(trans->get(j + Map::kProtoTransitionMapOffset)->IsMap()); - CHECK(trans->get(j + Map::kProtoTransitionPrototypeOffset)->IsJSObject()); + Object* proto = trans->get(j + Map::kProtoTransitionPrototypeOffset); + CHECK(proto->IsTheHole() || proto->IsJSObject()); } // Make sure next prototype is placed on an old-space evacuation candidate. Handle<JSObject> prototype; PagedSpace* space = HEAP->old_pointer_space(); do { - prototype = FACTORY->NewJSArray(32 * KB, FAST_ELEMENTS, TENURED); + prototype = FACTORY->NewJSArray(32 * KB, FAST_HOLEY_ELEMENTS, TENURED); } while (space->FirstPage() == space->LastPage() || !space->LastPage()->Contains(prototype->address())); @@ -1634,6 +1658,15 @@ TEST(ResetSharedFunctionInfoCountersDuringIncrementalMarking) { while (!marking->IsStopped() && !marking->IsComplete()) { marking->Step(1 * MB, IncrementalMarking::NO_GC_VIA_STACK_GUARD); } + if (!marking->IsStopped() || marking->should_hurry()) { + // We don't normally finish a GC via Step(), we normally finish by + // setting the stack guard and then do the final steps in the stack + // guard interrupt. But here we didn't ask for that, and there is no + // JS code running to trigger the interrupt, so we explicitly finalize + // here. + HEAP->CollectAllGarbage(Heap::kNoGCFlags, + "Test finalizing incremental mark-sweep"); + } CHECK_EQ(HEAP->global_ic_age(), f->shared()->ic_age()); CHECK_EQ(0, f->shared()->opt_count()); @@ -1680,3 +1713,191 @@ TEST(ResetSharedFunctionInfoCountersDuringMarkSweep) { CHECK_EQ(0, f->shared()->opt_count()); CHECK_EQ(0, f->shared()->code()->profiler_ticks()); } + + +// Test that HAllocateObject will always return an object in new-space. +TEST(OptimizedAllocationAlwaysInNewSpace) { + i::FLAG_allow_natives_syntax = true; + InitializeVM(); + if (!i::V8::UseCrankshaft() || i::FLAG_always_opt) return; + v8::HandleScope scope; + + FillUpNewSpace(HEAP->new_space()); + AlwaysAllocateScope always_allocate; + v8::Local<v8::Value> res = CompileRun( + "function c(x) {" + " this.x = x;" + " for (var i = 0; i < 32; i++) {" + " this['x' + i] = x;" + " }" + "}" + "function f(x) { return new c(x); };" + "f(1); f(2); f(3);" + "%OptimizeFunctionOnNextCall(f);" + "f(4);"); + CHECK_EQ(4, res->ToObject()->GetRealNamedProperty(v8_str("x"))->Int32Value()); + + Handle<JSObject> o = + v8::Utils::OpenHandle(*v8::Handle<v8::Object>::Cast(res)); + + CHECK(HEAP->InNewSpace(*o)); +} + + +static int CountMapTransitions(Map* map) { + int result = 0; + DescriptorArray* descs = map->instance_descriptors(); + for (int i = 0; i < descs->number_of_descriptors(); i++) { + if (descs->IsTransitionOnly(i)) { + result++; + } + } + return result; +} + + +// Test that map transitions are cleared and maps are collected with +// incremental marking as well. +TEST(Regress1465) { + i::FLAG_allow_natives_syntax = true; + i::FLAG_trace_incremental_marking = true; + InitializeVM(); + v8::HandleScope scope; + + #define TRANSITION_COUNT 256 + for (int i = 0; i < TRANSITION_COUNT; i++) { + EmbeddedVector<char, 64> buffer; + OS::SNPrintF(buffer, "var o = new Object; o.prop%d = %d;", i, i); + CompileRun(buffer.start()); + } + CompileRun("var root = new Object;"); + Handle<JSObject> root = + v8::Utils::OpenHandle( + *v8::Handle<v8::Object>::Cast( + v8::Context::GetCurrent()->Global()->Get(v8_str("root")))); + + // Count number of live transitions before marking. + int transitions_before = CountMapTransitions(root->map()); + CompileRun("%DebugPrint(root);"); + CHECK_EQ(TRANSITION_COUNT, transitions_before); + + // Go through all incremental marking steps in one swoop. + IncrementalMarking* marking = HEAP->incremental_marking(); + CHECK(marking->IsStopped()); + marking->Start(); + CHECK(marking->IsMarking()); + while (!marking->IsComplete()) { + marking->Step(MB, IncrementalMarking::NO_GC_VIA_STACK_GUARD); + } + CHECK(marking->IsComplete()); + HEAP->CollectAllGarbage(Heap::kNoGCFlags); + CHECK(marking->IsStopped()); + + // Count number of live transitions after marking. Note that one transition + // is left, because 'o' still holds an instance of one transition target. + int transitions_after = CountMapTransitions(root->map()); + CompileRun("%DebugPrint(root);"); + CHECK_EQ(1, transitions_after); +} + + +TEST(Regress2143a) { + i::FLAG_collect_maps = true; + i::FLAG_incremental_marking = true; + InitializeVM(); + v8::HandleScope scope; + + // Prepare a map transition from the root object together with a yet + // untransitioned root object. + CompileRun("var root = new Object;" + "root.foo = 0;" + "root = new Object;"); + + // Go through all incremental marking steps in one swoop. + IncrementalMarking* marking = HEAP->incremental_marking(); + CHECK(marking->IsStopped()); + marking->Start(); + CHECK(marking->IsMarking()); + while (!marking->IsComplete()) { + marking->Step(MB, IncrementalMarking::NO_GC_VIA_STACK_GUARD); + } + CHECK(marking->IsComplete()); + + // Compile a StoreIC that performs the prepared map transition. This + // will restart incremental marking and should make sure the root is + // marked grey again. + CompileRun("function f(o) {" + " o.foo = 0;" + "}" + "f(new Object);" + "f(root);"); + + // This bug only triggers with aggressive IC clearing. + HEAP->AgeInlineCaches(); + + // Explicitly request GC to perform final marking step and sweeping. + HEAP->CollectAllGarbage(Heap::kNoGCFlags); + CHECK(marking->IsStopped()); + + Handle<JSObject> root = + v8::Utils::OpenHandle( + *v8::Handle<v8::Object>::Cast( + v8::Context::GetCurrent()->Global()->Get(v8_str("root")))); + + // The root object should be in a sane state. + CHECK(root->IsJSObject()); + CHECK(root->map()->IsMap()); +} + + +TEST(Regress2143b) { + i::FLAG_collect_maps = true; + i::FLAG_incremental_marking = true; + i::FLAG_allow_natives_syntax = true; + InitializeVM(); + v8::HandleScope scope; + + // Prepare a map transition from the root object together with a yet + // untransitioned root object. + CompileRun("var root = new Object;" + "root.foo = 0;" + "root = new Object;"); + + // Go through all incremental marking steps in one swoop. + IncrementalMarking* marking = HEAP->incremental_marking(); + CHECK(marking->IsStopped()); + marking->Start(); + CHECK(marking->IsMarking()); + while (!marking->IsComplete()) { + marking->Step(MB, IncrementalMarking::NO_GC_VIA_STACK_GUARD); + } + CHECK(marking->IsComplete()); + + // Compile an optimized LStoreNamedField that performs the prepared + // map transition. This will restart incremental marking and should + // make sure the root is marked grey again. + CompileRun("function f(o) {" + " o.foo = 0;" + "}" + "f(new Object);" + "f(new Object);" + "%OptimizeFunctionOnNextCall(f);" + "f(root);" + "%DeoptimizeFunction(f);"); + + // This bug only triggers with aggressive IC clearing. + HEAP->AgeInlineCaches(); + + // Explicitly request GC to perform final marking step and sweeping. + HEAP->CollectAllGarbage(Heap::kNoGCFlags); + CHECK(marking->IsStopped()); + + Handle<JSObject> root = + v8::Utils::OpenHandle( + *v8::Handle<v8::Object>::Cast( + v8::Context::GetCurrent()->Global()->Get(v8_str("root")))); + + // The root object should be in a sane state. + CHECK(root->IsJSObject()); + CHECK(root->map()->IsMap()); +} diff --git a/deps/v8/test/cctest/test-list.cc b/deps/v8/test/cctest/test-list.cc index 7520b05fcb..740b432f3e 100644 --- a/deps/v8/test/cctest/test-list.cc +++ b/deps/v8/test/cctest/test-list.cc @@ -35,7 +35,7 @@ using namespace v8::internal; // Use a testing allocator that clears memory before deletion. class ZeroingAllocationPolicy { public: - static void* New(size_t size) { + void* New(size_t size) { // Stash the size in the first word to use for Delete. size_t true_size = size + sizeof(size_t); size_t* result = reinterpret_cast<size_t*>(malloc(true_size)); @@ -130,6 +130,18 @@ TEST(RemoveLast) { } +TEST(Allocate) { + List<int> list(4); + list.Add(1); + CHECK_EQ(1, list.length()); + list.Allocate(100); + CHECK_EQ(100, list.length()); + CHECK_LE(100, list.capacity()); + list[99] = 123; + CHECK_EQ(123, list[99]); +} + + TEST(Clear) { List<int> list(4); CHECK_EQ(0, list.length()); diff --git a/deps/v8/test/cctest/test-liveedit.cc b/deps/v8/test/cctest/test-liveedit.cc index 2498fca906..013de026f2 100644 --- a/deps/v8/test/cctest/test-liveedit.cc +++ b/deps/v8/test/cctest/test-liveedit.cc @@ -81,7 +81,8 @@ class ListDiffOutputWriter : public Comparator::Output { (*next_chunk_pointer_) = NULL; } void AddChunk(int pos1, int pos2, int len1, int len2) { - current_chunk_ = new DiffChunkStruct(pos1, pos2, len1, len2); + current_chunk_ = + new(Isolate::Current()->zone()) DiffChunkStruct(pos1, pos2, len1, len2); (*next_chunk_pointer_) = current_chunk_; next_chunk_pointer_ = ¤t_chunk_->next; } diff --git a/deps/v8/test/cctest/test-mark-compact.cc b/deps/v8/test/cctest/test-mark-compact.cc index 973af19662..27123704b1 100644 --- a/deps/v8/test/cctest/test-mark-compact.cc +++ b/deps/v8/test/cctest/test-mark-compact.cc @@ -531,18 +531,18 @@ TEST(BootUpMemoryUse) { // there we just skip the test. if (initial_memory >= 0) { InitializeVM(); - intptr_t booted_memory = MemoryInUse(); + intptr_t delta = MemoryInUse() - initial_memory; if (sizeof(initial_memory) == 8) { if (v8::internal::Snapshot::IsEnabled()) { - CHECK_LE(booted_memory - initial_memory, 6686 * 1024); // 6476. + CHECK_LE(delta, 3600 * 1024); // 3396. } else { - CHECK_LE(booted_memory - initial_memory, 6809 * 1024); // 6628. + CHECK_LE(delta, 4000 * 1024); // 3948. } } else { if (v8::internal::Snapshot::IsEnabled()) { - CHECK_LE(booted_memory - initial_memory, 6532 * 1024); // 6388. + CHECK_LE(delta, 2600 * 1024); // 2484. } else { - CHECK_LE(booted_memory - initial_memory, 6940 * 1024); // 6456 + CHECK_LE(delta, 2950 * 1024); // 2844 } } } diff --git a/deps/v8/test/cctest/test-parsing.cc b/deps/v8/test/cctest/test-parsing.cc index 6bcae7c308..b9123f01f0 100755 --- a/deps/v8/test/cctest/test-parsing.cc +++ b/deps/v8/test/cctest/test-parsing.cc @@ -1016,7 +1016,8 @@ TEST(ScopePositions) { FACTORY->NewStringFromUtf8(i::CStrVector(program.start()))); CHECK_EQ(source->length(), kProgramSize); i::Handle<i::Script> script = FACTORY->NewScript(source); - i::Parser parser(script, i::kAllowLazy | i::EXTENDED_MODE, NULL, NULL); + i::Parser parser(script, i::kAllowLazy | i::EXTENDED_MODE, NULL, NULL, + i::Isolate::Current()->zone()); i::CompilationInfo info(script); info.MarkAsGlobal(); info.SetLanguageMode(source_data[i].language_mode); @@ -1060,7 +1061,7 @@ void TestParserSync(i::Handle<i::String> source, int flags) { i::Handle<i::Script> script = FACTORY->NewScript(source); bool save_harmony_scoping = i::FLAG_harmony_scoping; i::FLAG_harmony_scoping = harmony_scoping; - i::Parser parser(script, flags, NULL, NULL); + i::Parser parser(script, flags, NULL, NULL, i::Isolate::Current()->zone()); i::CompilationInfo info(script); info.MarkAsGlobal(); i::FunctionLiteral* function = parser.ParseProgram(&info); diff --git a/deps/v8/test/cctest/test-regexp.cc b/deps/v8/test/cctest/test-regexp.cc index d941d0f7b0..325c686063 100644 --- a/deps/v8/test/cctest/test-regexp.cc +++ b/deps/v8/test/cctest/test-regexp.cc @@ -1,4 +1,4 @@ -// Copyright 2008 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -88,7 +88,8 @@ static SmartArrayPointer<const char> Parse(const char* input) { CHECK(v8::internal::RegExpParser::ParseRegExp(&reader, false, &result)); CHECK(result.tree != NULL); CHECK(result.error.is_null()); - SmartArrayPointer<const char> output = result.tree->ToString(); + SmartArrayPointer<const char> output = + result.tree->ToString(Isolate::Current()->zone()); return output; } @@ -469,8 +470,10 @@ static bool NotWord(uc16 c) { static void TestCharacterClassEscapes(uc16 c, bool (pred)(uc16 c)) { ZoneScope scope(Isolate::Current(), DELETE_ON_EXIT); - ZoneList<CharacterRange>* ranges = new ZoneList<CharacterRange>(2); - CharacterRange::AddClassEscape(c, ranges); + Zone* zone = Isolate::Current()->zone(); + ZoneList<CharacterRange>* ranges = + new(zone) ZoneList<CharacterRange>(2, zone); + CharacterRange::AddClassEscape(c, ranges, zone); for (unsigned i = 0; i < (1 << 16); i++) { bool in_class = false; for (int j = 0; !in_class && j < ranges->length(); j++) { @@ -504,7 +507,16 @@ static RegExpNode* Compile(const char* input, bool multiline, bool is_ascii) { return NULL; Handle<String> pattern = isolate->factory()-> NewStringFromUtf8(CStrVector(input)); - RegExpEngine::Compile(&compile_data, false, multiline, pattern, is_ascii); + Handle<String> sample_subject = + isolate->factory()->NewStringFromUtf8(CStrVector("")); + RegExpEngine::Compile(&compile_data, + false, + false, + multiline, + pattern, + sample_subject, + is_ascii, + isolate->zone()); return compile_data.node; } @@ -555,7 +567,7 @@ TEST(SplayTreeSimple) { v8::internal::V8::Initialize(NULL); static const unsigned kLimit = 1000; ZoneScope zone_scope(Isolate::Current(), DELETE_ON_EXIT); - ZoneSplayTree<TestConfig> tree; + ZoneSplayTree<TestConfig> tree(Isolate::Current()->zone()); bool seen[kLimit]; for (unsigned i = 0; i < kLimit; i++) seen[i] = false; #define CHECK_MAPS_EQUAL() do { \ @@ -623,11 +635,12 @@ TEST(DispatchTableConstruction) { } // Enter test data into dispatch table. ZoneScope zone_scope(Isolate::Current(), DELETE_ON_EXIT); - DispatchTable table; + DispatchTable table(Isolate::Current()->zone()); for (int i = 0; i < kRangeCount; i++) { uc16* range = ranges[i]; for (int j = 0; j < 2 * kRangeSize; j += 2) - table.AddRange(CharacterRange(range[j], range[j + 1]), i); + table.AddRange(CharacterRange(range[j], range[j + 1]), i, + Isolate::Current()->zone()); } // Check that the table looks as we would expect for (int p = 0; p < kLimit; p++) { @@ -717,6 +730,7 @@ static ArchRegExpMacroAssembler::Result Execute(Code* code, input_start, input_end, captures, + 0, Isolate::Current()); } @@ -726,7 +740,8 @@ TEST(MacroAssemblerNativeSuccess) { ContextInitializer initializer; Factory* factory = Isolate::Current()->factory(); - ArchRegExpMacroAssembler m(NativeRegExpMacroAssembler::ASCII, 4); + ArchRegExpMacroAssembler m(NativeRegExpMacroAssembler::ASCII, 4, + Isolate::Current()->zone()); m.Succeed(); @@ -761,7 +776,8 @@ TEST(MacroAssemblerNativeSimple) { ContextInitializer initializer; Factory* factory = Isolate::Current()->factory(); - ArchRegExpMacroAssembler m(NativeRegExpMacroAssembler::ASCII, 4); + ArchRegExpMacroAssembler m(NativeRegExpMacroAssembler::ASCII, 4, + Isolate::Current()->zone()); uc16 foo_chars[3] = {'f', 'o', 'o'}; Vector<const uc16> foo(foo_chars, 3); @@ -818,7 +834,8 @@ TEST(MacroAssemblerNativeSimpleUC16) { ContextInitializer initializer; Factory* factory = Isolate::Current()->factory(); - ArchRegExpMacroAssembler m(NativeRegExpMacroAssembler::UC16, 4); + ArchRegExpMacroAssembler m(NativeRegExpMacroAssembler::UC16, 4, + Isolate::Current()->zone()); uc16 foo_chars[3] = {'f', 'o', 'o'}; Vector<const uc16> foo(foo_chars, 3); @@ -880,7 +897,8 @@ TEST(MacroAssemblerNativeBacktrack) { ContextInitializer initializer; Factory* factory = Isolate::Current()->factory(); - ArchRegExpMacroAssembler m(NativeRegExpMacroAssembler::ASCII, 0); + ArchRegExpMacroAssembler m(NativeRegExpMacroAssembler::ASCII, 0, + Isolate::Current()->zone()); Label fail; Label backtrack; @@ -918,7 +936,8 @@ TEST(MacroAssemblerNativeBackReferenceASCII) { ContextInitializer initializer; Factory* factory = Isolate::Current()->factory(); - ArchRegExpMacroAssembler m(NativeRegExpMacroAssembler::ASCII, 4); + ArchRegExpMacroAssembler m(NativeRegExpMacroAssembler::ASCII, 4, + Isolate::Current()->zone()); m.WriteCurrentPositionToRegister(0, 0); m.AdvanceCurrentPosition(2); @@ -965,7 +984,8 @@ TEST(MacroAssemblerNativeBackReferenceUC16) { ContextInitializer initializer; Factory* factory = Isolate::Current()->factory(); - ArchRegExpMacroAssembler m(NativeRegExpMacroAssembler::UC16, 4); + ArchRegExpMacroAssembler m(NativeRegExpMacroAssembler::UC16, 4, + Isolate::Current()->zone()); m.WriteCurrentPositionToRegister(0, 0); m.AdvanceCurrentPosition(2); @@ -995,11 +1015,11 @@ TEST(MacroAssemblerNativeBackReferenceUC16) { int output[4]; NativeRegExpMacroAssembler::Result result = Execute(*code, - *input, - 0, - start_adr, - start_adr + input->length() * 2, - output); + *input, + 0, + start_adr, + start_adr + input->length() * 2, + output); CHECK_EQ(NativeRegExpMacroAssembler::SUCCESS, result); CHECK_EQ(0, output[0]); @@ -1015,7 +1035,8 @@ TEST(MacroAssemblernativeAtStart) { ContextInitializer initializer; Factory* factory = Isolate::Current()->factory(); - ArchRegExpMacroAssembler m(NativeRegExpMacroAssembler::ASCII, 0); + ArchRegExpMacroAssembler m(NativeRegExpMacroAssembler::ASCII, 0, + Isolate::Current()->zone()); Label not_at_start, newline, fail; m.CheckNotAtStart(¬_at_start); @@ -1072,7 +1093,8 @@ TEST(MacroAssemblerNativeBackRefNoCase) { ContextInitializer initializer; Factory* factory = Isolate::Current()->factory(); - ArchRegExpMacroAssembler m(NativeRegExpMacroAssembler::ASCII, 4); + ArchRegExpMacroAssembler m(NativeRegExpMacroAssembler::ASCII, 4, + Isolate::Current()->zone()); Label fail, succ; @@ -1129,7 +1151,8 @@ TEST(MacroAssemblerNativeRegisters) { ContextInitializer initializer; Factory* factory = Isolate::Current()->factory(); - ArchRegExpMacroAssembler m(NativeRegExpMacroAssembler::ASCII, 6); + ArchRegExpMacroAssembler m(NativeRegExpMacroAssembler::ASCII, 6, + Isolate::Current()->zone()); uc16 foo_chars[3] = {'f', 'o', 'o'}; Vector<const uc16> foo(foo_chars, 3); @@ -1231,7 +1254,8 @@ TEST(MacroAssemblerStackOverflow) { Isolate* isolate = Isolate::Current(); Factory* factory = isolate->factory(); - ArchRegExpMacroAssembler m(NativeRegExpMacroAssembler::ASCII, 0); + ArchRegExpMacroAssembler m(NativeRegExpMacroAssembler::ASCII, 0, + Isolate::Current()->zone()); Label loop; m.Bind(&loop); @@ -1269,7 +1293,8 @@ TEST(MacroAssemblerNativeLotsOfRegisters) { Isolate* isolate = Isolate::Current(); Factory* factory = isolate->factory(); - ArchRegExpMacroAssembler m(NativeRegExpMacroAssembler::ASCII, 2); + ArchRegExpMacroAssembler m(NativeRegExpMacroAssembler::ASCII, 2, + Isolate::Current()->zone()); // At least 2048, to ensure the allocated space for registers // span one full page. @@ -1387,16 +1412,18 @@ TEST(AddInverseToTable) { static const int kRangeCount = 16; for (int t = 0; t < 10; t++) { ZoneScope zone_scope(Isolate::Current(), DELETE_ON_EXIT); + Zone* zone = Isolate::Current()->zone(); ZoneList<CharacterRange>* ranges = - new ZoneList<CharacterRange>(kRangeCount); + new(zone) + ZoneList<CharacterRange>(kRangeCount, zone); for (int i = 0; i < kRangeCount; i++) { int from = PseudoRandom(t + 87, i + 25) % kLimit; int to = from + (PseudoRandom(i + 87, t + 25) % (kLimit / 20)); if (to > kLimit) to = kLimit; - ranges->Add(CharacterRange(from, to)); + ranges->Add(CharacterRange(from, to), zone); } - DispatchTable table; - DispatchTableConstructor cons(&table, false); + DispatchTable table(zone); + DispatchTableConstructor cons(&table, false, Isolate::Current()->zone()); cons.set_choice_index(0); cons.AddInverse(ranges); for (int i = 0; i < kLimit; i++) { @@ -1408,11 +1435,12 @@ TEST(AddInverseToTable) { } } ZoneScope zone_scope(Isolate::Current(), DELETE_ON_EXIT); + Zone* zone = Isolate::Current()->zone(); ZoneList<CharacterRange>* ranges = - new ZoneList<CharacterRange>(1); - ranges->Add(CharacterRange(0xFFF0, 0xFFFE)); - DispatchTable table; - DispatchTableConstructor cons(&table, false); + new(zone) ZoneList<CharacterRange>(1, zone); + ranges->Add(CharacterRange(0xFFF0, 0xFFFE), zone); + DispatchTable table(zone); + DispatchTableConstructor cons(&table, false, Isolate::Current()->zone()); cons.set_choice_index(0); cons.AddInverse(ranges); CHECK(!table.Get(0xFFFE)->Get(0)); @@ -1521,9 +1549,11 @@ TEST(UncanonicalizeEquivalence) { static void TestRangeCaseIndependence(CharacterRange input, Vector<CharacterRange> expected) { ZoneScope zone_scope(Isolate::Current(), DELETE_ON_EXIT); + Zone* zone = Isolate::Current()->zone(); int count = expected.length(); - ZoneList<CharacterRange>* list = new ZoneList<CharacterRange>(count); - input.AddCaseEquivalents(list, false); + ZoneList<CharacterRange>* list = + new(zone) ZoneList<CharacterRange>(count, zone); + input.AddCaseEquivalents(list, false, zone); CHECK_EQ(count, list->length()); for (int i = 0; i < list->length(); i++) { CHECK_EQ(expected[i].from(), list->at(i).from()); @@ -1585,18 +1615,21 @@ static bool InClass(uc16 c, ZoneList<CharacterRange>* ranges) { TEST(CharClassDifference) { v8::internal::V8::Initialize(NULL); ZoneScope zone_scope(Isolate::Current(), DELETE_ON_EXIT); - ZoneList<CharacterRange>* base = new ZoneList<CharacterRange>(1); - base->Add(CharacterRange::Everything()); - Vector<const uc16> overlay = CharacterRange::GetWordBounds(); + Zone* zone = Isolate::Current()->zone(); + ZoneList<CharacterRange>* base = + new(zone) ZoneList<CharacterRange>(1, zone); + base->Add(CharacterRange::Everything(), zone); + Vector<const int> overlay = CharacterRange::GetWordBounds(); ZoneList<CharacterRange>* included = NULL; ZoneList<CharacterRange>* excluded = NULL; - CharacterRange::Split(base, overlay, &included, &excluded); + CharacterRange::Split(base, overlay, &included, &excluded, + Isolate::Current()->zone()); for (int i = 0; i < (1 << 16); i++) { bool in_base = InClass(i, base); if (in_base) { bool in_overlay = false; for (int j = 0; !in_overlay && j < overlay.length(); j += 2) { - if (overlay[j] <= i && i <= overlay[j+1]) + if (overlay[j] <= i && i < overlay[j+1]) in_overlay = true; } CHECK_EQ(in_overlay, InClass(i, included)); @@ -1612,12 +1645,14 @@ TEST(CharClassDifference) { TEST(CanonicalizeCharacterSets) { v8::internal::V8::Initialize(NULL); ZoneScope scope(Isolate::Current(), DELETE_ON_EXIT); - ZoneList<CharacterRange>* list = new ZoneList<CharacterRange>(4); + Zone* zone = Isolate::Current()->zone(); + ZoneList<CharacterRange>* list = + new(zone) ZoneList<CharacterRange>(4, zone); CharacterSet set(list); - list->Add(CharacterRange(10, 20)); - list->Add(CharacterRange(30, 40)); - list->Add(CharacterRange(50, 60)); + list->Add(CharacterRange(10, 20), zone); + list->Add(CharacterRange(30, 40), zone); + list->Add(CharacterRange(50, 60), zone); set.Canonicalize(); ASSERT_EQ(3, list->length()); ASSERT_EQ(10, list->at(0).from()); @@ -1628,9 +1663,9 @@ TEST(CanonicalizeCharacterSets) { ASSERT_EQ(60, list->at(2).to()); list->Rewind(0); - list->Add(CharacterRange(10, 20)); - list->Add(CharacterRange(50, 60)); - list->Add(CharacterRange(30, 40)); + list->Add(CharacterRange(10, 20), zone); + list->Add(CharacterRange(50, 60), zone); + list->Add(CharacterRange(30, 40), zone); set.Canonicalize(); ASSERT_EQ(3, list->length()); ASSERT_EQ(10, list->at(0).from()); @@ -1641,11 +1676,11 @@ TEST(CanonicalizeCharacterSets) { ASSERT_EQ(60, list->at(2).to()); list->Rewind(0); - list->Add(CharacterRange(30, 40)); - list->Add(CharacterRange(10, 20)); - list->Add(CharacterRange(25, 25)); - list->Add(CharacterRange(100, 100)); - list->Add(CharacterRange(1, 1)); + list->Add(CharacterRange(30, 40), zone); + list->Add(CharacterRange(10, 20), zone); + list->Add(CharacterRange(25, 25), zone); + list->Add(CharacterRange(100, 100), zone); + list->Add(CharacterRange(1, 1), zone); set.Canonicalize(); ASSERT_EQ(5, list->length()); ASSERT_EQ(1, list->at(0).from()); @@ -1660,31 +1695,22 @@ TEST(CanonicalizeCharacterSets) { ASSERT_EQ(100, list->at(4).to()); list->Rewind(0); - list->Add(CharacterRange(10, 19)); - list->Add(CharacterRange(21, 30)); - list->Add(CharacterRange(20, 20)); + list->Add(CharacterRange(10, 19), zone); + list->Add(CharacterRange(21, 30), zone); + list->Add(CharacterRange(20, 20), zone); set.Canonicalize(); ASSERT_EQ(1, list->length()); ASSERT_EQ(10, list->at(0).from()); ASSERT_EQ(30, list->at(0).to()); } -// Checks whether a character is in the set represented by a list of ranges. -static bool CharacterInSet(ZoneList<CharacterRange>* set, uc16 value) { - for (int i = 0; i < set->length(); i++) { - CharacterRange range = set->at(i); - if (range.from() <= value && value <= range.to()) { - return true; - } - } - return false; -} TEST(CharacterRangeMerge) { v8::internal::V8::Initialize(NULL); ZoneScope zone_scope(Isolate::Current(), DELETE_ON_EXIT); - ZoneList<CharacterRange> l1(4); - ZoneList<CharacterRange> l2(4); + ZoneList<CharacterRange> l1(4, Isolate::Current()->zone()); + ZoneList<CharacterRange> l2(4, Isolate::Current()->zone()); + Zone* zone = Isolate::Current()->zone(); // Create all combinations of intersections of ranges, both singletons and // longer. @@ -1699,8 +1725,8 @@ TEST(CharacterRangeMerge) { // Y - outside after for (int i = 0; i < 5; i++) { - l1.Add(CharacterRange::Singleton(offset + 2)); - l2.Add(CharacterRange::Singleton(offset + i)); + l1.Add(CharacterRange::Singleton(offset + 2), zone); + l2.Add(CharacterRange::Singleton(offset + i), zone); offset += 6; } @@ -1715,8 +1741,8 @@ TEST(CharacterRangeMerge) { // Y - disjoint after for (int i = 0; i < 7; i++) { - l1.Add(CharacterRange::Range(offset + 2, offset + 4)); - l2.Add(CharacterRange::Singleton(offset + i)); + l1.Add(CharacterRange::Range(offset + 2, offset + 4), zone); + l2.Add(CharacterRange::Singleton(offset + i), zone); offset += 8; } @@ -1736,96 +1762,35 @@ TEST(CharacterRangeMerge) { // YYYYYYYYYYYY - containing entirely. for (int i = 0; i < 9; i++) { - l1.Add(CharacterRange::Range(offset + 6, offset + 15)); // Length 8. - l2.Add(CharacterRange::Range(offset + 2 * i, offset + 2 * i + 3)); + l1.Add(CharacterRange::Range(offset + 6, offset + 15), zone); // Length 8. + l2.Add(CharacterRange::Range(offset + 2 * i, offset + 2 * i + 3), zone); offset += 22; } - l1.Add(CharacterRange::Range(offset + 6, offset + 15)); - l2.Add(CharacterRange::Range(offset + 6, offset + 15)); + l1.Add(CharacterRange::Range(offset + 6, offset + 15), zone); + l2.Add(CharacterRange::Range(offset + 6, offset + 15), zone); offset += 22; - l1.Add(CharacterRange::Range(offset + 6, offset + 15)); - l2.Add(CharacterRange::Range(offset + 4, offset + 17)); + l1.Add(CharacterRange::Range(offset + 6, offset + 15), zone); + l2.Add(CharacterRange::Range(offset + 4, offset + 17), zone); offset += 22; // Different kinds of multi-range overlap: // XXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXX // YYYY Y YYYY Y YYYY Y YYYY Y YYYY Y YYYY Y - l1.Add(CharacterRange::Range(offset, offset + 21)); - l1.Add(CharacterRange::Range(offset + 31, offset + 52)); + l1.Add(CharacterRange::Range(offset, offset + 21), zone); + l1.Add(CharacterRange::Range(offset + 31, offset + 52), zone); for (int i = 0; i < 6; i++) { - l2.Add(CharacterRange::Range(offset + 2, offset + 5)); - l2.Add(CharacterRange::Singleton(offset + 8)); + l2.Add(CharacterRange::Range(offset + 2, offset + 5), zone); + l2.Add(CharacterRange::Singleton(offset + 8), zone); offset += 9; } ASSERT(CharacterRange::IsCanonical(&l1)); ASSERT(CharacterRange::IsCanonical(&l2)); - ZoneList<CharacterRange> first_only(4); - ZoneList<CharacterRange> second_only(4); - ZoneList<CharacterRange> both(4); - - // Merge one direction. - CharacterRange::Merge(&l1, &l2, &first_only, &second_only, &both); - - CHECK(CharacterRange::IsCanonical(&first_only)); - CHECK(CharacterRange::IsCanonical(&second_only)); - CHECK(CharacterRange::IsCanonical(&both)); - - for (uc16 i = 0; i < offset; i++) { - bool in_first = CharacterInSet(&l1, i); - bool in_second = CharacterInSet(&l2, i); - CHECK((in_first && !in_second) == CharacterInSet(&first_only, i)); - CHECK((!in_first && in_second) == CharacterInSet(&second_only, i)); - CHECK((in_first && in_second) == CharacterInSet(&both, i)); - } - - first_only.Clear(); - second_only.Clear(); - both.Clear(); - - // Merge other direction. - CharacterRange::Merge(&l2, &l1, &second_only, &first_only, &both); - - CHECK(CharacterRange::IsCanonical(&first_only)); - CHECK(CharacterRange::IsCanonical(&second_only)); - CHECK(CharacterRange::IsCanonical(&both)); - - for (uc16 i = 0; i < offset; i++) { - bool in_first = CharacterInSet(&l1, i); - bool in_second = CharacterInSet(&l2, i); - CHECK((in_first && !in_second) == CharacterInSet(&first_only, i)); - CHECK((!in_first && in_second) == CharacterInSet(&second_only, i)); - CHECK((in_first && in_second) == CharacterInSet(&both, i)); - } - - first_only.Clear(); - second_only.Clear(); - both.Clear(); - - // Merge but don't record all combinations. - CharacterRange::Merge(&l1, &l2, NULL, NULL, &both); - - CHECK(CharacterRange::IsCanonical(&both)); - - for (uc16 i = 0; i < offset; i++) { - bool in_first = CharacterInSet(&l1, i); - bool in_second = CharacterInSet(&l2, i); - CHECK((in_first && in_second) == CharacterInSet(&both, i)); - } - - // Merge into same set. - ZoneList<CharacterRange> all(4); - CharacterRange::Merge(&l1, &l2, &all, &all, &all); - - CHECK(CharacterRange::IsCanonical(&all)); - - for (uc16 i = 0; i < offset; i++) { - bool in_first = CharacterInSet(&l1, i); - bool in_second = CharacterInSet(&l2, i); - CHECK((in_first || in_second) == CharacterInSet(&all, i)); - } + ZoneList<CharacterRange> first_only(4, Isolate::Current()->zone()); + ZoneList<CharacterRange> second_only(4, Isolate::Current()->zone()); + ZoneList<CharacterRange> both(4, Isolate::Current()->zone()); } diff --git a/deps/v8/test/cctest/test-spaces.cc b/deps/v8/test/cctest/test-spaces.cc index 92de2a60fa..0e957048ee 100644 --- a/deps/v8/test/cctest/test-spaces.cc +++ b/deps/v8/test/cctest/test-spaces.cc @@ -140,8 +140,8 @@ TEST(MemoryAllocator) { heap->MaxReserved(), OLD_POINTER_SPACE, NOT_EXECUTABLE); - Page* first_page = - memory_allocator->AllocatePage(&faked_space, NOT_EXECUTABLE); + Page* first_page = memory_allocator->AllocatePage( + faked_space.AreaSize(), &faked_space, NOT_EXECUTABLE); first_page->InsertAfter(faked_space.anchor()->prev_page()); CHECK(first_page->is_valid()); @@ -153,8 +153,8 @@ TEST(MemoryAllocator) { } // Again, we should get n or n - 1 pages. - Page* other = - memory_allocator->AllocatePage(&faked_space, NOT_EXECUTABLE); + Page* other = memory_allocator->AllocatePage( + faked_space.AreaSize(), &faked_space, NOT_EXECUTABLE); CHECK(other->is_valid()); total_pages++; other->InsertAfter(first_page); diff --git a/deps/v8/test/cctest/test-strings.cc b/deps/v8/test/cctest/test-strings.cc index e11349bc85..7cddff3309 100644 --- a/deps/v8/test/cctest/test-strings.cc +++ b/deps/v8/test/cctest/test-strings.cc @@ -82,6 +82,7 @@ static void InitializeBuildingBlocks( Handle<String> building_blocks[NUMBER_OF_BUILDING_BLOCKS]) { // A list of pointers that we don't have any interest in cleaning up. // If they are reachable from a root then leak detection won't complain. + Zone* zone = Isolate::Current()->zone(); for (int i = 0; i < NUMBER_OF_BUILDING_BLOCKS; i++) { int len = gen() % 16; if (len > 14) { @@ -113,11 +114,11 @@ static void InitializeBuildingBlocks( break; } case 2: { - uc16* buf = ZONE->NewArray<uc16>(len); + uc16* buf = zone->NewArray<uc16>(len); for (int j = 0; j < len; j++) { buf[j] = gen() % 65536; } - Resource* resource = new Resource(Vector<const uc16>(buf, len)); + Resource* resource = new(zone) Resource(Vector<const uc16>(buf, len)); building_blocks[i] = FACTORY->NewExternalStringFromTwoByte(resource); for (int j = 0; j < len; j++) { CHECK_EQ(buf[j], building_blocks[i]->Get(j)); @@ -348,10 +349,11 @@ TEST(Utf8Conversion) { TEST(ExternalShortStringAdd) { - ZoneScope zone(Isolate::Current(), DELETE_ON_EXIT); + ZoneScope zonescope(Isolate::Current(), DELETE_ON_EXIT); InitializeVM(); v8::HandleScope handle_scope; + Zone* zone = Isolate::Current()->zone(); // Make sure we cover all always-flat lengths and at least one above. static const int kMaxLength = 20; @@ -365,25 +367,25 @@ TEST(ExternalShortStringAdd) { // Generate short ascii and non-ascii external strings. for (int i = 0; i <= kMaxLength; i++) { - char* ascii = ZONE->NewArray<char>(i + 1); + char* ascii = zone->NewArray<char>(i + 1); for (int j = 0; j < i; j++) { ascii[j] = 'a'; } // Terminating '\0' is left out on purpose. It is not required for external // string data. AsciiResource* ascii_resource = - new AsciiResource(Vector<const char>(ascii, i)); + new(zone) AsciiResource(Vector<const char>(ascii, i)); v8::Local<v8::String> ascii_external_string = v8::String::NewExternal(ascii_resource); ascii_external_strings->Set(v8::Integer::New(i), ascii_external_string); - uc16* non_ascii = ZONE->NewArray<uc16>(i + 1); + uc16* non_ascii = zone->NewArray<uc16>(i + 1); for (int j = 0; j < i; j++) { non_ascii[j] = 0x1234; } // Terminating '\0' is left out on purpose. It is not required for external // string data. - Resource* resource = new Resource(Vector<const uc16>(non_ascii, i)); + Resource* resource = new(zone) Resource(Vector<const uc16>(non_ascii, i)); v8::Local<v8::String> non_ascii_external_string = v8::String::NewExternal(resource); non_ascii_external_strings->Set(v8::Integer::New(i), @@ -587,3 +589,105 @@ TEST(SliceFromSlice) { CHECK(SlicedString::cast(*string)->parent()->IsSeqString()); CHECK_EQ("cdefghijklmnopqrstuvwx", *(string->ToCString())); } + + +TEST(AsciiArrayJoin) { + // Set heap limits. + 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(&constraints); + + // String s is made of 2^17 = 131072 'c' characters and a is an array + // starting with 'bad', followed by 2^14 times the string s. That means the + // total length of the concatenated strings is 2^31 + 3. So on 32bit systems + // summing the lengths of the strings (as Smis) overflows and wraps. + static const char* join_causing_out_of_memory = + "var two_14 = Math.pow(2, 14);" + "var two_17 = Math.pow(2, 17);" + "var s = Array(two_17 + 1).join('c');" + "var a = ['bad'];" + "for (var i = 1; i <= two_14; i++) a.push(s);" + "a.join("");"; + + v8::HandleScope scope; + LocalContext context; + v8::V8::IgnoreOutOfMemoryException(); + v8::Local<v8::Script> script = + v8::Script::Compile(v8::String::New(join_causing_out_of_memory)); + v8::Local<v8::Value> result = script->Run(); + + // Check for out of memory state. + CHECK(result.IsEmpty()); + CHECK(context->HasOutOfMemoryException()); +} + + +static void CheckException(const char* source) { + // An empty handle is returned upon exception. + CHECK(CompileRun(source).IsEmpty()); +} + + +TEST(RobustSubStringStub) { + // This tests whether the SubStringStub can handle unsafe arguments. + // If not recognized, those unsafe arguments lead to out-of-bounds reads. + FLAG_allow_natives_syntax = true; + InitializeVM(); + HandleScope scope; + v8::Local<v8::Value> result; + Handle<String> string; + CompileRun("var short = 'abcdef';"); + + // Invalid indices. + CheckException("%_SubString(short, 0, 10000);"); + CheckException("%_SubString(short, -1234, 5);"); + CheckException("%_SubString(short, 5, 2);"); + // Special HeapNumbers. + CheckException("%_SubString(short, 1, Infinity);"); + CheckException("%_SubString(short, NaN, 5);"); + // String arguments. + CheckException("%_SubString(short, '2', '5');"); + // Ordinary HeapNumbers can be handled (in runtime). + result = CompileRun("%_SubString(short, Math.sqrt(4), 5.1);"); + string = v8::Utils::OpenHandle(v8::String::Cast(*result)); + CHECK_EQ("cde", *(string->ToCString())); + + CompileRun("var long = 'abcdefghijklmnopqrstuvwxyz';"); + // Invalid indices. + CheckException("%_SubString(long, 0, 10000);"); + CheckException("%_SubString(long, -1234, 17);"); + CheckException("%_SubString(long, 17, 2);"); + // Special HeapNumbers. + CheckException("%_SubString(long, 1, Infinity);"); + CheckException("%_SubString(long, NaN, 17);"); + // String arguments. + CheckException("%_SubString(long, '2', '17');"); + // Ordinary HeapNumbers within bounds can be handled (in runtime). + result = CompileRun("%_SubString(long, Math.sqrt(4), 17.1);"); + string = v8::Utils::OpenHandle(v8::String::Cast(*result)); + CHECK_EQ("cdefghijklmnopq", *(string->ToCString())); + + // Test that out-of-bounds substring of a slice fails when the indices + // would have been valid for the underlying string. + CompileRun("var slice = long.slice(1, 15);"); + CheckException("%_SubString(slice, 0, 17);"); +} + + +TEST(RegExpOverflow) { + // Result string has the length 2^32, causing a 32-bit integer overflow. + InitializeVM(); + HandleScope scope; + 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()); +} diff --git a/deps/v8/test/cctest/test-thread-termination.cc b/deps/v8/test/cctest/test-thread-termination.cc index 1aa57e3081..cebabaa97e 100644 --- a/deps/v8/test/cctest/test-thread-termination.cc +++ b/deps/v8/test/cctest/test-thread-termination.cc @@ -255,6 +255,10 @@ TEST(TerminateMultipleV8ThreadsDefaultIsolate) { threads[i]->Join(); delete threads[i]; } + { + v8::Locker locker; + v8::Locker::StopPreemption(); + } delete semaphore; semaphore = NULL; diff --git a/deps/v8/test/cctest/test-weakmaps.cc b/deps/v8/test/cctest/test-weakmaps.cc index 56d593628a..7bba7b6486 100644 --- a/deps/v8/test/cctest/test-weakmaps.cc +++ b/deps/v8/test/cctest/test-weakmaps.cc @@ -48,11 +48,11 @@ static Handle<JSWeakMap> AllocateJSWeakMap() { static void PutIntoWeakMap(Handle<JSWeakMap> weakmap, Handle<JSObject> key, - int value) { + Handle<Object> value) { Handle<ObjectHashTable> table = PutIntoObjectHashTable( Handle<ObjectHashTable>(ObjectHashTable::cast(weakmap->table())), Handle<JSObject>(JSObject::cast(*key)), - Handle<Smi>(Smi::FromInt(value))); + value); weakmap->set_table(*table); } @@ -65,6 +65,7 @@ static void WeakPointerCallback(v8::Persistent<v8::Value> handle, void* id) { TEST(Weakness) { + FLAG_incremental_marking = false; LocalContext context; v8::HandleScope scope; Handle<JSWeakMap> weakmap = AllocateJSWeakMap(); @@ -83,7 +84,9 @@ TEST(Weakness) { // Put entry into weak map. { v8::HandleScope scope; - PutIntoWeakMap(weakmap, Handle<JSObject>(JSObject::cast(*key)), 23); + PutIntoWeakMap(weakmap, + Handle<JSObject>(JSObject::cast(*key)), + Handle<Smi>(Smi::FromInt(23))); } CHECK_EQ(1, ObjectHashTable::cast(weakmap->table())->NumberOfElements()); @@ -133,7 +136,7 @@ TEST(Shrinking) { Handle<Map> map = FACTORY->NewMap(JS_OBJECT_TYPE, JSObject::kHeaderSize); for (int i = 0; i < 32; i++) { Handle<JSObject> object = FACTORY->NewJSObjectFromMap(map); - PutIntoWeakMap(weakmap, object, i); + PutIntoWeakMap(weakmap, object, Handle<Smi>(Smi::FromInt(i))); } } @@ -152,3 +155,72 @@ TEST(Shrinking) { // Check shrunk capacity. CHECK_EQ(32, ObjectHashTable::cast(weakmap->table())->Capacity()); } + + +// Test that weak map values on an evacuation candidate which are not reachable +// by other paths are correctly recorded in the slots buffer. +TEST(Regress2060a) { + FLAG_always_compact = true; + LocalContext context; + v8::HandleScope scope; + Handle<JSFunction> function = + FACTORY->NewFunction(FACTORY->function_symbol(), FACTORY->null_value()); + Handle<JSObject> key = FACTORY->NewJSObject(function); + Handle<JSWeakMap> weakmap = AllocateJSWeakMap(); + + // Start second old-space page so that values land on evacuation candidate. + Page* first_page = HEAP->old_pointer_space()->anchor()->next_page(); + FACTORY->NewFixedArray(900 * KB / kPointerSize, TENURED); + + // Fill up weak map with values on an evacuation candidate. + { + v8::HandleScope scope; + for (int i = 0; i < 32; i++) { + Handle<JSObject> object = FACTORY->NewJSObject(function, TENURED); + CHECK(!HEAP->InNewSpace(object->address())); + CHECK(!first_page->Contains(object->address())); + PutIntoWeakMap(weakmap, key, object); + } + } + + // Force compacting garbage collection. + CHECK(FLAG_always_compact); + HEAP->CollectAllGarbage(Heap::kNoGCFlags); +} + + +// Test that weak map keys on an evacuation candidate which are reachable by +// other strong paths are correctly recorded in the slots buffer. +TEST(Regress2060b) { + FLAG_always_compact = true; +#ifdef DEBUG + FLAG_verify_heap = true; +#endif + LocalContext context; + v8::HandleScope scope; + Handle<JSFunction> function = + FACTORY->NewFunction(FACTORY->function_symbol(), FACTORY->null_value()); + + // Start second old-space page so that keys land on evacuation candidate. + Page* first_page = HEAP->old_pointer_space()->anchor()->next_page(); + FACTORY->NewFixedArray(900 * KB / kPointerSize, TENURED); + + // Fill up weak map with keys on an evacuation candidate. + Handle<JSObject> keys[32]; + for (int i = 0; i < 32; i++) { + keys[i] = FACTORY->NewJSObject(function, TENURED); + CHECK(!HEAP->InNewSpace(keys[i]->address())); + CHECK(!first_page->Contains(keys[i]->address())); + } + Handle<JSWeakMap> weakmap = AllocateJSWeakMap(); + for (int i = 0; i < 32; i++) { + PutIntoWeakMap(weakmap, keys[i], Handle<Smi>(Smi::FromInt(i))); + } + + // Force compacting garbage collection. The subsequent collections are used + // to verify that key references were actually updated. + CHECK(FLAG_always_compact); + HEAP->CollectAllGarbage(Heap::kNoGCFlags); + HEAP->CollectAllGarbage(Heap::kNoGCFlags); + HEAP->CollectAllGarbage(Heap::kNoGCFlags); +} |