diff options
Diffstat (limited to 'src/3rdparty/v8/test/cctest')
32 files changed, 3657 insertions, 691 deletions
diff --git a/src/3rdparty/v8/test/cctest/cctest.gyp b/src/3rdparty/v8/test/cctest/cctest.gyp index a242fe3..80eecfd 100644 --- a/src/3rdparty/v8/test/cctest/cctest.gyp +++ b/src/3rdparty/v8/test/cctest/cctest.gyp @@ -79,6 +79,7 @@ 'test-lockers.cc', 'test-log.cc', 'test-mark-compact.cc', + 'test-object-observe.cc', 'test-parsing.cc', 'test-platform-tls.cc', 'test-profile-generator.cc', @@ -118,7 +119,7 @@ 'test-disasm-arm.cc' ], }], - ['v8_target_arch=="mips"', { + ['v8_target_arch=="mipsel"', { 'sources': [ 'test-assembler-mips.cc', 'test-disasm-mips.cc', diff --git a/src/3rdparty/v8/test/cctest/cctest.h b/src/3rdparty/v8/test/cctest/cctest.h index 0b93562..f3961a4 100644 --- a/src/3rdparty/v8/test/cctest/cctest.h +++ b/src/3rdparty/v8/test/cctest/cctest.h @@ -214,4 +214,23 @@ static inline v8::Local<v8::Value> CompileRun(const char* source) { } +// Helper function that compiles and runs the source with given origin. +static inline v8::Local<v8::Value> CompileRunWithOrigin(const char* source, + const char* origin_url, + int line_number, + int column_number) { + v8::ScriptOrigin origin(v8::String::New(origin_url), + v8::Integer::New(line_number), + v8::Integer::New(column_number)); + return v8::Script::Compile(v8::String::New(source), &origin)->Run(); +} + + +// Pick a slightly different port to allow tests to be run in parallel. +static inline int FlagDependentPortOffset() { + return ::v8::internal::FLAG_crankshaft == false ? 100 : + ::v8::internal::FLAG_always_opt ? 200 : 0; +} + + #endif // ifndef CCTEST_H_ diff --git a/src/3rdparty/v8/test/cctest/cctest.status b/src/3rdparty/v8/test/cctest/cctest.status index af28be1..ab59e33 100644 --- a/src/3rdparty/v8/test/cctest/cctest.status +++ b/src/3rdparty/v8/test/cctest/cctest.status @@ -27,6 +27,7 @@ prefix cctest +# All tests prefixed with 'Bug' are expected to fail. test-api/Bug*: FAIL ############################################################################## @@ -43,6 +44,9 @@ test-heap-profiler/HeapSnapshotsDiff: PASS || FAIL test-serialize/TestThatAlwaysFails: FAIL test-serialize/DependentTestThatAlwaysFails: FAIL +# This test always fails. It tests that LiveEdit causes abort when turned off. +test-debug/LiveEditDisabled: FAIL + # TODO(gc): Temporarily disabled in the GC branch. test-log/EquivalenceOfLoggingAndTraversal: PASS || FAIL @@ -64,11 +68,6 @@ test-api/OutOfMemoryNested: SKIP # BUG(355): Test crashes on ARM. test-log/ProfLazyMode: SKIP -# BUG(945): Socket connect fails on ARM -test-debug/DebuggerAgent: SKIP -test-debug/DebuggerAgentProtocolOverflowHeader: SKIP -test-sockets/Socket: SKIP - # BUG(1075): Unresolved crashes. test-serialize/Deserialize: SKIP test-serialize/DeserializeFromSecondSerializationAndRunScript2: SKIP @@ -76,16 +75,15 @@ 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 == android_arm || $arch == android_ia32 ] +# Tests crash as there is no /tmp directory in Android. +test-log/LogAccessorCallbacks: SKIP +test-log/LogCallbacks: SKIP +test-log/ProfLazyMode: SKIP -############################################################################## -[ $arch == mips && $crankshaft ] +# platform-tls.h does not contain an ANDROID-related header. +test-platform-tls/FastTLS: SKIP -# Tests that time out with crankshaft. -test-debug/ThreadedDebugging: SKIP -test-debug/DebugBreakLoop: SKIP +# This test times out. +test-threads/ThreadJoinSelf: SKIP diff --git a/src/3rdparty/v8/test/cctest/test-alloc.cc b/src/3rdparty/v8/test/cctest/test-alloc.cc index e195d14..7ba2583 100644 --- a/src/3rdparty/v8/test/cctest/test-alloc.cc +++ b/src/3rdparty/v8/test/cctest/test-alloc.cc @@ -1,4 +1,4 @@ -// Copyright 2011 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: @@ -34,7 +34,8 @@ using namespace v8::internal; -static inline void SimulateFullSpace(PagedSpace* space) { +// Also used in test-heap.cc test cases. +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()); @@ -150,12 +151,21 @@ TEST(StressJS) { Handle<Map> map(function->initial_map()); Handle<DescriptorArray> instance_descriptors(map->instance_descriptors()); Handle<Foreign> foreign = FACTORY->NewForeign(&kDescriptor); - instance_descriptors = FACTORY->CopyAppendForeignDescriptor( - instance_descriptors, - FACTORY->NewStringFromAscii(Vector<const char>("get", 3)), - foreign, - static_cast<PropertyAttributes>(0)); - map->set_instance_descriptors(*instance_descriptors); + Handle<String> name = + FACTORY->NewStringFromAscii(Vector<const char>("get", 3)); + ASSERT(instance_descriptors->IsEmpty()); + + Handle<DescriptorArray> new_descriptors = FACTORY->NewDescriptorArray(0, 1); + + v8::internal::DescriptorArray::WhitenessWitness witness(*new_descriptors); + map->set_instance_descriptors(*new_descriptors); + + CallbacksDescriptor d(*name, + *foreign, + static_cast<PropertyAttributes>(0), + v8::internal::PropertyDetails::kInitialIndex); + map->AppendDescriptor(&d, witness); + // Add the Foo constructor the global object. env->Global()->Set(v8::String::New("Foo"), v8::Utils::ToLocal(function)); // Call the accessor through JavaScript. diff --git a/src/3rdparty/v8/test/cctest/test-api.cc b/src/3rdparty/v8/test/cctest/test-api.cc index 9122781..f7325df 100644 --- a/src/3rdparty/v8/test/cctest/test-api.cc +++ b/src/3rdparty/v8/test/cctest/test-api.cc @@ -27,12 +27,18 @@ #include <limits.h> +#ifndef WIN32 +#include <signal.h> // kill +#include <unistd.h> // getpid +#endif // WIN32 + #include "v8.h" #include "api.h" #include "isolate.h" #include "compilation-cache.h" #include "execution.h" +#include "objects.h" #include "snapshot.h" #include "platform.h" #include "utils.h" @@ -398,6 +404,10 @@ THREADED_TEST(ScriptUsingStringResource) { CHECK(source->IsExternal()); CHECK_EQ(resource, static_cast<TestResource*>(source->GetExternalStringResource())); + String::Encoding encoding = String::UNKNOWN_ENCODING; + CHECK_EQ(static_cast<const String::ExternalStringResourceBase*>(resource), + source->GetExternalStringResourceBase(&encoding)); + CHECK_EQ(String::TWO_BYTE_ENCODING, encoding); HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); CHECK_EQ(0, dispose_count); } @@ -413,9 +423,16 @@ THREADED_TEST(ScriptUsingAsciiStringResource) { { v8::HandleScope scope; LocalContext env; - Local<String> source = - String::NewExternal(new TestAsciiResource(i::StrDup(c_source), - &dispose_count)); + TestAsciiResource* resource = new TestAsciiResource(i::StrDup(c_source), + &dispose_count); + Local<String> source = String::NewExternal(resource); + CHECK(source->IsExternalAscii()); + CHECK_EQ(static_cast<const String::ExternalStringResourceBase*>(resource), + source->GetExternalAsciiStringResource()); + String::Encoding encoding = String::UNKNOWN_ENCODING; + CHECK_EQ(static_cast<const String::ExternalStringResourceBase*>(resource), + source->GetExternalStringResourceBase(&encoding)); + CHECK_EQ(String::ASCII_ENCODING, encoding); Local<Script> script = Script::Compile(source); Local<Value> value = script->Run(); CHECK(value->IsNumber()); @@ -439,6 +456,11 @@ THREADED_TEST(ScriptMakingExternalString) { // Trigger GCs so that the newly allocated string moves to old gen. HEAP->CollectGarbage(i::NEW_SPACE); // in survivor space now HEAP->CollectGarbage(i::NEW_SPACE); // in old gen now + CHECK_EQ(source->IsExternal(), false); + CHECK_EQ(source->IsExternalAscii(), false); + String::Encoding encoding = String::UNKNOWN_ENCODING; + CHECK_EQ(NULL, source->GetExternalStringResourceBase(&encoding)); + CHECK_EQ(String::ASCII_ENCODING, encoding); bool success = source->MakeExternal(new TestResource(two_byte_source, &dispose_count)); CHECK(success); @@ -947,22 +969,33 @@ THREADED_TEST(FindInstanceInPrototypeChain) { THREADED_TEST(TinyInteger) { v8::HandleScope scope; LocalContext env; + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + int32_t value = 239; Local<v8::Integer> value_obj = v8::Integer::New(value); CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); + + value_obj = v8::Integer::New(value, isolate); + CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); } THREADED_TEST(BigSmiInteger) { v8::HandleScope scope; LocalContext env; + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + int32_t value = i::Smi::kMaxValue; // We cannot add one to a Smi::kMaxValue without wrapping. if (i::kSmiValueSize < 32) { CHECK(i::Smi::IsValid(value)); CHECK(!i::Smi::IsValid(value + 1)); + Local<v8::Integer> value_obj = v8::Integer::New(value); CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); + + value_obj = v8::Integer::New(value, isolate); + CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); } } @@ -970,6 +1003,8 @@ THREADED_TEST(BigSmiInteger) { THREADED_TEST(BigInteger) { v8::HandleScope scope; LocalContext env; + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + // We cannot add one to a Smi::kMaxValue without wrapping. if (i::kSmiValueSize < 32) { // The casts allow this to compile, even if Smi::kMaxValue is 2^31-1. @@ -978,8 +1013,12 @@ THREADED_TEST(BigInteger) { static_cast<int32_t>(static_cast<uint32_t>(i::Smi::kMaxValue) + 1); CHECK(value > i::Smi::kMaxValue); CHECK(!i::Smi::IsValid(value)); + Local<v8::Integer> value_obj = v8::Integer::New(value); CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); + + value_obj = v8::Integer::New(value, isolate); + CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); } } @@ -987,42 +1026,66 @@ THREADED_TEST(BigInteger) { THREADED_TEST(TinyUnsignedInteger) { v8::HandleScope scope; LocalContext env; + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + uint32_t value = 239; + Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value); CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); + + value_obj = v8::Integer::NewFromUnsigned(value, isolate); + CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); } THREADED_TEST(BigUnsignedSmiInteger) { v8::HandleScope scope; LocalContext env; + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + uint32_t value = static_cast<uint32_t>(i::Smi::kMaxValue); CHECK(i::Smi::IsValid(value)); CHECK(!i::Smi::IsValid(value + 1)); + Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value); CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); + + value_obj = v8::Integer::NewFromUnsigned(value, isolate); + CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); } THREADED_TEST(BigUnsignedInteger) { v8::HandleScope scope; LocalContext env; + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + uint32_t value = static_cast<uint32_t>(i::Smi::kMaxValue) + 1; CHECK(value > static_cast<uint32_t>(i::Smi::kMaxValue)); CHECK(!i::Smi::IsValid(value)); + Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value); CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); + + value_obj = v8::Integer::NewFromUnsigned(value, isolate); + CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); } THREADED_TEST(OutOfSignedRangeUnsignedInteger) { v8::HandleScope scope; LocalContext env; + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + uint32_t INT32_MAX_AS_UINT = (1U << 31) - 1; uint32_t value = INT32_MAX_AS_UINT + 1; CHECK(value > INT32_MAX_AS_UINT); // No overflow. + Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value); CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); + + value_obj = v8::Integer::NewFromUnsigned(value, isolate); + CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); } @@ -2080,6 +2143,10 @@ THREADED_TEST(HiddenProperties) { HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); + CHECK(obj->SetHiddenValue(key, Handle<Value>())); + CHECK(obj->GetHiddenValue(key).IsEmpty()); + + CHECK(obj->SetHiddenValue(key, v8::Integer::New(2002))); CHECK(obj->DeleteHiddenValue(key)); CHECK(obj->GetHiddenValue(key).IsEmpty()); } @@ -2182,6 +2249,14 @@ THREADED_TEST(GlobalHandle) { } CHECK_EQ(global->Length(), 3); global.Dispose(); + + { + v8::HandleScope scope; + Local<String> str = v8_str("str"); + global = v8::Persistent<String>::New(str); + } + CHECK_EQ(global->Length(), 3); + global.Dispose(v8::Isolate::GetCurrent()); } @@ -2372,6 +2447,100 @@ THREADED_TEST(ApiObjectGroupsCycle) { } +// TODO(mstarzinger): This should be a THREADED_TEST but causes failures +// on the buildbots, so was made non-threaded for the time being. +TEST(ApiObjectGroupsCycleForScavenger) { + HandleScope scope; + LocalContext env; + + WeakCallCounter counter(1234); + + Persistent<Object> g1s1; + Persistent<Object> g1s2; + Persistent<Object> g2s1; + Persistent<Object> g2s2; + Persistent<Object> g3s1; + Persistent<Object> g3s2; + + { + HandleScope scope; + g1s1 = Persistent<Object>::New(Object::New()); + g1s2 = Persistent<Object>::New(Object::New()); + g1s1.MakeWeak(reinterpret_cast<void*>(&counter), &WeakPointerCallback); + g1s2.MakeWeak(reinterpret_cast<void*>(&counter), &WeakPointerCallback); + + g2s1 = Persistent<Object>::New(Object::New()); + g2s2 = Persistent<Object>::New(Object::New()); + g2s1.MakeWeak(reinterpret_cast<void*>(&counter), &WeakPointerCallback); + g2s2.MakeWeak(reinterpret_cast<void*>(&counter), &WeakPointerCallback); + + g3s1 = Persistent<Object>::New(Object::New()); + g3s2 = Persistent<Object>::New(Object::New()); + g3s1.MakeWeak(reinterpret_cast<void*>(&counter), &WeakPointerCallback); + g3s2.MakeWeak(reinterpret_cast<void*>(&counter), &WeakPointerCallback); + } + + // Make a root. + Persistent<Object> root = Persistent<Object>::New(g1s1); + root.MarkPartiallyDependent(); + + // Connect groups. We're building the following cycle: + // G1: { g1s1, g2s1 }, g1s1 implicitly references g2s1, ditto for other + // groups. + { + g1s1.MarkPartiallyDependent(); + g1s2.MarkPartiallyDependent(); + g2s1.MarkPartiallyDependent(); + g2s2.MarkPartiallyDependent(); + g3s1.MarkPartiallyDependent(); + g3s2.MarkPartiallyDependent(); + Persistent<Value> g1_objects[] = { g1s1, g1s2 }; + Persistent<Value> g2_objects[] = { g2s1, g2s2 }; + Persistent<Value> g3_objects[] = { g3s1, g3s2 }; + V8::AddObjectGroup(g1_objects, 2); + g1s1->Set(v8_str("x"), g2s1); + V8::AddObjectGroup(g2_objects, 2); + g2s1->Set(v8_str("x"), g3s1); + V8::AddObjectGroup(g3_objects, 2); + g3s1->Set(v8_str("x"), g1s1); + } + + HEAP->CollectGarbage(i::NEW_SPACE); + + // All objects should be alive. + CHECK_EQ(0, counter.NumberOfWeakCalls()); + + // Weaken the root. + root.MakeWeak(reinterpret_cast<void*>(&counter), &WeakPointerCallback); + root.MarkPartiallyDependent(); + + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + // Groups are deleted, rebuild groups. + { + g1s1.MarkPartiallyDependent(isolate); + g1s2.MarkPartiallyDependent(isolate); + g2s1.MarkPartiallyDependent(isolate); + g2s2.MarkPartiallyDependent(isolate); + g3s1.MarkPartiallyDependent(isolate); + g3s2.MarkPartiallyDependent(isolate); + Persistent<Value> g1_objects[] = { g1s1, g1s2 }; + Persistent<Value> g2_objects[] = { g2s1, g2s2 }; + Persistent<Value> g3_objects[] = { g3s1, g3s2 }; + V8::AddObjectGroup(g1_objects, 2); + g1s1->Set(v8_str("x"), g2s1); + V8::AddObjectGroup(g2_objects, 2); + g2s1->Set(v8_str("x"), g3s1); + V8::AddObjectGroup(g3_objects, 2); + g3s1->Set(v8_str("x"), g1s1); + } + + HEAP->CollectGarbage(i::NEW_SPACE); + + // All objects should be gone. 7 global handles in total. + CHECK_EQ(7, counter.NumberOfWeakCalls()); +} + + THREADED_TEST(ScriptException) { v8::HandleScope scope; LocalContext env; @@ -2388,20 +2557,19 @@ THREADED_TEST(ScriptException) { bool message_received; -static void check_message(v8::Handle<v8::Message> message, - v8::Handle<Value> data) { - CHECK_EQ(5.76, data->NumberValue()); +static void check_message_0(v8::Handle<v8::Message> message, + v8::Handle<Value> data) { CHECK_EQ(6.75, message->GetScriptResourceName()->NumberValue()); CHECK_EQ(7.56, message->GetScriptData()->NumberValue()); message_received = true; } -THREADED_TEST(MessageHandlerData) { +THREADED_TEST(MessageHandler0) { message_received = false; v8::HandleScope scope; CHECK(!message_received); - v8::V8::AddMessageListener(check_message, v8_num(5.76)); + v8::V8::AddMessageListener(check_message_0); LocalContext context; v8::ScriptOrigin origin = v8::ScriptOrigin(v8_str("6.75")); @@ -2411,7 +2579,56 @@ THREADED_TEST(MessageHandlerData) { script->Run(); CHECK(message_received); // clear out the message listener - v8::V8::RemoveMessageListeners(check_message); + v8::V8::RemoveMessageListeners(check_message_0); +} + + +static void check_message_1(v8::Handle<v8::Message> message, + v8::Handle<Value> data) { + CHECK(data->IsNumber()); + CHECK_EQ(1337, data->Int32Value()); + message_received = true; +} + + +TEST(MessageHandler1) { + message_received = false; + v8::HandleScope scope; + CHECK(!message_received); + v8::V8::AddMessageListener(check_message_1); + LocalContext context; + CompileRun("throw 1337;"); + CHECK(message_received); + // clear out the message listener + v8::V8::RemoveMessageListeners(check_message_1); +} + + +static void check_message_2(v8::Handle<v8::Message> message, + v8::Handle<Value> data) { + LocalContext context; + CHECK(data->IsObject()); + v8::Local<v8::Value> hidden_property = + v8::Object::Cast(*data)->GetHiddenValue(v8_str("hidden key")); + CHECK(v8_str("hidden value")->Equals(hidden_property)); + message_received = true; +} + + +TEST(MessageHandler2) { + message_received = false; + v8::HandleScope scope; + CHECK(!message_received); + v8::V8::AddMessageListener(check_message_2); + LocalContext context; + v8::Local<v8::Value> error = v8::Exception::Error(v8_str("custom error")); + v8::Object::Cast(*error)->SetHiddenValue(v8_str("hidden key"), + v8_str("hidden value")); + context->Global()->Set(v8_str("error"), error); + CompileRun("throw error;"); + CHECK(message_received); + // clear out the message listener + v8::V8::RemoveMessageListeners(check_message_2); } @@ -2697,7 +2914,7 @@ TEST(HugeConsStringOutOfMemory) { static const int K = 1024; v8::ResourceConstraints constraints; constraints.set_max_young_space_size(256 * K); - constraints.set_max_old_space_size(2 * K * K); + constraints.set_max_old_space_size(3 * K * K); v8::SetResourceConstraints(&constraints); // Execute a script that causes out of memory. @@ -3052,7 +3269,33 @@ TEST(APIThrowMessageOverwrittenToString) { "Number.prototype.toString = function() { return 'Whoops'; };" "ReferenceError.prototype.toString = Object.prototype.toString;"); CompileRun("asdf;"); - v8::V8::RemoveMessageListeners(check_message); + v8::V8::RemoveMessageListeners(check_reference_error_message); +} + + +static void check_custom_error_message( + v8::Handle<v8::Message> message, + v8::Handle<v8::Value> data) { + const char* uncaught_error = "Uncaught MyError toString"; + CHECK(message->Get()->Equals(v8_str(uncaught_error))); +} + + +TEST(CustomErrorToString) { + v8::HandleScope scope; + v8::V8::AddMessageListener(check_custom_error_message); + LocalContext context; + CompileRun( + "function MyError(name, message) { " + " this.name = name; " + " this.message = message; " + "} " + "MyError.prototype = Object.create(Error.prototype); " + "MyError.prototype.toString = function() { " + " return 'MyError toString'; " + "}; " + "throw new MyError('my name', 'my message'); "); + v8::V8::RemoveMessageListeners(check_custom_error_message); } @@ -3073,7 +3316,7 @@ TEST(APIThrowMessage) { LocalContext context(0, templ); CompileRun("ThrowFromC();"); CHECK(message_received); - v8::V8::RemoveMessageListeners(check_message); + v8::V8::RemoveMessageListeners(receive_message); } @@ -3091,7 +3334,7 @@ TEST(APIThrowMessageAndVerboseTryCatch) { CHECK(try_catch.HasCaught()); CHECK(result.IsEmpty()); CHECK(message_received); - v8::V8::RemoveMessageListeners(check_message); + v8::V8::RemoveMessageListeners(receive_message); } @@ -3710,6 +3953,36 @@ THREADED_TEST(SimplePropertyWrite) { } +THREADED_TEST(SetterOnly) { + v8::HandleScope scope; + Local<ObjectTemplate> templ = ObjectTemplate::New(); + templ->SetAccessor(v8_str("x"), NULL, SetXValue, v8_str("donut")); + LocalContext context; + context->Global()->Set(v8_str("obj"), templ->NewInstance()); + Local<Script> script = Script::Compile(v8_str("obj.x = 4; obj.x")); + for (int i = 0; i < 10; i++) { + CHECK(xValue.IsEmpty()); + script->Run(); + CHECK_EQ(v8_num(4), xValue); + xValue.Dispose(); + xValue = v8::Persistent<Value>(); + } +} + + +THREADED_TEST(NoAccessors) { + v8::HandleScope scope; + Local<ObjectTemplate> templ = ObjectTemplate::New(); + templ->SetAccessor(v8_str("x"), NULL, NULL, v8_str("donut")); + LocalContext context; + context->Global()->Set(v8_str("obj"), templ->NewInstance()); + Local<Script> script = Script::Compile(v8_str("obj.x = 4; obj.x")); + for (int i = 0; i < 10; i++) { + script->Run(); + } +} + + static v8::Handle<Value> XPropertyGetter(Local<String> property, const AccessorInfo& info) { ApiTestFuzzer::Fuzz(); @@ -4605,6 +4878,18 @@ THREADED_TEST(SimpleExtensions) { } +THREADED_TEST(NullExtensions) { + v8::HandleScope handle_scope; + v8::RegisterExtension(new Extension("nulltest", NULL)); + const char* extension_names[] = { "nulltest" }; + v8::ExtensionConfiguration extensions(1, extension_names); + v8::Handle<Context> context = Context::New(&extensions); + Context::Scope lock(context); + v8::Handle<Value> result = Script::Compile(v8_str("1+3"))->Run(); + CHECK_EQ(result, v8::Integer::New(4)); +} + + static const char* kEmbeddedExtensionSource = "function Ret54321(){return 54321;}~~@@$" "$%% THIS IS A SERIES OF NON-NULL-TERMINATED STRINGS."; @@ -5014,7 +5299,6 @@ TEST(RegexpOutOfMemory) { static void MissingScriptInfoMessageListener(v8::Handle<v8::Message> message, v8::Handle<Value> data) { - CHECK_EQ(v8::Undefined(), data); CHECK(message->GetScriptResourceName()->IsUndefined()); CHECK_EQ(v8::Undefined(), message->GetScriptResourceName()); message->GetLineNumber(); @@ -5126,18 +5410,28 @@ THREADED_TEST(IndependentWeakHandle) { v8::Persistent<Context> context = Context::New(); Context::Scope context_scope(context); - v8::Persistent<v8::Object> object_a; + v8::Persistent<v8::Object> object_a, object_b; { v8::HandleScope handle_scope; object_a = v8::Persistent<v8::Object>::New(v8::Object::New()); + object_b = v8::Persistent<v8::Object>::New(v8::Object::New()); } + v8::Isolate* isolate = v8::Isolate::GetCurrent(); bool object_a_disposed = false; + bool object_b_disposed = false; object_a.MakeWeak(&object_a_disposed, &DisposeAndSetFlag); + object_b.MakeWeak(&object_b_disposed, &DisposeAndSetFlag); + CHECK(!object_a.IsIndependent()); + CHECK(!object_b.IsIndependent(isolate)); object_a.MarkIndependent(); + object_b.MarkIndependent(isolate); + CHECK(object_a.IsIndependent()); + CHECK(object_b.IsIndependent(isolate)); HEAP->PerformScavenge(); CHECK(object_a_disposed); + CHECK(object_b_disposed); } @@ -5218,7 +5512,7 @@ THREADED_TEST(IndependentHandleRevival) { object.MarkIndependent(); HEAP->PerformScavenge(); CHECK(revived); - HEAP->CollectAllGarbage(true); + HEAP->CollectAllGarbage(i::Heap::kAbortIncrementalMarkingMask); { v8::HandleScope handle_scope; v8::Local<String> y_str = v8_str("y"); @@ -5543,6 +5837,7 @@ THREADED_TEST(StringWrite) { v8::Handle<String> str = v8_str("abcde"); // abc<Icelandic eth><Unicode snowman>. v8::Handle<String> str2 = v8_str("abc\303\260\342\230\203"); + v8::Handle<String> str3 = v8::String::New("abc\0def", 7); const int kStride = 4; // Must match stride in for loops in JS below. CompileRun( "var left = '';" @@ -5753,6 +6048,28 @@ THREADED_TEST(StringWrite) { CHECK_NE(0, strcmp(utf8buf, "abc\303\260\342\230\203")); utf8buf[8] = '\0'; CHECK_EQ(0, strcmp(utf8buf, "abc\303\260\342\230\203")); + + memset(utf8buf, 0x1, sizeof(utf8buf)); + utf8buf[5] = 'X'; + len = str->WriteUtf8(utf8buf, sizeof(utf8buf), &charlen, + String::NO_NULL_TERMINATION); + CHECK_EQ(5, len); + CHECK_EQ('X', utf8buf[5]); // Test that the sixth character is untouched. + CHECK_EQ(5, charlen); + utf8buf[5] = '\0'; + CHECK_EQ(0, strcmp(utf8buf, "abcde")); + + memset(buf, 0x1, sizeof(buf)); + len = str3->WriteAscii(buf); + CHECK_EQ(7, len); + CHECK_EQ(0, strcmp("abc def", buf)); + + memset(buf, 0x1, sizeof(buf)); + len = str3->WriteAscii(buf, 0, -1, String::PRESERVE_ASCII_NULL); + CHECK_EQ(7, len); + CHECK_EQ(0, strcmp("abc", buf)); + CHECK_EQ(0, buf[3]); + CHECK_EQ(0, strcmp("def", buf + 4)); } @@ -7662,7 +7979,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); @@ -9370,7 +9687,8 @@ static void GenerateSomeGarbage() { v8::Handle<v8::Value> DirectApiCallback(const v8::Arguments& args) { static int count = 0; if (count++ % 3 == 0) { - HEAP-> CollectAllGarbage(true); // This should move the stub + HEAP->CollectAllGarbage(i::Heap::kAbortIncrementalMarkingMask); + // This should move the stub GenerateSomeGarbage(); // This should ensure the old stub memory is flushed } return v8::Handle<v8::Value>(); @@ -9425,7 +9743,7 @@ THREADED_TEST(CallICFastApi_DirectCall_Throw) { v8::Handle<v8::Value> DirectGetterCallback(Local<String> name, const v8::AccessorInfo& info) { if (++p_getter_count % 3 == 0) { - HEAP->CollectAllGarbage(true); + HEAP->CollectAllGarbage(i::Heap::kAbortIncrementalMarkingMask); GenerateSomeGarbage(); } return v8::Handle<v8::Value>(); @@ -10260,6 +10578,7 @@ static v8::Handle<Value> ChildGetter(Local<String> name, THREADED_TEST(Overriding) { + i::FLAG_es5_readonly = true; v8::HandleScope scope; LocalContext context; @@ -10306,11 +10625,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()); } @@ -10721,18 +11040,21 @@ TEST(DontLeakGlobalObjects) { { v8::HandleScope scope; LocalContext context; } + v8::V8::ContextDisposedNotification(); CheckSurvivingGlobalObjectsCount(0); { v8::HandleScope scope; LocalContext context; v8_compile("Date")->Run(); } + v8::V8::ContextDisposedNotification(); CheckSurvivingGlobalObjectsCount(0); { v8::HandleScope scope; LocalContext context; v8_compile("/aaa/")->Run(); } + v8::V8::ContextDisposedNotification(); CheckSurvivingGlobalObjectsCount(0); { v8::HandleScope scope; @@ -10741,6 +11063,7 @@ TEST(DontLeakGlobalObjects) { LocalContext context(&extensions); v8_compile("gc();")->Run(); } + v8::V8::ContextDisposedNotification(); CheckSurvivingGlobalObjectsCount(0); } } @@ -10871,6 +11194,307 @@ THREADED_TEST(NestedHandleScopeAndContexts) { } +static i::Handle<i::JSFunction>* foo_ptr = NULL; +static int foo_count = 0; +static i::Handle<i::JSFunction>* bar_ptr = NULL; +static int bar_count = 0; + + +static void entry_hook(uintptr_t function, + uintptr_t return_addr_location) { + i::Code* code = i::Code::GetCodeFromTargetAddress( + reinterpret_cast<i::Address>(function)); + CHECK(code != NULL); + + if (bar_ptr != NULL && code == (*bar_ptr)->code()) + ++bar_count; + + if (foo_ptr != NULL && code == (*foo_ptr)->code()) + ++foo_count; + + // TODO(siggi): Verify return_addr_location. + // This can be done by capturing JitCodeEvents, but requires an ordered + // collection. +} + + +static void RunLoopInNewEnv() { + bar_ptr = NULL; + foo_ptr = NULL; + + v8::HandleScope outer; + v8::Persistent<Context> env = Context::New(); + env->Enter(); + + const char* script = + "function bar() {" + " var sum = 0;" + " for (i = 0; i < 100; ++i)" + " sum = foo(i);" + " return sum;" + "}" + "function foo(i) { return i * i; }"; + CompileRun(script); + i::Handle<i::JSFunction> bar = + i::Handle<i::JSFunction>::cast( + v8::Utils::OpenHandle(*env->Global()->Get(v8_str("bar")))); + ASSERT(*bar); + + i::Handle<i::JSFunction> foo = + i::Handle<i::JSFunction>::cast( + v8::Utils::OpenHandle(*env->Global()->Get(v8_str("foo")))); + ASSERT(*foo); + + bar_ptr = &bar; + foo_ptr = &foo; + + v8::Handle<v8::Value> value = CompileRun("bar();"); + CHECK(value->IsNumber()); + CHECK_EQ(9801.0, v8::Number::Cast(*value)->Value()); + + // Test the optimized codegen path. + value = CompileRun("%OptimizeFunctionOnNextCall(foo);" + "bar();"); + CHECK(value->IsNumber()); + CHECK_EQ(9801.0, v8::Number::Cast(*value)->Value()); + + env->Exit(); +} + + +TEST(SetFunctionEntryHook) { + i::FLAG_allow_natives_syntax = true; + + // Test setting and resetting the entry hook. + // Nulling it should always succeed. + CHECK(v8::V8::SetFunctionEntryHook(NULL)); + + CHECK(v8::V8::SetFunctionEntryHook(entry_hook)); + // Setting a hook while one's active should fail. + CHECK_EQ(false, v8::V8::SetFunctionEntryHook(entry_hook)); + + CHECK(v8::V8::SetFunctionEntryHook(NULL)); + + // Reset the entry count to zero and set the entry hook. + bar_count = 0; + foo_count = 0; + CHECK(v8::V8::SetFunctionEntryHook(entry_hook)); + RunLoopInNewEnv(); + + CHECK_EQ(2, bar_count); + CHECK_EQ(200, foo_count); + + // Clear the entry hook and count. + bar_count = 0; + foo_count = 0; + v8::V8::SetFunctionEntryHook(NULL); + + // Clear the compilation cache to make sure we don't reuse the + // functions from the previous invocation. + v8::internal::Isolate::Current()->compilation_cache()->Clear(); + + // Verify that entry hooking is now disabled. + RunLoopInNewEnv(); + CHECK_EQ(0u, bar_count); + CHECK_EQ(0u, foo_count); +} + + +static i::HashMap* code_map = NULL; +static int saw_bar = 0; +static int move_events = 0; + + +static bool FunctionNameIs(const char* expected, + const v8::JitCodeEvent* event) { + // Log lines for functions are of the general form: + // "LazyCompile:<type><function_name>", where the type is one of + // "*", "~" or "". + static const char kPreamble[] = "LazyCompile:"; + static size_t kPreambleLen = sizeof(kPreamble) - 1; + + if (event->name.len < sizeof(kPreamble) - 1 || + strncmp(kPreamble, event->name.str, kPreambleLen) != 0) { + return false; + } + + const char* tail = event->name.str + kPreambleLen; + size_t tail_len = event->name.len - kPreambleLen; + size_t expected_len = strlen(expected); + if (tail_len == expected_len + 1) { + if (*tail == '*' || *tail == '~') { + --tail_len; + ++tail; + } else { + return false; + } + } + + if (tail_len != expected_len) + return false; + + return strncmp(tail, expected, expected_len) == 0; +} + + +static void event_handler(const v8::JitCodeEvent* event) { + CHECK(event != NULL); + CHECK(code_map != NULL); + + switch (event->type) { + case v8::JitCodeEvent::CODE_ADDED: { + CHECK(event->code_start != NULL); + CHECK_NE(0, static_cast<int>(event->code_len)); + CHECK(event->name.str != NULL); + i::HashMap::Entry* entry = + code_map->Lookup(event->code_start, + i::ComputePointerHash(event->code_start), + true); + entry->value = reinterpret_cast<void*>(event->code_len); + + if (FunctionNameIs("bar", event)) { + ++saw_bar; + } + } + break; + + case v8::JitCodeEvent::CODE_MOVED: { + uint32_t hash = i::ComputePointerHash(event->code_start); + // We would like to never see code move that we haven't seen before, + // but the code creation event does not happen until the line endings + // have been calculated (this is so that we can report the line in the + // script at which the function source is found, see + // Compiler::RecordFunctionCompilation) and the line endings + // calculations can cause a GC, which can move the newly created code + // before its existence can be logged. + i::HashMap::Entry* entry = + code_map->Lookup(event->code_start, hash, false); + if (entry != NULL) { + ++move_events; + + CHECK_EQ(reinterpret_cast<void*>(event->code_len), entry->value); + code_map->Remove(event->code_start, hash); + + entry = code_map->Lookup(event->new_code_start, + i::ComputePointerHash(event->new_code_start), + true); + CHECK(entry != NULL); + entry->value = reinterpret_cast<void*>(event->code_len); + } + } + break; + + case v8::JitCodeEvent::CODE_REMOVED: + // Object/code removal events are currently not dispatched from the GC. + CHECK(false); + break; + default: + // Impossible event. + CHECK(false); + break; + } +} + + +// Implemented in the test-alloc.cc test suite. +void SimulateFullSpace(i::PagedSpace* space); + + +static bool MatchPointers(void* key1, void* key2) { + return key1 == key2; +} + + +TEST(SetJitCodeEventHandler) { + const char* script = + "function bar() {" + " var sum = 0;" + " for (i = 0; i < 100; ++i)" + " sum = foo(i);" + " return sum;" + "}" + "function foo(i) { return i * i; };" + "bar();"; + + // Run this test in a new isolate to make sure we don't + // have remnants of state from other code. + v8::Isolate* isolate = v8::Isolate::New(); + isolate->Enter(); + + { + i::HashMap code(MatchPointers); + code_map = &code; + + saw_bar = 0; + move_events = 0; + + i::FLAG_stress_compaction = true; + V8::SetJitCodeEventHandler(v8::kJitCodeEventDefault, event_handler); + + v8::HandleScope scope; + // Generate new code objects sparsely distributed across several + // different fragmented code-space pages. + const int kIterations = 10; + for (int i = 0; i < kIterations; ++i) { + LocalContext env; + + v8::Handle<v8::Script> compiled_script; + { + i::AlwaysAllocateScope always_allocate; + SimulateFullSpace(HEAP->code_space()); + compiled_script = v8_compile(script); + } + compiled_script->Run(); + + // Clear the compilation cache to get more wastage. + ISOLATE->compilation_cache()->Clear(); + } + + // Force code movement. + HEAP->CollectAllAvailableGarbage("TestSetJitCodeEventHandler"); + + CHECK_LE(kIterations, saw_bar); + CHECK_NE(0, move_events); + + code_map = NULL; + V8::SetJitCodeEventHandler(v8::kJitCodeEventDefault, NULL); + } + + isolate->Exit(); + isolate->Dispose(); + + // Do this in a new isolate. + isolate = v8::Isolate::New(); + isolate->Enter(); + + // Verify that we get callbacks for existing code objects when we + // request enumeration of existing code. + { + v8::HandleScope scope; + LocalContext env; + CompileRun(script); + + // Now get code through initial iteration. + i::HashMap code(MatchPointers); + code_map = &code; + + V8::SetJitCodeEventHandler(v8::kJitCodeEventEnumExisting, event_handler); + V8::SetJitCodeEventHandler(v8::kJitCodeEventDefault, NULL); + + code_map = NULL; + + // We expect that we got some events. Note that if we could get code removal + // notifications, we could compare two collections, one created by listening + // from the time of creation of an isolate, and the other by subscribing + // with EnumExisting. + CHECK_NE(0, code.occupancy()); + } + + isolate->Exit(); + isolate->Dispose(); +} + + static int64_t cast(intptr_t x) { return static_cast<int64_t>(x); } @@ -12040,7 +12664,7 @@ class RegExpStringModificationTest { // Inject the input as a global variable. i::Handle<i::String> input_name = FACTORY->NewStringFromAscii(i::Vector<const char>("input", 5)); - i::Isolate::Current()->global_context()->global()->SetProperty( + i::Isolate::Current()->native_context()->global_object()->SetProperty( *input_name, *input_, NONE, @@ -12148,9 +12772,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); @@ -12162,12 +12787,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; @@ -13617,6 +14243,41 @@ THREADED_TEST(ExternalArrayInfo) { } +void ExternalArrayLimitTestHelper(v8::ExternalArrayType array_type, int size) { + v8::Handle<v8::Object> obj = v8::Object::New(); + v8::V8::SetFatalErrorHandler(StoringErrorCallback); + last_location = last_message = NULL; + obj->SetIndexedPropertiesToExternalArrayData(NULL, array_type, size); + CHECK(!obj->HasIndexedPropertiesInExternalArrayData()); + CHECK_NE(NULL, last_location); + CHECK_NE(NULL, last_message); +} + + +TEST(ExternalArrayLimits) { + v8::HandleScope scope; + LocalContext context; + ExternalArrayLimitTestHelper(v8::kExternalByteArray, 0x40000000); + ExternalArrayLimitTestHelper(v8::kExternalByteArray, 0xffffffff); + ExternalArrayLimitTestHelper(v8::kExternalUnsignedByteArray, 0x40000000); + ExternalArrayLimitTestHelper(v8::kExternalUnsignedByteArray, 0xffffffff); + ExternalArrayLimitTestHelper(v8::kExternalShortArray, 0x40000000); + ExternalArrayLimitTestHelper(v8::kExternalShortArray, 0xffffffff); + ExternalArrayLimitTestHelper(v8::kExternalUnsignedShortArray, 0x40000000); + ExternalArrayLimitTestHelper(v8::kExternalUnsignedShortArray, 0xffffffff); + ExternalArrayLimitTestHelper(v8::kExternalIntArray, 0x40000000); + ExternalArrayLimitTestHelper(v8::kExternalIntArray, 0xffffffff); + ExternalArrayLimitTestHelper(v8::kExternalUnsignedIntArray, 0x40000000); + ExternalArrayLimitTestHelper(v8::kExternalUnsignedIntArray, 0xffffffff); + ExternalArrayLimitTestHelper(v8::kExternalFloatArray, 0x40000000); + ExternalArrayLimitTestHelper(v8::kExternalFloatArray, 0xffffffff); + ExternalArrayLimitTestHelper(v8::kExternalDoubleArray, 0x40000000); + ExternalArrayLimitTestHelper(v8::kExternalDoubleArray, 0xffffffff); + ExternalArrayLimitTestHelper(v8::kExternalPixelArray, 0x40000000); + ExternalArrayLimitTestHelper(v8::kExternalPixelArray, 0xffffffff); +} + + THREADED_TEST(ScriptContextDependence) { v8::HandleScope scope; LocalContext c1; @@ -13995,6 +14656,89 @@ TEST(SourceURLInStackTrace) { } +v8::Handle<Value> AnalyzeStackOfInlineScriptWithSourceURL( + const v8::Arguments& args) { + v8::HandleScope scope; + v8::Handle<v8::StackTrace> stackTrace = + v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kDetailed); + CHECK_EQ(4, stackTrace->GetFrameCount()); + v8::Handle<v8::String> url = v8_str("url"); + for (int i = 0; i < 3; i++) { + v8::Handle<v8::String> name = + stackTrace->GetFrame(i)->GetScriptNameOrSourceURL(); + CHECK(!name.IsEmpty()); + CHECK_EQ(url, name); + } + return v8::Undefined(); +} + + +TEST(InlineScriptWithSourceURLInStackTrace) { + v8::HandleScope scope; + Local<ObjectTemplate> templ = ObjectTemplate::New(); + templ->Set(v8_str("AnalyzeStackOfInlineScriptWithSourceURL"), + v8::FunctionTemplate::New( + AnalyzeStackOfInlineScriptWithSourceURL)); + LocalContext context(0, templ); + + const char *source = + "function outer() {\n" + "function bar() {\n" + " AnalyzeStackOfInlineScriptWithSourceURL();\n" + "}\n" + "function foo() {\n" + "\n" + " bar();\n" + "}\n" + "foo();\n" + "}\n" + "outer()\n" + "//@ sourceURL=source_url"; + CHECK(CompileRunWithOrigin(source, "url", 0, 1)->IsUndefined()); +} + + +v8::Handle<Value> AnalyzeStackOfDynamicScriptWithSourceURL( + const v8::Arguments& args) { + v8::HandleScope scope; + v8::Handle<v8::StackTrace> stackTrace = + v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kDetailed); + CHECK_EQ(4, stackTrace->GetFrameCount()); + v8::Handle<v8::String> url = v8_str("source_url"); + for (int i = 0; i < 3; i++) { + v8::Handle<v8::String> name = + stackTrace->GetFrame(i)->GetScriptNameOrSourceURL(); + CHECK(!name.IsEmpty()); + CHECK_EQ(url, name); + } + return v8::Undefined(); +} + + +TEST(DynamicWithSourceURLInStackTrace) { + v8::HandleScope scope; + Local<ObjectTemplate> templ = ObjectTemplate::New(); + templ->Set(v8_str("AnalyzeStackOfDynamicScriptWithSourceURL"), + v8::FunctionTemplate::New( + AnalyzeStackOfDynamicScriptWithSourceURL)); + LocalContext context(0, templ); + + const char *source = + "function outer() {\n" + "function bar() {\n" + " AnalyzeStackOfDynamicScriptWithSourceURL();\n" + "}\n" + "function foo() {\n" + "\n" + " bar();\n" + "}\n" + "foo();\n" + "}\n" + "outer()\n" + "//@ sourceURL=source_url"; + CHECK(CompileRunWithOrigin(source, "url", 0, 0)->IsUndefined()); +} + static void CreateGarbageInOldSpace() { v8::HandleScope scope; i::AlwaysAllocateScope always_allocate; @@ -14182,11 +14926,12 @@ THREADED_TEST(GetHeapStatistics) { class VisitorImpl : public v8::ExternalResourceVisitor { public: - VisitorImpl(TestResource* r1, TestResource* r2) - : resource1_(r1), - resource2_(r2), - found_resource1_(false), - found_resource2_(false) {} + explicit VisitorImpl(TestResource** resource) { + for (int i = 0; i < 4; i++) { + resource_[i] = resource[i]; + found_resource_[i] = false; + } + } virtual ~VisitorImpl() {} virtual void VisitExternalString(v8::Handle<v8::String> string) { if (!string->IsExternal()) { @@ -14196,25 +14941,22 @@ class VisitorImpl : public v8::ExternalResourceVisitor { v8::String::ExternalStringResource* resource = string->GetExternalStringResource(); CHECK(resource); - if (resource1_ == resource) { - CHECK(!found_resource1_); - found_resource1_ = true; - } - if (resource2_ == resource) { - CHECK(!found_resource2_); - found_resource2_ = true; + for (int i = 0; i < 4; i++) { + if (resource_[i] == resource) { + CHECK(!found_resource_[i]); + found_resource_[i] = true; + } } } void CheckVisitedResources() { - CHECK(found_resource1_); - CHECK(found_resource2_); + for (int i = 0; i < 4; i++) { + CHECK(found_resource_[i]); + } } private: - v8::String::ExternalStringResource* resource1_; - v8::String::ExternalStringResource* resource2_; - bool found_resource1_; - bool found_resource2_; + v8::String::ExternalStringResource* resource_[4]; + bool found_resource_[4]; }; TEST(VisitExternalStrings) { @@ -14222,16 +14964,33 @@ TEST(VisitExternalStrings) { LocalContext env; const char* string = "Some string"; uint16_t* two_byte_string = AsciiToTwoByteString(string); - TestResource* resource1 = new TestResource(two_byte_string); - v8::Local<v8::String> string1 = v8::String::NewExternal(resource1); - TestResource* resource2 = new TestResource(two_byte_string); - v8::Local<v8::String> string2 = v8::String::NewExternal(resource2); - - // We need to add usages for string1 and string2 to avoid warnings in GCC 4.7 + TestResource* resource[4]; + resource[0] = new TestResource(two_byte_string); + v8::Local<v8::String> string0 = v8::String::NewExternal(resource[0]); + resource[1] = new TestResource(two_byte_string); + v8::Local<v8::String> string1 = v8::String::NewExternal(resource[1]); + + // Externalized symbol. + resource[2] = new TestResource(two_byte_string); + v8::Local<v8::String> string2 = v8::String::NewSymbol(string); + CHECK(string2->MakeExternal(resource[2])); + + // Symbolized External. + resource[3] = new TestResource(AsciiToTwoByteString("Some other string")); + v8::Local<v8::String> string3 = v8::String::NewExternal(resource[3]); + HEAP->CollectAllAvailableGarbage(); // Tenure string. + // Turn into a symbol. + i::Handle<i::String> string3_i = v8::Utils::OpenHandle(*string3); + CHECK(!HEAP->LookupSymbol(*string3_i)->IsFailure()); + CHECK(string3_i->IsSymbol()); + + // We need to add usages for string* to avoid warnings in GCC 4.7 + CHECK(string0->IsExternal()); CHECK(string1->IsExternal()); CHECK(string2->IsExternal()); + CHECK(string3->IsExternal()); - VisitorImpl visitor(resource1, resource2); + VisitorImpl visitor(resource); v8::V8::VisitExternalResources(&visitor); visitor.CheckVisitedResources(); } @@ -14416,6 +15175,7 @@ TEST(Regress528) { context->Exit(); } context.Dispose(); + v8::V8::ContextDisposedNotification(); for (gc_count = 1; gc_count < 10; gc_count++) { other_context->Enter(); CompileRun(source_simple); @@ -14438,6 +15198,7 @@ TEST(Regress528) { context->Exit(); } context.Dispose(); + v8::V8::ContextDisposedNotification(); for (gc_count = 1; gc_count < 10; gc_count++) { other_context->Enter(); CompileRun(source_eval); @@ -14465,6 +15226,7 @@ TEST(Regress528) { context->Exit(); } context.Dispose(); + v8::V8::ContextDisposedNotification(); for (gc_count = 1; gc_count < 10; gc_count++) { other_context->Enter(); CompileRun(source_exception); @@ -14476,6 +15238,7 @@ TEST(Regress528) { CHECK_EQ((snapshot_enabled ? 2 : 1), GetGlobalObjectsCount()); other_context.Dispose(); + v8::V8::ContextDisposedNotification(); } @@ -14565,6 +15328,8 @@ THREADED_TEST(FunctionGetScriptId) { static v8::Handle<Value> GetterWhichReturns42(Local<String> name, const AccessorInfo& info) { + CHECK(v8::Utils::OpenHandle(*info.This())->IsJSObject()); + CHECK(v8::Utils::OpenHandle(*info.Holder())->IsJSObject()); return v8_num(42); } @@ -14572,7 +15337,29 @@ static v8::Handle<Value> GetterWhichReturns42(Local<String> name, static void SetterWhichSetsYOnThisTo23(Local<String> name, Local<Value> value, const AccessorInfo& info) { + CHECK(v8::Utils::OpenHandle(*info.This())->IsJSObject()); + CHECK(v8::Utils::OpenHandle(*info.Holder())->IsJSObject()); + info.This()->Set(v8_str("y"), v8_num(23)); +} + + +Handle<Value> FooGetInterceptor(Local<String> name, + const AccessorInfo& info) { + CHECK(v8::Utils::OpenHandle(*info.This())->IsJSObject()); + CHECK(v8::Utils::OpenHandle(*info.Holder())->IsJSObject()); + if (!name->Equals(v8_str("foo"))) return Handle<Value>(); + return v8_num(42); +} + + +Handle<Value> FooSetInterceptor(Local<String> name, + Local<Value> value, + const AccessorInfo& info) { + CHECK(v8::Utils::OpenHandle(*info.This())->IsJSObject()); + CHECK(v8::Utils::OpenHandle(*info.Holder())->IsJSObject()); + if (!name->Equals(v8_str("foo"))) return Handle<Value>(); info.This()->Set(v8_str("y"), v8_num(23)); + return v8_num(23); } @@ -15606,6 +16393,45 @@ TEST(DontDeleteCellLoadICAPI) { } +class Visitor42 : public v8::PersistentHandleVisitor { + public: + explicit Visitor42(v8::Persistent<v8::Object> object) + : counter_(0), object_(object) { } + + virtual void VisitPersistentHandle(Persistent<Value> value, + uint16_t class_id) { + if (class_id == 42) { + CHECK(value->IsObject()); + v8::Persistent<v8::Object> visited = + v8::Persistent<v8::Object>::Cast(value); + CHECK_EQ(42, visited.WrapperClassId()); + CHECK_EQ(object_, visited); + ++counter_; + } + } + + int counter_; + v8::Persistent<v8::Object> object_; +}; + + +TEST(PersistentHandleVisitor) { + v8::HandleScope scope; + LocalContext context; + v8::Persistent<v8::Object> object = + v8::Persistent<v8::Object>::New(v8::Object::New()); + CHECK_EQ(0, object.WrapperClassId()); + object.SetWrapperClassId(42); + CHECK_EQ(42, object.WrapperClassId()); + + Visitor42 visitor(object); + v8::V8::VisitHandlesWithClassIds(&visitor); + CHECK_EQ(1, visitor.counter_); + + object.Dispose(); +} + + TEST(RegExp) { v8::HandleScope scope; LocalContext context; @@ -16041,6 +16867,24 @@ THREADED_TEST(AllowCodeGenFromStrings) { } +TEST(SetErrorMessageForCodeGenFromStrings) { + v8::HandleScope scope; + LocalContext context; + TryCatch try_catch; + + Handle<String> message = v8_str("Message") ; + Handle<String> expected_message = v8_str("Uncaught EvalError: Message"); + V8::SetAllowCodeGenerationFromStringsCallback(&CodeGenerationDisallowed); + context->AllowCodeGenerationFromStrings(false); + context->SetErrorMessageForCodeGenerationFromStrings(message); + Handle<Value> result = CompileRun("eval('42')"); + CHECK(result.IsEmpty()); + CHECK(try_catch.HasCaught()); + Handle<String> actual_message = try_catch.Message()->Get(); + CHECK(expected_message->Equals(actual_message)); +} + + static v8::Handle<Value> NonObjectThis(const v8::Arguments& args) { return v8::Undefined(); } @@ -16094,7 +16938,8 @@ THREADED_TEST(Regress1516) { CHECK_LE(1, elements); } - i::Isolate::Current()->heap()->CollectAllGarbage(true); + i::Isolate::Current()->heap()->CollectAllGarbage( + i::Heap::kAbortIncrementalMarkingMask); { i::Object* raw_map_cache = i::Isolate::Current()->context()->map_cache(); if (raw_map_cache != i::Isolate::Current()->heap()->undefined_value()) { i::MapCache* map_cache = i::MapCache::cast(raw_map_cache); @@ -16594,3 +17439,459 @@ TEST(StringEmpty) { 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()); + } +} + + +static void Helper137002(bool do_store, + bool polymorphic, + bool remove_accessor, + bool interceptor) { + LocalContext context; + Local<ObjectTemplate> templ = ObjectTemplate::New(); + if (interceptor) { + templ->SetNamedPropertyHandler(FooGetInterceptor, FooSetInterceptor); + } else { + templ->SetAccessor(v8_str("foo"), + GetterWhichReturns42, + SetterWhichSetsYOnThisTo23); + } + context->Global()->Set(v8_str("obj"), templ->NewInstance()); + + // Turn monomorphic on slow object with native accessor, then turn + // polymorphic, finally optimize to create negative lookup and fail. + CompileRun(do_store ? + "function f(x) { x.foo = void 0; }" : + "function f(x) { return x.foo; }"); + CompileRun("obj.y = void 0;"); + if (!interceptor) { + CompileRun("%OptimizeObjectForAddingMultipleProperties(obj, 1);"); + } + CompileRun("obj.__proto__ = null;" + "f(obj); f(obj); f(obj);"); + if (polymorphic) { + CompileRun("f({});"); + } + CompileRun("obj.y = void 0;" + "%OptimizeFunctionOnNextCall(f);"); + if (remove_accessor) { + CompileRun("delete obj.foo;"); + } + CompileRun("var result = f(obj);"); + if (do_store) { + CompileRun("result = obj.y;"); + } + if (remove_accessor && !interceptor) { + CHECK(context->Global()->Get(v8_str("result"))->IsUndefined()); + } else { + CHECK_EQ(do_store ? 23 : 42, + context->Global()->Get(v8_str("result"))->Int32Value()); + } +} + + +THREADED_TEST(Regress137002a) { + i::FLAG_allow_natives_syntax = true; + i::FLAG_compilation_cache = false; + v8::HandleScope scope; + for (int i = 0; i < 16; i++) { + Helper137002(i & 8, i & 4, i & 2, i & 1); + } +} + + +THREADED_TEST(Regress137002b) { + i::FLAG_allow_natives_syntax = true; + v8::HandleScope scope; + LocalContext context; + Local<ObjectTemplate> templ = ObjectTemplate::New(); + templ->SetAccessor(v8_str("foo"), + GetterWhichReturns42, + SetterWhichSetsYOnThisTo23); + context->Global()->Set(v8_str("obj"), templ->NewInstance()); + + // Turn monomorphic on slow object with native accessor, then just + // delete the property and fail. + CompileRun("function load(x) { return x.foo; }" + "function store(x) { x.foo = void 0; }" + "function keyed_load(x, key) { return x[key]; }" + // Second version of function has a different source (add void 0) + // so that it does not share code with the first version. This + // ensures that the ICs are monomorphic. + "function load2(x) { void 0; return x.foo; }" + "function store2(x) { void 0; x.foo = void 0; }" + "function keyed_load2(x, key) { void 0; return x[key]; }" + + "obj.y = void 0;" + "obj.__proto__ = null;" + "var subobj = {};" + "subobj.y = void 0;" + "subobj.__proto__ = obj;" + "%OptimizeObjectForAddingMultipleProperties(obj, 1);" + + // Make the ICs monomorphic. + "load(obj); load(obj);" + "load2(subobj); load2(subobj);" + "store(obj); store(obj);" + "store2(subobj); store2(subobj);" + "keyed_load(obj, 'foo'); keyed_load(obj, 'foo');" + "keyed_load2(subobj, 'foo'); keyed_load2(subobj, 'foo');" + + // Actually test the shiny new ICs and better not crash. This + // serves as a regression test for issue 142088 as well. + "load(obj);" + "load2(subobj);" + "store(obj);" + "store2(subobj);" + "keyed_load(obj, 'foo');" + "keyed_load2(subobj, 'foo');" + + // Delete the accessor. It better not be called any more now. + "delete obj.foo;" + "obj.y = void 0;" + "subobj.y = void 0;" + + "var load_result = load(obj);" + "var load_result2 = load2(subobj);" + "var keyed_load_result = keyed_load(obj, 'foo');" + "var keyed_load_result2 = keyed_load2(subobj, 'foo');" + "store(obj);" + "store2(subobj);" + "var y_from_obj = obj.y;" + "var y_from_subobj = subobj.y;"); + CHECK(context->Global()->Get(v8_str("load_result"))->IsUndefined()); + CHECK(context->Global()->Get(v8_str("load_result2"))->IsUndefined()); + CHECK(context->Global()->Get(v8_str("keyed_load_result"))->IsUndefined()); + CHECK(context->Global()->Get(v8_str("keyed_load_result2"))->IsUndefined()); + CHECK(context->Global()->Get(v8_str("y_from_obj"))->IsUndefined()); + CHECK(context->Global()->Get(v8_str("y_from_subobj"))->IsUndefined()); +} + + +THREADED_TEST(Regress142088) { + i::FLAG_allow_natives_syntax = true; + v8::HandleScope scope; + LocalContext context; + Local<ObjectTemplate> templ = ObjectTemplate::New(); + templ->SetAccessor(v8_str("foo"), + GetterWhichReturns42, + SetterWhichSetsYOnThisTo23); + context->Global()->Set(v8_str("obj"), templ->NewInstance()); + + CompileRun("function load(x) { return x.foo; }" + "var o = Object.create(obj);" + "%OptimizeObjectForAddingMultipleProperties(obj, 1);" + "load(o); load(o); load(o); load(o);"); +} + + +THREADED_TEST(Regress137496) { + i::FLAG_expose_gc = true; + v8::HandleScope scope; + LocalContext context; + + // Compile a try-finally clause where the finally block causes a GC + // while there still is a message pending for external reporting. + TryCatch try_catch; + try_catch.SetVerbose(true); + CompileRun("try { throw new Error(); } finally { gc(); }"); + CHECK(try_catch.HasCaught()); +} + + +THREADED_TEST(Regress149912) { + v8::HandleScope scope; + LocalContext context; + Handle<FunctionTemplate> templ = FunctionTemplate::New(); + AddInterceptor(templ, EmptyInterceptorGetter, EmptyInterceptorSetter); + context->Global()->Set(v8_str("Bug"), templ->GetFunction()); + CompileRun("Number.prototype.__proto__ = new Bug; var x = 0; x.foo();"); +} + + +THREADED_TEST(Regress157124) { + v8::HandleScope scope; + LocalContext context; + Local<ObjectTemplate> templ = ObjectTemplate::New(); + Local<Object> obj = templ->NewInstance(); + obj->GetIdentityHash(); + obj->DeleteHiddenValue(v8_str("Bug")); +} + + +#ifndef WIN32 +class ThreadInterruptTest { + public: + ThreadInterruptTest() : sem_(NULL), sem_value_(0) { } + ~ThreadInterruptTest() { delete sem_; } + + void RunTest() { + sem_ = i::OS::CreateSemaphore(0); + + InterruptThread i_thread(this); + i_thread.Start(); + + sem_->Wait(); + CHECK_EQ(kExpectedValue, sem_value_); + } + + private: + static const int kExpectedValue = 1; + + class InterruptThread : public i::Thread { + public: + explicit InterruptThread(ThreadInterruptTest* test) + : Thread("InterruptThread"), test_(test) {} + + virtual void Run() { + struct sigaction action; + + // Ensure that we'll enter waiting condition + i::OS::Sleep(100); + + // Setup signal handler + memset(&action, 0, sizeof(action)); + action.sa_handler = SignalHandler; + sigaction(SIGCHLD, &action, NULL); + + // Send signal + kill(getpid(), SIGCHLD); + + // Ensure that if wait has returned because of error + i::OS::Sleep(100); + + // Set value and signal semaphore + test_->sem_value_ = 1; + test_->sem_->Signal(); + } + + static void SignalHandler(int signal) { + } + + private: + ThreadInterruptTest* test_; + struct sigaction sa_; + }; + + i::Semaphore* sem_; + volatile int sem_value_; +}; + + +THREADED_TEST(SemaphoreInterruption) { + ThreadInterruptTest().RunTest(); +} +#endif // WIN32 diff --git a/src/3rdparty/v8/test/cctest/test-assembler-arm.cc b/src/3rdparty/v8/test/cctest/test-assembler-arm.cc index ecbf956..cdab1b9 100644 --- a/src/3rdparty/v8/test/cctest/test-assembler-arm.cc +++ b/src/3rdparty/v8/test/cctest/test-assembler-arm.cc @@ -1,4 +1,4 @@ -// Copyright 2011 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: @@ -642,8 +642,8 @@ TEST(8) { // single precision values around in memory. Assembler assm(Isolate::Current(), NULL, 0); - if (CpuFeatures::IsSupported(VFP3)) { - CpuFeatures::Scope scope(VFP3); + if (CpuFeatures::IsSupported(VFP2)) { + CpuFeatures::Scope scope(VFP2); __ mov(ip, Operand(sp)); __ stm(db_w, sp, r4.bit() | fp.bit() | lr.bit()); @@ -753,8 +753,8 @@ TEST(9) { // single precision values around in memory. Assembler assm(Isolate::Current(), NULL, 0); - if (CpuFeatures::IsSupported(VFP3)) { - CpuFeatures::Scope scope(VFP3); + if (CpuFeatures::IsSupported(VFP2)) { + CpuFeatures::Scope scope(VFP2); __ mov(ip, Operand(sp)); __ stm(db_w, sp, r4.bit() | fp.bit() | lr.bit()); @@ -868,8 +868,8 @@ TEST(10) { // single precision values around in memory. Assembler assm(Isolate::Current(), NULL, 0); - if (CpuFeatures::IsSupported(VFP3)) { - CpuFeatures::Scope scope(VFP3); + if (CpuFeatures::IsSupported(VFP2)) { + CpuFeatures::Scope scope(VFP2); __ mov(ip, Operand(sp)); __ stm(db_w, sp, r4.bit() | fp.bit() | lr.bit()); diff --git a/src/3rdparty/v8/test/cctest/test-ast.cc b/src/3rdparty/v8/test/cctest/test-ast.cc index 80c7fdf..c72f87e 100644 --- a/src/3rdparty/v8/test/cctest/test-ast.cc +++ b/src/3rdparty/v8/test/cctest/test-ast.cc @@ -39,8 +39,10 @@ TEST(List) { List<AstNode*>* list = new List<AstNode*>(0); CHECK_EQ(0, list->length()); - ZoneScope zone_scope(Isolate::Current(), DELETE_ON_EXIT); - AstNodeFactory<AstNullVisitor> factory(Isolate::Current()); + Isolate* isolate = Isolate::Current(); + Zone* zone = isolate->runtime_zone(); + ZoneScope zone_scope(zone, DELETE_ON_EXIT); + AstNodeFactory<AstNullVisitor> factory(isolate, zone); AstNode* node = factory.NewEmptyStatement(); list->Add(node); CHECK_EQ(1, list->length()); diff --git a/src/3rdparty/v8/test/cctest/test-compiler.cc b/src/3rdparty/v8/test/cctest/test-compiler.cc index 9ca0b0a..7700a98 100644 --- a/src/3rdparty/v8/test/cctest/test-compiler.cc +++ b/src/3rdparty/v8/test/cctest/test-compiler.cc @@ -68,15 +68,9 @@ v8::Handle<v8::Value> PrintExtension::Print(const v8::Arguments& args) { for (int i = 0; i < args.Length(); i++) { if (i != 0) printf(" "); v8::HandleScope scope; - v8::Handle<v8::Value> arg = args[i]; - v8::Handle<v8::String> string_obj = arg->ToString(); - if (string_obj.IsEmpty()) return string_obj; - int length = string_obj->Length(); - uint16_t* string = NewArray<uint16_t>(length + 1); - string_obj->Write(string); - for (int j = 0; j < length; j++) - printf("%lc", static_cast<wchar_t>(string[j])); - DeleteArray(string); + v8::String::Utf8Value str(args[i]); + if (*str == NULL) return v8::Undefined(); + printf("%s", *str); } printf("\n"); return v8::Undefined(); @@ -101,14 +95,14 @@ static void InitializeVM() { static MaybeObject* GetGlobalProperty(const char* name) { Handle<String> symbol = FACTORY->LookupAsciiSymbol(name); - return Isolate::Current()->context()->global()->GetProperty(*symbol); + return Isolate::Current()->context()->global_object()->GetProperty(*symbol); } static void SetGlobalProperty(const char* name, Object* value) { Handle<Object> object(value); Handle<String> symbol = FACTORY->LookupAsciiSymbol(name); - Handle<JSObject> global(Isolate::Current()->context()->global()); + Handle<JSObject> global(Isolate::Current()->context()->global_object()); SetProperty(global, symbol, object, NONE, kNonStrictMode); } @@ -120,12 +114,13 @@ static Handle<JSFunction> Compile(const char* source) { Handle<String>(), 0, 0, + Handle<Context>(Isolate::Current()->native_context()), NULL, NULL, Handle<String>::null(), NOT_NATIVES_CODE); return FACTORY->NewFunctionFromSharedFunctionInfo(shared_function, - Isolate::Current()->global_context()); + Isolate::Current()->native_context()); } @@ -138,7 +133,7 @@ static double Inc(int x) { if (fun.is_null()) return -1; bool has_pending_exception; - Handle<JSObject> global(Isolate::Current()->context()->global()); + Handle<JSObject> global(Isolate::Current()->context()->global_object()); Execution::Call(fun, global, 0, NULL, &has_pending_exception); CHECK(!has_pending_exception); return GetGlobalProperty("result")->ToObjectChecked()->Number(); @@ -159,7 +154,7 @@ static double Add(int x, int y) { SetGlobalProperty("x", Smi::FromInt(x)); SetGlobalProperty("y", Smi::FromInt(y)); bool has_pending_exception; - Handle<JSObject> global(Isolate::Current()->context()->global()); + Handle<JSObject> global(Isolate::Current()->context()->global_object()); Execution::Call(fun, global, 0, NULL, &has_pending_exception); CHECK(!has_pending_exception); return GetGlobalProperty("result")->ToObjectChecked()->Number(); @@ -179,7 +174,7 @@ static double Abs(int x) { SetGlobalProperty("x", Smi::FromInt(x)); bool has_pending_exception; - Handle<JSObject> global(Isolate::Current()->context()->global()); + Handle<JSObject> global(Isolate::Current()->context()->global_object()); Execution::Call(fun, global, 0, NULL, &has_pending_exception); CHECK(!has_pending_exception); return GetGlobalProperty("result")->ToObjectChecked()->Number(); @@ -200,7 +195,7 @@ static double Sum(int n) { SetGlobalProperty("n", Smi::FromInt(n)); bool has_pending_exception; - Handle<JSObject> global(Isolate::Current()->context()->global()); + Handle<JSObject> global(Isolate::Current()->context()->global_object()); Execution::Call(fun, global, 0, NULL, &has_pending_exception); CHECK(!has_pending_exception); return GetGlobalProperty("result")->ToObjectChecked()->Number(); @@ -221,7 +216,7 @@ TEST(Print) { Handle<JSFunction> fun = Compile(source); if (fun.is_null()) return; bool has_pending_exception; - Handle<JSObject> global(Isolate::Current()->context()->global()); + Handle<JSObject> global(Isolate::Current()->context()->global_object()); Execution::Call(fun, global, 0, NULL, &has_pending_exception); CHECK(!has_pending_exception); } @@ -254,7 +249,7 @@ TEST(Stuff) { Handle<JSFunction> fun = Compile(source); CHECK(!fun.is_null()); bool has_pending_exception; - Handle<JSObject> global(Isolate::Current()->context()->global()); + Handle<JSObject> global(Isolate::Current()->context()->global_object()); Execution::Call(fun, global, 0, NULL, &has_pending_exception); CHECK(!has_pending_exception); CHECK_EQ(511.0, GetGlobalProperty("r")->ToObjectChecked()->Number()); @@ -269,7 +264,7 @@ TEST(UncaughtThrow) { Handle<JSFunction> fun = Compile(source); CHECK(!fun.is_null()); bool has_pending_exception; - Handle<JSObject> global(Isolate::Current()->context()->global()); + Handle<JSObject> global(Isolate::Current()->context()->global_object()); Execution::Call(fun, global, 0, NULL, &has_pending_exception); CHECK(has_pending_exception); CHECK_EQ(42.0, Isolate::Current()->pending_exception()-> @@ -294,12 +289,12 @@ TEST(C2JSFrames) { // Run the generated code to populate the global object with 'foo'. bool has_pending_exception; - Handle<JSObject> global(Isolate::Current()->context()->global()); + Handle<JSObject> global(Isolate::Current()->context()->global_object()); Execution::Call(fun0, global, 0, NULL, &has_pending_exception); CHECK(!has_pending_exception); Object* foo_symbol = FACTORY->LookupAsciiSymbol("foo")->ToObjectChecked(); - MaybeObject* fun1_object = Isolate::Current()->context()->global()-> + MaybeObject* fun1_object = Isolate::Current()->context()->global_object()-> GetProperty(String::cast(foo_symbol)); Handle<Object> fun1(fun1_object->ToObjectChecked()); CHECK(fun1->IsJSFunction()); @@ -352,6 +347,38 @@ TEST(GetScriptLineNumber) { } +// Test that optimized code for different closures is actually shared +// immediately by the FastNewClosureStub when run in the same context. +TEST(OptimizedCodeSharing) { + // Skip test if --cache-optimized-code is not activated by default because + // FastNewClosureStub that is baked into the snapshot is incorrect. + if (!FLAG_cache_optimized_code) return; + FLAG_allow_natives_syntax = true; + InitializeVM(); + v8::HandleScope scope; + for (int i = 0; i < 10; i++) { + LocalContext env; + env->Global()->Set(v8::String::New("x"), v8::Integer::New(i)); + CompileRun("function MakeClosure() {" + " return function() { return x; };" + "}" + "var closure0 = MakeClosure();" + "%DebugPrint(closure0());" + "%OptimizeFunctionOnNextCall(closure0);" + "%DebugPrint(closure0());" + "var closure1 = MakeClosure();" + "var closure2 = MakeClosure();"); + Handle<JSFunction> fun1 = v8::Utils::OpenHandle( + *v8::Local<v8::Function>::Cast(env->Global()->Get(v8_str("closure1")))); + Handle<JSFunction> fun2 = v8::Utils::OpenHandle( + *v8::Local<v8::Function>::Cast(env->Global()->Get(v8_str("closure2")))); + CHECK(fun1->IsOptimized() || !fun1->IsOptimizable()); + CHECK(fun2->IsOptimized() || !fun2->IsOptimizable()); + CHECK_EQ(fun1->code(), fun2->code()); + } +} + + #ifdef ENABLE_DISASSEMBLER static Handle<JSFunction> GetJSFunction(v8::Handle<v8::Object> obj, const char* property_name) { @@ -374,15 +401,16 @@ static void CheckCodeForUnsafeLiteral(Handle<JSFunction> f) { Address end = pc + decode_size; v8::internal::EmbeddedVector<char, 128> decode_buffer; + v8::internal::EmbeddedVector<char, 128> smi_hex_buffer; + Smi* smi = Smi::FromInt(12345678); + OS::SNPrintF(smi_hex_buffer, "0x%lx", reinterpret_cast<intptr_t>(smi)); while (pc < end) { int num_const = d.ConstantPoolSizeAt(pc); if (num_const >= 0) { pc += (num_const + 1) * kPointerSize; } else { pc += d.InstructionDecode(decode_buffer, pc); - CHECK(strstr(decode_buffer.start(), "mov eax,0x178c29c") == NULL); - CHECK(strstr(decode_buffer.start(), "push 0x178c29c") == NULL); - CHECK(strstr(decode_buffer.start(), "0x178c29c") == NULL); + CHECK(strstr(decode_buffer.start(), smi_hex_buffer.start()) == NULL); } } } diff --git a/src/3rdparty/v8/test/cctest/test-dataflow.cc b/src/3rdparty/v8/test/cctest/test-dataflow.cc index a63008d..ae33279 100644 --- a/src/3rdparty/v8/test/cctest/test-dataflow.cc +++ b/src/3rdparty/v8/test/cctest/test-dataflow.cc @@ -36,8 +36,8 @@ 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()->runtime_zone(); + ZoneScope zone_scope(zone, DELETE_ON_EXIT); { BitVector v(15, zone); v.Add(1); diff --git a/src/3rdparty/v8/test/cctest/test-debug.cc b/src/3rdparty/v8/test/cctest/test-debug.cc index 9c831fb..3caeb1b 100644 --- a/src/3rdparty/v8/test/cctest/test-debug.cc +++ b/src/3rdparty/v8/test/cctest/test-debug.cc @@ -197,10 +197,9 @@ static bool HasDebugInfo(v8::Handle<v8::Function> fun) { // number. static int SetBreakPoint(Handle<v8::internal::JSFunction> fun, int position) { static int break_point = 0; - Handle<v8::internal::SharedFunctionInfo> shared(fun->shared()); v8::internal::Debug* debug = v8::internal::Isolate::Current()->debug(); debug->SetBreakPoint( - shared, + fun, Handle<Object>(v8::internal::Smi::FromInt(++break_point)), &position); return break_point; @@ -515,7 +514,7 @@ void CheckDebugBreakFunction(DebugLocalContext* env, // there ClearBreakPoint(bp); CHECK(!debug->HasDebugInfo(shared)); - CHECK(debug->EnsureDebugInfo(shared)); + CHECK(debug->EnsureDebugInfo(shared, fun)); TestBreakLocationIterator it2(Debug::GetDebugInfo(shared)); it2.FindBreakLocationFromPosition(position); actual_mode = it2.it()->rinfo()->rmode(); @@ -2441,7 +2440,7 @@ TEST(DebuggerStatementBreakpoint) { } -// Thest that the evaluation of expressions when a break point is hit generates +// Test that the evaluation of expressions when a break point is hit generates // the correct results. TEST(DebugEvaluate) { v8::HandleScope scope; @@ -2557,6 +2556,98 @@ TEST(DebugEvaluate) { CheckDebuggerUnloaded(); } + +int debugEventCount = 0; +static void CheckDebugEvent(const v8::Debug::EventDetails& eventDetails) { + if (eventDetails.GetEvent() == v8::Break) ++debugEventCount; +} + +// Test that the conditional breakpoints work event if code generation from +// strings is prohibited in the debugee context. +TEST(ConditionalBreakpointWithCodeGenerationDisallowed) { + v8::HandleScope scope; + DebugLocalContext env; + env.ExposeDebug(); + + v8::Debug::SetDebugEventListener2(CheckDebugEvent); + + v8::Local<v8::Function> foo = CompileFunction(&env, + "function foo(x) {\n" + " var s = 'String value2';\n" + " return s + x;\n" + "}", + "foo"); + + // Set conditional breakpoint with condition 'true'. + CompileRun("debug.Debug.setBreakPoint(foo, 2, 0, 'true')"); + + debugEventCount = 0; + env->AllowCodeGenerationFromStrings(false); + foo->Call(env->Global(), 0, NULL); + CHECK_EQ(1, debugEventCount); + + v8::Debug::SetDebugEventListener2(NULL); + CheckDebuggerUnloaded(); +} + + +bool checkedDebugEvals = true; +v8::Handle<v8::Function> checkGlobalEvalFunction; +v8::Handle<v8::Function> checkFrameEvalFunction; +static void CheckDebugEval(const v8::Debug::EventDetails& eventDetails) { + if (eventDetails.GetEvent() == v8::Break) { + ++debugEventCount; + v8::HandleScope handleScope; + + v8::Handle<v8::Value> args[] = { eventDetails.GetExecutionState() }; + CHECK(checkGlobalEvalFunction->Call( + eventDetails.GetEventContext()->Global(), 1, args)->IsTrue()); + CHECK(checkFrameEvalFunction->Call( + eventDetails.GetEventContext()->Global(), 1, args)->IsTrue()); + } +} + +// Test that the evaluation of expressions when a break point is hit generates +// the correct results in case code generation from strings is disallowed in the +// debugee context. +TEST(DebugEvaluateWithCodeGenerationDisallowed) { + v8::HandleScope scope; + DebugLocalContext env; + env.ExposeDebug(); + + v8::Debug::SetDebugEventListener2(CheckDebugEval); + + v8::Local<v8::Function> foo = CompileFunction(&env, + "var global = 'Global';\n" + "function foo(x) {\n" + " var local = 'Local';\n" + " debugger;\n" + " return local + x;\n" + "}", + "foo"); + checkGlobalEvalFunction = CompileFunction(&env, + "function checkGlobalEval(exec_state) {\n" + " return exec_state.evaluateGlobal('global').value() === 'Global';\n" + "}", + "checkGlobalEval"); + + checkFrameEvalFunction = CompileFunction(&env, + "function checkFrameEval(exec_state) {\n" + " return exec_state.frame(0).evaluate('local').value() === 'Local';\n" + "}", + "checkFrameEval"); + debugEventCount = 0; + env->AllowCodeGenerationFromStrings(false); + foo->Call(env->Global(), 0, NULL); + CHECK_EQ(1, debugEventCount); + + checkGlobalEvalFunction.Clear(); + checkFrameEvalFunction.Clear(); + v8::Debug::SetDebugEventListener2(NULL); + CheckDebuggerUnloaded(); +} + + // Copies a C string to a 16-bit string. Does not check for buffer overflow. // Does not use the V8 engine to convert strings, so it can be used // in any thread. Returns the length of the string. @@ -4086,15 +4177,12 @@ TEST(StepWithException) { TEST(DebugBreak) { +#ifdef VERIFY_HEAP + i::FLAG_verify_heap = true; +#endif v8::HandleScope scope; DebugLocalContext env; - // This test should be run with option --verify-heap. As --verify-heap is - // only available in debug mode only check for it in that case. -#ifdef DEBUG - CHECK(v8::internal::FLAG_verify_heap); -#endif - // Register a debug event listener which sets the break flag and counts. v8::Debug::SetDebugEventListener(DebugEventBreak); @@ -4333,9 +4421,9 @@ TEST(InterceptorPropertyMirror) { "named_values[%d] instanceof debug.PropertyMirror", i); CHECK(CompileRun(buffer.start())->BooleanValue()); - // 5 is PropertyType.Interceptor OS::SNPrintF(buffer, "named_values[%d].propertyType()", i); - CHECK_EQ(5, CompileRun(buffer.start())->Int32Value()); + CHECK_EQ(v8::internal::INTERCEPTOR, + CompileRun(buffer.start())->Int32Value()); OS::SNPrintF(buffer, "named_values[%d].isNative()", i); CHECK(CompileRun(buffer.start())->BooleanValue()); @@ -5893,9 +5981,9 @@ TEST(DebuggerAgent) { i::Debugger* debugger = i::Isolate::Current()->debugger(); // Make sure these ports is not used by other tests to allow tests to run in // parallel. - const int kPort1 = 5858; - const int kPort2 = 5857; - const int kPort3 = 5856; + const int kPort1 = 5858 + FlagDependentPortOffset(); + const int kPort2 = 5857 + FlagDependentPortOffset(); + const int kPort3 = 5856 + FlagDependentPortOffset(); // Make a string with the port2 number. const int kPortBufferLen = 6; @@ -5994,7 +6082,7 @@ void DebuggerAgentProtocolServerThread::Run() { TEST(DebuggerAgentProtocolOverflowHeader) { // Make sure this port is not used by other tests to allow tests to run in // parallel. - const int kPort = 5860; + const int kPort = 5860 + FlagDependentPortOffset(); static const char* kLocalhost = "localhost"; // Make a string with the port number. @@ -7409,4 +7497,94 @@ TEST(DebugBreakInline) { } +static void DebugEventStepNext(v8::DebugEvent event, + v8::Handle<v8::Object> exec_state, + v8::Handle<v8::Object> event_data, + v8::Handle<v8::Value> data) { + if (event == v8::Break) { + PrepareStep(StepNext); + } +} + + +static void RunScriptInANewCFrame(const char* source) { + v8::TryCatch try_catch; + CompileRun(source); + CHECK(try_catch.HasCaught()); +} + + +TEST(Regress131642) { + // Bug description: + // When doing StepNext through the first script, the debugger is not reset + // after exiting through exception. A flawed implementation enabling the + // debugger to step into Array.prototype.forEach breaks inside the callback + // for forEach in the second script under the assumption that we are in a + // recursive call. In an attempt to step out, we crawl the stack using the + // recorded frame pointer from the first script and fail when not finding it + // on the stack. + v8::HandleScope scope; + DebugLocalContext env; + v8::Debug::SetDebugEventListener(DebugEventStepNext); + + // We step through the first script. It exits through an exception. We run + // this inside a new frame to record a different FP than the second script + // would expect. + const char* script_1 = "debugger; throw new Error();"; + RunScriptInANewCFrame(script_1); + + // The second script uses forEach. + const char* script_2 = "[0].forEach(function() { });"; + CompileRun(script_2); + + v8::Debug::SetDebugEventListener(NULL); +} + + +// Import from test-heap.cc +int CountNativeContexts(); + + +static void NopListener(v8::DebugEvent event, + v8::Handle<v8::Object> exec_state, + v8::Handle<v8::Object> event_data, + v8::Handle<v8::Value> data) { +} + + +TEST(DebuggerCreatesContextIffActive) { + v8::HandleScope scope; + DebugLocalContext env; + CHECK_EQ(1, CountNativeContexts()); + + v8::Debug::SetDebugEventListener(NULL); + CompileRun("debugger;"); + CHECK_EQ(1, CountNativeContexts()); + + v8::Debug::SetDebugEventListener(NopListener); + CompileRun("debugger;"); + CHECK_EQ(2, CountNativeContexts()); + + v8::Debug::SetDebugEventListener(NULL); +} + + +TEST(LiveEditEnabled) { + v8::internal::FLAG_allow_natives_syntax = true; + v8::HandleScope scope; + LocalContext context; + v8::Debug::SetLiveEditEnabled(true); + CompileRun("%LiveEditCompareStrings('', '')"); +} + + +TEST(LiveEditDisabled) { + v8::internal::FLAG_allow_natives_syntax = true; + v8::HandleScope scope; + LocalContext context; + v8::Debug::SetLiveEditEnabled(false); + CompileRun("%LiveEditCompareStrings('', '')"); +} + + #endif // ENABLE_DEBUGGER_SUPPORT diff --git a/src/3rdparty/v8/test/cctest/test-decls.cc b/src/3rdparty/v8/test/cctest/test-decls.cc index e6bdc9f..6fc6012 100644 --- a/src/3rdparty/v8/test/cctest/test-decls.cc +++ b/src/3rdparty/v8/test/cctest/test-decls.cc @@ -37,7 +37,8 @@ using namespace v8; enum Expectations { EXPECT_RESULT, - EXPECT_EXCEPTION + EXPECT_EXCEPTION, + EXPECT_ERROR }; @@ -72,6 +73,10 @@ class DeclarationContext { void InitializeIfNeeded(); + // Perform optional initialization steps on the context after it has + // been created. Defaults to none but may be overwritten. + virtual void PostInitializeContext(Handle<Context> context) {} + // Get the holder for the interceptor. Default to the instance template // but may be overwritten. virtual Local<ObjectTemplate> GetHolder(Local<FunctionTemplate> function) { @@ -91,7 +96,6 @@ class DeclarationContext { private: bool is_initialized_; Persistent<Context> context_; - Local<String> property_; int get_count_; int set_count_; @@ -120,6 +124,7 @@ void DeclarationContext::InitializeIfNeeded() { context_ = Context::New(0, function->InstanceTemplate(), Local<Value>()); context_->Enter(); is_initialized_ = true; + PostInitializeContext(context_); } @@ -134,7 +139,13 @@ void DeclarationContext::Check(const char* source, HandleScope scope; TryCatch catcher; catcher.SetVerbose(true); - Local<Value> result = Script::Compile(String::New(source))->Run(); + Local<Script> script = Script::Compile(String::New(source)); + if (expectations == EXPECT_ERROR) { + CHECK(script.IsEmpty()); + return; + } + CHECK(!script.IsEmpty()); + Local<Value> result = script->Run(); CHECK_EQ(get, get_count()); CHECK_EQ(set, set_count()); CHECK_EQ(query, query_count()); @@ -536,9 +547,9 @@ TEST(ExistsInPrototype) { { ExistsInPrototypeContext context; context.Check("var x; x", - 0, // get 0, - 0, // declaration + 0, + 0, EXPECT_RESULT, Undefined()); } @@ -546,7 +557,7 @@ TEST(ExistsInPrototype) { context.Check("var x = 0; x", 0, 0, - 0, // declaration + 0, EXPECT_RESULT, Number::New(0)); } @@ -554,7 +565,7 @@ TEST(ExistsInPrototype) { context.Check("const x; x", 0, 0, - 0, // declaration + 0, EXPECT_RESULT, Undefined()); } @@ -562,7 +573,7 @@ TEST(ExistsInPrototype) { context.Check("const x = 0; x", 0, 0, - 0, // declaration + 0, EXPECT_RESULT, Number::New(0)); } } @@ -591,7 +602,305 @@ TEST(AbsentInPrototype) { context.Check("if (false) { var x = 0; }; x", 0, 0, - 0, // declaration + 0, EXPECT_RESULT, Undefined()); } } + + + +class ExistsInHiddenPrototypeContext: public DeclarationContext { + public: + ExistsInHiddenPrototypeContext() { + hidden_proto_ = FunctionTemplate::New(); + hidden_proto_->SetHiddenPrototype(true); + } + + protected: + virtual v8::Handle<Integer> Query(Local<String> key) { + // Let it seem that the property exists in the hidden prototype object. + return Integer::New(v8::None); + } + + // Install the hidden prototype after the global object has been created. + virtual void PostInitializeContext(Handle<Context> context) { + Local<Object> global_object = context->Global(); + Local<Object> hidden_proto = hidden_proto_->GetFunction()->NewInstance(); + context->DetachGlobal(); + context->Global()->SetPrototype(hidden_proto); + context->ReattachGlobal(global_object); + } + + // Use the hidden prototype as the holder for the interceptors. + virtual Local<ObjectTemplate> GetHolder(Local<FunctionTemplate> function) { + return hidden_proto_->InstanceTemplate(); + } + + private: + Local<FunctionTemplate> hidden_proto_; +}; + + +TEST(ExistsInHiddenPrototype) { + i::FLAG_es52_globals = true; + HandleScope scope; + + { ExistsInHiddenPrototypeContext context; + context.Check("var x; x", + 1, // access + 0, + 2, // declaration + initialization + EXPECT_EXCEPTION); // x is not defined! + } + + { ExistsInHiddenPrototypeContext context; + context.Check("var x = 0; x", + 1, // access + 1, // initialization + 2, // declaration + initialization + EXPECT_RESULT, Number::New(0)); + } + + { ExistsInHiddenPrototypeContext context; + context.Check("function x() { }; x", + 0, + 0, + 0, + EXPECT_RESULT); + } + + // TODO(mstarzinger): The semantics of global const is vague. + { ExistsInHiddenPrototypeContext context; + context.Check("const x; x", + 0, + 0, + 1, // (re-)declaration + EXPECT_RESULT, Undefined()); + } + + // TODO(mstarzinger): The semantics of global const is vague. + { ExistsInHiddenPrototypeContext context; + context.Check("const x = 0; x", + 0, + 0, + 1, // (re-)declaration + EXPECT_RESULT, Number::New(0)); + } +} + + + +class SimpleContext { + public: + SimpleContext() { + context_ = Context::New(0); + context_->Enter(); + } + + virtual ~SimpleContext() { + context_->Exit(); + context_.Dispose(); + } + + void Check(const char* source, + Expectations expectations, + v8::Handle<Value> value = Local<Value>()) { + HandleScope scope; + TryCatch catcher; + catcher.SetVerbose(true); + Local<Script> script = Script::Compile(String::New(source)); + if (expectations == EXPECT_ERROR) { + CHECK(script.IsEmpty()); + return; + } + CHECK(!script.IsEmpty()); + Local<Value> result = script->Run(); + if (expectations == EXPECT_RESULT) { + CHECK(!catcher.HasCaught()); + if (!value.IsEmpty()) { + CHECK_EQ(value, result); + } + } else { + CHECK(expectations == EXPECT_EXCEPTION); + CHECK(catcher.HasCaught()); + if (!value.IsEmpty()) { + CHECK_EQ(value, catcher.Exception()); + } + } + } + + private: + Persistent<Context> context_; +}; + + +TEST(MultiScriptConflicts) { + HandleScope scope; + + { SimpleContext context; + context.Check("var x = 1; x", + EXPECT_RESULT, Number::New(1)); + context.Check("var x = 2; x", + EXPECT_RESULT, Number::New(2)); + context.Check("const x = 3; x", + EXPECT_RESULT, Number::New(3)); + context.Check("const x = 4; x", + EXPECT_RESULT, Number::New(4)); + context.Check("x = 5; x", + EXPECT_RESULT, Number::New(5)); + context.Check("var x = 6; x", + EXPECT_RESULT, Number::New(6)); + context.Check("this.x", + EXPECT_RESULT, Number::New(6)); + context.Check("function x() { return 7 }; x()", + EXPECT_RESULT, Number::New(7)); + } + + { SimpleContext context; + context.Check("const x = 1; x", + EXPECT_RESULT, Number::New(1)); + context.Check("var x = 2; x", // assignment ignored + EXPECT_RESULT, Number::New(1)); + context.Check("const x = 3; x", + EXPECT_RESULT, Number::New(1)); + context.Check("x = 4; x", // assignment ignored + EXPECT_RESULT, Number::New(1)); + context.Check("var x = 5; x", // assignment ignored + EXPECT_RESULT, Number::New(1)); + context.Check("this.x", + EXPECT_RESULT, Number::New(1)); + context.Check("function x() { return 7 }; x", + EXPECT_EXCEPTION); + } + + i::FLAG_use_strict = true; + i::FLAG_harmony_scoping = true; + + { SimpleContext context; + context.Check("var x = 1; x", + EXPECT_RESULT, Number::New(1)); + context.Check("x", + EXPECT_RESULT, Number::New(1)); + context.Check("this.x", + EXPECT_RESULT, Number::New(1)); + } + + { SimpleContext context; + context.Check("function x() { return 4 }; x()", + EXPECT_RESULT, Number::New(4)); + context.Check("x()", + EXPECT_RESULT, Number::New(4)); + context.Check("this.x()", + EXPECT_RESULT, Number::New(4)); + } + + { SimpleContext context; + context.Check("let x = 2; x", + EXPECT_RESULT, Number::New(2)); + context.Check("x", + EXPECT_RESULT, Number::New(2)); + // TODO(rossberg): The current ES6 draft spec does not reflect lexical + // bindings on the global object. However, this will probably change, in + // which case we reactivate the following test. + // context.Check("this.x", + // EXPECT_RESULT, Number::New(2)); + } + + { SimpleContext context; + context.Check("const x = 3; x", + EXPECT_RESULT, Number::New(3)); + context.Check("x", + EXPECT_RESULT, Number::New(3)); + // TODO(rossberg): The current ES6 draft spec does not reflect lexical + // bindings on the global object. However, this will probably change, in + // which case we reactivate the following test. + // context.Check("this.x", + // EXPECT_RESULT, Number::New(3)); + } + + // TODO(rossberg): All of the below should actually be errors in Harmony. + + { SimpleContext context; + context.Check("var x = 1; x", + EXPECT_RESULT, Number::New(1)); + context.Check("let x = 2; x", + EXPECT_RESULT, Number::New(2)); + } + + { SimpleContext context; + context.Check("var x = 1; x", + EXPECT_RESULT, Number::New(1)); + context.Check("const x = 2; x", + EXPECT_RESULT, Number::New(2)); + } + + { SimpleContext context; + context.Check("function x() { return 1 }; x()", + EXPECT_RESULT, Number::New(1)); + context.Check("let x = 2; x", + EXPECT_RESULT, Number::New(2)); + } + + { SimpleContext context; + context.Check("function x() { return 1 }; x()", + EXPECT_RESULT, Number::New(1)); + context.Check("const x = 2; x", + EXPECT_RESULT, Number::New(2)); + } + + { SimpleContext context; + context.Check("let x = 1; x", + EXPECT_RESULT, Number::New(1)); + context.Check("var x = 2; x", + EXPECT_ERROR); + } + + { SimpleContext context; + context.Check("let x = 1; x", + EXPECT_RESULT, Number::New(1)); + context.Check("let x = 2; x", + EXPECT_ERROR); + } + + { SimpleContext context; + context.Check("let x = 1; x", + EXPECT_RESULT, Number::New(1)); + context.Check("const x = 2; x", + EXPECT_ERROR); + } + + { SimpleContext context; + context.Check("let x = 1; x", + EXPECT_RESULT, Number::New(1)); + context.Check("function x() { return 2 }; x()", + EXPECT_ERROR); + } + + { SimpleContext context; + context.Check("const x = 1; x", + EXPECT_RESULT, Number::New(1)); + context.Check("var x = 2; x", + EXPECT_ERROR); + } + + { SimpleContext context; + context.Check("const x = 1; x", + EXPECT_RESULT, Number::New(1)); + context.Check("let x = 2; x", + EXPECT_ERROR); + } + + { SimpleContext context; + context.Check("const x = 1; x", + EXPECT_RESULT, Number::New(1)); + context.Check("const x = 2; x", + EXPECT_ERROR); + } + + { SimpleContext context; + context.Check("const x = 1; x", + EXPECT_RESULT, Number::New(1)); + context.Check("function x() { return 2 }; x()", + EXPECT_ERROR); + } +} diff --git a/src/3rdparty/v8/test/cctest/test-dictionary.cc b/src/3rdparty/v8/test/cctest/test-dictionary.cc index 793e228..00e3833 100644 --- a/src/3rdparty/v8/test/cctest/test-dictionary.cc +++ b/src/3rdparty/v8/test/cctest/test-dictionary.cc @@ -48,24 +48,24 @@ TEST(ObjectHashTable) { table = PutIntoObjectHashTable(table, a, b); CHECK_EQ(table->NumberOfElements(), 1); CHECK_EQ(table->Lookup(*a), *b); - CHECK_EQ(table->Lookup(*b), HEAP->undefined_value()); + CHECK_EQ(table->Lookup(*b), HEAP->the_hole_value()); // Keys still have to be valid after objects were moved. HEAP->CollectGarbage(NEW_SPACE); CHECK_EQ(table->NumberOfElements(), 1); CHECK_EQ(table->Lookup(*a), *b); - CHECK_EQ(table->Lookup(*b), HEAP->undefined_value()); + CHECK_EQ(table->Lookup(*b), HEAP->the_hole_value()); // Keys that are overwritten should not change number of elements. table = PutIntoObjectHashTable(table, a, FACTORY->NewJSArray(13)); CHECK_EQ(table->NumberOfElements(), 1); CHECK_NE(table->Lookup(*a), *b); - // Keys mapped to undefined should be removed permanently. - table = PutIntoObjectHashTable(table, a, FACTORY->undefined_value()); + // Keys mapped to the hole should be removed permanently. + table = PutIntoObjectHashTable(table, a, FACTORY->the_hole_value()); CHECK_EQ(table->NumberOfElements(), 0); CHECK_EQ(table->NumberOfDeletedElements(), 1); - CHECK_EQ(table->Lookup(*a), HEAP->undefined_value()); + CHECK_EQ(table->Lookup(*a), HEAP->the_hole_value()); // Keys should map back to their respective values and also should get // an identity hash code generated. @@ -85,7 +85,7 @@ TEST(ObjectHashTable) { Handle<JSObject> key = FACTORY->NewJSArray(7); CHECK(key->GetIdentityHash(ALLOW_CREATION)->ToObjectChecked()->IsSmi()); CHECK_EQ(table->FindEntry(*key), ObjectHashTable::kNotFound); - CHECK_EQ(table->Lookup(*key), HEAP->undefined_value()); + CHECK_EQ(table->Lookup(*key), HEAP->the_hole_value()); CHECK(key->GetIdentityHash(OMIT_CREATION)->ToObjectChecked()->IsSmi()); } @@ -93,7 +93,7 @@ TEST(ObjectHashTable) { // should not get an identity hash code generated. for (int i = 0; i < 100; i++) { Handle<JSObject> key = FACTORY->NewJSArray(7); - CHECK_EQ(table->Lookup(*key), HEAP->undefined_value()); + CHECK_EQ(table->Lookup(*key), HEAP->the_hole_value()); CHECK_EQ(key->GetIdentityHash(OMIT_CREATION), HEAP->undefined_value()); } } @@ -105,6 +105,12 @@ TEST(ObjectHashSetCausesGC) { LocalContext context; Handle<ObjectHashSet> table = FACTORY->NewObjectHashSet(1); Handle<JSObject> key = FACTORY->NewJSArray(0); + v8::Handle<v8::Object> key_obj = v8::Utils::ToLocal(key); + + // Force allocation of hash table backing store for hidden properties. + key_obj->SetHiddenValue(v8_str("key 1"), v8_str("val 1")); + key_obj->SetHiddenValue(v8_str("key 2"), v8_str("val 2")); + key_obj->SetHiddenValue(v8_str("key 3"), v8_str("val 3")); // Simulate a full heap so that generating an identity hash code // in subsequent calls will request GC. @@ -128,13 +134,19 @@ TEST(ObjectHashTableCausesGC) { LocalContext context; Handle<ObjectHashTable> table = FACTORY->NewObjectHashTable(1); Handle<JSObject> key = FACTORY->NewJSArray(0); + v8::Handle<v8::Object> key_obj = v8::Utils::ToLocal(key); + + // Force allocation of hash table backing store for hidden properties. + key_obj->SetHiddenValue(v8_str("key 1"), v8_str("val 1")); + key_obj->SetHiddenValue(v8_str("key 2"), v8_str("val 2")); + key_obj->SetHiddenValue(v8_str("key 3"), v8_str("val 3")); // Simulate a full heap so that generating an identity hash code // in subsequent calls will request GC. FLAG_gc_interval = 0; // Calling Lookup() should not cause GC ever. - CHECK(table->Lookup(*key)->IsUndefined()); + CHECK(table->Lookup(*key)->IsTheHole()); // Calling Put() should request GC by returning a failure. CHECK(table->Put(*key, *key)->IsRetryAfterGC()); diff --git a/src/3rdparty/v8/test/cctest/test-disasm-arm.cc b/src/3rdparty/v8/test/cctest/test-disasm-arm.cc index 0e9432d..3a2d9e8 100644 --- a/src/3rdparty/v8/test/cctest/test-disasm-arm.cc +++ b/src/3rdparty/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/src/3rdparty/v8/test/cctest/test-flags.cc b/src/3rdparty/v8/test/cctest/test-flags.cc index 32f1264..9cb12c4 100644 --- a/src/3rdparty/v8/test/cctest/test-flags.cc +++ b/src/3rdparty/v8/test/cctest/test-flags.cc @@ -159,7 +159,7 @@ TEST(Flags6) { CHECK_EQ(3, FlagList::SetFlagsFromCommandLine(&argc, const_cast<char **>(argv), true)); - CHECK_EQ(4, argc); + CHECK_EQ(2, argc); } @@ -232,3 +232,16 @@ TEST(FlagsJSArguments4) { CHECK_EQ(0, FLAG_js_arguments.argc()); } + +TEST(FlagsRemoveIncomplete) { + // Test that processed command line arguments are removed, even + // if the list of arguments ends unexpectedly. + SetFlagsToDefault(); + int argc = 3; + const char* argv[] = { "", "--crankshaft", "--expose-debug-as" }; + CHECK_EQ(2, FlagList::SetFlagsFromCommandLine(&argc, + const_cast<char **>(argv), + true)); + CHECK_NE(NULL, argv[1]); + CHECK_EQ(argc, 2); +} diff --git a/src/3rdparty/v8/test/cctest/test-func-name-inference.cc b/src/3rdparty/v8/test/cctest/test-func-name-inference.cc index 8f405b7..cda6aa0 100644 --- a/src/3rdparty/v8/test/cctest/test-func-name-inference.cc +++ b/src/3rdparty/v8/test/cctest/test-func-name-inference.cc @@ -28,6 +28,7 @@ #include "v8.h" #include "api.h" +#include "debug.h" #include "runtime.h" #include "cctest.h" @@ -87,10 +88,10 @@ static void CheckFunctionName(v8::Handle<v8::Script> script, #ifdef ENABLE_DEBUGGER_SUPPORT // Obtain SharedFunctionInfo for the function. + Isolate::Current()->debug()->PrepareForBreakPoints(); Object* shared_func_info_ptr = - Runtime::FindSharedFunctionInfoInScript(Isolate::Current(), - i_script, - func_pos); + Isolate::Current()->debug()->FindSharedFunctionInfoInScript(i_script, + func_pos); CHECK(shared_func_info_ptr != HEAP->undefined_value()); Handle<SharedFunctionInfo> shared_func_info( SharedFunctionInfo::cast(shared_func_info_ptr)); @@ -398,5 +399,45 @@ TEST(AssignmentAndCall) { // The inferred name is empty, because this is an assignment of a result. CheckFunctionName(script, "return 1", ""); // See MultipleAssignments test. - CheckFunctionName(script, "return 2", "Enclosing.Bar"); + // TODO(2276): Lazy compiling the enclosing outer closure would yield + // in "Enclosing.Bar" being the inferred name here. + CheckFunctionName(script, "return 2", "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/src/3rdparty/v8/test/cctest/test-heap-profiler.cc b/src/3rdparty/v8/test/cctest/test-heap-profiler.cc index b5815dc..2a60785 100644 --- a/src/3rdparty/v8/test/cctest/test-heap-profiler.cc +++ b/src/3rdparty/v8/test/cctest/test-heap-profiler.cc @@ -7,6 +7,7 @@ #include "v8.h" #include "cctest.h" +#include "hashmap.h" #include "heap-profiler.h" #include "snapshot.h" #include "debug.h" @@ -27,10 +28,14 @@ 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(); @@ -38,11 +43,15 @@ class NamedEntriesDetector { 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); - } + 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); } } } @@ -107,9 +116,6 @@ 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. @@ -122,9 +128,7 @@ TEST(HeapSnapshot) { 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); @@ -158,9 +162,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()); } @@ -479,66 +483,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::kProperty, "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 { @@ -623,7 +567,7 @@ TEST(HeapSnapshotJSONSerialization) { // Get node and edge "member" offsets. v8::Local<v8::Value> meta_analysis_result = CompileRun( "var meta = parsed.snapshot.meta;\n" - "var edges_index_offset = meta.node_fields.indexOf('edges_index');\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" @@ -633,7 +577,13 @@ TEST(HeapSnapshotJSONSerialization) { " meta.edge_types[edge_type_offset].indexOf('property');\n" "var shortcut_type =" " meta.edge_types[edge_type_offset].indexOf('shortcut');\n" - "parsed.nodes.concat(0, 0, 0, 0, 0, 0, parsed.edges.length);"); + "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. @@ -642,8 +592,9 @@ TEST(HeapSnapshotJSONSerialization) { " var nodes = parsed.nodes;\n" " var edges = parsed.edges;\n" " var strings = parsed.strings;\n" - " for (var i = nodes[pos + edges_index_offset],\n" - " count = nodes[pos + node_fields_count + edges_index_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" @@ -659,8 +610,8 @@ TEST(HeapSnapshotJSONSerialization) { "GetChildPosByProperty(\n" " GetChildPosByProperty(\n" " GetChildPosByProperty(" - " parsed.edges[parsed.nodes[edges_index_offset]" - " + edge_to_node_offset + edge_fields_count]," + " parsed.edges[edge_to_node_offset" + " + edge_fields_count]," " \"b\", property_type),\n" " \"x\", property_type)," " \"s\", property_type)"); @@ -669,8 +620,7 @@ TEST(HeapSnapshotJSONSerialization) { "GetChildPosByProperty(\n" " GetChildPosByProperty(\n" " GetChildPosByProperty(" - " parsed.edges[parsed.nodes[edges_index_offset]" - " + edge_to_node_offset]," + " parsed.edges[edge_to_node_offset]," " \"b\", property_type),\n" " \"x\", property_type)," " \"s\", property_type)"); @@ -763,9 +713,13 @@ class TestStatsStream : public v8::OutputStream { } // namespace -static TestStatsStream GetHeapStatsUpdate() { +static TestStatsStream GetHeapStatsUpdate( + v8::SnapshotObjectId* object_id = NULL) { TestStatsStream stream; - v8::HeapProfiler::PushHeapObjectsStats(&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; } @@ -776,15 +730,16 @@ TEST(HeapSnapshotObjectsStats) { LocalContext env; v8::HeapProfiler::StartHeapObjectsTracking(); - // We have to call GC 5 times. In other case the garbage will be + // We have to call GC 6 times. In other case the garbage will be // the reason of flakiness. - for (int i = 0; i < 5; ++i) { + for (int i = 0; i < 6; ++i) { HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); } + v8::SnapshotObjectId initial_id; { // Single chunk of data expected in update. Initial data. - TestStatsStream stats_update = GetHeapStatsUpdate(); + 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()); @@ -792,13 +747,18 @@ TEST(HeapSnapshotObjectsStats) { } // No data expected in update because nothing has happened. - CHECK_EQ(0, GetHeapStatsUpdate().updates_written()); + 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(); + 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()); @@ -807,7 +767,9 @@ TEST(HeapSnapshotObjectsStats) { } // No data expected in update because nothing happened. - CHECK_EQ(0, GetHeapStatsUpdate().updates_written()); + 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; @@ -1503,6 +1465,36 @@ TEST(FastCaseGetter) { CHECK_NE(NULL, setterFunction); } +TEST(HiddenPropertiesFastCase) { + v8::HandleScope scope; + LocalContext env; + + CompileRun( + "function C(x) { this.a = this; this.b = x; }\n" + "c = new C(2012);\n"); + const v8::HeapSnapshot* snapshot = + v8::HeapProfiler::TakeSnapshot(v8_str("HiddenPropertiesFastCase1")); + const v8::HeapGraphNode* global = GetGlobalObject(snapshot); + const v8::HeapGraphNode* c = + GetProperty(global, v8::HeapGraphEdge::kProperty, "c"); + CHECK_NE(NULL, c); + const v8::HeapGraphNode* hidden_props = + GetProperty(c, v8::HeapGraphEdge::kInternal, "hidden_properties"); + CHECK_EQ(NULL, hidden_props); + + v8::Handle<v8::Value> cHandle = env->Global()->Get(v8::String::New("c")); + CHECK(!cHandle.IsEmpty() && cHandle->IsObject()); + cHandle->ToObject()->SetHiddenValue(v8_str("key"), v8_str("val")); + + snapshot = v8::HeapProfiler::TakeSnapshot( + v8_str("HiddenPropertiesFastCase2")); + global = GetGlobalObject(snapshot); + c = GetProperty(global, v8::HeapGraphEdge::kProperty, "c"); + CHECK_NE(NULL, c); + hidden_props = GetProperty(c, v8::HeapGraphEdge::kInternal, + "hidden_properties"); + CHECK_NE(NULL, hidden_props); +} bool HasWeakEdge(const v8::HeapGraphNode* node) { for (int i = 0; i < node->GetChildrenCount(); ++i) { @@ -1545,7 +1537,7 @@ TEST(WeakGlobalHandle) { } -TEST(WeakGlobalContextRefs) { +TEST(WeakNativeContextRefs) { v8::HandleScope scope; LocalContext env; @@ -1557,10 +1549,10 @@ TEST(WeakGlobalContextRefs) { const v8::HeapGraphNode* global_handles = GetNode( gc_roots, v8::HeapGraphNode::kObject, "(Global handles)"); CHECK_NE(NULL, global_handles); - const v8::HeapGraphNode* global_context = GetNode( - global_handles, v8::HeapGraphNode::kHidden, "system / GlobalContext"); - CHECK_NE(NULL, global_context); - CHECK(HasWeakEdge(global_context)); + const v8::HeapGraphNode* native_context = GetNode( + global_handles, v8::HeapGraphNode::kHidden, "system / NativeContext"); + CHECK_NE(NULL, native_context); + CHECK(HasWeakEdge(native_context)); } @@ -1583,6 +1575,7 @@ TEST(SfiAndJsFunctionWeakRefs) { } +#ifdef ENABLE_DEBUGGER_SUPPORT TEST(NoDebugObjectInSnapshot) { v8::HandleScope scope; LocalContext env; @@ -1607,6 +1600,7 @@ TEST(NoDebugObjectInSnapshot) { } CHECK_EQ(1, globals_count); } +#endif // ENABLE_DEBUGGER_SUPPORT TEST(PersistentHandleCount) { @@ -1682,3 +1676,26 @@ TEST(NoRefsToNonEssentialEntries) { GetProperty(global_object, v8::HeapGraphEdge::kInternal, "elements"); CHECK_EQ(NULL, elements); } + + +TEST(MapHasDescriptorsAndTransitions) { + v8::HandleScope scope; + LocalContext env; + CompileRun("obj = { a: 10 };\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, "obj"); + CHECK_NE(NULL, global_object); + + const v8::HeapGraphNode* map = + GetProperty(global_object, v8::HeapGraphEdge::kInternal, "map"); + CHECK_NE(NULL, map); + const v8::HeapGraphNode* own_descriptors = GetProperty( + map, v8::HeapGraphEdge::kInternal, "descriptors"); + CHECK_NE(NULL, own_descriptors); + const v8::HeapGraphNode* own_transitions = GetProperty( + map, v8::HeapGraphEdge::kInternal, "transitions"); + CHECK_EQ(NULL, own_transitions); +} diff --git a/src/3rdparty/v8/test/cctest/test-heap.cc b/src/3rdparty/v8/test/cctest/test-heap.cc index baee7e4..0d72ff7 100644 --- a/src/3rdparty/v8/test/cctest/test-heap.cc +++ b/src/3rdparty/v8/test/cctest/test-heap.cc @@ -4,10 +4,12 @@ #include "v8.h" +#include "compilation-cache.h" #include "execution.h" #include "factory.h" #include "macro-assembler.h" #include "global-handles.h" +#include "stub-cache.h" #include "cctest.h" #include "snapshot.h" @@ -22,6 +24,19 @@ static void InitializeVM() { } +// Go through all incremental marking steps in one swoop. +static void SimulateIncrementalMarking() { + 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()); +} + + static void CheckMap(Map* map, int type, int instance_size) { CHECK(map->IsHeapObject()); #ifdef DEBUG @@ -158,7 +173,8 @@ TEST(HeapObjects) { String* object_symbol = String::cast(HEAP->Object_symbol()); CHECK( - Isolate::Current()->context()->global()->HasLocalProperty(object_symbol)); + Isolate::Current()->context()->global_object()->HasLocalProperty( + object_symbol)); // Check ToString for oddballs CheckOddball(HEAP->true_value(), "true"); @@ -214,7 +230,7 @@ TEST(GarbageCollection) { Handle<Map> initial_map = FACTORY->NewMap(JS_OBJECT_TYPE, JSObject::kHeaderSize); function->set_initial_map(*initial_map); - Isolate::Current()->context()->global()->SetProperty( + Isolate::Current()->context()->global_object()->SetProperty( *name, *function, NONE, kNonStrictMode)->ToObjectChecked(); // Allocate an object. Unrooted after leaving the scope. Handle<JSObject> obj = FACTORY->NewJSObject(function); @@ -230,9 +246,10 @@ TEST(GarbageCollection) { HEAP->CollectGarbage(NEW_SPACE); // Function should be alive. - CHECK(Isolate::Current()->context()->global()->HasLocalProperty(*name)); + CHECK(Isolate::Current()->context()->global_object()-> + HasLocalProperty(*name)); // Check function is retained. - Object* func_value = Isolate::Current()->context()->global()-> + Object* func_value = Isolate::Current()->context()->global_object()-> GetProperty(*name)->ToObjectChecked(); CHECK(func_value->IsJSFunction()); Handle<JSFunction> function(JSFunction::cast(func_value)); @@ -241,7 +258,7 @@ TEST(GarbageCollection) { HandleScope inner_scope; // Allocate another object, make it reachable from global. Handle<JSObject> obj = FACTORY->NewJSObject(function); - Isolate::Current()->context()->global()->SetProperty( + Isolate::Current()->context()->global_object()->SetProperty( *obj_name, *obj, NONE, kNonStrictMode)->ToObjectChecked(); obj->SetProperty( *prop_name, Smi::FromInt(23), NONE, kNonStrictMode)->ToObjectChecked(); @@ -250,10 +267,11 @@ TEST(GarbageCollection) { // After gc, it should survive. HEAP->CollectGarbage(NEW_SPACE); - CHECK(Isolate::Current()->context()->global()->HasLocalProperty(*obj_name)); - CHECK(Isolate::Current()->context()->global()-> + CHECK(Isolate::Current()->context()->global_object()-> + HasLocalProperty(*obj_name)); + CHECK(Isolate::Current()->context()->global_object()-> GetProperty(*obj_name)->ToObjectChecked()->IsJSObject()); - Object* obj = Isolate::Current()->context()->global()-> + Object* obj = Isolate::Current()->context()->global_object()-> GetProperty(*obj_name)->ToObjectChecked(); JSObject* js_obj = JSObject::cast(obj); CHECK_EQ(Smi::FromInt(23), js_obj->GetProperty(*prop_name)); @@ -416,6 +434,7 @@ TEST(WeakGlobalHandlesMark) { global_handles->Destroy(h1.location()); } + TEST(DeleteWeakGlobalHandle) { InitializeVM(); GlobalHandles* global_handles = Isolate::Current()->global_handles(); @@ -446,6 +465,7 @@ TEST(DeleteWeakGlobalHandle) { CHECK(WeakPointerCleared); } + static const char* not_so_random_string_table[] = { "abstract", "boolean", @@ -562,7 +582,7 @@ TEST(ObjectProperties) { v8::HandleScope sc; String* object_symbol = String::cast(HEAP->Object_symbol()); - Object* raw_object = Isolate::Current()->context()->global()-> + Object* raw_object = Isolate::Current()->context()->global_object()-> GetProperty(object_symbol)->ToObjectChecked(); JSFunction* object_function = JSFunction::cast(raw_object); Handle<JSFunction> constructor(object_function); @@ -659,7 +679,7 @@ TEST(JSArray) { v8::HandleScope sc; Handle<String> name = FACTORY->LookupAsciiSymbol("Array"); - Object* raw_object = Isolate::Current()->context()->global()-> + Object* raw_object = Isolate::Current()->context()->global_object()-> GetProperty(*name)->ToObjectChecked(); Handle<JSFunction> function = Handle<JSFunction>( JSFunction::cast(raw_object)); @@ -674,7 +694,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(); @@ -706,7 +726,7 @@ TEST(JSObjectCopy) { v8::HandleScope sc; String* object_symbol = String::cast(HEAP->Object_symbol()); - Object* raw_object = Isolate::Current()->context()->global()-> + Object* raw_object = Isolate::Current()->context()->global_object()-> GetProperty(object_symbol)->ToObjectChecked(); JSFunction* object_function = JSFunction::cast(raw_object); Handle<JSFunction> constructor(object_function); @@ -812,7 +832,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++] = @@ -873,7 +895,7 @@ TEST(Regression39128) { // Step 1: prepare a map for the object. We add 1 inobject property to it. Handle<JSFunction> object_ctor( - Isolate::Current()->global_context()->object_function()); + Isolate::Current()->native_context()->object_function()); CHECK(object_ctor->has_initial_map()); Handle<Map> object_map(object_ctor->initial_map()); // Create a map with single inobject property. @@ -934,9 +956,9 @@ TEST(Regression39128) { TEST(TestCodeFlushing) { - i::FLAG_allow_natives_syntax = true; // If we do not flush code this test is invalid. if (!FLAG_flush_code) return; + i::FLAG_allow_natives_syntax = true; InitializeVM(); v8::HandleScope scope; const char* source = "function foo() {" @@ -953,24 +975,22 @@ TEST(TestCodeFlushing) { } // Check function is compiled. - Object* func_value = Isolate::Current()->context()->global()-> + Object* func_value = Isolate::Current()->context()->global_object()-> GetProperty(*foo_name)->ToObjectChecked(); CHECK(func_value->IsJSFunction()); Handle<JSFunction> function(JSFunction::cast(func_value)); CHECK(function->shared()->is_compiled()); - // TODO(1609) Currently incremental marker does not support code flushing. - HEAP->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask); - HEAP->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask); - + // The code will survive at least two GCs. + HEAP->CollectAllGarbage(Heap::kNoGCFlags); + HEAP->CollectAllGarbage(Heap::kNoGCFlags); CHECK(function->shared()->is_compiled()); - HEAP->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask); - HEAP->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask); - HEAP->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask); - HEAP->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask); - HEAP->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask); - HEAP->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask); + // Simulate several GCs that use full marking. + const int kAgingThreshold = 6; + for (int i = 0; i < kAgingThreshold; i++) { + HEAP->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask); + } // foo should no longer be in the compilation cache CHECK(!function->shared()->is_compiled() || function->IsOptimized()); @@ -982,10 +1002,201 @@ TEST(TestCodeFlushing) { } -// Count the number of global contexts in the weak list of global contexts. -static int CountGlobalContexts() { +TEST(TestCodeFlushingIncremental) { + // If we do not flush code this test is invalid. + if (!FLAG_flush_code || !FLAG_flush_code_incrementally) return; + i::FLAG_allow_natives_syntax = true; + InitializeVM(); + v8::HandleScope scope; + const char* source = "function foo() {" + " var x = 42;" + " var y = 42;" + " var z = x + y;" + "};" + "foo()"; + Handle<String> foo_name = FACTORY->LookupAsciiSymbol("foo"); + + // This compile will add the code to the compilation cache. + { v8::HandleScope scope; + CompileRun(source); + } + + // Check function is compiled. + Object* func_value = Isolate::Current()->context()->global_object()-> + GetProperty(*foo_name)->ToObjectChecked(); + CHECK(func_value->IsJSFunction()); + Handle<JSFunction> function(JSFunction::cast(func_value)); + CHECK(function->shared()->is_compiled()); + + // The code will survive at least two GCs. + HEAP->CollectAllGarbage(Heap::kNoGCFlags); + HEAP->CollectAllGarbage(Heap::kNoGCFlags); + CHECK(function->shared()->is_compiled()); + + // Simulate several GCs that use incremental marking. + const int kAgingThreshold = 6; + for (int i = 0; i < kAgingThreshold; i++) { + HEAP->incremental_marking()->Abort(); + SimulateIncrementalMarking(); + HEAP->CollectAllGarbage(Heap::kNoGCFlags); + } + CHECK(!function->shared()->is_compiled() || function->IsOptimized()); + CHECK(!function->is_compiled() || function->IsOptimized()); + + // This compile will compile the function again. + { v8::HandleScope scope; + CompileRun("foo();"); + } + + // Simulate several GCs that use incremental marking but make sure + // the loop breaks once the function is enqueued as a candidate. + for (int i = 0; i < kAgingThreshold; i++) { + HEAP->incremental_marking()->Abort(); + SimulateIncrementalMarking(); + if (!function->next_function_link()->IsUndefined()) break; + HEAP->CollectAllGarbage(Heap::kNoGCFlags); + } + + // Force optimization while incremental marking is active and while + // the function is enqueued as a candidate. + { v8::HandleScope scope; + CompileRun("%OptimizeFunctionOnNextCall(foo); foo();"); + } + + // Simulate one final GC to make sure the candidate queue is sane. + HEAP->CollectAllGarbage(Heap::kNoGCFlags); + CHECK(function->shared()->is_compiled() || !function->IsOptimized()); + CHECK(function->is_compiled() || !function->IsOptimized()); +} + + +TEST(TestCodeFlushingIncrementalScavenge) { + // If we do not flush code this test is invalid. + if (!FLAG_flush_code || !FLAG_flush_code_incrementally) return; + i::FLAG_allow_natives_syntax = true; + InitializeVM(); + v8::HandleScope scope; + const char* source = "var foo = function() {" + " var x = 42;" + " var y = 42;" + " var z = x + y;" + "};" + "foo();" + "var bar = function() {" + " var x = 23;" + "};" + "bar();"; + Handle<String> foo_name = FACTORY->LookupAsciiSymbol("foo"); + Handle<String> bar_name = FACTORY->LookupAsciiSymbol("bar"); + + // Perfrom one initial GC to enable code flushing. + HEAP->CollectAllGarbage(Heap::kNoGCFlags); + + // This compile will add the code to the compilation cache. + { v8::HandleScope scope; + CompileRun(source); + } + + // Check functions are compiled. + Object* func_value = Isolate::Current()->context()->global_object()-> + GetProperty(*foo_name)->ToObjectChecked(); + CHECK(func_value->IsJSFunction()); + Handle<JSFunction> function(JSFunction::cast(func_value)); + CHECK(function->shared()->is_compiled()); + Object* func_value2 = Isolate::Current()->context()->global_object()-> + GetProperty(*bar_name)->ToObjectChecked(); + CHECK(func_value2->IsJSFunction()); + Handle<JSFunction> function2(JSFunction::cast(func_value2)); + CHECK(function2->shared()->is_compiled()); + + // Clear references to functions so that one of them can die. + { v8::HandleScope scope; + CompileRun("foo = 0; bar = 0;"); + } + + // Bump the code age so that flushing is triggered while the function + // object is still located in new-space. + const int kAgingThreshold = 6; + function->shared()->set_code_age(kAgingThreshold); + function2->shared()->set_code_age(kAgingThreshold); + + // Simulate incremental marking so that the functions are enqueued as + // code flushing candidates. Then kill one of the functions. Finally + // perform a scavenge while incremental marking is still running. + SimulateIncrementalMarking(); + *function2.location() = NULL; + HEAP->CollectGarbage(NEW_SPACE, "test scavenge while marking"); + + // Simulate one final GC to make sure the candidate queue is sane. + HEAP->CollectAllGarbage(Heap::kNoGCFlags); + CHECK(!function->shared()->is_compiled() || function->IsOptimized()); + CHECK(!function->is_compiled() || function->IsOptimized()); +} + + +TEST(TestCodeFlushingIncrementalAbort) { + // If we do not flush code this test is invalid. + if (!FLAG_flush_code || !FLAG_flush_code_incrementally) return; + i::FLAG_allow_natives_syntax = true; + InitializeVM(); + v8::HandleScope scope; + const char* source = "function foo() {" + " var x = 42;" + " var y = 42;" + " var z = x + y;" + "};" + "foo()"; + Handle<String> foo_name = FACTORY->LookupAsciiSymbol("foo"); + + // This compile will add the code to the compilation cache. + { v8::HandleScope scope; + CompileRun(source); + } + + // Check function is compiled. + Object* func_value = Isolate::Current()->context()->global_object()-> + GetProperty(*foo_name)->ToObjectChecked(); + CHECK(func_value->IsJSFunction()); + Handle<JSFunction> function(JSFunction::cast(func_value)); + CHECK(function->shared()->is_compiled()); + + // The code will survive at least two GCs. + HEAP->CollectAllGarbage(Heap::kNoGCFlags); + HEAP->CollectAllGarbage(Heap::kNoGCFlags); + CHECK(function->shared()->is_compiled()); + + // Bump the code age so that flushing is triggered. + const int kAgingThreshold = 6; + function->shared()->set_code_age(kAgingThreshold); + + // Simulate incremental marking so that the function is enqueued as + // code flushing candidate. + SimulateIncrementalMarking(); + + // Enable the debugger and add a breakpoint while incremental marking + // is running so that incremental marking aborts and code flushing is + // disabled. + int position = 0; + Handle<Object> breakpoint_object(Smi::FromInt(0)); + ISOLATE->debug()->SetBreakPoint(function, breakpoint_object, &position); + ISOLATE->debug()->ClearAllBreakPoints(); + + // Force optimization now that code flushing is disabled. + { v8::HandleScope scope; + CompileRun("%OptimizeFunctionOnNextCall(foo); foo();"); + } + + // Simulate one final GC to make sure the candidate queue is sane. + HEAP->CollectAllGarbage(Heap::kNoGCFlags); + CHECK(function->shared()->is_compiled() || !function->IsOptimized()); + CHECK(function->is_compiled() || !function->IsOptimized()); +} + + +// Count the number of native contexts in the weak list of native contexts. +int CountNativeContexts() { int count = 0; - Object* object = HEAP->global_contexts_list(); + Object* object = HEAP->native_contexts_list(); while (!object->IsUndefined()) { count++; object = Context::cast(object)->get(Context::NEXT_CONTEXT_LINK); @@ -995,7 +1206,7 @@ static int CountGlobalContexts() { // Count the number of user functions in the weak list of optimized -// functions attached to a global context. +// functions attached to a native context. static int CountOptimizedUserFunctions(v8::Handle<v8::Context> context) { int count = 0; Handle<Context> icontext = v8::Utils::OpenHandle(*context); @@ -1016,7 +1227,7 @@ TEST(TestInternalWeakLists) { v8::HandleScope scope; v8::Persistent<v8::Context> ctx[kNumTestContexts]; - CHECK_EQ(0, CountGlobalContexts()); + CHECK_EQ(0, CountNativeContexts()); // Create a number of global contests which gets linked together. for (int i = 0; i < kNumTestContexts; i++) { @@ -1024,7 +1235,7 @@ TEST(TestInternalWeakLists) { bool opt = (FLAG_always_opt && i::V8::UseCrankshaft()); - CHECK_EQ(i + 1, CountGlobalContexts()); + CHECK_EQ(i + 1, CountNativeContexts()); ctx[i]->Enter(); @@ -1059,6 +1270,7 @@ TEST(TestInternalWeakLists) { } // Mark compact handles the weak references. + ISOLATE->compilation_cache()->Clear(); HEAP->CollectAllGarbage(Heap::kNoGCFlags); CHECK_EQ(opt ? 4 : 0, CountOptimizedUserFunctions(ctx[i])); @@ -1084,7 +1296,7 @@ TEST(TestInternalWeakLists) { // Force compilation cache cleanup. HEAP->CollectAllGarbage(Heap::kNoGCFlags); - // Dispose the global contexts one by one. + // Dispose the native contexts one by one. for (int i = 0; i < kNumTestContexts; i++) { ctx[i].Dispose(); ctx[i].Clear(); @@ -1092,23 +1304,23 @@ TEST(TestInternalWeakLists) { // Scavenge treats these references as strong. for (int j = 0; j < 10; j++) { HEAP->PerformScavenge(); - CHECK_EQ(kNumTestContexts - i, CountGlobalContexts()); + CHECK_EQ(kNumTestContexts - i, CountNativeContexts()); } // Mark compact handles the weak references. HEAP->CollectAllGarbage(Heap::kNoGCFlags); - CHECK_EQ(kNumTestContexts - i - 1, CountGlobalContexts()); + CHECK_EQ(kNumTestContexts - i - 1, CountNativeContexts()); } - CHECK_EQ(0, CountGlobalContexts()); + CHECK_EQ(0, CountNativeContexts()); } -// Count the number of global contexts in the weak list of global contexts +// Count the number of native contexts in the weak list of native contexts // causing a GC after the specified number of elements. -static int CountGlobalContextsWithGC(int n) { +static int CountNativeContextsWithGC(int n) { int count = 0; - Handle<Object> object(HEAP->global_contexts_list()); + Handle<Object> object(HEAP->native_contexts_list()); while (!object->IsUndefined()) { count++; if (count == n) HEAP->CollectAllGarbage(Heap::kNoGCFlags); @@ -1120,7 +1332,7 @@ static int CountGlobalContextsWithGC(int n) { // Count the number of user functions in the weak list of optimized -// functions attached to a global context causing a GC after the +// functions attached to a native context causing a GC after the // specified number of elements. static int CountOptimizedUserFunctionsWithGC(v8::Handle<v8::Context> context, int n) { @@ -1146,14 +1358,14 @@ TEST(TestInternalWeakListsTraverseWithGC) { v8::HandleScope scope; v8::Persistent<v8::Context> ctx[kNumTestContexts]; - CHECK_EQ(0, CountGlobalContexts()); + CHECK_EQ(0, CountNativeContexts()); // Create an number of contexts and check the length of the weak list both // with and without GCs while iterating the list. for (int i = 0; i < kNumTestContexts; i++) { ctx[i] = v8::Context::New(); - CHECK_EQ(i + 1, CountGlobalContexts()); - CHECK_EQ(i + 1, CountGlobalContextsWithGC(i / 2 + 1)); + CHECK_EQ(i + 1, CountNativeContexts()); + CHECK_EQ(i + 1, CountNativeContextsWithGC(i / 2 + 1)); } bool opt = (FLAG_always_opt && i::V8::UseCrankshaft()); @@ -1197,6 +1409,7 @@ TEST(TestSizeOfObjects) { HEAP->CollectAllGarbage(Heap::kNoGCFlags); HEAP->CollectAllGarbage(Heap::kNoGCFlags); HEAP->CollectAllGarbage(Heap::kNoGCFlags); + HEAP->CollectAllGarbage(Heap::kNoGCFlags); CHECK(HEAP->old_pointer_space()->IsSweepingComplete()); int initial_size = static_cast<int>(HEAP->SizeOfObjects()); @@ -1237,7 +1450,9 @@ TEST(TestSizeOfObjectsVsHeapIteratorPrecision) { for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) { - size_of_objects_2 += obj->Size(); + if (!obj->IsFreeSpace()) { + size_of_objects_2 += obj->Size(); + } } // Delta must be within 5% of the larger result. // TODO(gc): Tighten this up by distinguishing between byte @@ -1278,7 +1493,8 @@ TEST(GrowAndShrinkNewSpace) { InitializeVM(); NewSpace* new_space = HEAP->new_space(); - if (HEAP->ReservedSemiSpaceSize() == HEAP->InitialSemiSpaceSize()) { + if (HEAP->ReservedSemiSpaceSize() == HEAP->InitialSemiSpaceSize() || + HEAP->MaxSemiSpaceSize() == 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. @@ -1326,7 +1542,8 @@ TEST(GrowAndShrinkNewSpace) { TEST(CollectingAllAvailableGarbageShrinksNewSpace) { InitializeVM(); - if (HEAP->ReservedSemiSpaceSize() == HEAP->InitialSemiSpaceSize()) { + if (HEAP->ReservedSemiSpaceSize() == HEAP->InitialSemiSpaceSize() || + HEAP->MaxSemiSpaceSize() == 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. @@ -1359,7 +1576,7 @@ static int NumberOfGlobalObjects() { // Test that we don't embed maps from foreign contexts into // optimized code. -TEST(LeakGlobalContextViaMap) { +TEST(LeakNativeContextViaMap) { i::FLAG_allow_natives_syntax = true; bool snapshot_enabled = i::Snapshot::IsEnabled(); v8::HandleScope outer_scope; @@ -1386,6 +1603,7 @@ TEST(LeakGlobalContextViaMap) { ctx2->Exit(); ctx1->Exit(); ctx1.Dispose(); + v8::V8::ContextDisposedNotification(); } HEAP->CollectAllAvailableGarbage(); CHECK_EQ((snapshot_enabled ? 3 : 2), NumberOfGlobalObjects()); @@ -1397,7 +1615,7 @@ TEST(LeakGlobalContextViaMap) { // Test that we don't embed functions from foreign contexts into // optimized code. -TEST(LeakGlobalContextViaFunction) { +TEST(LeakNativeContextViaFunction) { i::FLAG_allow_natives_syntax = true; bool snapshot_enabled = i::Snapshot::IsEnabled(); v8::HandleScope outer_scope; @@ -1424,6 +1642,7 @@ TEST(LeakGlobalContextViaFunction) { ctx2->Exit(); ctx1->Exit(); ctx1.Dispose(); + v8::V8::ContextDisposedNotification(); } HEAP->CollectAllAvailableGarbage(); CHECK_EQ((snapshot_enabled ? 3 : 2), NumberOfGlobalObjects()); @@ -1433,7 +1652,7 @@ TEST(LeakGlobalContextViaFunction) { } -TEST(LeakGlobalContextViaMapKeyed) { +TEST(LeakNativeContextViaMapKeyed) { i::FLAG_allow_natives_syntax = true; bool snapshot_enabled = i::Snapshot::IsEnabled(); v8::HandleScope outer_scope; @@ -1460,6 +1679,7 @@ TEST(LeakGlobalContextViaMapKeyed) { ctx2->Exit(); ctx1->Exit(); ctx1.Dispose(); + v8::V8::ContextDisposedNotification(); } HEAP->CollectAllAvailableGarbage(); CHECK_EQ((snapshot_enabled ? 3 : 2), NumberOfGlobalObjects()); @@ -1469,7 +1689,7 @@ TEST(LeakGlobalContextViaMapKeyed) { } -TEST(LeakGlobalContextViaMapProto) { +TEST(LeakNativeContextViaMapProto) { i::FLAG_allow_natives_syntax = true; bool snapshot_enabled = i::Snapshot::IsEnabled(); v8::HandleScope outer_scope; @@ -1500,6 +1720,7 @@ TEST(LeakGlobalContextViaMapProto) { ctx2->Exit(); ctx1->Exit(); ctx1.Dispose(); + v8::V8::ContextDisposedNotification(); } HEAP->CollectAllAvailableGarbage(); CHECK_EQ((snapshot_enabled ? 3 : 2), NumberOfGlobalObjects()); @@ -1511,9 +1732,10 @@ TEST(LeakGlobalContextViaMapProto) { TEST(InstanceOfStubWriteBarrier) { i::FLAG_allow_natives_syntax = true; -#ifdef DEBUG +#ifdef VERIFY_HEAP i::FLAG_verify_heap = true; #endif + InitializeVM(); if (!i::V8::UseCrankshaft()) return; v8::HandleScope outer_scope; @@ -1585,22 +1807,24 @@ TEST(PrototypeTransitionClearing) { // Verify that only dead prototype transitions are cleared. CHECK_EQ(10, baseObject->map()->NumberOfProtoTransitions()); HEAP->CollectAllGarbage(Heap::kNoGCFlags); - CHECK_EQ(10 - 3, baseObject->map()->NumberOfProtoTransitions()); + const int transitions = 10 - 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++) { + FixedArray* trans = baseObject->map()->GetPrototypeTransitions(); + 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())); @@ -1608,7 +1832,8 @@ TEST(PrototypeTransitionClearing) { // clearing correctly records slots in prototype transition array. i::FLAG_always_compact = true; Handle<Map> map(baseObject->map()); - CHECK(!space->LastPage()->Contains(map->prototype_transitions()->address())); + CHECK(!space->LastPage()->Contains( + map->GetPrototypeTransitions()->address())); CHECK(space->LastPage()->Contains(prototype->address())); baseObject->SetPrototype(*prototype, false)->ToObjectChecked(); CHECK(map->GetPrototypeTransition(*prototype)->IsMap()); @@ -1619,9 +1844,10 @@ TEST(PrototypeTransitionClearing) { TEST(ResetSharedFunctionInfoCountersDuringIncrementalMarking) { i::FLAG_allow_natives_syntax = true; -#ifdef DEBUG +#ifdef VERIFY_HEAP i::FLAG_verify_heap = true; #endif + InitializeVM(); if (!i::V8::UseCrankshaft()) return; v8::HandleScope outer_scope; @@ -1674,9 +1900,10 @@ TEST(ResetSharedFunctionInfoCountersDuringIncrementalMarking) { TEST(ResetSharedFunctionInfoCountersDuringMarkSweep) { i::FLAG_allow_natives_syntax = true; -#ifdef DEBUG +#ifdef VERIFY_HEAP i::FLAG_verify_heap = true; #endif + InitializeVM(); if (!i::V8::UseCrankshaft()) return; v8::HandleScope outer_scope; @@ -1743,14 +1970,7 @@ TEST(OptimizedAllocationAlwaysInNewSpace) { 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; + return map->transitions()->number_of_transitions(); } @@ -1761,14 +1981,18 @@ TEST(Regress1465) { i::FLAG_trace_incremental_marking = true; InitializeVM(); v8::HandleScope scope; + static const int transitions_count = 256; - #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()); + { + AlwaysAllocateScope always_allocate; + for (int i = 0; i < transitions_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;"); } - CompileRun("var root = new Object;"); + Handle<JSObject> root = v8::Utils::OpenHandle( *v8::Handle<v8::Object>::Cast( @@ -1777,19 +2001,10 @@ TEST(Regress1465) { // Count number of live transitions before marking. int transitions_before = CountMapTransitions(root->map()); CompileRun("%DebugPrint(root);"); - CHECK_EQ(TRANSITION_COUNT, transitions_before); + CHECK_EQ(transitions_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()); + SimulateIncrementalMarking(); 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. @@ -1797,3 +2012,496 @@ TEST(Regress1465) { 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;"); + + SimulateIncrementalMarking(); + + // 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); + + 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;"); + + SimulateIncrementalMarking(); + + // 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); + + 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()); +} + + +// Implemented in the test-alloc.cc test suite. +void SimulateFullSpace(PagedSpace* space); + + +TEST(ReleaseOverReservedPages) { + i::FLAG_trace_gc = true; + // The optimizer can allocate stuff, messing up the test. + i::FLAG_crankshaft = false; + i::FLAG_always_opt = false; + InitializeVM(); + v8::HandleScope scope; + static const int number_of_test_pages = 20; + + // Prepare many pages with low live-bytes count. + PagedSpace* old_pointer_space = HEAP->old_pointer_space(); + CHECK_EQ(1, old_pointer_space->CountTotalPages()); + for (int i = 0; i < number_of_test_pages; i++) { + AlwaysAllocateScope always_allocate; + SimulateFullSpace(old_pointer_space); + FACTORY->NewFixedArray(1, TENURED); + } + CHECK_EQ(number_of_test_pages + 1, old_pointer_space->CountTotalPages()); + + // Triggering one GC will cause a lot of garbage to be discovered but + // even spread across all allocated pages. + HEAP->CollectAllGarbage(Heap::kNoGCFlags, "triggered for preparation"); + CHECK_EQ(number_of_test_pages + 1, old_pointer_space->CountTotalPages()); + + // Triggering subsequent GCs should cause at least half of the pages + // to be released to the OS after at most two cycles. + HEAP->CollectAllGarbage(Heap::kNoGCFlags, "triggered by test 1"); + CHECK_GE(number_of_test_pages + 1, old_pointer_space->CountTotalPages()); + HEAP->CollectAllGarbage(Heap::kNoGCFlags, "triggered by test 2"); + CHECK_GE(number_of_test_pages + 1, old_pointer_space->CountTotalPages() * 2); + + // Triggering a last-resort GC should cause all pages to be released to the + // OS so that other processes can seize the memory. If we get a failure here + // where there are 2 pages left instead of 1, then we should increase the + // size of the first page a little in SizeOfFirstPage in spaces.cc. The + // first page should be small in order to reduce memory used when the VM + // boots, but if the 20 small arrays don't fit on the first page then that's + // an indication that it is too small. + HEAP->CollectAllAvailableGarbage("triggered really hard"); + CHECK_EQ(1, old_pointer_space->CountTotalPages()); +} + + +TEST(Regress2237) { + InitializeVM(); + v8::HandleScope scope; + Handle<String> slice(HEAP->empty_string()); + + { + // Generate a parent that lives in new-space. + v8::HandleScope inner_scope; + const char* c = "This text is long enough to trigger sliced strings."; + Handle<String> s = FACTORY->NewStringFromAscii(CStrVector(c)); + CHECK(s->IsSeqAsciiString()); + CHECK(HEAP->InNewSpace(*s)); + + // Generate a sliced string that is based on the above parent and + // lives in old-space. + FillUpNewSpace(HEAP->new_space()); + AlwaysAllocateScope always_allocate; + Handle<String> t; + // TODO(mstarzinger): Unfortunately FillUpNewSpace() still leaves + // some slack, so we need to allocate a few sliced strings. + for (int i = 0; i < 16; i++) { + t = FACTORY->NewProperSubString(s, 5, 35); + } + CHECK(t->IsSlicedString()); + CHECK(!HEAP->InNewSpace(*t)); + *slice.location() = *t.location(); + } + + CHECK(SlicedString::cast(*slice)->parent()->IsSeqAsciiString()); + HEAP->CollectAllGarbage(Heap::kNoGCFlags); + CHECK(SlicedString::cast(*slice)->parent()->IsSeqAsciiString()); +} + + +#ifdef OBJECT_PRINT +TEST(PrintSharedFunctionInfo) { + InitializeVM(); + v8::HandleScope scope; + const char* source = "f = function() { return 987654321; }\n" + "g = function() { return 123456789; }\n"; + CompileRun(source); + Handle<JSFunction> g = + v8::Utils::OpenHandle( + *v8::Handle<v8::Function>::Cast( + v8::Context::GetCurrent()->Global()->Get(v8_str("g")))); + + AssertNoAllocation no_alloc; + g->shared()->PrintLn(); +} +#endif // OBJECT_PRINT + + +TEST(Regress2211) { + InitializeVM(); + v8::HandleScope scope; + + v8::Handle<v8::String> value = v8_str("val string"); + Smi* hash = Smi::FromInt(321); + Heap* heap = Isolate::Current()->heap(); + + for (int i = 0; i < 2; i++) { + // Store identity hash first and common hidden property second. + v8::Handle<v8::Object> obj = v8::Object::New(); + Handle<JSObject> internal_obj = v8::Utils::OpenHandle(*obj); + CHECK(internal_obj->HasFastProperties()); + + // In the first iteration, set hidden value first and identity hash second. + // In the second iteration, reverse the order. + if (i == 0) obj->SetHiddenValue(v8_str("key string"), value); + MaybeObject* maybe_obj = internal_obj->SetIdentityHash(hash, + ALLOW_CREATION); + CHECK(!maybe_obj->IsFailure()); + if (i == 1) obj->SetHiddenValue(v8_str("key string"), value); + + // Check values. + CHECK_EQ(hash, + internal_obj->GetHiddenProperty(heap->identity_hash_symbol())); + CHECK(value->Equals(obj->GetHiddenValue(v8_str("key string")))); + + // Check size. + DescriptorArray* descriptors = internal_obj->map()->instance_descriptors(); + ObjectHashTable* hashtable = ObjectHashTable::cast( + internal_obj->FastPropertyAt(descriptors->GetFieldIndex(0))); + // HashTable header (5) and 4 initial entries (8). + CHECK_LE(hashtable->SizeFor(hashtable->length()), 13 * kPointerSize); + } +} + + +TEST(IncrementalMarkingClearsTypeFeedbackCells) { + if (i::FLAG_always_opt) return; + InitializeVM(); + v8::HandleScope scope; + v8::Local<v8::Value> fun1, fun2; + + { + LocalContext env; + CompileRun("function fun() {};"); + fun1 = env->Global()->Get(v8_str("fun")); + } + + { + LocalContext env; + CompileRun("function fun() {};"); + fun2 = env->Global()->Get(v8_str("fun")); + } + + // Prepare function f that contains type feedback for closures + // originating from two different native contexts. + v8::Context::GetCurrent()->Global()->Set(v8_str("fun1"), fun1); + v8::Context::GetCurrent()->Global()->Set(v8_str("fun2"), fun2); + CompileRun("function f(a, b) { a(); b(); } f(fun1, fun2);"); + Handle<JSFunction> f = + v8::Utils::OpenHandle( + *v8::Handle<v8::Function>::Cast( + v8::Context::GetCurrent()->Global()->Get(v8_str("f")))); + Handle<TypeFeedbackCells> cells(TypeFeedbackInfo::cast( + f->shared()->code()->type_feedback_info())->type_feedback_cells()); + + CHECK_EQ(2, cells->CellCount()); + CHECK(cells->Cell(0)->value()->IsJSFunction()); + CHECK(cells->Cell(1)->value()->IsJSFunction()); + + SimulateIncrementalMarking(); + HEAP->CollectAllGarbage(Heap::kNoGCFlags); + + CHECK_EQ(2, cells->CellCount()); + CHECK(cells->Cell(0)->value()->IsTheHole()); + CHECK(cells->Cell(1)->value()->IsTheHole()); +} + + +static Code* FindFirstIC(Code* code, Code::Kind kind) { + int mask = RelocInfo::ModeMask(RelocInfo::CODE_TARGET) | + RelocInfo::ModeMask(RelocInfo::CONSTRUCT_CALL) | + RelocInfo::ModeMask(RelocInfo::CODE_TARGET_WITH_ID) | + RelocInfo::ModeMask(RelocInfo::CODE_TARGET_CONTEXT); + for (RelocIterator it(code, mask); !it.done(); it.next()) { + RelocInfo* info = it.rinfo(); + Code* target = Code::GetCodeFromTargetAddress(info->target_address()); + if (target->is_inline_cache_stub() && target->kind() == kind) { + return target; + } + } + return NULL; +} + + +TEST(IncrementalMarkingPreservesMonomorhpicIC) { + if (i::FLAG_always_opt) return; + InitializeVM(); + v8::HandleScope scope; + + // Prepare function f that contains a monomorphic IC for object + // originating from the same native context. + CompileRun("function fun() { this.x = 1; }; var obj = new fun();" + "function f(o) { return o.x; } f(obj); f(obj);"); + Handle<JSFunction> f = + v8::Utils::OpenHandle( + *v8::Handle<v8::Function>::Cast( + v8::Context::GetCurrent()->Global()->Get(v8_str("f")))); + + Code* ic_before = FindFirstIC(f->shared()->code(), Code::LOAD_IC); + CHECK(ic_before->ic_state() == MONOMORPHIC); + + SimulateIncrementalMarking(); + HEAP->CollectAllGarbage(Heap::kNoGCFlags); + + Code* ic_after = FindFirstIC(f->shared()->code(), Code::LOAD_IC); + CHECK(ic_after->ic_state() == MONOMORPHIC); +} + + +TEST(IncrementalMarkingClearsMonomorhpicIC) { + if (i::FLAG_always_opt) return; + InitializeVM(); + v8::HandleScope scope; + v8::Local<v8::Value> obj1; + + { + LocalContext env; + CompileRun("function fun() { this.x = 1; }; var obj = new fun();"); + obj1 = env->Global()->Get(v8_str("obj")); + } + + // Prepare function f that contains a monomorphic IC for object + // originating from a different native context. + v8::Context::GetCurrent()->Global()->Set(v8_str("obj1"), obj1); + CompileRun("function f(o) { return o.x; } f(obj1); f(obj1);"); + Handle<JSFunction> f = + v8::Utils::OpenHandle( + *v8::Handle<v8::Function>::Cast( + v8::Context::GetCurrent()->Global()->Get(v8_str("f")))); + + Code* ic_before = FindFirstIC(f->shared()->code(), Code::LOAD_IC); + CHECK(ic_before->ic_state() == MONOMORPHIC); + + // Fire context dispose notification. + v8::V8::ContextDisposedNotification(); + SimulateIncrementalMarking(); + HEAP->CollectAllGarbage(Heap::kNoGCFlags); + + Code* ic_after = FindFirstIC(f->shared()->code(), Code::LOAD_IC); + CHECK(ic_after->ic_state() == UNINITIALIZED); +} + + +TEST(IncrementalMarkingClearsPolymorhpicIC) { + if (i::FLAG_always_opt) return; + InitializeVM(); + v8::HandleScope scope; + v8::Local<v8::Value> obj1, obj2; + + { + LocalContext env; + CompileRun("function fun() { this.x = 1; }; var obj = new fun();"); + obj1 = env->Global()->Get(v8_str("obj")); + } + + { + LocalContext env; + CompileRun("function fun() { this.x = 2; }; var obj = new fun();"); + obj2 = env->Global()->Get(v8_str("obj")); + } + + // Prepare function f that contains a polymorphic IC for objects + // originating from two different native contexts. + v8::Context::GetCurrent()->Global()->Set(v8_str("obj1"), obj1); + v8::Context::GetCurrent()->Global()->Set(v8_str("obj2"), obj2); + CompileRun("function f(o) { return o.x; } f(obj1); f(obj1); f(obj2);"); + Handle<JSFunction> f = + v8::Utils::OpenHandle( + *v8::Handle<v8::Function>::Cast( + v8::Context::GetCurrent()->Global()->Get(v8_str("f")))); + + Code* ic_before = FindFirstIC(f->shared()->code(), Code::LOAD_IC); + CHECK(ic_before->ic_state() == MEGAMORPHIC); + + // Fire context dispose notification. + v8::V8::ContextDisposedNotification(); + SimulateIncrementalMarking(); + HEAP->CollectAllGarbage(Heap::kNoGCFlags); + + Code* ic_after = FindFirstIC(f->shared()->code(), Code::LOAD_IC); + CHECK(ic_after->ic_state() == UNINITIALIZED); +} + + +class SourceResource: public v8::String::ExternalAsciiStringResource { + public: + explicit SourceResource(const char* data) + : data_(data), length_(strlen(data)) { } + + virtual void Dispose() { + i::DeleteArray(data_); + data_ = NULL; + } + + const char* data() const { return data_; } + + size_t length() const { return length_; } + + bool IsDisposed() { return data_ == NULL; } + + private: + const char* data_; + size_t length_; +}; + + +TEST(ReleaseStackTraceData) { + // Test that the data retained by the Error.stack accessor is released + // after the first time the accessor is fired. We use external string + // to check whether the data is being released since the external string + // resource's callback is fired when the external string is GC'ed. + InitializeVM(); + v8::HandleScope scope; + static const char* source = "var error = 1; " + "try { " + " throw new Error(); " + "} catch (e) { " + " error = e; " + "} "; + SourceResource* resource = new SourceResource(i::StrDup(source)); + { + v8::HandleScope scope; + v8::Handle<v8::String> source_string = v8::String::NewExternal(resource); + v8::Script::Compile(source_string)->Run(); + CHECK(!resource->IsDisposed()); + } + HEAP->CollectAllAvailableGarbage(); + // External source is being retained by the stack trace. + CHECK(!resource->IsDisposed()); + + CompileRun("error.stack; error.stack;"); + HEAP->CollectAllAvailableGarbage(); + // External source has been released. + CHECK(resource->IsDisposed()); + + delete resource; +} + + +TEST(Regression144230) { + InitializeVM(); + v8::HandleScope scope; + + // First make sure that the uninitialized CallIC stub is on a single page + // that will later be selected as an evacuation candidate. + { + v8::HandleScope inner_scope; + AlwaysAllocateScope always_allocate; + SimulateFullSpace(HEAP->code_space()); + ISOLATE->stub_cache()->ComputeCallInitialize(9, RelocInfo::CODE_TARGET); + } + + // Second compile a CallIC and execute it once so that it gets patched to + // the pre-monomorphic stub. These code objects are on yet another page. + { + v8::HandleScope inner_scope; + AlwaysAllocateScope always_allocate; + SimulateFullSpace(HEAP->code_space()); + CompileRun("var o = { f:function(a,b,c,d,e,f,g,h,i) {}};" + "function call() { o.f(1,2,3,4,5,6,7,8,9); };" + "call();"); + } + + // Third we fill up the last page of the code space so that it does not get + // chosen as an evacuation candidate. + { + v8::HandleScope inner_scope; + AlwaysAllocateScope always_allocate; + CompileRun("for (var i = 0; i < 2000; i++) {" + " eval('function f' + i + '() { return ' + i +'; };' +" + " 'f' + i + '();');" + "}"); + } + HEAP->CollectAllGarbage(Heap::kNoGCFlags); + + // Fourth is the tricky part. Make sure the code containing the CallIC is + // visited first without clearing the IC. The shared function info is then + // visited later, causing the CallIC to be cleared. + Handle<String> name = FACTORY->LookupAsciiSymbol("call"); + Handle<GlobalObject> global(ISOLATE->context()->global_object()); + MaybeObject* maybe_call = global->GetProperty(*name); + JSFunction* call = JSFunction::cast(maybe_call->ToObjectChecked()); + USE(global->SetProperty(*name, Smi::FromInt(0), NONE, kNonStrictMode)); + ISOLATE->compilation_cache()->Clear(); + call->shared()->set_ic_age(HEAP->global_ic_age() + 1); + Handle<Object> call_code(call->code()); + Handle<Object> call_function(call); + + // Now we are ready to mess up the heap. + HEAP->CollectAllGarbage(Heap::kReduceMemoryFootprintMask); + + // Either heap verification caught the problem already or we go kaboom once + // the CallIC is executed the next time. + USE(global->SetProperty(*name, *call_function, NONE, kNonStrictMode)); + CompileRun("call();"); +} diff --git a/src/3rdparty/v8/test/cctest/test-list.cc b/src/3rdparty/v8/test/cctest/test-list.cc index 4c78f02..740b432 100644 --- a/src/3rdparty/v8/test/cctest/test-list.cc +++ b/src/3rdparty/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)); diff --git a/src/3rdparty/v8/test/cctest/test-liveedit.cc b/src/3rdparty/v8/test/cctest/test-liveedit.cc index 2498fca..2c89a38 100644 --- a/src/3rdparty/v8/test/cctest/test-liveedit.cc +++ b/src/3rdparty/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()->runtime_zone()) DiffChunkStruct( + pos1, pos2, len1, len2); (*next_chunk_pointer_) = current_chunk_; next_chunk_pointer_ = ¤t_chunk_->next; } @@ -95,7 +96,7 @@ void CompareStringsOneWay(const char* s1, const char* s2, int expected_diff_parameter = -1) { StringCompareInput input(s1, s2); - ZoneScope zone_scope(Isolate::Current(), DELETE_ON_EXIT); + ZoneScope zone_scope(Isolate::Current()->runtime_zone(), DELETE_ON_EXIT); DiffChunkStruct* first_chunk; ListDiffOutputWriter writer(&first_chunk); diff --git a/src/3rdparty/v8/test/cctest/test-mark-compact.cc b/src/3rdparty/v8/test/cctest/test-mark-compact.cc index 700f322..c0ab763 100644 --- a/src/3rdparty/v8/test/cctest/test-mark-compact.cc +++ b/src/3rdparty/v8/test/cctest/test-mark-compact.cc @@ -194,7 +194,7 @@ TEST(MarkCompactCollector) { Map::cast(HEAP->AllocateMap(JS_OBJECT_TYPE, JSObject::kHeaderSize)->ToObjectChecked()); function->set_initial_map(initial_map); - Isolate::Current()->context()->global()->SetProperty( + Isolate::Current()->context()->global_object()->SetProperty( func_name, function, NONE, kNonStrictMode)->ToObjectChecked(); JSObject* obj = JSObject::cast( @@ -203,8 +203,9 @@ TEST(MarkCompactCollector) { func_name = String::cast(HEAP->LookupAsciiSymbol("theFunction")->ToObjectChecked()); - CHECK(Isolate::Current()->context()->global()->HasLocalProperty(func_name)); - Object* func_value = Isolate::Current()->context()->global()-> + CHECK(Isolate::Current()->context()->global_object()-> + HasLocalProperty(func_name)); + Object* func_value = Isolate::Current()->context()->global_object()-> GetProperty(func_name)->ToObjectChecked(); CHECK(func_value->IsJSFunction()); function = JSFunction::cast(func_value); @@ -212,7 +213,7 @@ TEST(MarkCompactCollector) { obj = JSObject::cast(HEAP->AllocateJSObject(function)->ToObjectChecked()); String* obj_name = String::cast(HEAP->LookupAsciiSymbol("theObject")->ToObjectChecked()); - Isolate::Current()->context()->global()->SetProperty( + Isolate::Current()->context()->global_object()->SetProperty( obj_name, obj, NONE, kNonStrictMode)->ToObjectChecked(); String* prop_name = String::cast(HEAP->LookupAsciiSymbol("theSlot")->ToObjectChecked()); @@ -225,10 +226,11 @@ TEST(MarkCompactCollector) { obj_name = String::cast(HEAP->LookupAsciiSymbol("theObject")->ToObjectChecked()); - CHECK(Isolate::Current()->context()->global()->HasLocalProperty(obj_name)); - CHECK(Isolate::Current()->context()->global()-> + CHECK(Isolate::Current()->context()->global_object()-> + HasLocalProperty(obj_name)); + CHECK(Isolate::Current()->context()->global_object()-> GetProperty(obj_name)->ToObjectChecked()->IsJSObject()); - obj = JSObject::cast(Isolate::Current()->context()->global()-> + obj = JSObject::cast(Isolate::Current()->context()->global_object()-> GetProperty(obj_name)->ToObjectChecked()); prop_name = String::cast(HEAP->LookupAsciiSymbol("theSlot")->ToObjectChecked()); @@ -526,23 +528,26 @@ static intptr_t MemoryInUse() { TEST(BootUpMemoryUse) { intptr_t initial_memory = MemoryInUse(); - FLAG_crankshaft = false; // Avoid flakiness. + // Avoid flakiness. + FLAG_crankshaft = false; + FLAG_parallel_recompilation = false; + // Only Linux has the proc filesystem and only if it is mapped. If it's not // 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, 3600 * 1024); // 3396. + CHECK_LE(delta, 3600 * 1024); // 3396. } else { - CHECK_LE(booted_memory - initial_memory, 3600 * 1024); // 3432. + CHECK_LE(delta, 4000 * 1024); // 3948. } } else { if (v8::internal::Snapshot::IsEnabled()) { - CHECK_LE(booted_memory - initial_memory, 2800 * 1024); // 2484. + CHECK_LE(delta, 2500 * 1024); // 2400. } else { - CHECK_LE(booted_memory - initial_memory, 2950 * 1024); // 2844 + CHECK_LE(delta, 2860 * 1024); // 2760. } } } diff --git a/src/3rdparty/v8/test/cctest/test-object-observe.cc b/src/3rdparty/v8/test/cctest/test-object-observe.cc new file mode 100644 index 0000000..374dca4 --- /dev/null +++ b/src/3rdparty/v8/test/cctest/test-object-observe.cc @@ -0,0 +1,196 @@ +// Copyright 2012 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "v8.h" + +#include "cctest.h" + +using namespace v8; + +namespace { +// Need to create a new isolate when FLAG_harmony_observation is on. +class HarmonyIsolate { + public: + HarmonyIsolate() { + i::FLAG_harmony_observation = true; + isolate_ = Isolate::New(); + isolate_->Enter(); + } + + ~HarmonyIsolate() { + isolate_->Exit(); + isolate_->Dispose(); + } + + private: + Isolate* isolate_; +}; +} + +TEST(PerIsolateState) { + HarmonyIsolate isolate; + HandleScope scope; + LocalContext context1; + CompileRun( + "var count = 0;" + "var calls = 0;" + "var observer = function(records) { count = records.length; calls++ };" + "var obj = {};" + "Object.observe(obj, observer);"); + Handle<Value> observer = CompileRun("observer"); + Handle<Value> obj = CompileRun("obj"); + Handle<Value> notify_fun1 = CompileRun( + "(function() { obj.foo = 'bar'; })"); + Handle<Value> notify_fun2; + { + LocalContext context2; + context2->Global()->Set(String::New("obj"), obj); + notify_fun2 = CompileRun( + "(function() { obj.foo = 'baz'; })"); + } + Handle<Value> notify_fun3; + { + LocalContext context3; + context3->Global()->Set(String::New("obj"), obj); + notify_fun3 = CompileRun( + "(function() { obj.foo = 'bat'; })"); + } + { + LocalContext context4; + context4->Global()->Set(String::New("observer"), observer); + context4->Global()->Set(String::New("fun1"), notify_fun1); + context4->Global()->Set(String::New("fun2"), notify_fun2); + context4->Global()->Set(String::New("fun3"), notify_fun3); + CompileRun("fun1(); fun2(); fun3(); Object.deliverChangeRecords(observer)"); + } + CHECK_EQ(1, CompileRun("calls")->Int32Value()); + CHECK_EQ(3, CompileRun("count")->Int32Value()); +} + +TEST(EndOfMicrotaskDelivery) { + HarmonyIsolate isolate; + HandleScope scope; + LocalContext context; + CompileRun( + "var obj = {};" + "var count = 0;" + "var observer = function(records) { count = records.length };" + "Object.observe(obj, observer);" + "obj.foo = 'bar';"); + CHECK_EQ(1, CompileRun("count")->Int32Value()); +} + +TEST(DeliveryOrdering) { + HarmonyIsolate isolate; + HandleScope scope; + LocalContext context; + CompileRun( + "var obj1 = {};" + "var obj2 = {};" + "var ordering = [];" + "function observer2() { ordering.push(2); };" + "function observer1() { ordering.push(1); };" + "function observer3() { ordering.push(3); };" + "Object.observe(obj1, observer1);" + "Object.observe(obj1, observer2);" + "Object.observe(obj1, observer3);" + "obj1.foo = 'bar';"); + CHECK_EQ(3, CompileRun("ordering.length")->Int32Value()); + CHECK_EQ(1, CompileRun("ordering[0]")->Int32Value()); + CHECK_EQ(2, CompileRun("ordering[1]")->Int32Value()); + CHECK_EQ(3, CompileRun("ordering[2]")->Int32Value()); + CompileRun( + "ordering = [];" + "Object.observe(obj2, observer3);" + "Object.observe(obj2, observer2);" + "Object.observe(obj2, observer1);" + "obj2.foo = 'baz'"); + CHECK_EQ(3, CompileRun("ordering.length")->Int32Value()); + CHECK_EQ(1, CompileRun("ordering[0]")->Int32Value()); + CHECK_EQ(2, CompileRun("ordering[1]")->Int32Value()); + CHECK_EQ(3, CompileRun("ordering[2]")->Int32Value()); +} + +TEST(DeliveryOrderingReentrant) { + HarmonyIsolate isolate; + HandleScope scope; + LocalContext context; + CompileRun( + "var obj = {};" + "var reentered = false;" + "var ordering = [];" + "function observer1() { ordering.push(1); };" + "function observer2() {" + " if (!reentered) {" + " obj.foo = 'baz';" + " reentered = true;" + " }" + " ordering.push(2);" + "};" + "function observer3() { ordering.push(3); };" + "Object.observe(obj, observer1);" + "Object.observe(obj, observer2);" + "Object.observe(obj, observer3);" + "obj.foo = 'bar';"); + CHECK_EQ(5, CompileRun("ordering.length")->Int32Value()); + CHECK_EQ(1, CompileRun("ordering[0]")->Int32Value()); + CHECK_EQ(2, CompileRun("ordering[1]")->Int32Value()); + CHECK_EQ(3, CompileRun("ordering[2]")->Int32Value()); + // Note that we re-deliver to observers 1 and 2, while observer3 + // already received the second record during the first round. + CHECK_EQ(1, CompileRun("ordering[3]")->Int32Value()); + CHECK_EQ(2, CompileRun("ordering[1]")->Int32Value()); +} + +TEST(ObjectHashTableGrowth) { + HarmonyIsolate isolate; + HandleScope scope; + // Initializing this context sets up initial hash tables. + LocalContext context; + Handle<Value> obj = CompileRun("obj = {};"); + Handle<Value> observer = CompileRun( + "var ran = false;" + "(function() { ran = true })"); + { + // As does initializing this context. + LocalContext context2; + context2->Global()->Set(String::New("obj"), obj); + context2->Global()->Set(String::New("observer"), observer); + CompileRun( + "var objArr = [];" + // 100 objects should be enough to make the hash table grow + // (and thus relocate). + "for (var i = 0; i < 100; ++i) {" + " objArr.push({});" + " Object.observe(objArr[objArr.length-1], function(){});" + "}" + "Object.observe(obj, observer);"); + } + // obj is now marked "is_observed", but our map has moved. + CompileRun("obj.foo = 'bar'"); + CHECK(CompileRun("ran")->BooleanValue()); +} diff --git a/src/3rdparty/v8/test/cctest/test-parsing.cc b/src/3rdparty/v8/test/cctest/test-parsing.cc index 6bcae7c..717c665 100755 --- a/src/3rdparty/v8/test/cctest/test-parsing.cc +++ b/src/3rdparty/v8/test/cctest/test-parsing.cc @@ -354,7 +354,8 @@ TEST(Regress928) { v8::HandleScope handles; i::Handle<i::String> source( FACTORY->NewStringFromAscii(i::CStrVector(program))); - i::ScriptDataImpl* data = i::ParserApi::PartialPreParse(source, NULL, false); + i::GenericStringUtf16CharacterStream stream(source, 0, source->length()); + i::ScriptDataImpl* data = i::ParserApi::PreParse(&stream, NULL, false); CHECK(!data->HasError()); data->Initialize(); @@ -1016,11 +1017,11 @@ 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::CompilationInfo info(script); + i::CompilationInfoWithZone info(script); + i::Parser parser(&info, i::kAllowLazy | i::EXTENDED_MODE, NULL, NULL); info.MarkAsGlobal(); info.SetLanguageMode(source_data[i].language_mode); - i::FunctionLiteral* function = parser.ParseProgram(&info); + i::FunctionLiteral* function = parser.ParseProgram(); CHECK(function != NULL); // Check scope types and positions. @@ -1060,10 +1061,10 @@ 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::CompilationInfo info(script); + i::CompilationInfoWithZone info(script); + i::Parser parser(&info, flags, NULL, NULL); info.MarkAsGlobal(); - i::FunctionLiteral* function = parser.ParseProgram(&info); + i::FunctionLiteral* function = parser.ParseProgram(); i::FLAG_harmony_scoping = save_harmony_scoping; i::String* type_string = NULL; @@ -1147,6 +1148,7 @@ TEST(ParserSync) { { "with ({})", "" }, { "switch (12) { case 12: ", "}" }, { "switch (12) { default: ", "}" }, + { "switch (12) { ", "case 12: }" }, { "label2: ", "" }, { NULL, NULL } }; @@ -1236,3 +1238,26 @@ TEST(ParserSync) { } } } + + +TEST(PreparserStrictOctal) { + // Test that syntax error caused by octal literal is reported correctly as + // such (issue 2220). + v8::internal::FLAG_min_preparse_length = 1; // Force preparsing. + v8::V8::Initialize(); + v8::HandleScope scope; + v8::Context::Scope context_scope(v8::Context::New()); + v8::TryCatch try_catch; + const char* script = + "\"use strict\"; \n" + "a = function() { \n" + " b = function() { \n" + " 01; \n" + " }; \n" + "}; \n"; + v8::Script::Compile(v8::String::New(script)); + CHECK(try_catch.HasCaught()); + v8::String::Utf8Value exception(try_catch.Exception()); + CHECK_EQ("SyntaxError: Octal literals are not allowed in strict mode.", + *exception); +} diff --git a/src/3rdparty/v8/test/cctest/test-platform-linux.cc b/src/3rdparty/v8/test/cctest/test-platform-linux.cc index 2a8d497..47b99f0 100644 --- a/src/3rdparty/v8/test/cctest/test-platform-linux.cc +++ b/src/3rdparty/v8/test/cctest/test-platform-linux.cc @@ -79,3 +79,9 @@ TEST(VirtualMemory) { CHECK(vm->Uncommit(block_addr, block_size)); delete vm; } + + +TEST(GetCurrentProcessId) { + OS::SetUp(); + CHECK_EQ(static_cast<int>(getpid()), OS::GetCurrentProcessId()); +} diff --git a/src/3rdparty/v8/test/cctest/test-platform-win32.cc b/src/3rdparty/v8/test/cctest/test-platform-win32.cc index 36b30aa..668ccdb 100644 --- a/src/3rdparty/v8/test/cctest/test-platform-win32.cc +++ b/src/3rdparty/v8/test/cctest/test-platform-win32.cc @@ -25,3 +25,10 @@ TEST(VirtualMemory) { CHECK(vm->Uncommit(block_addr, block_size)); delete vm; } + + +TEST(GetCurrentProcessId) { + OS::SetUp(); + CHECK_EQ(static_cast<int>(::GetCurrentProcessId()), + OS::GetCurrentProcessId()); +} diff --git a/src/3rdparty/v8/test/cctest/test-random.cc b/src/3rdparty/v8/test/cctest/test-random.cc index a1f4931..86d6d8c 100644 --- a/src/3rdparty/v8/test/cctest/test-random.cc +++ b/src/3rdparty/v8/test/cctest/test-random.cc @@ -52,7 +52,7 @@ void TestSeeds(Handle<JSFunction> fun, uint32_t state0, uint32_t state1) { bool has_pending_exception; - Handle<JSObject> global(context->global()); + Handle<JSObject> global(context->global_object()); Handle<ByteArray> seeds(context->random_seed()); SetSeeds(seeds, state0, state1); @@ -77,7 +77,7 @@ TEST(CrankshaftRandom) { env->Enter(); Handle<Context> context(Isolate::Current()->context()); - Handle<JSObject> global(context->global()); + Handle<JSObject> global(context->global_object()); Handle<ByteArray> seeds(context->random_seed()); bool has_pending_exception; @@ -85,7 +85,7 @@ TEST(CrankshaftRandom) { Object* symbol = FACTORY->LookupAsciiSymbol("f")->ToObjectChecked(); MaybeObject* fun_object = - context->global()->GetProperty(String::cast(symbol)); + context->global_object()->GetProperty(String::cast(symbol)); Handle<JSFunction> fun(JSFunction::cast(fun_object->ToObjectChecked())); // Optimize function. diff --git a/src/3rdparty/v8/test/cctest/test-regexp.cc b/src/3rdparty/v8/test/cctest/test-regexp.cc index e89e6cd..e433b92 100644 --- a/src/3rdparty/v8/test/cctest/test-regexp.cc +++ b/src/3rdparty/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: @@ -72,23 +72,26 @@ using namespace v8::internal; static bool CheckParse(const char* input) { V8::Initialize(NULL); v8::HandleScope scope; - ZoneScope zone_scope(Isolate::Current(), DELETE_ON_EXIT); + ZoneScope zone_scope(Isolate::Current()->runtime_zone(), DELETE_ON_EXIT); FlatStringReader reader(Isolate::Current(), CStrVector(input)); RegExpCompileData result; - return v8::internal::RegExpParser::ParseRegExp(&reader, false, &result); + return v8::internal::RegExpParser::ParseRegExp( + &reader, false, &result, Isolate::Current()->runtime_zone()); } static SmartArrayPointer<const char> Parse(const char* input) { V8::Initialize(NULL); v8::HandleScope scope; - ZoneScope zone_scope(Isolate::Current(), DELETE_ON_EXIT); + ZoneScope zone_scope(Isolate::Current()->runtime_zone(), DELETE_ON_EXIT); FlatStringReader reader(Isolate::Current(), CStrVector(input)); RegExpCompileData result; - CHECK(v8::internal::RegExpParser::ParseRegExp(&reader, false, &result)); + CHECK(v8::internal::RegExpParser::ParseRegExp( + &reader, false, &result, Isolate::Current()->runtime_zone())); 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()->runtime_zone()); return output; } @@ -96,10 +99,11 @@ static bool CheckSimple(const char* input) { V8::Initialize(NULL); v8::HandleScope scope; unibrow::Utf8InputBuffer<> buffer(input, StrLength(input)); - ZoneScope zone_scope(Isolate::Current(), DELETE_ON_EXIT); + ZoneScope zone_scope(Isolate::Current()->runtime_zone(), DELETE_ON_EXIT); FlatStringReader reader(Isolate::Current(), CStrVector(input)); RegExpCompileData result; - CHECK(v8::internal::RegExpParser::ParseRegExp(&reader, false, &result)); + CHECK(v8::internal::RegExpParser::ParseRegExp( + &reader, false, &result, Isolate::Current()->runtime_zone())); CHECK(result.tree != NULL); CHECK(result.error.is_null()); return result.simple; @@ -114,10 +118,11 @@ static MinMaxPair CheckMinMaxMatch(const char* input) { V8::Initialize(NULL); v8::HandleScope scope; unibrow::Utf8InputBuffer<> buffer(input, StrLength(input)); - ZoneScope zone_scope(Isolate::Current(), DELETE_ON_EXIT); + ZoneScope zone_scope(Isolate::Current()->runtime_zone(), DELETE_ON_EXIT); FlatStringReader reader(Isolate::Current(), CStrVector(input)); RegExpCompileData result; - CHECK(v8::internal::RegExpParser::ParseRegExp(&reader, false, &result)); + CHECK(v8::internal::RegExpParser::ParseRegExp( + &reader, false, &result, Isolate::Current()->runtime_zone())); CHECK(result.tree != NULL); CHECK(result.error.is_null()); int min_match = result.tree->min_match(); @@ -262,6 +267,7 @@ TEST(Parser) { CHECK_PARSE_EQ("\\u003z", "'u003z'"); CHECK_PARSE_EQ("foo[z]*", "(: 'foo' (# 0 - g [z]))"); + CHECK_SIMPLE("", false); CHECK_SIMPLE("a", true); CHECK_SIMPLE("a|b", false); CHECK_SIMPLE("a\\n", false); @@ -385,10 +391,11 @@ static void ExpectError(const char* input, const char* expected) { V8::Initialize(NULL); v8::HandleScope scope; - ZoneScope zone_scope(Isolate::Current(), DELETE_ON_EXIT); + ZoneScope zone_scope(Isolate::Current()->runtime_zone(), DELETE_ON_EXIT); FlatStringReader reader(Isolate::Current(), CStrVector(input)); RegExpCompileData result; - CHECK(!v8::internal::RegExpParser::ParseRegExp(&reader, false, &result)); + CHECK(!v8::internal::RegExpParser::ParseRegExp( + &reader, false, &result, Isolate::Current()->runtime_zone())); CHECK(result.tree == NULL); CHECK(!result.error.is_null()); SmartArrayPointer<char> str = result.error->ToCString(ALLOW_NULLS); @@ -468,9 +475,11 @@ 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); + ZoneScope scope(Isolate::Current()->runtime_zone(), DELETE_ON_EXIT); + Zone* zone = Isolate::Current()->runtime_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++) { @@ -500,14 +509,21 @@ static RegExpNode* Compile(const char* input, bool multiline, bool is_ascii) { FlatStringReader reader(isolate, CStrVector(input)); RegExpCompileData compile_data; if (!v8::internal::RegExpParser::ParseRegExp(&reader, multiline, - &compile_data)) + &compile_data, + isolate->runtime_zone())) return NULL; Handle<String> pattern = isolate->factory()-> NewStringFromUtf8(CStrVector(input)); Handle<String> sample_subject = isolate->factory()->NewStringFromUtf8(CStrVector("")); - RegExpEngine::Compile( - &compile_data, false, multiline, pattern, sample_subject, is_ascii); + RegExpEngine::Compile(&compile_data, + false, + false, + multiline, + pattern, + sample_subject, + is_ascii, + isolate->runtime_zone()); return compile_data.node; } @@ -517,7 +533,7 @@ static void Execute(const char* input, bool is_ascii, bool dot_output = false) { v8::HandleScope scope; - ZoneScope zone_scope(Isolate::Current(), DELETE_ON_EXIT); + ZoneScope zone_scope(Isolate::Current()->runtime_zone(), DELETE_ON_EXIT); RegExpNode* node = Compile(input, multiline, is_ascii); USE(node); #ifdef DEBUG @@ -557,8 +573,8 @@ static unsigned PseudoRandom(int i, int j) { TEST(SplayTreeSimple) { v8::internal::V8::Initialize(NULL); static const unsigned kLimit = 1000; - ZoneScope zone_scope(Isolate::Current(), DELETE_ON_EXIT); - ZoneSplayTree<TestConfig> tree; + ZoneScope zone_scope(Isolate::Current()->runtime_zone(), DELETE_ON_EXIT); + ZoneSplayTree<TestConfig> tree(Isolate::Current()->runtime_zone()); bool seen[kLimit]; for (unsigned i = 0; i < kLimit; i++) seen[i] = false; #define CHECK_MAPS_EQUAL() do { \ @@ -625,12 +641,13 @@ TEST(DispatchTableConstruction) { } } // Enter test data into dispatch table. - ZoneScope zone_scope(Isolate::Current(), DELETE_ON_EXIT); - DispatchTable table; + ZoneScope zone_scope(Isolate::Current()->runtime_zone(), DELETE_ON_EXIT); + DispatchTable table(Isolate::Current()->runtime_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()->runtime_zone()); } // Check that the table looks as we would expect for (int p = 0; p < kLimit; p++) { @@ -692,7 +709,8 @@ typedef RegExpMacroAssemblerMIPS ArchRegExpMacroAssembler; class ContextInitializer { public: ContextInitializer() - : env_(), scope_(), zone_(Isolate::Current(), DELETE_ON_EXIT) { + : env_(), scope_(), zone_(Isolate::Current()->runtime_zone(), + DELETE_ON_EXIT) { env_ = v8::Context::New(); env_->Enter(); } @@ -720,6 +738,7 @@ static ArchRegExpMacroAssembler::Result Execute(Code* code, input_start, input_end, captures, + 0, Isolate::Current()); } @@ -729,7 +748,8 @@ TEST(MacroAssemblerNativeSuccess) { ContextInitializer initializer; Factory* factory = Isolate::Current()->factory(); - ArchRegExpMacroAssembler m(NativeRegExpMacroAssembler::ASCII, 4); + ArchRegExpMacroAssembler m(NativeRegExpMacroAssembler::ASCII, 4, + Isolate::Current()->runtime_zone()); m.Succeed(); @@ -764,7 +784,8 @@ TEST(MacroAssemblerNativeSimple) { ContextInitializer initializer; Factory* factory = Isolate::Current()->factory(); - ArchRegExpMacroAssembler m(NativeRegExpMacroAssembler::ASCII, 4); + ArchRegExpMacroAssembler m(NativeRegExpMacroAssembler::ASCII, 4, + Isolate::Current()->runtime_zone()); uc16 foo_chars[3] = {'f', 'o', 'o'}; Vector<const uc16> foo(foo_chars, 3); @@ -821,7 +842,8 @@ TEST(MacroAssemblerNativeSimpleUC16) { ContextInitializer initializer; Factory* factory = Isolate::Current()->factory(); - ArchRegExpMacroAssembler m(NativeRegExpMacroAssembler::UC16, 4); + ArchRegExpMacroAssembler m(NativeRegExpMacroAssembler::UC16, 4, + Isolate::Current()->runtime_zone()); uc16 foo_chars[3] = {'f', 'o', 'o'}; Vector<const uc16> foo(foo_chars, 3); @@ -883,7 +905,8 @@ TEST(MacroAssemblerNativeBacktrack) { ContextInitializer initializer; Factory* factory = Isolate::Current()->factory(); - ArchRegExpMacroAssembler m(NativeRegExpMacroAssembler::ASCII, 0); + ArchRegExpMacroAssembler m(NativeRegExpMacroAssembler::ASCII, 0, + Isolate::Current()->runtime_zone()); Label fail; Label backtrack; @@ -921,7 +944,8 @@ TEST(MacroAssemblerNativeBackReferenceASCII) { ContextInitializer initializer; Factory* factory = Isolate::Current()->factory(); - ArchRegExpMacroAssembler m(NativeRegExpMacroAssembler::ASCII, 4); + ArchRegExpMacroAssembler m(NativeRegExpMacroAssembler::ASCII, 4, + Isolate::Current()->runtime_zone()); m.WriteCurrentPositionToRegister(0, 0); m.AdvanceCurrentPosition(2); @@ -968,7 +992,8 @@ TEST(MacroAssemblerNativeBackReferenceUC16) { ContextInitializer initializer; Factory* factory = Isolate::Current()->factory(); - ArchRegExpMacroAssembler m(NativeRegExpMacroAssembler::UC16, 4); + ArchRegExpMacroAssembler m(NativeRegExpMacroAssembler::UC16, 4, + Isolate::Current()->runtime_zone()); m.WriteCurrentPositionToRegister(0, 0); m.AdvanceCurrentPosition(2); @@ -998,11 +1023,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]); @@ -1018,7 +1043,8 @@ TEST(MacroAssemblernativeAtStart) { ContextInitializer initializer; Factory* factory = Isolate::Current()->factory(); - ArchRegExpMacroAssembler m(NativeRegExpMacroAssembler::ASCII, 0); + ArchRegExpMacroAssembler m(NativeRegExpMacroAssembler::ASCII, 0, + Isolate::Current()->runtime_zone()); Label not_at_start, newline, fail; m.CheckNotAtStart(¬_at_start); @@ -1075,7 +1101,8 @@ TEST(MacroAssemblerNativeBackRefNoCase) { ContextInitializer initializer; Factory* factory = Isolate::Current()->factory(); - ArchRegExpMacroAssembler m(NativeRegExpMacroAssembler::ASCII, 4); + ArchRegExpMacroAssembler m(NativeRegExpMacroAssembler::ASCII, 4, + Isolate::Current()->runtime_zone()); Label fail, succ; @@ -1132,7 +1159,8 @@ TEST(MacroAssemblerNativeRegisters) { ContextInitializer initializer; Factory* factory = Isolate::Current()->factory(); - ArchRegExpMacroAssembler m(NativeRegExpMacroAssembler::ASCII, 6); + ArchRegExpMacroAssembler m(NativeRegExpMacroAssembler::ASCII, 6, + Isolate::Current()->runtime_zone()); uc16 foo_chars[3] = {'f', 'o', 'o'}; Vector<const uc16> foo(foo_chars, 3); @@ -1234,7 +1262,8 @@ TEST(MacroAssemblerStackOverflow) { Isolate* isolate = Isolate::Current(); Factory* factory = isolate->factory(); - ArchRegExpMacroAssembler m(NativeRegExpMacroAssembler::ASCII, 0); + ArchRegExpMacroAssembler m(NativeRegExpMacroAssembler::ASCII, 0, + Isolate::Current()->runtime_zone()); Label loop; m.Bind(&loop); @@ -1272,7 +1301,8 @@ TEST(MacroAssemblerNativeLotsOfRegisters) { Isolate* isolate = Isolate::Current(); Factory* factory = isolate->factory(); - ArchRegExpMacroAssembler m(NativeRegExpMacroAssembler::ASCII, 2); + ArchRegExpMacroAssembler m(NativeRegExpMacroAssembler::ASCII, 2, + Isolate::Current()->runtime_zone()); // At least 2048, to ensure the allocated space for registers // span one full page. @@ -1319,7 +1349,8 @@ TEST(MacroAssemblerNativeLotsOfRegisters) { TEST(MacroAssembler) { V8::Initialize(NULL); byte codes[1024]; - RegExpMacroAssemblerIrregexp m(Vector<byte>(codes, 1024)); + RegExpMacroAssemblerIrregexp m(Vector<byte>(codes, 1024), + Isolate::Current()->runtime_zone()); // ^f(o)o. Label fail, fail2, start; uc16 foo_chars[3]; @@ -1389,17 +1420,20 @@ TEST(AddInverseToTable) { static const int kLimit = 1000; static const int kRangeCount = 16; for (int t = 0; t < 10; t++) { - ZoneScope zone_scope(Isolate::Current(), DELETE_ON_EXIT); + ZoneScope zone_scope(Isolate::Current()->runtime_zone(), DELETE_ON_EXIT); + Zone* zone = Isolate::Current()->runtime_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()->runtime_zone()); cons.set_choice_index(0); cons.AddInverse(ranges); for (int i = 0; i < kLimit; i++) { @@ -1410,12 +1444,14 @@ TEST(AddInverseToTable) { CHECK_EQ(is_on, set->Get(0) == false); } } - ZoneScope zone_scope(Isolate::Current(), DELETE_ON_EXIT); + ZoneScope zone_scope(Isolate::Current()->runtime_zone(), DELETE_ON_EXIT); + Zone* zone = Isolate::Current()->runtime_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()->runtime_zone()); cons.set_choice_index(0); cons.AddInverse(ranges); CHECK(!table.Get(0xFFFE)->Get(0)); @@ -1523,10 +1559,12 @@ TEST(UncanonicalizeEquivalence) { static void TestRangeCaseIndependence(CharacterRange input, Vector<CharacterRange> expected) { - ZoneScope zone_scope(Isolate::Current(), DELETE_ON_EXIT); + ZoneScope zone_scope(Isolate::Current()->runtime_zone(), DELETE_ON_EXIT); + Zone* zone = Isolate::Current()->runtime_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()); @@ -1587,13 +1625,16 @@ 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()); + ZoneScope zone_scope(Isolate::Current()->runtime_zone(), DELETE_ON_EXIT); + Zone* zone = Isolate::Current()->runtime_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()->runtime_zone()); for (int i = 0; i < (1 << 16); i++) { bool in_base = InClass(i, base); if (in_base) { @@ -1614,13 +1655,15 @@ TEST(CharClassDifference) { TEST(CanonicalizeCharacterSets) { v8::internal::V8::Initialize(NULL); - ZoneScope scope(Isolate::Current(), DELETE_ON_EXIT); - ZoneList<CharacterRange>* list = new ZoneList<CharacterRange>(4); + ZoneScope scope(Isolate::Current()->runtime_zone(), DELETE_ON_EXIT); + Zone* zone = Isolate::Current()->runtime_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()); @@ -1631,9 +1674,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()); @@ -1644,11 +1687,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()); @@ -1663,9 +1706,9 @@ 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()); @@ -1675,9 +1718,10 @@ TEST(CanonicalizeCharacterSets) { TEST(CharacterRangeMerge) { v8::internal::V8::Initialize(NULL); - ZoneScope zone_scope(Isolate::Current(), DELETE_ON_EXIT); - ZoneList<CharacterRange> l1(4); - ZoneList<CharacterRange> l2(4); + ZoneScope zone_scope(Isolate::Current()->runtime_zone(), DELETE_ON_EXIT); + ZoneList<CharacterRange> l1(4, Isolate::Current()->runtime_zone()); + ZoneList<CharacterRange> l2(4, Isolate::Current()->runtime_zone()); + Zone* zone = Isolate::Current()->runtime_zone(); // Create all combinations of intersections of ranges, both singletons and // longer. @@ -1692,8 +1736,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; } @@ -1708,8 +1752,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; } @@ -1729,35 +1773,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); + ZoneList<CharacterRange> first_only(4, Isolate::Current()->runtime_zone()); + ZoneList<CharacterRange> second_only(4, Isolate::Current()->runtime_zone()); + ZoneList<CharacterRange> both(4, Isolate::Current()->runtime_zone()); } diff --git a/src/3rdparty/v8/test/cctest/test-serialize.cc b/src/3rdparty/v8/test/cctest/test-serialize.cc index e426e7b..8279182 100644 --- a/src/3rdparty/v8/test/cctest/test-serialize.cc +++ b/src/3rdparty/v8/test/cctest/test-serialize.cc @@ -196,8 +196,7 @@ class FileByteSink : public SnapshotByteSink { int data_space_used, int code_space_used, int map_space_used, - int cell_space_used, - int large_space_used); + int cell_space_used); private: FILE* fp_; @@ -211,8 +210,7 @@ void FileByteSink::WriteSpaceUsed( int data_space_used, int code_space_used, int map_space_used, - int cell_space_used, - int large_space_used) { + int cell_space_used) { int file_name_length = StrLength(file_name_) + 10; Vector<char> name = Vector<char>::New(file_name_length + 1); OS::SNPrintF(name, "%s.size", file_name_); @@ -224,7 +222,6 @@ void FileByteSink::WriteSpaceUsed( fprintf(fp, "code %d\n", code_space_used); fprintf(fp, "map %d\n", map_space_used); fprintf(fp, "cell %d\n", cell_space_used); - fprintf(fp, "large %d\n", large_space_used); fclose(fp); } @@ -233,6 +230,15 @@ static bool WriteToFile(const char* snapshot_file) { FileByteSink file(snapshot_file); StartupSerializer ser(&file); ser.Serialize(); + + file.WriteSpaceUsed( + ser.CurrentAllocationAddress(NEW_SPACE), + ser.CurrentAllocationAddress(OLD_POINTER_SPACE), + ser.CurrentAllocationAddress(OLD_DATA_SPACE), + ser.CurrentAllocationAddress(CODE_SPACE), + ser.CurrentAllocationAddress(MAP_SPACE), + ser.CurrentAllocationAddress(CELL_SPACE)); + return true; } @@ -250,18 +256,22 @@ static void Serialize() { // Test that the whole heap can be serialized. TEST(Serialize) { - Serializer::Enable(); - v8::V8::Initialize(); - Serialize(); + if (!Snapshot::HaveASnapshotToStartFrom()) { + Serializer::Enable(); + v8::V8::Initialize(); + Serialize(); + } } // Test that heap serialization is non-destructive. TEST(SerializeTwice) { - Serializer::Enable(); - v8::V8::Initialize(); - Serialize(); - Serialize(); + if (!Snapshot::HaveASnapshotToStartFrom()) { + Serializer::Enable(); + v8::V8::Initialize(); + Serialize(); + Serialize(); + } } @@ -275,11 +285,11 @@ static void Deserialize() { static void SanityCheck() { v8::HandleScope scope; -#ifdef DEBUG +#ifdef VERIFY_HEAP HEAP->Verify(); #endif - CHECK(Isolate::Current()->global()->IsJSObject()); - CHECK(Isolate::Current()->global_context()->IsContext()); + CHECK(Isolate::Current()->global_object()->IsJSObject()); + CHECK(Isolate::Current()->native_context()->IsContext()); CHECK(HEAP->symbol_table()->IsSymbolTable()); CHECK(!FACTORY->LookupAsciiSymbol("Empty")->IsFailure()); } @@ -289,7 +299,7 @@ DEPENDENT_TEST(Deserialize, Serialize) { // The serialize-deserialize tests only work if the VM is built without // serialization. That doesn't matter. We don't need to be able to // serialize a snapshot in a VM that is booted from a snapshot. - if (!Snapshot::IsEnabled()) { + if (!Snapshot::HaveASnapshotToStartFrom()) { v8::HandleScope scope; Deserialize(); @@ -302,7 +312,7 @@ DEPENDENT_TEST(Deserialize, Serialize) { DEPENDENT_TEST(DeserializeFromSecondSerialization, SerializeTwice) { - if (!Snapshot::IsEnabled()) { + if (!Snapshot::HaveASnapshotToStartFrom()) { v8::HandleScope scope; Deserialize(); @@ -315,7 +325,7 @@ DEPENDENT_TEST(DeserializeFromSecondSerialization, SerializeTwice) { DEPENDENT_TEST(DeserializeAndRunScript2, Serialize) { - if (!Snapshot::IsEnabled()) { + if (!Snapshot::HaveASnapshotToStartFrom()) { v8::HandleScope scope; Deserialize(); @@ -332,7 +342,7 @@ DEPENDENT_TEST(DeserializeAndRunScript2, Serialize) { DEPENDENT_TEST(DeserializeFromSecondSerializationAndRunScript2, SerializeTwice) { - if (!Snapshot::IsEnabled()) { + if (!Snapshot::HaveASnapshotToStartFrom()) { v8::HandleScope scope; Deserialize(); @@ -348,63 +358,74 @@ DEPENDENT_TEST(DeserializeFromSecondSerializationAndRunScript2, TEST(PartialSerialization) { - Serializer::Enable(); - v8::V8::Initialize(); + if (!Snapshot::HaveASnapshotToStartFrom()) { + Serializer::Enable(); + v8::V8::Initialize(); - v8::Persistent<v8::Context> env = v8::Context::New(); - ASSERT(!env.IsEmpty()); - env->Enter(); - // Make sure all builtin scripts are cached. - { HandleScope scope; - for (int i = 0; i < Natives::GetBuiltinsCount(); i++) { - Isolate::Current()->bootstrapper()->NativesSourceLookup(i); + v8::Persistent<v8::Context> env = v8::Context::New(); + ASSERT(!env.IsEmpty()); + env->Enter(); + // Make sure all builtin scripts are cached. + { HandleScope scope; + for (int i = 0; i < Natives::GetBuiltinsCount(); i++) { + Isolate::Current()->bootstrapper()->NativesSourceLookup(i); + } } - } - HEAP->CollectAllGarbage(Heap::kNoGCFlags); - HEAP->CollectAllGarbage(Heap::kNoGCFlags); + HEAP->CollectAllGarbage(Heap::kNoGCFlags); + HEAP->CollectAllGarbage(Heap::kNoGCFlags); - Object* raw_foo; - { - v8::HandleScope handle_scope; - v8::Local<v8::String> foo = v8::String::New("foo"); - ASSERT(!foo.IsEmpty()); - raw_foo = *(v8::Utils::OpenHandle(*foo)); - } - - int file_name_length = StrLength(FLAG_testing_serialization_file) + 10; - Vector<char> startup_name = Vector<char>::New(file_name_length + 1); - OS::SNPrintF(startup_name, "%s.startup", FLAG_testing_serialization_file); + Object* raw_foo; + { + v8::HandleScope handle_scope; + v8::Local<v8::String> foo = v8::String::New("foo"); + ASSERT(!foo.IsEmpty()); + raw_foo = *(v8::Utils::OpenHandle(*foo)); + } - env->Exit(); - env.Dispose(); + int file_name_length = StrLength(FLAG_testing_serialization_file) + 10; + Vector<char> startup_name = Vector<char>::New(file_name_length + 1); + OS::SNPrintF(startup_name, "%s.startup", FLAG_testing_serialization_file); - FileByteSink startup_sink(startup_name.start()); - startup_name.Dispose(); - StartupSerializer startup_serializer(&startup_sink); - startup_serializer.SerializeStrongReferences(); - - FileByteSink partial_sink(FLAG_testing_serialization_file); - PartialSerializer p_ser(&startup_serializer, &partial_sink); - p_ser.Serialize(&raw_foo); - startup_serializer.SerializeWeakReferences(); - partial_sink.WriteSpaceUsed(p_ser.CurrentAllocationAddress(NEW_SPACE), - p_ser.CurrentAllocationAddress(OLD_POINTER_SPACE), - p_ser.CurrentAllocationAddress(OLD_DATA_SPACE), - p_ser.CurrentAllocationAddress(CODE_SPACE), - p_ser.CurrentAllocationAddress(MAP_SPACE), - p_ser.CurrentAllocationAddress(CELL_SPACE), - p_ser.CurrentAllocationAddress(LO_SPACE)); + env->Exit(); + env.Dispose(); + + FileByteSink startup_sink(startup_name.start()); + StartupSerializer startup_serializer(&startup_sink); + startup_serializer.SerializeStrongReferences(); + + FileByteSink partial_sink(FLAG_testing_serialization_file); + PartialSerializer p_ser(&startup_serializer, &partial_sink); + p_ser.Serialize(&raw_foo); + startup_serializer.SerializeWeakReferences(); + + partial_sink.WriteSpaceUsed( + p_ser.CurrentAllocationAddress(NEW_SPACE), + p_ser.CurrentAllocationAddress(OLD_POINTER_SPACE), + p_ser.CurrentAllocationAddress(OLD_DATA_SPACE), + p_ser.CurrentAllocationAddress(CODE_SPACE), + p_ser.CurrentAllocationAddress(MAP_SPACE), + p_ser.CurrentAllocationAddress(CELL_SPACE)); + + startup_sink.WriteSpaceUsed( + startup_serializer.CurrentAllocationAddress(NEW_SPACE), + startup_serializer.CurrentAllocationAddress(OLD_POINTER_SPACE), + startup_serializer.CurrentAllocationAddress(OLD_DATA_SPACE), + startup_serializer.CurrentAllocationAddress(CODE_SPACE), + startup_serializer.CurrentAllocationAddress(MAP_SPACE), + startup_serializer.CurrentAllocationAddress(CELL_SPACE)); + startup_name.Dispose(); + } } -static void ReserveSpaceForPartialSnapshot(const char* file_name) { +static void ReserveSpaceForSnapshot(Deserializer* deserializer, + const char* file_name) { int file_name_length = StrLength(file_name) + 10; Vector<char> name = Vector<char>::New(file_name_length + 1); OS::SNPrintF(name, "%s.size", file_name); FILE* fp = OS::FOpen(name.start(), "r"); name.Dispose(); int new_size, pointer_size, data_size, code_size, map_size, cell_size; - int large_size; #ifdef _MSC_VER // Avoid warning about unsafe fscanf from MSVC. // Please note that this is only fine if %c and %s are not being used. @@ -416,18 +437,16 @@ static void ReserveSpaceForPartialSnapshot(const char* file_name) { CHECK_EQ(1, fscanf(fp, "code %d\n", &code_size)); CHECK_EQ(1, fscanf(fp, "map %d\n", &map_size)); CHECK_EQ(1, fscanf(fp, "cell %d\n", &cell_size)); - CHECK_EQ(1, fscanf(fp, "large %d\n", &large_size)); #ifdef _MSC_VER #undef fscanf #endif fclose(fp); - HEAP->ReserveSpace(new_size, - pointer_size, - data_size, - code_size, - map_size, - cell_size, - large_size); + deserializer->set_reservation(NEW_SPACE, new_size); + deserializer->set_reservation(OLD_POINTER_SPACE, pointer_size); + deserializer->set_reservation(OLD_DATA_SPACE, data_size); + deserializer->set_reservation(CODE_SPACE, code_size); + deserializer->set_reservation(MAP_SPACE, map_size); + deserializer->set_reservation(CELL_SPACE, cell_size); } @@ -441,7 +460,6 @@ DEPENDENT_TEST(PartialDeserialization, PartialSerialization) { startup_name.Dispose(); const char* file_name = FLAG_testing_serialization_file; - ReserveSpaceForPartialSnapshot(file_name); int snapshot_size = 0; byte* snapshot = ReadBytes(file_name, &snapshot_size); @@ -450,18 +468,19 @@ DEPENDENT_TEST(PartialDeserialization, PartialSerialization) { { SnapshotByteSource source(snapshot, snapshot_size); Deserializer deserializer(&source); + ReserveSpaceForSnapshot(&deserializer, file_name); deserializer.DeserializePartial(&root); CHECK(root->IsString()); } v8::HandleScope handle_scope; Handle<Object> root_handle(root); - ReserveSpaceForPartialSnapshot(file_name); Object* root2; { SnapshotByteSource source(snapshot, snapshot_size); Deserializer deserializer(&source); + ReserveSpaceForSnapshot(&deserializer, file_name); deserializer.DeserializePartial(&root2); CHECK(root2->IsString()); CHECK(*root_handle == root2); @@ -471,53 +490,64 @@ DEPENDENT_TEST(PartialDeserialization, PartialSerialization) { TEST(ContextSerialization) { - Serializer::Enable(); - v8::V8::Initialize(); + if (!Snapshot::HaveASnapshotToStartFrom()) { + Serializer::Enable(); + v8::V8::Initialize(); - v8::Persistent<v8::Context> env = v8::Context::New(); - ASSERT(!env.IsEmpty()); - env->Enter(); - // Make sure all builtin scripts are cached. - { HandleScope scope; - for (int i = 0; i < Natives::GetBuiltinsCount(); i++) { - Isolate::Current()->bootstrapper()->NativesSourceLookup(i); + v8::Persistent<v8::Context> env = v8::Context::New(); + ASSERT(!env.IsEmpty()); + env->Enter(); + // Make sure all builtin scripts are cached. + { HandleScope scope; + for (int i = 0; i < Natives::GetBuiltinsCount(); i++) { + Isolate::Current()->bootstrapper()->NativesSourceLookup(i); + } } - } - // If we don't do this then we end up with a stray root pointing at the - // context even after we have disposed of env. - HEAP->CollectAllGarbage(Heap::kNoGCFlags); + // If we don't do this then we end up with a stray root pointing at the + // context even after we have disposed of env. + HEAP->CollectAllGarbage(Heap::kNoGCFlags); + + int file_name_length = StrLength(FLAG_testing_serialization_file) + 10; + Vector<char> startup_name = Vector<char>::New(file_name_length + 1); + OS::SNPrintF(startup_name, "%s.startup", FLAG_testing_serialization_file); - int file_name_length = StrLength(FLAG_testing_serialization_file) + 10; - Vector<char> startup_name = Vector<char>::New(file_name_length + 1); - OS::SNPrintF(startup_name, "%s.startup", FLAG_testing_serialization_file); + env->Exit(); - env->Exit(); + Object* raw_context = *(v8::Utils::OpenHandle(*env)); - Object* raw_context = *(v8::Utils::OpenHandle(*env)); + env.Dispose(); - env.Dispose(); + FileByteSink startup_sink(startup_name.start()); + StartupSerializer startup_serializer(&startup_sink); + startup_serializer.SerializeStrongReferences(); + + FileByteSink partial_sink(FLAG_testing_serialization_file); + PartialSerializer p_ser(&startup_serializer, &partial_sink); + p_ser.Serialize(&raw_context); + startup_serializer.SerializeWeakReferences(); + + partial_sink.WriteSpaceUsed( + p_ser.CurrentAllocationAddress(NEW_SPACE), + p_ser.CurrentAllocationAddress(OLD_POINTER_SPACE), + p_ser.CurrentAllocationAddress(OLD_DATA_SPACE), + p_ser.CurrentAllocationAddress(CODE_SPACE), + p_ser.CurrentAllocationAddress(MAP_SPACE), + p_ser.CurrentAllocationAddress(CELL_SPACE)); - FileByteSink startup_sink(startup_name.start()); - startup_name.Dispose(); - StartupSerializer startup_serializer(&startup_sink); - startup_serializer.SerializeStrongReferences(); - - FileByteSink partial_sink(FLAG_testing_serialization_file); - PartialSerializer p_ser(&startup_serializer, &partial_sink); - p_ser.Serialize(&raw_context); - startup_serializer.SerializeWeakReferences(); - partial_sink.WriteSpaceUsed(p_ser.CurrentAllocationAddress(NEW_SPACE), - p_ser.CurrentAllocationAddress(OLD_POINTER_SPACE), - p_ser.CurrentAllocationAddress(OLD_DATA_SPACE), - p_ser.CurrentAllocationAddress(CODE_SPACE), - p_ser.CurrentAllocationAddress(MAP_SPACE), - p_ser.CurrentAllocationAddress(CELL_SPACE), - p_ser.CurrentAllocationAddress(LO_SPACE)); + startup_sink.WriteSpaceUsed( + startup_serializer.CurrentAllocationAddress(NEW_SPACE), + startup_serializer.CurrentAllocationAddress(OLD_POINTER_SPACE), + startup_serializer.CurrentAllocationAddress(OLD_DATA_SPACE), + startup_serializer.CurrentAllocationAddress(CODE_SPACE), + startup_serializer.CurrentAllocationAddress(MAP_SPACE), + startup_serializer.CurrentAllocationAddress(CELL_SPACE)); + startup_name.Dispose(); + } } DEPENDENT_TEST(ContextDeserialization, ContextSerialization) { - if (!Snapshot::IsEnabled()) { + if (!Snapshot::HaveASnapshotToStartFrom()) { int file_name_length = StrLength(FLAG_testing_serialization_file) + 10; Vector<char> startup_name = Vector<char>::New(file_name_length + 1); OS::SNPrintF(startup_name, "%s.startup", FLAG_testing_serialization_file); @@ -526,7 +556,6 @@ DEPENDENT_TEST(ContextDeserialization, ContextSerialization) { startup_name.Dispose(); const char* file_name = FLAG_testing_serialization_file; - ReserveSpaceForPartialSnapshot(file_name); int snapshot_size = 0; byte* snapshot = ReadBytes(file_name, &snapshot_size); @@ -535,18 +564,19 @@ DEPENDENT_TEST(ContextDeserialization, ContextSerialization) { { SnapshotByteSource source(snapshot, snapshot_size); Deserializer deserializer(&source); + ReserveSpaceForSnapshot(&deserializer, file_name); deserializer.DeserializePartial(&root); CHECK(root->IsContext()); } v8::HandleScope handle_scope; Handle<Object> root_handle(root); - ReserveSpaceForPartialSnapshot(file_name); Object* root2; { SnapshotByteSource source(snapshot, snapshot_size); Deserializer deserializer(&source); + ReserveSpaceForSnapshot(&deserializer, file_name); deserializer.DeserializePartial(&root2); CHECK(root2->IsContext()); CHECK(*root_handle != root2); @@ -555,119 +585,6 @@ DEPENDENT_TEST(ContextDeserialization, ContextSerialization) { } -TEST(LinearAllocation) { - v8::V8::Initialize(); - int new_space_max = 512 * KB; - int paged_space_max = Page::kMaxNonCodeHeapObjectSize; - int code_space_max = HEAP->code_space()->AreaSize(); - - for (int size = 1000; size < 5 * MB; size += size >> 1) { - size &= ~8; // Round. - int new_space_size = (size < new_space_max) ? size : new_space_max; - int paged_space_size = (size < paged_space_max) ? size : paged_space_max; - HEAP->ReserveSpace( - new_space_size, - paged_space_size, // Old pointer space. - paged_space_size, // Old data space. - HEAP->code_space()->RoundSizeDownToObjectAlignment(code_space_max), - HEAP->map_space()->RoundSizeDownToObjectAlignment(paged_space_size), - HEAP->cell_space()->RoundSizeDownToObjectAlignment(paged_space_size), - size); // Large object space. - LinearAllocationScope linear_allocation_scope; - const int kSmallFixedArrayLength = 4; - const int kSmallFixedArraySize = - FixedArray::kHeaderSize + kSmallFixedArrayLength * kPointerSize; - const int kSmallStringLength = 16; - const int kSmallStringSize = - (SeqAsciiString::kHeaderSize + kSmallStringLength + - kObjectAlignmentMask) & ~kObjectAlignmentMask; - const int kMapSize = Map::kSize; - - Object* new_last = NULL; - for (int i = 0; - i + kSmallFixedArraySize <= new_space_size; - i += kSmallFixedArraySize) { - Object* obj = - HEAP->AllocateFixedArray(kSmallFixedArrayLength)->ToObjectChecked(); - if (new_last != NULL) { - CHECK(reinterpret_cast<char*>(obj) == - reinterpret_cast<char*>(new_last) + kSmallFixedArraySize); - } - new_last = obj; - } - - Object* pointer_last = NULL; - for (int i = 0; - i + kSmallFixedArraySize <= paged_space_size; - i += kSmallFixedArraySize) { - Object* obj = HEAP->AllocateFixedArray(kSmallFixedArrayLength, - TENURED)->ToObjectChecked(); - int old_page_fullness = i % Page::kPageSize; - int page_fullness = (i + kSmallFixedArraySize) % Page::kPageSize; - if (page_fullness < old_page_fullness || - page_fullness > HEAP->old_pointer_space()->AreaSize()) { - i = RoundUp(i, Page::kPageSize); - pointer_last = NULL; - } - if (pointer_last != NULL) { - CHECK(reinterpret_cast<char*>(obj) == - reinterpret_cast<char*>(pointer_last) + kSmallFixedArraySize); - } - pointer_last = obj; - } - - Object* data_last = NULL; - for (int i = 0; - i + kSmallStringSize <= paged_space_size; - i += kSmallStringSize) { - Object* obj = HEAP->AllocateRawAsciiString(kSmallStringLength, - TENURED)->ToObjectChecked(); - int old_page_fullness = i % Page::kPageSize; - int page_fullness = (i + kSmallStringSize) % Page::kPageSize; - if (page_fullness < old_page_fullness || - page_fullness > HEAP->old_data_space()->AreaSize()) { - i = RoundUp(i, Page::kPageSize); - data_last = NULL; - } - if (data_last != NULL) { - CHECK(reinterpret_cast<char*>(obj) == - reinterpret_cast<char*>(data_last) + kSmallStringSize); - } - data_last = obj; - } - - Object* map_last = NULL; - for (int i = 0; i + kMapSize <= paged_space_size; i += kMapSize) { - Object* obj = HEAP->AllocateMap(JS_OBJECT_TYPE, - 42 * kPointerSize)->ToObjectChecked(); - int old_page_fullness = i % Page::kPageSize; - int page_fullness = (i + kMapSize) % Page::kPageSize; - if (page_fullness < old_page_fullness || - page_fullness > HEAP->map_space()->AreaSize()) { - i = RoundUp(i, Page::kPageSize); - map_last = NULL; - } - if (map_last != NULL) { - CHECK(reinterpret_cast<char*>(obj) == - reinterpret_cast<char*>(map_last) + kMapSize); - } - map_last = obj; - } - - if (size > Page::kMaxNonCodeHeapObjectSize) { - // Support for reserving space in large object space is not there yet, - // but using an always-allocate scope is fine for now. - AlwaysAllocateScope always; - int large_object_array_length = - (size - FixedArray::kHeaderSize) / kPointerSize; - Object* obj = HEAP->AllocateFixedArray(large_object_array_length, - TENURED)->ToObjectChecked(); - CHECK(!obj->IsFailure()); - } - } -} - - TEST(TestThatAlwaysSucceeds) { } diff --git a/src/3rdparty/v8/test/cctest/test-sockets.cc b/src/3rdparty/v8/test/cctest/test-sockets.cc index ad73540..2f7941c 100644 --- a/src/3rdparty/v8/test/cctest/test-sockets.cc +++ b/src/3rdparty/v8/test/cctest/test-sockets.cc @@ -124,7 +124,7 @@ static void SendAndReceive(int port, char *data, int len) { TEST(Socket) { // Make sure this port is not used by other tests to allow tests to run in // parallel. - static const int kPort = 5859; + static const int kPort = 5859 + FlagDependentPortOffset(); bool ok; diff --git a/src/3rdparty/v8/test/cctest/test-strings.cc b/src/3rdparty/v8/test/cctest/test-strings.cc index d86886f..5a9ccbb 100644 --- a/src/3rdparty/v8/test/cctest/test-strings.cc +++ b/src/3rdparty/v8/test/cctest/test-strings.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Check that we can traverse very deep stacks of ConsStrings using // StringInputBuffer. Check that Get(int) works on very deep stacks @@ -11,6 +11,7 @@ #include "api.h" #include "factory.h" +#include "objects.h" #include "cctest.h" #include "zone-inl.h" @@ -82,6 +83,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()->runtime_zone(); for (int i = 0; i < NUMBER_OF_BUILDING_BLOCKS; i++) { int len = gen() % 16; if (len > 14) { @@ -113,11 +115,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)); @@ -233,7 +235,7 @@ TEST(Traverse) { InitializeVM(); v8::HandleScope scope; Handle<String> building_blocks[NUMBER_OF_BUILDING_BLOCKS]; - ZoneScope zone(Isolate::Current(), DELETE_ON_EXIT); + ZoneScope zone(Isolate::Current()->runtime_zone(), DELETE_ON_EXIT); InitializeBuildingBlocks(building_blocks); Handle<String> flat = ConstructBalanced(building_blocks); FlattenString(flat); @@ -348,10 +350,11 @@ TEST(Utf8Conversion) { TEST(ExternalShortStringAdd) { - ZoneScope zone(Isolate::Current(), DELETE_ON_EXIT); + ZoneScope zonescope(Isolate::Current()->runtime_zone(), DELETE_ON_EXIT); InitializeVM(); v8::HandleScope handle_scope; + Zone* zone = Isolate::Current()->runtime_zone(); // Make sure we cover all always-flat lengths and at least one above. static const int kMaxLength = 20; @@ -365,25 +368,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), @@ -438,7 +441,7 @@ TEST(CachedHashOverflow) { // We incorrectly allowed strings to be tagged as array indices even if their // values didn't fit in the hash field. // See http://code.google.com/p/v8/issues/detail?id=728 - ZoneScope zone(Isolate::Current(), DELETE_ON_EXIT); + ZoneScope zone(Isolate::Current()->runtime_zone(), DELETE_ON_EXIT); InitializeVM(); v8::HandleScope handle_scope; @@ -672,3 +675,43 @@ TEST(RobustSubStringStub) { 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()); +} + + +TEST(StringReplaceAtomTwoByteResult) { + InitializeVM(); + HandleScope scope; + LocalContext context; + v8::Local<v8::Value> result = CompileRun( + "var subject = 'ascii~only~string~'; " + "var replace = '\x80'; " + "subject.replace(/~/g, replace); "); + CHECK(result->IsString()); + Handle<String> string = v8::Utils::OpenHandle(v8::String::Cast(*result)); + CHECK(string->IsSeqTwoByteString()); + + v8::Local<v8::String> expected = v8_str("ascii\x80only\x80string\x80"); + CHECK(expected->Equals(result)); +} + + +TEST(IsAscii) { + CHECK(String::IsAscii(static_cast<char*>(NULL), 0)); + CHECK(String::IsAscii(static_cast<uc16*>(NULL), 0)); +} diff --git a/src/3rdparty/v8/test/cctest/test-utils.cc b/src/3rdparty/v8/test/cctest/test-utils.cc index df8ff72..c83acb9 100644 --- a/src/3rdparty/v8/test/cctest/test-utils.cc +++ b/src/3rdparty/v8/test/cctest/test-utils.cc @@ -55,6 +55,22 @@ TEST(Utils1) { CHECK_EQ(-2, -8 >> 2); CHECK_EQ(-2, static_cast<int8_t>(-8) >> 2); CHECK_EQ(-2, static_cast<int>(static_cast<intptr_t>(-8) >> 2)); + + CHECK_EQ(-1000000, FastD2IChecked(-1000000.0)); + CHECK_EQ(-1, FastD2IChecked(-1.0)); + CHECK_EQ(0, FastD2IChecked(0.0)); + CHECK_EQ(1, FastD2IChecked(1.0)); + CHECK_EQ(1000000, FastD2IChecked(1000000.0)); + + CHECK_EQ(-1000000, FastD2IChecked(-1000000.123)); + CHECK_EQ(-1, FastD2IChecked(-1.234)); + CHECK_EQ(0, FastD2IChecked(0.345)); + CHECK_EQ(1, FastD2IChecked(1.234)); + CHECK_EQ(1000000, FastD2IChecked(1000000.123)); + + CHECK_EQ(INT_MAX, FastD2IChecked(1.0e100)); + CHECK_EQ(INT_MIN, FastD2IChecked(-1.0e100)); + CHECK_EQ(INT_MIN, FastD2IChecked(OS::nan_value())); } diff --git a/src/3rdparty/v8/test/cctest/test-weakmaps.cc b/src/3rdparty/v8/test/cctest/test-weakmaps.cc index 7bba7b6..7c98c57 100644 --- a/src/3rdparty/v8/test/cctest/test-weakmaps.cc +++ b/src/3rdparty/v8/test/cctest/test-weakmaps.cc @@ -193,9 +193,10 @@ TEST(Regress2060a) { // other strong paths are correctly recorded in the slots buffer. TEST(Regress2060b) { FLAG_always_compact = true; -#ifdef DEBUG +#ifdef VERIFY_HEAP FLAG_verify_heap = true; #endif + LocalContext context; v8::HandleScope scope; Handle<JSFunction> function = diff --git a/src/3rdparty/v8/test/cctest/testcfg.py b/src/3rdparty/v8/test/cctest/testcfg.py index f1387e8..69a5db2 100644 --- a/src/3rdparty/v8/test/cctest/testcfg.py +++ b/src/3rdparty/v8/test/cctest/testcfg.py @@ -25,11 +25,70 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import test import os -from os.path import join, dirname, exists -import platform -import utils +import shutil + +from testrunner.local import commands +from testrunner.local import testsuite +from testrunner.local import utils +from testrunner.objects import testcase + + +class CcTestSuite(testsuite.TestSuite): + + def __init__(self, name, root): + super(CcTestSuite, self).__init__(name, root) + self.serdes_dir = os.path.normpath( + os.path.join(root, "..", "..", "out", ".serdes")) + if os.path.exists(self.serdes_dir): + shutil.rmtree(self.serdes_dir, True) + os.makedirs(self.serdes_dir) + + def ListTests(self, context): + if utils.IsWindows(): + shell += '.exe' + shell = os.path.abspath(os.path.join(context.shell_dir, self.shell())) + output = commands.Execute([context.command_prefix, + shell, + '--list', + context.extra_flags]) + if output.exit_code != 0: + print output.stdout + print output.stderr + return [] + tests = [] + for test_desc in output.stdout.strip().split(): + raw_test, dependency = test_desc.split('<') + if dependency != '': + dependency = raw_test.split('/')[0] + '/' + dependency + else: + dependency = None + test = testcase.TestCase(self, raw_test, dependency=dependency) + tests.append(test) + tests.sort() + return tests + + def GetFlagsForTestCase(self, testcase, context): + testname = testcase.path.split(os.path.sep)[-1] + serialization_file = os.path.join(self.serdes_dir, "serdes_" + testname) + serialization_file += ''.join(testcase.flags).replace('-', '_') + return (testcase.flags + [testcase.path] + context.mode_flags + + ["--testing_serialization_file=" + serialization_file]) + + def shell(self): + return "cctest" + + +def GetSuite(name, root): + return CcTestSuite(name, root) + + +# Deprecated definitions below. +# TODO(jkummerow): Remove when SCons is no longer supported. + + +from os.path import exists, join, normpath +import test class CcTestCase(test.TestCase): @@ -93,7 +152,8 @@ class CcTestConfiguration(test.TestConfiguration): if utils.IsWindows(): executable += '.exe' executable = join(self.context.buildspace, executable) - output = test.Execute([executable, '--list'], self.context) + full_command = self.context.processor([executable, '--list']) + output = test.Execute(full_command, self.context) if output.exit_code != 0: print output.stdout print output.stderr |