diff options
author | Ryan <ry@tinyclouds.org> | 2009-06-08 18:34:06 +0200 |
---|---|---|
committer | Ryan <ry@tinyclouds.org> | 2009-06-08 18:34:06 +0200 |
commit | 696f02455792b368249bf9b013dde637b5ec31fd (patch) | |
tree | 95b2dbd6c2537df9df52f6627aac36fcf05f6a7a /deps/v8/test | |
parent | f6a7fe26574defaa807a13248102ebe0f23270af (diff) | |
download | node-696f02455792b368249bf9b013dde637b5ec31fd.tar.gz |
Upgrade to v8 1.2.7
Diffstat (limited to 'deps/v8/test')
38 files changed, 2365 insertions, 181 deletions
diff --git a/deps/v8/test/cctest/SConscript b/deps/v8/test/cctest/SConscript index 740acbaa6..7506d2921 100644 --- a/deps/v8/test/cctest/SConscript +++ b/deps/v8/test/cctest/SConscript @@ -48,6 +48,7 @@ SOURCES = { 'test-list.cc', 'test-lock.cc', 'test-log.cc', + 'test-log-utils.cc', 'test-mark-compact.cc', 'test-regexp.cc', 'test-serialize.cc', @@ -64,6 +65,7 @@ SOURCES = { 'test-disasm-ia32.cc', 'test-log-ia32.cc' ], + 'arch:x64': ['test-assembler-x64.cc'], 'os:linux': ['test-platform-linux.cc'], 'os:macos': ['test-platform-macos.cc'], 'os:nullos': ['test-platform-nullos.cc'], diff --git a/deps/v8/test/cctest/cctest.status b/deps/v8/test/cctest/cctest.status index 7b43c8de9..a8c218016 100644 --- a/deps/v8/test/cctest/cctest.status +++ b/deps/v8/test/cctest/cctest.status @@ -40,6 +40,15 @@ test-spaces/LargeObjectSpace: PASS || FAIL # BUG(240): Test seems flaky on ARM. test-api/RegExpInterruption: SKIP +# We cannot assume that we can throw OutOfMemory exceptions in all situations. +# Apparently our ARM box is in such a state. Skip the test as it also runs for +# a long time. +test-api/OutOfMemory: SKIP +test-api/OutOfMemoryNested: SKIP + +# BUG(355): Test crashes on ARM. +test-log/ProfLazyMode: SKIP + [ $simulator == arm ] # BUG(271): During exception propagation, we compare pointers into the diff --git a/deps/v8/test/cctest/test-api.cc b/deps/v8/test/cctest/test-api.cc index 4b55b6be0..48157d80b 100644 --- a/deps/v8/test/cctest/test-api.cc +++ b/deps/v8/test/cctest/test-api.cc @@ -30,6 +30,7 @@ #include "v8.h" #include "api.h" +#include "compilation-cache.h" #include "snapshot.h" #include "platform.h" #include "top.h" @@ -464,6 +465,7 @@ THREADED_TEST(ScriptUsingStringResource) { v8::internal::Heap::CollectAllGarbage(); CHECK_EQ(0, TestResource::dispose_count); } + v8::internal::CompilationCache::Clear(); v8::internal::Heap::CollectAllGarbage(); CHECK_EQ(1, TestResource::dispose_count); } @@ -484,6 +486,7 @@ THREADED_TEST(ScriptUsingAsciiStringResource) { v8::internal::Heap::CollectAllGarbage(); CHECK_EQ(0, TestAsciiResource::dispose_count); } + v8::internal::CompilationCache::Clear(); v8::internal::Heap::CollectAllGarbage(); CHECK_EQ(1, TestAsciiResource::dispose_count); } @@ -505,6 +508,7 @@ THREADED_TEST(ScriptMakingExternalString) { v8::internal::Heap::CollectAllGarbage(); CHECK_EQ(0, TestResource::dispose_count); } + v8::internal::CompilationCache::Clear(); v8::internal::Heap::CollectAllGarbage(); CHECK_EQ(1, TestResource::dispose_count); } @@ -527,35 +531,43 @@ THREADED_TEST(ScriptMakingExternalAsciiString) { v8::internal::Heap::CollectAllGarbage(); CHECK_EQ(0, TestAsciiResource::dispose_count); } + v8::internal::CompilationCache::Clear(); v8::internal::Heap::CollectAllGarbage(); CHECK_EQ(1, TestAsciiResource::dispose_count); } THREADED_TEST(UsingExternalString) { - v8::HandleScope scope; - uint16_t* two_byte_string = AsciiToTwoByteString("test string"); - Local<String> string = String::NewExternal(new TestResource(two_byte_string)); - i::Handle<i::String> istring = v8::Utils::OpenHandle(*string); - // Trigger GCs so that the newly allocated string moves to old gen. - i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now - i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now - i::Handle<i::String> isymbol = i::Factory::SymbolFromString(istring); - CHECK(isymbol->IsSymbol()); + { + v8::HandleScope scope; + uint16_t* two_byte_string = AsciiToTwoByteString("test string"); + Local<String> string = + String::NewExternal(new TestResource(two_byte_string)); + i::Handle<i::String> istring = v8::Utils::OpenHandle(*string); + // Trigger GCs so that the newly allocated string moves to old gen. + i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now + i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now + i::Handle<i::String> isymbol = i::Factory::SymbolFromString(istring); + CHECK(isymbol->IsSymbol()); + } + i::Heap::CollectAllGarbage(); } THREADED_TEST(UsingExternalAsciiString) { - v8::HandleScope scope; - const char* one_byte_string = "test string"; - Local<String> string = String::NewExternal( - new TestAsciiResource(i::StrDup(one_byte_string))); - i::Handle<i::String> istring = v8::Utils::OpenHandle(*string); - // Trigger GCs so that the newly allocated string moves to old gen. - i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now - i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now - i::Handle<i::String> isymbol = i::Factory::SymbolFromString(istring); - CHECK(isymbol->IsSymbol()); + { + v8::HandleScope scope; + const char* one_byte_string = "test string"; + Local<String> string = String::NewExternal( + new TestAsciiResource(i::StrDup(one_byte_string))); + i::Handle<i::String> istring = v8::Utils::OpenHandle(*string); + // Trigger GCs so that the newly allocated string moves to old gen. + i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now + i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now + i::Handle<i::String> isymbol = i::Factory::SymbolFromString(istring); + CHECK(isymbol->IsSymbol()); + } + i::Heap::CollectAllGarbage(); } @@ -1327,6 +1339,38 @@ THREADED_TEST(HiddenProperties) { } +static v8::Handle<Value> InterceptorForHiddenProperties( + Local<String> name, const AccessorInfo& info) { + // Make sure objects move. + bool saved_always_compact = i::FLAG_always_compact; + if (!i::FLAG_never_compact) { + i::FLAG_always_compact = true; + } + // The whole goal of this interceptor is to cause a GC during local property + // lookup. + i::Heap::CollectAllGarbage(); + i::FLAG_always_compact = saved_always_compact; + return v8::Handle<Value>(); +} + + +THREADED_TEST(HiddenPropertiesWithInterceptors) { + v8::HandleScope scope; + LocalContext context; + + v8::Local<v8::String> key = v8_str("api-test::hidden-key"); + + // Associate an interceptor with an object and start setting hidden values. + Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); + Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate(); + instance_templ->SetNamedPropertyHandler(InterceptorForHiddenProperties); + Local<v8::Function> function = fun_templ->GetFunction(); + Local<v8::Object> obj = function->NewInstance(); + CHECK(obj->SetHiddenValue(key, v8::Integer::New(2302))); + CHECK_EQ(2302, obj->GetHiddenValue(key)->Int32Value()); +} + + THREADED_TEST(External) { v8::HandleScope scope; int x = 3; @@ -4471,10 +4515,6 @@ THREADED_TEST(EvalAliasedDynamic) { v8::HandleScope scope; LocalContext current; - // This sets 'global' to the real global object (as opposed to the - // proxy). It is highly implementation dependent, so take care. - current->Global()->Set(v8_str("global"), current->Global()->GetPrototype()); - // Tests where aliased eval can only be resolved dynamically. Local<Script> script = Script::Compile(v8_str("function f(x) { " @@ -4483,7 +4523,7 @@ THREADED_TEST(EvalAliasedDynamic) { "}" "foo = 0;" "result1 = f(new Object());" - "result2 = f(global);" + "result2 = f(this);" "var x = new Object();" "x.eval = function(x) { return 1; };" "result3 = f(x);")); @@ -4498,7 +4538,7 @@ THREADED_TEST(EvalAliasedDynamic) { " var bar = 2;" " with (x) { return eval('bar'); }" "}" - "f(global)")); + "f(this)")); script->Run(); CHECK(try_catch.HasCaught()); try_catch.Reset(); @@ -4585,6 +4625,44 @@ THREADED_TEST(CrossEval) { } +// Test that calling eval in a context which has been detached from +// its global throws an exception. This behavior is consistent with +// other JavaScript implementations. +THREADED_TEST(EvalInDetachedGlobal) { + v8::HandleScope scope; + + v8::Persistent<Context> context0 = Context::New(); + v8::Persistent<Context> context1 = Context::New(); + + // Setup function in context0 that uses eval from context0. + context0->Enter(); + v8::Handle<v8::Value> fun = + CompileRun("var x = 42;" + "(function() {" + " var e = eval;" + " return function(s) { return e(s); }" + "})()"); + context0->Exit(); + + // Put the function into context1 and call it before and after + // detaching the global. Before detaching, the call succeeds and + // after detaching and exception is thrown. + context1->Enter(); + context1->Global()->Set(v8_str("fun"), fun); + v8::Handle<v8::Value> x_value = CompileRun("fun('x')"); + CHECK_EQ(42, x_value->Int32Value()); + context0->DetachGlobal(); + v8::TryCatch catcher; + x_value = CompileRun("fun('x')"); + CHECK(x_value.IsEmpty()); + CHECK(catcher.HasCaught()); + context1->Exit(); + + context1.Dispose(); + context0.Dispose(); +} + + THREADED_TEST(CrossLazyLoad) { v8::HandleScope scope; LocalContext other; @@ -4607,6 +4685,12 @@ THREADED_TEST(CrossLazyLoad) { static v8::Handle<Value> call_as_function(const v8::Arguments& args) { ApiTestFuzzer::Fuzz(); + if (args.IsConstructCall()) { + if (args[0]->IsInt32()) { + return v8_num(-args[0]->Int32Value()); + } + } + return args[0]; } @@ -4660,9 +4744,9 @@ THREADED_TEST(CallAsFunction) { // Check that the call-as-function handler can be called through // new. Currently, there is no way to check in the call-as-function // handler if it has been called through new or not. - value = CompileRun("new obj(42)"); + value = CompileRun("new obj(43)"); CHECK(!try_catch.HasCaught()); - CHECK_EQ(42, value->Int32Value()); + CHECK_EQ(-43, value->Int32Value()); } @@ -4777,6 +4861,23 @@ THREADED_TEST(InterceptorHasOwnPropertyCausingGC) { } +typedef v8::Handle<Value> (*NamedPropertyGetter)(Local<String> property, + const AccessorInfo& info); + + +static void CheckInterceptorLoadIC(NamedPropertyGetter getter, + const char* source, + int expected) { + v8::HandleScope scope; + v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); + templ->SetNamedPropertyHandler(getter); + LocalContext context; + context->Global()->Set(v8_str("o"), templ->NewInstance()); + v8::Handle<Value> value = CompileRun(source); + CHECK_EQ(expected, value->Int32Value()); +} + + static v8::Handle<Value> InterceptorLoadICGetter(Local<String> name, const AccessorInfo& info) { ApiTestFuzzer::Fuzz(); @@ -4787,17 +4888,100 @@ static v8::Handle<Value> InterceptorLoadICGetter(Local<String> name, // This test should hit the load IC for the interceptor case. THREADED_TEST(InterceptorLoadIC) { - v8::HandleScope scope; - v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); - templ->SetNamedPropertyHandler(InterceptorLoadICGetter); - LocalContext context; - context->Global()->Set(v8_str("o"), templ->NewInstance()); - v8::Handle<Value> value = CompileRun( + CheckInterceptorLoadIC(InterceptorLoadICGetter, "var result = 0;" "for (var i = 0; i < 1000; i++) {" " result = o.x;" - "}"); - CHECK_EQ(42, value->Int32Value()); + "}", + 42); +} + + +// Below go several tests which verify that JITing for various +// configurations of interceptor and explicit fields works fine +// (those cases are special cased to get better performance). + +static v8::Handle<Value> InterceptorLoadXICGetter(Local<String> name, + const AccessorInfo& info) { + ApiTestFuzzer::Fuzz(); + return v8_str("x")->Equals(name) + ? v8::Integer::New(42) : v8::Handle<v8::Value>(); +} + + +THREADED_TEST(InterceptorLoadICWithFieldOnHolder) { + CheckInterceptorLoadIC(InterceptorLoadXICGetter, + "var result = 0;" + "o.y = 239;" + "for (var i = 0; i < 1000; i++) {" + " result = o.y;" + "}", + 239); +} + + +THREADED_TEST(InterceptorLoadICWithSubstitutedProto) { + CheckInterceptorLoadIC(InterceptorLoadXICGetter, + "var result = 0;" + "o.__proto__ = { 'y': 239 };" + "for (var i = 0; i < 1000; i++) {" + " result = o.y + o.x;" + "}", + 239 + 42); +} + + +THREADED_TEST(InterceptorLoadICWithPropertyOnProto) { + CheckInterceptorLoadIC(InterceptorLoadXICGetter, + "var result = 0;" + "o.__proto__.y = 239;" + "for (var i = 0; i < 1000; i++) {" + " result = o.y + o.x;" + "}", + 239 + 42); +} + + +THREADED_TEST(InterceptorLoadICUndefined) { + CheckInterceptorLoadIC(InterceptorLoadXICGetter, + "var result = 0;" + "for (var i = 0; i < 1000; i++) {" + " result = (o.y == undefined) ? 239 : 42;" + "}", + 239); +} + + +THREADED_TEST(InterceptorLoadICWithOverride) { + CheckInterceptorLoadIC(InterceptorLoadXICGetter, + "fst = new Object(); fst.__proto__ = o;" + "snd = new Object(); snd.__proto__ = fst;" + "var result1 = 0;" + "for (var i = 0; i < 1000; i++) {" + " result1 = snd.x;" + "}" + "fst.x = 239;" + "var result = 0;" + "for (var i = 0; i < 1000; i++) {" + " result = snd.x;" + "}" + "result + result1", + 239 + 42); +} + + +static v8::Handle<Value> InterceptorLoadICGetter0(Local<String> name, + const AccessorInfo& info) { + ApiTestFuzzer::Fuzz(); + CHECK(v8_str("x")->Equals(name)); + return v8::Integer::New(0); +} + + +THREADED_TEST(InterceptorReturningZero) { + CheckInterceptorLoadIC(InterceptorLoadICGetter0, + "o.x == undefined ? 1 : 0", + 0); } @@ -6571,3 +6755,135 @@ TEST(ForceSetWithInterceptor) { CHECK_EQ(1, force_set_set_count); CHECK_EQ(6, force_set_get_count); } + + +THREADED_TEST(ForceDelete) { + v8::HandleScope scope; + v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(); + LocalContext context(NULL, templ); + v8::Handle<v8::Object> global = context->Global(); + + // Ordinary properties + v8::Handle<v8::String> simple_property = v8::String::New("p"); + global->Set(simple_property, v8::Int32::New(4), v8::DontDelete); + CHECK_EQ(4, global->Get(simple_property)->Int32Value()); + // This should fail because the property is dont-delete. + CHECK(!global->Delete(simple_property)); + CHECK_EQ(4, global->Get(simple_property)->Int32Value()); + // This should succeed even though the property is dont-delete. + CHECK(global->ForceDelete(simple_property)); + CHECK(global->Get(simple_property)->IsUndefined()); +} + + +static int force_delete_interceptor_count = 0; +static bool pass_on_delete = false; + + +static v8::Handle<v8::Boolean> ForceDeleteDeleter( + v8::Local<v8::String> name, + const v8::AccessorInfo& info) { + force_delete_interceptor_count++; + if (pass_on_delete) { + return v8::Handle<v8::Boolean>(); + } else { + return v8::True(); + } +} + + +THREADED_TEST(ForceDeleteWithInterceptor) { + force_delete_interceptor_count = 0; + pass_on_delete = false; + + v8::HandleScope scope; + v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(); + templ->SetNamedPropertyHandler(0, 0, 0, ForceDeleteDeleter); + LocalContext context(NULL, templ); + v8::Handle<v8::Object> global = context->Global(); + + v8::Handle<v8::String> some_property = v8::String::New("a"); + global->Set(some_property, v8::Integer::New(42), v8::DontDelete); + + // Deleting a property should get intercepted and nothing should + // happen. + CHECK_EQ(0, force_delete_interceptor_count); + CHECK(global->Delete(some_property)); + CHECK_EQ(1, force_delete_interceptor_count); + CHECK_EQ(42, global->Get(some_property)->Int32Value()); + // Deleting the property when the interceptor returns an empty + // handle should not delete the property since it is DontDelete. + pass_on_delete = true; + CHECK(!global->Delete(some_property)); + CHECK_EQ(2, force_delete_interceptor_count); + CHECK_EQ(42, global->Get(some_property)->Int32Value()); + // Forcing the property to be deleted should delete the value + // without calling the interceptor. + CHECK(global->ForceDelete(some_property)); + CHECK(global->Get(some_property)->IsUndefined()); + CHECK_EQ(2, force_delete_interceptor_count); +} + + +v8::Persistent<Context> calling_context0; +v8::Persistent<Context> calling_context1; +v8::Persistent<Context> calling_context2; + + +// Check that the call to the callback is initiated in +// calling_context2, the directly calling context is calling_context1 +// and the callback itself is in calling_context0. +static v8::Handle<Value> GetCallingContextCallback(const v8::Arguments& args) { + ApiTestFuzzer::Fuzz(); + CHECK(Context::GetCurrent() == calling_context0); + CHECK(Context::GetCalling() == calling_context1); + CHECK(Context::GetEntered() == calling_context2); + return v8::Integer::New(42); +} + + +THREADED_TEST(GetCallingContext) { + v8::HandleScope scope; + + calling_context0 = Context::New(); + calling_context1 = Context::New(); + calling_context2 = Context::New(); + + // Allow cross-domain access. + Local<String> token = v8_str("<security token>"); + calling_context0->SetSecurityToken(token); + calling_context1->SetSecurityToken(token); + calling_context2->SetSecurityToken(token); + + // Create an object with a C++ callback in context0. + calling_context0->Enter(); + Local<v8::FunctionTemplate> callback_templ = + v8::FunctionTemplate::New(GetCallingContextCallback); + calling_context0->Global()->Set(v8_str("callback"), + callback_templ->GetFunction()); + calling_context0->Exit(); + + // Expose context0 in context1 and setup a function that calls the + // callback function. + calling_context1->Enter(); + calling_context1->Global()->Set(v8_str("context0"), + calling_context0->Global()); + CompileRun("function f() { context0.callback() }"); + calling_context1->Exit(); + + // Expose context1 in context2 and call the callback function in + // context0 indirectly through f in context1. + calling_context2->Enter(); + calling_context2->Global()->Set(v8_str("context1"), + calling_context1->Global()); + CompileRun("context1.f()"); + calling_context2->Exit(); + + // Dispose the contexts to allow them to be garbage collected. + calling_context0.Dispose(); + calling_context1.Dispose(); + calling_context2.Dispose(); + calling_context0.Clear(); + calling_context1.Clear(); + calling_context2.Clear(); +} diff --git a/deps/v8/test/cctest/test-assembler-x64.cc b/deps/v8/test/cctest/test-assembler-x64.cc new file mode 100644 index 000000000..43ba4e913 --- /dev/null +++ b/deps/v8/test/cctest/test-assembler-x64.cc @@ -0,0 +1,251 @@ +// Copyright 2009 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <stdlib.h> + +#include "v8.h" + +#include "macro-assembler.h" +#include "factory.h" +#include "platform.h" +#include "serialize.h" +#include "cctest.h" + +using v8::internal::byte; +using v8::internal::OS; +using v8::internal::Assembler; +using v8::internal::Operand; +using v8::internal::Immediate; +using v8::internal::Label; +using v8::internal::rax; +using v8::internal::rsi; +using v8::internal::rdi; +using v8::internal::rbp; +using v8::internal::rsp; +using v8::internal::FUNCTION_CAST; +using v8::internal::CodeDesc; +using v8::internal::less_equal; +using v8::internal::not_equal; +using v8::internal::greater; + + +// Test the x64 assembler by compiling some simple functions into +// a buffer and executing them. These tests do not initialize the +// V8 library, create a context, or use any V8 objects. +// The AMD64 calling convention is used, with the first five arguments +// in RSI, RDI, RDX, RCX, R8, and R9, and floating point arguments in +// the XMM registers. The return value is in RAX. +// This calling convention is used on Linux, with GCC, and on Mac OS, +// with GCC. A different convention is used on 64-bit windows. + +typedef int (*F0)(); +typedef int (*F1)(int x); +typedef int (*F2)(int x, int y); + +#define __ assm. + + +TEST(AssemblerX64ReturnOperation) { + // Allocate an executable page of memory. + size_t actual_size; + byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize, + &actual_size, + true)); + CHECK(buffer); + Assembler assm(buffer, actual_size); + + // Assemble a simple function that copies argument 2 and returns it. + __ movq(rax, rsi); + __ nop(); + __ ret(0); + + CodeDesc desc; + assm.GetCode(&desc); + // Call the function from C++. + int result = FUNCTION_CAST<F2>(buffer)(3, 2); + CHECK_EQ(2, result); +} + +TEST(AssemblerX64StackOperations) { + // Allocate an executable page of memory. + size_t actual_size; + byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize, + &actual_size, + true)); + CHECK(buffer); + Assembler assm(buffer, actual_size); + + // Assemble a simple function that copies argument 2 and returns it. + // We compile without stack frame pointers, so the gdb debugger shows + // incorrect stack frames when debugging this function (which has them). + __ push(rbp); + __ movq(rbp, rsp); + __ push(rsi); // Value at (rbp - 8) + __ push(rsi); // Value at (rbp - 16) + __ push(rdi); // Value at (rbp - 24) + __ pop(rax); + __ pop(rax); + __ pop(rax); + __ pop(rbp); + __ nop(); + __ ret(0); + + CodeDesc desc; + assm.GetCode(&desc); + // Call the function from C++. + int result = FUNCTION_CAST<F2>(buffer)(3, 2); + CHECK_EQ(2, result); +} + +TEST(AssemblerX64ArithmeticOperations) { + // Allocate an executable page of memory. + size_t actual_size; + byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize, + &actual_size, + true)); + CHECK(buffer); + Assembler assm(buffer, actual_size); + + // Assemble a simple function that copies argument 2 and returns it. + __ movq(rax, rsi); + __ add(rax, rdi); + __ ret(0); + + CodeDesc desc; + assm.GetCode(&desc); + // Call the function from C++. + int result = FUNCTION_CAST<F2>(buffer)(3, 2); + CHECK_EQ(5, result); +} + +TEST(AssemblerX64MemoryOperands) { + // Allocate an executable page of memory. + size_t actual_size; + byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize, + &actual_size, + true)); + CHECK(buffer); + Assembler assm(buffer, actual_size); + + // Assemble a simple function that copies argument 2 and returns it. + __ push(rbp); + __ movq(rbp, rsp); + __ push(rsi); // Value at (rbp - 8) + __ push(rsi); // Value at (rbp - 16) + __ push(rdi); // Value at (rbp - 24) + const int kStackElementSize = 8; + __ movq(rax, Operand(rbp, -3 * kStackElementSize)); + __ pop(rsi); + __ pop(rsi); + __ pop(rsi); + __ pop(rbp); + __ nop(); + __ ret(0); + + CodeDesc desc; + assm.GetCode(&desc); + // Call the function from C++. + int result = FUNCTION_CAST<F2>(buffer)(3, 2); + CHECK_EQ(3, result); +} + +TEST(AssemblerX64ControlFlow) { + // Allocate an executable page of memory. + size_t actual_size; + byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize, + &actual_size, + true)); + CHECK(buffer); + Assembler assm(buffer, actual_size); + + // Assemble a simple function that copies argument 2 and returns it. + __ push(rbp); + __ movq(rbp, rsp); + __ movq(rax, rdi); + Label target; + __ jmp(&target); + __ movq(rax, rsi); + __ bind(&target); + __ pop(rbp); + __ ret(0); + + CodeDesc desc; + assm.GetCode(&desc); + // Call the function from C++. + int result = FUNCTION_CAST<F2>(buffer)(3, 2); + CHECK_EQ(3, result); +} + +TEST(AssemblerX64LoopImmediates) { + // Allocate an executable page of memory. + size_t actual_size; + byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize, + &actual_size, + true)); + CHECK(buffer); + Assembler assm(buffer, actual_size); + // Assemble two loops using rax as counter, and verify the ending counts. + Label Fail; + __ movq(rax, Immediate(-3)); + Label Loop1_test; + Label Loop1_body; + __ jmp(&Loop1_test); + __ bind(&Loop1_body); + __ add(rax, Immediate(7)); + __ bind(&Loop1_test); + __ cmp(rax, Immediate(20)); + __ j(less_equal, &Loop1_body); + // Did the loop terminate with the expected value? + __ cmp(rax, Immediate(25)); + __ j(not_equal, &Fail); + + Label Loop2_test; + Label Loop2_body; + __ movq(rax, Immediate(0x11FEED00)); + __ jmp(&Loop2_test); + __ bind(&Loop2_body); + __ add(rax, Immediate(-0x1100)); + __ bind(&Loop2_test); + __ cmp(rax, Immediate(0x11FE8000)); + __ j(greater, &Loop2_body); + // Did the loop terminate with the expected value? + __ cmp(rax, Immediate(0x11FE7600)); + __ j(not_equal, &Fail); + + __ movq(rax, Immediate(1)); + __ ret(0); + __ bind(&Fail); + __ movq(rax, Immediate(0)); + __ ret(0); + + CodeDesc desc; + assm.GetCode(&desc); + // Call the function from C++. + int result = FUNCTION_CAST<F0>(buffer)(); + CHECK_EQ(1, result); +} +#undef __ diff --git a/deps/v8/test/cctest/test-debug.cc b/deps/v8/test/cctest/test-debug.cc index 288efbaed..92f48e1cf 100644 --- a/deps/v8/test/cctest/test-debug.cc +++ b/deps/v8/test/cctest/test-debug.cc @@ -30,6 +30,7 @@ #include "v8.h" #include "api.h" +#include "compilation-cache.h" #include "debug.h" #include "platform.h" #include "stub-cache.h" @@ -370,7 +371,8 @@ static void PrepareStep(StepAction step_action) { // This function is in namespace v8::internal to be friend with class // v8::internal::Debug. -namespace v8 { namespace internal { // NOLINT +namespace v8 { +namespace internal { // Collect the currently debugged functions. Handle<FixedArray> GetDebuggedFunctions() { @@ -3311,6 +3313,82 @@ TEST(HiddenPrototypePropertyMirror) { } +static v8::Handle<v8::Value> ProtperyXNativeGetter( + v8::Local<v8::String> property, const v8::AccessorInfo& info) { + return v8::Integer::New(10); +} + + +TEST(NativeGetterPropertyMirror) { + // Create a V8 environment with debug access. + v8::HandleScope scope; + DebugLocalContext env; + env.ExposeDebug(); + + v8::Handle<v8::String> name = v8::String::New("x"); + // Create object with named accessor. + v8::Handle<v8::ObjectTemplate> named = v8::ObjectTemplate::New(); + named->SetAccessor(name, &ProtperyXNativeGetter, NULL, + v8::Handle<v8::Value>(), v8::DEFAULT, v8::None); + + // Create object with named property getter. + env->Global()->Set(v8::String::New("instance"), named->NewInstance()); + CHECK_EQ(10, CompileRun("instance.x")->Int32Value()); + + // Get mirror for the object with property getter. + CompileRun("instance_mirror = debug.MakeMirror(instance);"); + CHECK(CompileRun( + "instance_mirror instanceof debug.ObjectMirror")->BooleanValue()); + + CompileRun("named_names = instance_mirror.propertyNames();"); + CHECK_EQ(1, CompileRun("named_names.length")->Int32Value()); + CHECK(CompileRun("named_names[0] == 'x'")->BooleanValue()); + CHECK(CompileRun( + "instance_mirror.property('x').value().isNumber()")->BooleanValue()); + CHECK(CompileRun( + "instance_mirror.property('x').value().value() == 10")->BooleanValue()); +} + + +static v8::Handle<v8::Value> ProtperyXNativeGetterThrowingError( + v8::Local<v8::String> property, const v8::AccessorInfo& info) { + return CompileRun("throw new Error('Error message');"); +} + + +TEST(NativeGetterThrowingErrorPropertyMirror) { + // Create a V8 environment with debug access. + v8::HandleScope scope; + DebugLocalContext env; + env.ExposeDebug(); + + v8::Handle<v8::String> name = v8::String::New("x"); + // Create object with named accessor. + v8::Handle<v8::ObjectTemplate> named = v8::ObjectTemplate::New(); + named->SetAccessor(name, &ProtperyXNativeGetterThrowingError, NULL, + v8::Handle<v8::Value>(), v8::DEFAULT, v8::None); + + // Create object with named property getter. + env->Global()->Set(v8::String::New("instance"), named->NewInstance()); + + // Get mirror for the object with property getter. + CompileRun("instance_mirror = debug.MakeMirror(instance);"); + CHECK(CompileRun( + "instance_mirror instanceof debug.ObjectMirror")->BooleanValue()); + CompileRun("named_names = instance_mirror.propertyNames();"); + CHECK_EQ(1, CompileRun("named_names.length")->Int32Value()); + CHECK(CompileRun("named_names[0] == 'x'")->BooleanValue()); + CHECK(CompileRun( + "instance_mirror.property('x').value().isError()")->BooleanValue()); + + // Check that the message is that passed to the Error constructor. + CHECK(CompileRun( + "instance_mirror.property('x').value().message() == 'Error message'")-> + BooleanValue()); +} + + + // Multithreaded tests of JSON debugger protocol // Support classes @@ -4166,11 +4244,8 @@ TEST(DebuggerUnload) { } -// Debugger message handler which counts the number of times it is called. -static int message_handler_hit_count = 0; -static void MessageHandlerHitCount(const v8::Debug::Message& message) { - message_handler_hit_count++; - +// Sends continue command to the debugger. +static void SendContinueCommand() { const int kBufferSize = 1000; uint16_t buffer[kBufferSize]; const char* command_continue = @@ -4182,6 +4257,15 @@ static void MessageHandlerHitCount(const v8::Debug::Message& message) { } +// Debugger message handler which counts the number of times it is called. +static int message_handler_hit_count = 0; +static void MessageHandlerHitCount(const v8::Debug::Message& message) { + message_handler_hit_count++; + + SendContinueCommand(); +} + + // Test clearing the debug message handler. TEST(DebuggerClearMessageHandler) { v8::HandleScope scope; @@ -4525,6 +4609,8 @@ class EmptyExternalStringResource : public v8::String::ExternalStringResource { TEST(DebugGetLoadedScripts) { v8::HandleScope scope; DebugLocalContext env; + env.ExposeDebug(); + EmptyExternalStringResource source_ext_str; v8::Local<v8::String> source = v8::String::NewExternal(&source_ext_str); v8::Handle<v8::Script> evil_script = v8::Script::Compile(source); @@ -4538,11 +4624,15 @@ TEST(DebugGetLoadedScripts) { i::FLAG_allow_natives_syntax = true; CompileRun( "var scripts = %DebugGetLoadedScripts();" - "for (var i = 0; i < scripts.length; ++i) {" - " scripts[i].line_ends;" + "var count = scripts.length;" + "for (var i = 0; i < count; ++i) {" + " scripts[i].line_ends;" "}"); // Must not crash while accessing line_ends. i::FLAG_allow_natives_syntax = allow_natives_syntax; + + // Some scripts are retrieved - at least the number of native scripts. + CHECK_GT((*env)->Global()->Get(v8::String::New("count"))->Int32Value(), 8); } @@ -4574,7 +4664,6 @@ TEST(ScriptNameAndData) { v8::Handle<v8::Script> script1 = v8::Script::Compile(script, &origin1); script1->SetData(v8::String::New("data")); script1->Run(); - v8::Script::Compile(script, &origin1)->Run(); v8::Local<v8::Function> f; f = v8::Local<v8::Function>::Cast(env->Global()->Get(v8::String::New("f"))); @@ -4583,6 +4672,15 @@ TEST(ScriptNameAndData) { CHECK_EQ("name", last_script_name_hit); CHECK_EQ("data", last_script_data_hit); + // Compile the same script again without setting data. As the compilation + // cache is disabled when debugging expect the data to be missing. + v8::Script::Compile(script, &origin1)->Run(); + f = v8::Local<v8::Function>::Cast(env->Global()->Get(v8::String::New("f"))); + f->Call(env->Global(), 0, NULL); + CHECK_EQ(2, break_point_hit_count); + CHECK_EQ("name", last_script_name_hit); + CHECK_EQ("", last_script_data_hit); // Undefined results in empty string. + v8::Local<v8::String> data_obj_source = v8::String::New( "({ a: 'abc',\n" " b: 123,\n" @@ -4595,7 +4693,7 @@ TEST(ScriptNameAndData) { script2->SetData(data_obj); f = v8::Local<v8::Function>::Cast(env->Global()->Get(v8::String::New("f"))); f->Call(env->Global(), 0, NULL); - CHECK_EQ(2, break_point_hit_count); + CHECK_EQ(3, break_point_hit_count); CHECK_EQ("new name", last_script_name_hit); CHECK_EQ("abc 123", last_script_data_hit); } @@ -4612,16 +4710,9 @@ static void ContextCheckMessageHandler(const v8::Debug::Message& message) { expected_context_data)); message_handler_hit_count++; - const int kBufferSize = 1000; - uint16_t buffer[kBufferSize]; - const char* command_continue = - "{\"seq\":0," - "\"type\":\"request\"," - "\"command\":\"continue\"}"; - // Send a continue command for break events. if (message.GetEvent() == v8::Break) { - v8::Debug::SendCommand(buffer, AsciiToUtf16(command_continue, buffer)); + SendContinueCommand(); } } @@ -4679,4 +4770,418 @@ TEST(ContextData) { // Two times compile event and two times break event. CHECK_GT(message_handler_hit_count, 4); + + v8::Debug::SetMessageHandler2(NULL); + CheckDebuggerUnloaded(); +} + + +// Debug message handler which issues a debug break when it hits a break event. +static int message_handler_break_hit_count = 0; +static void DebugBreakMessageHandler(const v8::Debug::Message& message) { + // Schedule a debug break for break events. + if (message.IsEvent() && message.GetEvent() == v8::Break) { + message_handler_break_hit_count++; + if (message_handler_break_hit_count == 1) { + v8::Debug::DebugBreak(); + } + } + + // Issue a continue command if this event will not cause the VM to start + // running. + if (!message.WillStartRunning()) { + SendContinueCommand(); + } +} + + +// Test that a debug break can be scheduled while in a message handler. +TEST(DebugBreakInMessageHandler) { + v8::HandleScope scope; + DebugLocalContext env; + + v8::Debug::SetMessageHandler2(DebugBreakMessageHandler); + + // Test functions. + const char* script = "function f() { debugger; } function g() { }"; + CompileRun(script); + v8::Local<v8::Function> f = + v8::Local<v8::Function>::Cast(env->Global()->Get(v8::String::New("f"))); + v8::Local<v8::Function> g = + v8::Local<v8::Function>::Cast(env->Global()->Get(v8::String::New("g"))); + + // Call f then g. The debugger statement in f will casue a break which will + // cause another break. + f->Call(env->Global(), 0, NULL); + CHECK_EQ(2, message_handler_break_hit_count); + // Calling g will not cause any additional breaks. + g->Call(env->Global(), 0, NULL); + CHECK_EQ(2, message_handler_break_hit_count); +} + + +// Debug event handler which gets the function on the top frame and schedules a +// break a number of times. +static void DebugEventDebugBreak( + v8::DebugEvent event, + v8::Handle<v8::Object> exec_state, + v8::Handle<v8::Object> event_data, + v8::Handle<v8::Value> data) { + + if (event == v8::Break) { + break_point_hit_count++; + + // Get the name of the top frame function. + if (!frame_function_name.IsEmpty()) { + // Get the name of the function. + const int argc = 1; + v8::Handle<v8::Value> argv[argc] = { exec_state }; + v8::Handle<v8::Value> result = frame_function_name->Call(exec_state, + argc, argv); + if (result->IsUndefined()) { + last_function_hit[0] = '\0'; + } else { + CHECK(result->IsString()); + v8::Handle<v8::String> function_name(result->ToString()); + function_name->WriteAscii(last_function_hit); + } + } + + // Keep forcing breaks. + if (break_point_hit_count < 20) { + v8::Debug::DebugBreak(); + } + } +} + + +TEST(RegExpDebugBreak) { + v8::HandleScope scope; + DebugLocalContext env; + + i::FLAG_regexp_native = true; + + // Create a function for checking the function when hitting a break point. + frame_function_name = CompileFunction(&env, + frame_function_name_source, + "frame_function_name"); + + // Test RegExp which matches white spaces and comments at the begining of a + // source line. + const char* script = + "var sourceLineBeginningSkip = /^(?:[ \\v\\h]*(?:\\/\\*.*?\\*\\/)*)*/;\n" + "function f(s) { return s.match(sourceLineBeginningSkip)[0].length; }"; + + v8::Local<v8::Function> f = CompileFunction(script, "f"); + const int argc = 1; + v8::Handle<v8::Value> argv[argc] = { v8::String::New(" /* xxx */ a=0;") }; + v8::Local<v8::Value> result = f->Call(env->Global(), argc, argv); + CHECK_EQ(12, result->Int32Value()); + + v8::Debug::SetDebugEventListener(DebugEventDebugBreak); + v8::Debug::DebugBreak(); + result = f->Call(env->Global(), argc, argv); + + CHECK_EQ(20, break_point_hit_count); + CHECK_EQ("exec", last_function_hit); +} + + +// Common part of EvalContextData and NestedBreakEventContextData tests. +static void ExecuteScriptForContextCheck() { + // Create a context. + v8::Persistent<v8::Context> context_1; + v8::Handle<v8::ObjectTemplate> global_template = + v8::Handle<v8::ObjectTemplate>(); + v8::Handle<v8::Value> global_object = v8::Handle<v8::Value>(); + context_1 = v8::Context::New(NULL, global_template, global_object); + + // Default data value is undefined. + CHECK(context_1->GetData()->IsUndefined()); + + // Set and check a data value. + v8::Handle<v8::Value> data_1 = v8::Number::New(1); + context_1->SetData(data_1); + CHECK(context_1->GetData()->StrictEquals(data_1)); + + // Simple test function with eval that causes a break. + const char* source = "function f() { eval('debugger;'); }"; + + // Enter and run function in the context. + { + v8::Context::Scope context_scope(context_1); + expected_context = context_1; + expected_context_data = data_1; + v8::Local<v8::Function> f = CompileFunction(source, "f"); + f->Call(context_1->Global(), 0, NULL); + } +} + + +// Test which creates a context and sets embedder data on it. Checks that this +// data is set correctly and that when the debug message handler is called for +// break event in an eval statement the expected context is the one returned by +// Message.GetEventContext. +TEST(EvalContextData) { + v8::HandleScope scope; + v8::Debug::SetMessageHandler2(ContextCheckMessageHandler); + + ExecuteScriptForContextCheck(); + + // One time compile event and one time break event. + CHECK_GT(message_handler_hit_count, 2); + v8::Debug::SetMessageHandler2(NULL); + CheckDebuggerUnloaded(); +} + + +static bool sent_eval = false; +static int break_count = 0; +static int continue_command_send_count = 0; +// Check that the expected context is the one generating the debug event +// including the case of nested break event. +static void DebugEvalContextCheckMessageHandler( + const v8::Debug::Message& message) { + CHECK(message.GetEventContext() == expected_context); + CHECK(message.GetEventContext()->GetData()->StrictEquals( + expected_context_data)); + message_handler_hit_count++; + + if (message.IsEvent() && message.GetEvent() == v8::Break) { + break_count++; + if (!sent_eval) { + sent_eval = true; + + const int kBufferSize = 1000; + uint16_t buffer[kBufferSize]; + const char* eval_command = + "{\"seq\":0," + "\"type\":\"request\"," + "\"command\":\"evaluate\"," + "arguments:{\"expression\":\"debugger;\"," + "\"global\":true,\"disable_break\":false}}"; + + // Send evaluate command. + v8::Debug::SendCommand(buffer, AsciiToUtf16(eval_command, buffer)); + return; + } else { + // It's a break event caused by the evaluation request above. + SendContinueCommand(); + continue_command_send_count++; + } + } else if (message.IsResponse() && continue_command_send_count < 2) { + // Response to the evaluation request. We're still on the breakpoint so + // send continue. + SendContinueCommand(); + continue_command_send_count++; + } +} + + +// Tests that context returned for break event is correct when the event occurs +// in 'evaluate' debugger request. +TEST(NestedBreakEventContextData) { + v8::HandleScope scope; + break_count = 0; + message_handler_hit_count = 0; + v8::Debug::SetMessageHandler2(DebugEvalContextCheckMessageHandler); + + ExecuteScriptForContextCheck(); + + // One time compile event and two times break event. + CHECK_GT(message_handler_hit_count, 3); + + // One break from the source and another from the evaluate request. + CHECK_EQ(break_count, 2); + v8::Debug::SetMessageHandler2(NULL); + CheckDebuggerUnloaded(); +} + + +// Debug event listener which counts the script collected events. +int script_collected_count = 0; +static void DebugEventScriptCollectedEvent(v8::DebugEvent event, + v8::Handle<v8::Object> exec_state, + v8::Handle<v8::Object> event_data, + v8::Handle<v8::Value> data) { + // Count the number of breaks. + if (event == v8::ScriptCollected) { + script_collected_count++; + } +} + + +// Test that scripts collected are reported through the debug event listener. +TEST(ScriptCollectedEvent) { + break_point_hit_count = 0; + script_collected_count = 0; + v8::HandleScope scope; + DebugLocalContext env; + + // Request the loaded scripts to initialize the debugger script cache. + Debug::GetLoadedScripts(); + + // Do garbage collection to ensure that only the script in this test will be + // collected afterwards. + Heap::CollectAllGarbage(); + + script_collected_count = 0; + v8::Debug::SetDebugEventListener(DebugEventScriptCollectedEvent, + v8::Undefined()); + { + v8::Script::Compile(v8::String::New("eval('a=1')"))->Run(); + v8::Script::Compile(v8::String::New("eval('a=2')"))->Run(); + } + + // Do garbage collection to collect the script above which is no longer + // referenced. + Heap::CollectAllGarbage(); + + CHECK_EQ(2, script_collected_count); + + v8::Debug::SetDebugEventListener(NULL); + CheckDebuggerUnloaded(); +} + + +// Debug event listener which counts the script collected events. +int script_collected_message_count = 0; +static void ScriptCollectedMessageHandler(const v8::Debug::Message& message) { + // Count the number of scripts collected. + if (message.IsEvent() && message.GetEvent() == v8::ScriptCollected) { + script_collected_message_count++; + v8::Handle<v8::Context> context = message.GetEventContext(); + CHECK(context.IsEmpty()); + } +} + + +// Test that GetEventContext doesn't fail and return empty handle for +// ScriptCollected events. +TEST(ScriptCollectedEventContext) { + script_collected_message_count = 0; + v8::HandleScope scope; + + { // Scope for the DebugLocalContext. + DebugLocalContext env; + + // Request the loaded scripts to initialize the debugger script cache. + Debug::GetLoadedScripts(); + + // Do garbage collection to ensure that only the script in this test will be + // collected afterwards. + Heap::CollectAllGarbage(); + + v8::Debug::SetMessageHandler2(ScriptCollectedMessageHandler); + { + v8::Script::Compile(v8::String::New("eval('a=1')"))->Run(); + v8::Script::Compile(v8::String::New("eval('a=2')"))->Run(); + } + } + + // Do garbage collection to collect the script above which is no longer + // referenced. + Heap::CollectAllGarbage(); + + CHECK_EQ(2, script_collected_message_count); + + v8::Debug::SetMessageHandler2(NULL); +} + + +// Debug event listener which counts the after compile events. +int after_compile_message_count = 0; +static void AfterCompileMessageHandler(const v8::Debug::Message& message) { + // Count the number of scripts collected. + if (message.IsEvent()) { + if (message.GetEvent() == v8::AfterCompile) { + after_compile_message_count++; + } else if (message.GetEvent() == v8::Break) { + SendContinueCommand(); + } + } +} + + +// Tests that after compile event is sent as many times as there are scripts +// compiled. +TEST(AfterCompileMessageWhenMessageHandlerIsReset) { + v8::HandleScope scope; + DebugLocalContext env; + after_compile_message_count = 0; + const char* script = "var a=1"; + + v8::Debug::SetMessageHandler2(AfterCompileMessageHandler); + v8::Script::Compile(v8::String::New(script))->Run(); + v8::Debug::SetMessageHandler2(NULL); + + v8::Debug::SetMessageHandler2(AfterCompileMessageHandler); + v8::Debug::DebugBreak(); + v8::Script::Compile(v8::String::New(script))->Run(); + + // Setting listener to NULL should cause debugger unload. + v8::Debug::SetMessageHandler2(NULL); + CheckDebuggerUnloaded(); + + // Compilation cache should be disabled when debugger is active. + CHECK_EQ(2, after_compile_message_count); +} + + +// Tests that break event is sent when message handler is reset. +TEST(BreakMessageWhenMessageHandlerIsReset) { + v8::HandleScope scope; + DebugLocalContext env; + after_compile_message_count = 0; + const char* script = "function f() {};"; + + v8::Debug::SetMessageHandler2(AfterCompileMessageHandler); + v8::Script::Compile(v8::String::New(script))->Run(); + v8::Debug::SetMessageHandler2(NULL); + + v8::Debug::SetMessageHandler2(AfterCompileMessageHandler); + v8::Debug::DebugBreak(); + v8::Local<v8::Function> f = + v8::Local<v8::Function>::Cast(env->Global()->Get(v8::String::New("f"))); + f->Call(env->Global(), 0, NULL); + + // Setting message handler to NULL should cause debugger unload. + v8::Debug::SetMessageHandler2(NULL); + CheckDebuggerUnloaded(); + + // Compilation cache should be disabled when debugger is active. + CHECK_EQ(1, after_compile_message_count); +} + + +static int exception_event_count = 0; +static void ExceptionMessageHandler(const v8::Debug::Message& message) { + if (message.IsEvent() && message.GetEvent() == v8::Exception) { + exception_event_count++; + SendContinueCommand(); + } +} + + +// Tests that exception event is sent when message handler is reset. +TEST(ExceptionMessageWhenMessageHandlerIsReset) { + v8::HandleScope scope; + DebugLocalContext env; + exception_event_count = 0; + const char* script = "function f() {throw new Error()};"; + + v8::Debug::SetMessageHandler2(AfterCompileMessageHandler); + v8::Script::Compile(v8::String::New(script))->Run(); + v8::Debug::SetMessageHandler2(NULL); + + v8::Debug::SetMessageHandler2(ExceptionMessageHandler); + v8::Local<v8::Function> f = + v8::Local<v8::Function>::Cast(env->Global()->Get(v8::String::New("f"))); + f->Call(env->Global(), 0, NULL); + + // Setting message handler to NULL should cause debugger unload. + v8::Debug::SetMessageHandler2(NULL); + CheckDebuggerUnloaded(); + + CHECK_EQ(1, exception_event_count); } diff --git a/deps/v8/test/cctest/test-func-name-inference.cc b/deps/v8/test/cctest/test-func-name-inference.cc index d91f75fa0..1bfc8834a 100644 --- a/deps/v8/test/cctest/test-func-name-inference.cc +++ b/deps/v8/test/cctest/test-func-name-inference.cc @@ -32,15 +32,18 @@ #include "cctest.h" +using ::v8::internal::CStrVector; +using ::v8::internal::Factory; using ::v8::internal::Handle; +using ::v8::internal::Heap; using ::v8::internal::JSFunction; using ::v8::internal::Object; +using ::v8::internal::Runtime; using ::v8::internal::Script; +using ::v8::internal::SmartPointer; using ::v8::internal::SharedFunctionInfo; using ::v8::internal::String; -namespace i = ::v8::internal; - static v8::Persistent<v8::Context> env; @@ -66,19 +69,19 @@ static void CheckFunctionName(v8::Handle<v8::Script> script, // Find the position of a given func source substring in the source. Handle<String> func_pos_str = - i::Factory::NewStringFromAscii(i::CStrVector(func_pos_src)); - int func_pos = i::Runtime::StringMatch(script_src, func_pos_str, 0); + Factory::NewStringFromAscii(CStrVector(func_pos_src)); + int func_pos = Runtime::StringMatch(script_src, func_pos_str, 0); CHECK_NE(0, func_pos); // Obtain SharedFunctionInfo for the function. Object* shared_func_info_ptr = - i::Runtime::FindSharedFunctionInfoInScript(i_script, func_pos); - CHECK(shared_func_info_ptr != i::Heap::undefined_value()); + Runtime::FindSharedFunctionInfoInScript(i_script, func_pos); + CHECK(shared_func_info_ptr != Heap::undefined_value()); Handle<SharedFunctionInfo> shared_func_info( SharedFunctionInfo::cast(shared_func_info_ptr)); // Verify inferred function name. - i::SmartPointer<char> inferred_name = + SmartPointer<char> inferred_name = shared_func_info->inferred_name()->ToCString(); CHECK_EQ(ref_inferred_name, *inferred_name); } diff --git a/deps/v8/test/cctest/test-hashmap.cc b/deps/v8/test/cctest/test-hashmap.cc index 954dbe103..70213c9aa 100644 --- a/deps/v8/test/cctest/test-hashmap.cc +++ b/deps/v8/test/cctest/test-hashmap.cc @@ -38,20 +38,29 @@ static bool DefaultMatchFun(void* a, void* b) { } +typedef uint32_t (*IntKeyHash)(uint32_t key); + + class IntSet { public: - IntSet() : map_(DefaultMatchFun) {} + explicit IntSet(IntKeyHash hash) : hash_(hash), map_(DefaultMatchFun) {} void Insert(int x) { CHECK_NE(0, x); // 0 corresponds to (void*)NULL - illegal key value - HashMap::Entry* p = map_.Lookup(reinterpret_cast<void*>(x), Hash(x), true); + HashMap::Entry* p = map_.Lookup(reinterpret_cast<void*>(x), hash_(x), true); CHECK(p != NULL); // insert is set! CHECK_EQ(reinterpret_cast<void*>(x), p->key); // we don't care about p->value } + void Remove(int x) { + CHECK_NE(0, x); // 0 corresponds to (void*)NULL - illegal key value + map_.Remove(reinterpret_cast<void*>(x), hash_(x)); + } + bool Present(int x) { - HashMap::Entry* p = map_.Lookup(reinterpret_cast<void*>(x), Hash(x), false); + HashMap::Entry* p = + map_.Lookup(reinterpret_cast<void*>(x), hash_(x), false); if (p != NULL) { CHECK_EQ(reinterpret_cast<void*>(x), p->key); } @@ -72,13 +81,17 @@ class IntSet { } private: + IntKeyHash hash_; HashMap map_; - static uint32_t Hash(uint32_t key) { return key * 23; } }; -TEST(Set) { - IntSet set; +static uint32_t Hash(uint32_t key) { return 23; } +static uint32_t CollisionHash(uint32_t key) { return key & 0x3; } + + +void TestSet(IntKeyHash hash, int size) { + IntSet set(hash); CHECK_EQ(0, set.occupancy()); set.Insert(1); @@ -96,6 +109,18 @@ TEST(Set) { CHECK(!set.Present(4)); CHECK_EQ(3, set.occupancy()); + set.Remove(1); + CHECK(!set.Present(1)); + CHECK(set.Present(2)); + CHECK(set.Present(3)); + CHECK_EQ(2, set.occupancy()); + + set.Remove(3); + CHECK(!set.Present(1)); + CHECK(set.Present(2)); + CHECK(!set.Present(3)); + CHECK_EQ(1, set.occupancy()); + set.Clear(); CHECK_EQ(0, set.occupancy()); @@ -103,21 +128,49 @@ TEST(Set) { const int start = 453; const int factor = 13; const int offset = 7; - const uint32_t n = 1000; + const uint32_t n = size; int x = start; for (uint32_t i = 0; i < n; i++) { CHECK_EQ(i, static_cast<double>(set.occupancy())); set.Insert(x); - x = x*factor + offset; + x = x * factor + offset; } + CHECK_EQ(n, static_cast<double>(set.occupancy())); // Verify the same sequence of values. x = start; for (uint32_t i = 0; i < n; i++) { CHECK(set.Present(x)); - x = x*factor + offset; + x = x * factor + offset; } - CHECK_EQ(n, static_cast<double>(set.occupancy())); + + // Remove all these values. + x = start; + for (uint32_t i = 0; i < n; i++) { + CHECK_EQ(n - i, static_cast<double>(set.occupancy())); + CHECK(set.Present(x)); + set.Remove(x); + CHECK(!set.Present(x)); + x = x * factor + offset; + + // Verify the the expected values are still there. + int y = start; + for (uint32_t j = 0; j < n; j++) { + if (j <= i) { + CHECK(!set.Present(y)); + } else { + CHECK(set.Present(y)); + } + y = y * factor + offset; + } + } + CHECK_EQ(0, set.occupancy()); +} + + +TEST(Set) { + TestSet(Hash, 100); + TestSet(CollisionHash, 50); } diff --git a/deps/v8/test/cctest/test-heap.cc b/deps/v8/test/cctest/test-heap.cc index e35ac5fba..515657f71 100644 --- a/deps/v8/test/cctest/test-heap.cc +++ b/deps/v8/test/cctest/test-heap.cc @@ -36,34 +36,43 @@ TEST(HeapMaps) { InitializeVM(); CheckMap(Heap::meta_map(), MAP_TYPE, Map::kSize); CheckMap(Heap::heap_number_map(), HEAP_NUMBER_TYPE, HeapNumber::kSize); - CheckMap(Heap::fixed_array_map(), FIXED_ARRAY_TYPE, Array::kHeaderSize); + CheckMap(Heap::fixed_array_map(), FIXED_ARRAY_TYPE, Array::kAlignedSize); CheckMap(Heap::long_string_map(), LONG_STRING_TYPE, - SeqTwoByteString::kHeaderSize); + SeqTwoByteString::kAlignedSize); } static void CheckOddball(Object* obj, const char* string) { CHECK(obj->IsOddball()); +#ifndef V8_HOST_ARCH_64_BIT +// TODO(X64): Reenable when native builtins work. bool exc; Object* print_string = *Execution::ToString(Handle<Object>(obj), &exc); CHECK(String::cast(print_string)->IsEqualTo(CStrVector(string))); +#endif // V8_HOST_ARCH_64_BIT } static void CheckSmi(int value, const char* string) { +#ifndef V8_HOST_ARCH_64_BIT +// TODO(X64): Reenable when native builtins work. bool exc; Object* print_string = *Execution::ToString(Handle<Object>(Smi::FromInt(value)), &exc); CHECK(String::cast(print_string)->IsEqualTo(CStrVector(string))); +#endif // V8_HOST_ARCH_64_BIT } static void CheckNumber(double value, const char* string) { Object* obj = Heap::NumberFromDouble(value); CHECK(obj->IsNumber()); +#ifndef V8_HOST_ARCH_64_BIT +// TODO(X64): Reenable when native builtins work. bool exc; Object* print_string = *Execution::ToString(Handle<Object>(obj), &exc); CHECK(String::cast(print_string)->IsEqualTo(CStrVector(string))); +#endif // V8_HOST_ARCH_64_BIT } @@ -178,12 +187,16 @@ TEST(HeapObjects) { TEST(Tagging) { InitializeVM(); + int request = 24; + ASSERT_EQ(request, OBJECT_SIZE_ALIGN(request)); CHECK(Smi::FromInt(42)->IsSmi()); - CHECK(Failure::RetryAfterGC(12, NEW_SPACE)->IsFailure()); - CHECK_EQ(12, Failure::RetryAfterGC(12, NEW_SPACE)->requested()); - CHECK_EQ(NEW_SPACE, Failure::RetryAfterGC(12, NEW_SPACE)->allocation_space()); + CHECK(Failure::RetryAfterGC(request, NEW_SPACE)->IsFailure()); + CHECK_EQ(request, Failure::RetryAfterGC(request, NEW_SPACE)->requested()); + CHECK_EQ(NEW_SPACE, + Failure::RetryAfterGC(request, NEW_SPACE)->allocation_space()); CHECK_EQ(OLD_POINTER_SPACE, - Failure::RetryAfterGC(12, OLD_POINTER_SPACE)->allocation_space()); + Failure::RetryAfterGC(request, + OLD_POINTER_SPACE)->allocation_space()); CHECK(Failure::Exception()->IsFailure()); CHECK(Smi::FromInt(Smi::kMinValue)->IsSmi()); CHECK(Smi::FromInt(Smi::kMaxValue)->IsSmi()); @@ -315,7 +328,7 @@ static bool WeakPointerCleared = false; static void TestWeakGlobalHandleCallback(v8::Persistent<v8::Value> handle, void* id) { USE(handle); - if (1234 == reinterpret_cast<int>(id)) WeakPointerCleared = true; + if (1234 == reinterpret_cast<intptr_t>(id)) WeakPointerCleared = true; } @@ -385,7 +398,7 @@ TEST(WeakGlobalHandlesMark) { static void TestDeleteWeakGlobalHandleCallback( v8::Persistent<v8::Value> handle, void* id) { - if (1234 == reinterpret_cast<int>(id)) WeakPointerCleared = true; + if (1234 == reinterpret_cast<intptr_t>(id)) WeakPointerCleared = true; handle.Dispose(); } @@ -540,7 +553,7 @@ TEST(ObjectProperties) { CHECK(obj->HasLocalProperty(first)); // delete first - CHECK(obj->DeleteProperty(first)); + CHECK(obj->DeleteProperty(first, JSObject::NORMAL_DELETION)); CHECK(!obj->HasLocalProperty(first)); // add first and then second @@ -550,9 +563,9 @@ TEST(ObjectProperties) { CHECK(obj->HasLocalProperty(second)); // delete first and then second - CHECK(obj->DeleteProperty(first)); + CHECK(obj->DeleteProperty(first, JSObject::NORMAL_DELETION)); CHECK(obj->HasLocalProperty(second)); - CHECK(obj->DeleteProperty(second)); + CHECK(obj->DeleteProperty(second, JSObject::NORMAL_DELETION)); CHECK(!obj->HasLocalProperty(first)); CHECK(!obj->HasLocalProperty(second)); @@ -563,9 +576,9 @@ TEST(ObjectProperties) { CHECK(obj->HasLocalProperty(second)); // delete second and then first - CHECK(obj->DeleteProperty(second)); + CHECK(obj->DeleteProperty(second, JSObject::NORMAL_DELETION)); CHECK(obj->HasLocalProperty(first)); - CHECK(obj->DeleteProperty(first)); + CHECK(obj->DeleteProperty(first, JSObject::NORMAL_DELETION)); CHECK(!obj->HasLocalProperty(first)); CHECK(!obj->HasLocalProperty(second)); diff --git a/deps/v8/test/cctest/test-list.cc b/deps/v8/test/cctest/test-list.cc index 838a45d0d..624b6e939 100644 --- a/deps/v8/test/cctest/test-list.cc +++ b/deps/v8/test/cctest/test-list.cc @@ -65,3 +65,37 @@ TEST(ListAdd) { list.Add(list[0]); CHECK_EQ(1, list[4]); } + +// Test that we can add all elements from a list to another list. +TEST(ListAddAll) { + List<int, ZeroingAllocationPolicy> list(4); + list.Add(0); + list.Add(1); + list.Add(2); + + CHECK_EQ(3, list.length()); + for (int i = 0; i < 3; i++) { + CHECK_EQ(i, list[i]); + } + + List<int, ZeroingAllocationPolicy> other_list(4); + + // Add no elements to list since other_list is empty. + list.AddAll(other_list); + CHECK_EQ(3, list.length()); + for (int i = 0; i < 3; i++) { + CHECK_EQ(i, list[i]); + } + + // Add three elements to other_list. + other_list.Add(0); + other_list.Add(1); + other_list.Add(2); + + // Copy the three elements from other_list to list. + list.AddAll(other_list); + CHECK_EQ(6, list.length()); + for (int i = 0; i < 6; i++) { + CHECK_EQ(i % 3, list[i]); + } +} diff --git a/deps/v8/test/cctest/test-log-ia32.cc b/deps/v8/test/cctest/test-log-ia32.cc index 43cb294b1..a40a800ee 100644 --- a/deps/v8/test/cctest/test-log-ia32.cc +++ b/deps/v8/test/cctest/test-log-ia32.cc @@ -13,6 +13,7 @@ #include "top.h" #include "cctest.h" #include "disassembler.h" +#include "register-allocator-inl.h" using v8::Function; using v8::Local; @@ -36,13 +37,11 @@ static v8::Persistent<v8::Context> env; static struct { - StackTracer* tracer; TickSample* sample; -} trace_env = { NULL, NULL }; +} trace_env = { NULL }; -static void InitTraceEnv(StackTracer* tracer, TickSample* sample) { - trace_env.tracer = tracer; +static void InitTraceEnv(TickSample* sample) { trace_env.sample = sample; } @@ -52,7 +51,7 @@ static void DoTrace(Address fp) { // sp is only used to define stack high bound trace_env.sample->sp = reinterpret_cast<unsigned int>(trace_env.sample) - 10240; - trace_env.tracer->Trace(trace_env.sample); + StackTracer::Trace(trace_env.sample); } @@ -98,6 +97,8 @@ class TraceExtension : public v8::Extension { v8::Handle<String> name); static v8::Handle<v8::Value> Trace(const v8::Arguments& args); static v8::Handle<v8::Value> JSTrace(const v8::Arguments& args); + static v8::Handle<v8::Value> JSEntrySP(const v8::Arguments& args); + static v8::Handle<v8::Value> JSEntrySPLevel2(const v8::Arguments& args); private: static Address GetFP(const v8::Arguments& args); static const char* kSource; @@ -106,8 +107,9 @@ class TraceExtension : public v8::Extension { const char* TraceExtension::kSource = "native function trace();" - "native function js_trace();"; - + "native function js_trace();" + "native function js_entry_sp();" + "native function js_entry_sp_level2();"; v8::Handle<v8::FunctionTemplate> TraceExtension::GetNativeFunction( v8::Handle<String> name) { @@ -115,6 +117,10 @@ v8::Handle<v8::FunctionTemplate> TraceExtension::GetNativeFunction( return v8::FunctionTemplate::New(TraceExtension::Trace); } else if (name->Equals(String::New("js_trace"))) { return v8::FunctionTemplate::New(TraceExtension::JSTrace); + } else if (name->Equals(String::New("js_entry_sp"))) { + return v8::FunctionTemplate::New(TraceExtension::JSEntrySP); + } else if (name->Equals(String::New("js_entry_sp_level2"))) { + return v8::FunctionTemplate::New(TraceExtension::JSEntrySPLevel2); } else { CHECK(false); return v8::Handle<v8::FunctionTemplate>(); @@ -142,6 +148,34 @@ v8::Handle<v8::Value> TraceExtension::JSTrace(const v8::Arguments& args) { } +static Address GetJsEntrySp() { + CHECK_NE(NULL, Top::GetCurrentThread()); + return Top::js_entry_sp(Top::GetCurrentThread()); +} + + +v8::Handle<v8::Value> TraceExtension::JSEntrySP(const v8::Arguments& args) { + CHECK_NE(0, GetJsEntrySp()); + return v8::Undefined(); +} + + +static void CompileRun(const char* source) { + Script::Compile(String::New(source))->Run(); +} + + +v8::Handle<v8::Value> TraceExtension::JSEntrySPLevel2( + const v8::Arguments& args) { + v8::HandleScope scope; + const Address js_entry_sp = GetJsEntrySp(); + CHECK_NE(0, js_entry_sp); + CompileRun("js_entry_sp();"); + CHECK_EQ(js_entry_sp, GetJsEntrySp()); + return v8::Undefined(); +} + + static TraceExtension kTraceExtension; v8::DeclareExtension kTraceExtensionDeclaration(&kTraceExtension); @@ -163,11 +197,6 @@ static Handle<JSFunction> CompileFunction(const char* source) { } -static void CompileRun(const char* source) { - Script::Compile(String::New(source))->Run(); -} - - static Local<Value> GetGlobalProperty(const char* name) { return env->Global()->Get(String::New(name)); } @@ -198,7 +227,8 @@ static Handle<v8::internal::String> NewString(const char* s) { } -namespace v8 { namespace internal { +namespace v8 { +namespace internal { class CodeGeneratorPatcher { public: @@ -253,8 +283,7 @@ static void CreateTraceCallerFunction(const char* func_name, TEST(CFromJSStackTrace) { TickSample sample; - StackTracer tracer(reinterpret_cast<uintptr_t>(&sample)); - InitTraceEnv(&tracer, &sample); + InitTraceEnv(&sample); InitializeVM(); v8::HandleScope scope; @@ -275,8 +304,7 @@ TEST(CFromJSStackTrace) { TEST(PureJSStackTrace) { TickSample sample; - StackTracer tracer(reinterpret_cast<uintptr_t>(&sample)); - InitTraceEnv(&tracer, &sample); + InitTraceEnv(&sample); InitializeVM(); v8::HandleScope scope; @@ -321,11 +349,22 @@ static int CFunc(int depth) { TEST(PureCStackTrace) { TickSample sample; - StackTracer tracer(reinterpret_cast<uintptr_t>(&sample)); - InitTraceEnv(&tracer, &sample); + InitTraceEnv(&sample); // Check that sampler doesn't crash CHECK_EQ(10, CFunc(10)); } +TEST(JsEntrySp) { + InitializeVM(); + v8::HandleScope scope; + CHECK_EQ(0, GetJsEntrySp()); + CompileRun("a = 1; b = a + 1;"); + CHECK_EQ(0, GetJsEntrySp()); + CompileRun("js_entry_sp();"); + CHECK_EQ(0, GetJsEntrySp()); + CompileRun("js_entry_sp_level2();"); + CHECK_EQ(0, GetJsEntrySp()); +} + #endif // ENABLE_LOGGING_AND_PROFILING diff --git a/deps/v8/test/cctest/test-log-utils.cc b/deps/v8/test/cctest/test-log-utils.cc new file mode 100644 index 000000000..64e590099 --- /dev/null +++ b/deps/v8/test/cctest/test-log-utils.cc @@ -0,0 +1,132 @@ +// Copyright 2006-2009 the V8 project authors. All rights reserved. +// +// Tests of logging utilities from log-utils.h + +#ifdef ENABLE_LOGGING_AND_PROFILING + +#include "v8.h" + +#include "log-utils.h" +#include "cctest.h" + +using v8::internal::EmbeddedVector; +using v8::internal::LogDynamicBuffer; +using v8::internal::Vector; + +// Fills 'ref_buffer' with test data: a sequence of two-digit +// hex numbers: '0001020304...'. Then writes 'ref_buffer' contents to 'dynabuf'. +static void WriteData(LogDynamicBuffer* dynabuf, Vector<char>* ref_buffer) { + static const char kHex[] = "0123456789ABCDEF"; + CHECK_GT(ref_buffer->length(), 0); + CHECK_GT(513, ref_buffer->length()); + for (int i = 0, half_len = ref_buffer->length() >> 1; i < half_len; ++i) { + (*ref_buffer)[i << 1] = kHex[i >> 4]; + (*ref_buffer)[(i << 1) + 1] = kHex[i & 15]; + } + if (ref_buffer->length() & 1) { + ref_buffer->last() = kHex[ref_buffer->length() >> 5]; + } + CHECK_EQ(ref_buffer->length(), + dynabuf->Write(ref_buffer->start(), ref_buffer->length())); +} + + +static int ReadData( + LogDynamicBuffer* dynabuf, int start_pos, i::Vector<char>* buffer) { + return dynabuf->Read(start_pos, buffer->start(), buffer->length()); +} + + +// Helper function used by CHECK_EQ to compare Vectors. Templatized to +// accept both "char" and "const char" vector contents. +template <typename E, typename V> +static inline void CheckEqualsHelper(const char* file, int line, + const char* expected_source, + const Vector<E>& expected, + const char* value_source, + const Vector<V>& value) { + if (expected.length() != value.length()) { + V8_Fatal(file, line, "CHECK_EQ(%s, %s) failed\n" + "# Vectors lengths differ: %d expected, %d found", + expected_source, value_source, + expected.length(), value.length()); + } + if (strncmp(expected.start(), value.start(), expected.length()) != 0) { + V8_Fatal(file, line, "CHECK_EQ(%s, %s) failed\n" + "# Vectors contents differ:\n" + "# Expected: %.*s\n" + "# Found: %.*s", + expected_source, value_source, + expected.length(), expected.start(), + value.length(), value.start()); + } +} + + +TEST(DynaBufSingleBlock) { + LogDynamicBuffer dynabuf(32, 32, "", 0); + EmbeddedVector<char, 32> ref_buf; + WriteData(&dynabuf, &ref_buf); + EmbeddedVector<char, 32> buf; + CHECK_EQ(32, dynabuf.Read(0, buf.start(), buf.length())); + CHECK_EQ(32, ReadData(&dynabuf, 0, &buf)); + CHECK_EQ(ref_buf, buf); + + // Verify that we can't read and write past the end. + CHECK_EQ(0, dynabuf.Read(32, buf.start(), buf.length())); + CHECK_EQ(0, dynabuf.Write(buf.start(), buf.length())); +} + + +TEST(DynaBufCrossBlocks) { + LogDynamicBuffer dynabuf(32, 128, "", 0); + EmbeddedVector<char, 48> ref_buf; + WriteData(&dynabuf, &ref_buf); + CHECK_EQ(48, dynabuf.Write(ref_buf.start(), ref_buf.length())); + // Verify that we can't write data when remaining buffer space isn't enough. + CHECK_EQ(0, dynabuf.Write(ref_buf.start(), ref_buf.length())); + EmbeddedVector<char, 48> buf; + CHECK_EQ(48, ReadData(&dynabuf, 0, &buf)); + CHECK_EQ(ref_buf, buf); + CHECK_EQ(48, ReadData(&dynabuf, 48, &buf)); + CHECK_EQ(ref_buf, buf); + CHECK_EQ(0, ReadData(&dynabuf, 48 * 2, &buf)); +} + + +TEST(DynaBufReadTruncation) { + LogDynamicBuffer dynabuf(32, 128, "", 0); + EmbeddedVector<char, 128> ref_buf; + WriteData(&dynabuf, &ref_buf); + EmbeddedVector<char, 128> buf; + CHECK_EQ(128, ReadData(&dynabuf, 0, &buf)); + CHECK_EQ(ref_buf, buf); + // Try to read near the end with a buffer larger than remaining data size. + EmbeddedVector<char, 48> tail_buf; + CHECK_EQ(32, ReadData(&dynabuf, 128 - 32, &tail_buf)); + CHECK_EQ(ref_buf.SubVector(128 - 32, 128), tail_buf.SubVector(0, 32)); +} + + +TEST(DynaBufSealing) { + const char* seal = "Sealed"; + const int seal_size = strlen(seal); + LogDynamicBuffer dynabuf(32, 128, seal, seal_size); + EmbeddedVector<char, 100> ref_buf; + WriteData(&dynabuf, &ref_buf); + // Try to write data that will not fit in the buffer. + CHECK_EQ(0, dynabuf.Write(ref_buf.start(), 128 - 100 - seal_size + 1)); + // Now the buffer is sealed, writing of any amount of data is forbidden. + CHECK_EQ(0, dynabuf.Write(ref_buf.start(), 1)); + EmbeddedVector<char, 100> buf; + CHECK_EQ(100, ReadData(&dynabuf, 0, &buf)); + CHECK_EQ(ref_buf, buf); + // Check the seal. + EmbeddedVector<char, 50> seal_buf; + CHECK_EQ(seal_size, ReadData(&dynabuf, 100, &seal_buf)); + CHECK_EQ(v8::internal::CStrVector(seal), seal_buf.SubVector(0, seal_size)); + // Verify that there's no data beyond the seal. + CHECK_EQ(0, ReadData(&dynabuf, 100 + seal_size, &buf)); +} + +#endif // ENABLE_LOGGING_AND_PROFILING diff --git a/deps/v8/test/cctest/test-log.cc b/deps/v8/test/cctest/test-log.cc index 6a7e54f1d..f3f7efc71 100644 --- a/deps/v8/test/cctest/test-log.cc +++ b/deps/v8/test/cctest/test-log.cc @@ -7,15 +7,18 @@ #include "v8.h" #include "log.h" - #include "cctest.h" +using v8::internal::Address; +using v8::internal::EmbeddedVector; using v8::internal::Logger; +namespace i = v8::internal; + static void SetUp() { // Log to memory buffer. - v8::internal::FLAG_logfile = "*"; - v8::internal::FLAG_log = true; + i::FLAG_logfile = "*"; + i::FLAG_log = true; Logger::Setup(); } @@ -54,6 +57,7 @@ TEST(GetMessages) { memset(log_lines, 0, sizeof(log_lines)); // A bit more than the first line length. CHECK_EQ(line_1_len, Logger::GetLogLines(0, log_lines, line_1_len + 3)); + log_lines[line_1_len] = '\0'; CHECK_EQ(line_1, log_lines); memset(log_lines, 0, sizeof(log_lines)); const char* line_2 = "cccc,\"dddd\"\n"; @@ -82,6 +86,11 @@ TEST(GetMessages) { } +static int GetLogLines(int start_pos, i::Vector<char>* buffer) { + return Logger::GetLogLines(start_pos, buffer->start(), buffer->length()); +} + + TEST(BeyondWritePosition) { SetUp(); Logger::StringEvent("aaa", "bbb"); @@ -89,22 +98,24 @@ TEST(BeyondWritePosition) { // See Logger::StringEvent. const char* all_lines = "aaa,\"bbb\"\ncccc,\"dddd\"\n"; const int all_lines_len = strlen(all_lines); - CHECK_EQ(0, Logger::GetLogLines(all_lines_len, NULL, 1)); - CHECK_EQ(0, Logger::GetLogLines(all_lines_len, NULL, 100)); - CHECK_EQ(0, Logger::GetLogLines(all_lines_len + 1, NULL, 1)); - CHECK_EQ(0, Logger::GetLogLines(all_lines_len + 1, NULL, 100)); - CHECK_EQ(0, Logger::GetLogLines(all_lines_len + 100, NULL, 1)); - CHECK_EQ(0, Logger::GetLogLines(all_lines_len + 100, NULL, 100)); - CHECK_EQ(0, Logger::GetLogLines(10 * 1024 * 1024, NULL, 1)); - CHECK_EQ(0, Logger::GetLogLines(10 * 1024 * 1024, NULL, 100)); + EmbeddedVector<char, 100> buffer; + const int beyond_write_pos = all_lines_len; + CHECK_EQ(0, Logger::GetLogLines(beyond_write_pos, buffer.start(), 1)); + CHECK_EQ(0, GetLogLines(beyond_write_pos, &buffer)); + CHECK_EQ(0, Logger::GetLogLines(beyond_write_pos + 1, buffer.start(), 1)); + CHECK_EQ(0, GetLogLines(beyond_write_pos + 1, &buffer)); + CHECK_EQ(0, Logger::GetLogLines(beyond_write_pos + 100, buffer.start(), 1)); + CHECK_EQ(0, GetLogLines(beyond_write_pos + 100, &buffer)); + CHECK_EQ(0, Logger::GetLogLines(10 * 1024 * 1024, buffer.start(), 1)); + CHECK_EQ(0, GetLogLines(10 * 1024 * 1024, &buffer)); TearDown(); } TEST(MemoryLoggingTurnedOff) { // Log to stdout - v8::internal::FLAG_logfile = "-"; - v8::internal::FLAG_log = true; + i::FLAG_logfile = "-"; + i::FLAG_log = true; Logger::Setup(); CHECK_EQ(0, Logger::GetLogLines(0, NULL, 0)); CHECK_EQ(0, Logger::GetLogLines(100, NULL, 0)); @@ -114,4 +125,588 @@ TEST(MemoryLoggingTurnedOff) { } +static void CompileAndRunScript(const char *src) { + v8::Script::Compile(v8::String::New(src))->Run(); +} + + +namespace v8 { +namespace internal { + +class LoggerTestHelper : public AllStatic { + public: + static bool IsSamplerActive() { return Logger::IsProfilerSamplerActive(); } +}; + +} // namespace v8::internal +} // namespace v8 + +using v8::internal::LoggerTestHelper; + + +static int CheckThatProfilerWorks(int log_pos) { + Logger::ResumeProfiler(); + CHECK(LoggerTestHelper::IsSamplerActive()); + + // Verify that the current map of compiled functions has been logged. + EmbeddedVector<char, 102400> buffer; + int map_log_size = GetLogLines(log_pos, &buffer); + printf("map_log_size: %d\n", map_log_size); + CHECK_GT(map_log_size, 0); + CHECK_GT(buffer.length(), map_log_size); + log_pos += map_log_size; + // Check buffer contents. + buffer[map_log_size] = '\0'; + const char* code_creation = "\ncode-creation,"; // eq. to /^code-creation,/ + CHECK_NE(NULL, strstr(buffer.start(), code_creation)); + + // Force compiler to generate new code by parametrizing source. + EmbeddedVector<char, 100> script_src; + i::OS::SNPrintF(script_src, + "for (var i = 0; i < 1000; ++i) { " + "(function(x) { return %d * x; })(i); }", + log_pos); + // Run code for 200 msecs to get some ticks. + const double end_time = i::OS::TimeCurrentMillis() + 200; + while (i::OS::TimeCurrentMillis() < end_time) { + CompileAndRunScript(script_src.start()); + } + + Logger::PauseProfiler(); + CHECK(!LoggerTestHelper::IsSamplerActive()); + + // Wait 50 msecs to allow Profiler thread to process the last + // tick sample it has got. + i::OS::Sleep(50); + + // Now we must have compiler and tick records. + int log_size = GetLogLines(log_pos, &buffer); + printf("log_size: %d\n", log_size); + CHECK_GT(log_size, 0); + CHECK_GT(buffer.length(), log_size); + log_pos += log_size; + // Check buffer contents. + buffer[log_size] = '\0'; + const char* tick = "\ntick,"; + CHECK_NE(NULL, strstr(buffer.start(), code_creation)); + CHECK_NE(NULL, strstr(buffer.start(), tick)); + + return log_pos; +} + + +TEST(ProfLazyMode) { + const bool saved_prof_lazy = i::FLAG_prof_lazy; + const bool saved_prof = i::FLAG_prof; + const bool saved_prof_auto = i::FLAG_prof_auto; + i::FLAG_prof = true; + i::FLAG_prof_lazy = true; + i::FLAG_prof_auto = false; + i::FLAG_logfile = "*"; + + // If tests are being run manually, V8 will be already initialized + // by the test below. + const bool need_to_set_up_logger = i::V8::IsRunning(); + v8::HandleScope scope; + v8::Handle<v8::Context> env = v8::Context::New(); + if (need_to_set_up_logger) Logger::Setup(); + env->Enter(); + + // No sampling should happen prior to resuming profiler. + CHECK(!LoggerTestHelper::IsSamplerActive()); + + // Read initial logged data (static libs map). + EmbeddedVector<char, 102400> buffer; + int log_pos = GetLogLines(0, &buffer); + CHECK_GT(log_pos, 0); + CHECK_GT(buffer.length(), log_pos); + + CompileAndRunScript("var a = (function(x) { return x + 1; })(10);"); + + // Nothing must be logged while profiling is suspended. + CHECK_EQ(0, GetLogLines(log_pos, &buffer)); + + log_pos = CheckThatProfilerWorks(log_pos); + + CompileAndRunScript("var a = (function(x) { return x + 1; })(10);"); + + // No new data beyond last retrieved position. + CHECK_EQ(0, GetLogLines(log_pos, &buffer)); + + // Check that profiling can be resumed again. + CheckThatProfilerWorks(log_pos); + + env->Exit(); + Logger::TearDown(); + i::FLAG_prof_lazy = saved_prof_lazy; + i::FLAG_prof = saved_prof; + i::FLAG_prof_auto = saved_prof_auto; +} + + +static inline bool IsStringEqualTo(const char* r, const char* s) { + return strncmp(r, s, strlen(r)) == 0; +} + + +static bool Consume(const char* str, char** buf) { + if (IsStringEqualTo(str, *buf)) { + *buf += strlen(str); + return true; + } + return false; +} + + +namespace { + +// A code entity is a pointer to a position of code-creation event in buffer log +// offset to a point where entity size begins, i.e.: '255,"func"\n'. This makes +// comparing code entities pretty easy. +typedef char* CodeEntityInfo; + +class Interval { + public: + Interval() + : min_addr_(reinterpret_cast<Address>(-1)), + max_addr_(reinterpret_cast<Address>(0)), next_(NULL) {} + + ~Interval() { delete next_; } + + size_t Length() { + size_t result = max_addr_ - min_addr_ + 1; + if (next_ != NULL) result += next_->Length(); + return result; + } + + void CloneFrom(Interval* src) { + while (src != NULL) { + RegisterAddress(src->min_addr_); + RegisterAddress(src->max_addr_); + src = src->next_; + } + } + + bool Contains(Address addr) { + if (min_addr_ <= addr && addr <= max_addr_) { + return true; + } + if (next_ != NULL) { + return next_->Contains(addr); + } else { + return false; + } + } + + size_t GetIndex(Address addr) { + if (min_addr_ <= addr && addr <= max_addr_) { + return addr - min_addr_; + } + CHECK_NE(NULL, next_); + return (max_addr_ - min_addr_ + 1) + next_->GetIndex(addr); + } + + Address GetMinAddr() { + return next_ == NULL ? min_addr_ : i::Min(min_addr_, next_->GetMinAddr()); + } + + Address GetMaxAddr() { + return next_ == NULL ? max_addr_ : i::Max(max_addr_, next_->GetMaxAddr()); + } + + void RegisterAddress(Address addr) { + if (min_addr_ == reinterpret_cast<Address>(-1) + || (size_t)(addr > min_addr_ ? + addr - min_addr_ : min_addr_ - addr) < MAX_DELTA) { + if (addr < min_addr_) min_addr_ = addr; + if (addr > max_addr_) max_addr_ = addr; + } else { + if (next_ == NULL) next_ = new Interval(); + next_->RegisterAddress(addr); + } + } + + Address raw_min_addr() { return min_addr_; } + + Address raw_max_addr() { return max_addr_; } + + Interval* get_next() { return next_; } + + private: + static const size_t MAX_DELTA = 0x100000; + Address min_addr_; + Address max_addr_; + Interval* next_; +}; + + +// A structure used to return log parsing results. +class ParseLogResult { + public: + ParseLogResult() + : entities_map(NULL), entities(NULL), + max_entities(0) {} + + ~ParseLogResult() { + i::DeleteArray(entities_map); + i::DeleteArray(entities); + } + + void AllocateEntities() { + // Make sure that the test doesn't operate on a bogus log. + CHECK_GT(max_entities, 0); + CHECK_GT(bounds.GetMinAddr(), 0); + CHECK_GT(bounds.GetMaxAddr(), bounds.GetMinAddr()); + + entities = i::NewArray<CodeEntityInfo>(max_entities); + for (int i = 0; i < max_entities; ++i) { + entities[i] = NULL; + } + const size_t map_length = bounds.Length(); + entities_map = i::NewArray<int>(map_length); + for (size_t i = 0; i < map_length; ++i) { + entities_map[i] = -1; + } + } + + bool HasIndexForAddress(Address addr) { + return bounds.Contains(addr); + } + + size_t GetIndexForAddress(Address addr) { + CHECK(HasIndexForAddress(addr)); + return bounds.GetIndex(addr); + } + + CodeEntityInfo GetEntity(Address addr) { + if (HasIndexForAddress(addr)) { + size_t idx = GetIndexForAddress(addr); + int item = entities_map[idx]; + return item != -1 ? entities[item] : NULL; + } + return NULL; + } + + void ParseAddress(char* start) { + Address addr = + reinterpret_cast<Address>(strtoul(start, NULL, 16)); // NOLINT + bounds.RegisterAddress(addr); + } + + Address ConsumeAddress(char** start) { + char* end_ptr; + Address addr = + reinterpret_cast<Address>(strtoul(*start, &end_ptr, 16)); // NOLINT + CHECK(HasIndexForAddress(addr)); + *start = end_ptr; + return addr; + } + + Interval bounds; + // Memory map of entities start addresses. + int* entities_map; + // An array of code entities. + CodeEntityInfo* entities; + // Maximal entities count. Actual entities count can be lower, + // empty entity slots are pointing to NULL. + int max_entities; +}; + +} // namespace + + +typedef void (*ParserBlock)(char* start, char* end, ParseLogResult* result); + +static void ParserCycle( + char* start, char* end, ParseLogResult* result, + ParserBlock block_creation, ParserBlock block_delete, + ParserBlock block_move) { + + const char* code_creation = "code-creation,"; + const char* code_delete = "code-delete,"; + const char* code_move = "code-move,"; + + const char* lazy_compile = "LazyCompile,"; + const char* script = "Script,"; + const char* function = "Function,"; + + while (start < end) { + if (Consume(code_creation, &start)) { + if (Consume(lazy_compile, &start) + || Consume(script, &start) + || Consume(function, &start)) { + block_creation(start, end, result); + } + } else if (Consume(code_delete, &start)) { + block_delete(start, end, result); + } else if (Consume(code_move, &start)) { + block_move(start, end, result); + } + while (start < end && *start != '\n') ++start; + ++start; + } +} + + +static void Pass1CodeCreation(char* start, char* end, ParseLogResult* result) { + result->ParseAddress(start); + ++result->max_entities; +} + + +static void Pass1CodeDelete(char* start, char* end, ParseLogResult* result) { + result->ParseAddress(start); +} + + +static void Pass1CodeMove(char* start, char* end, ParseLogResult* result) { + result->ParseAddress(start); + // Skip old address. + while (start < end && *start != ',') ++start; + CHECK_GT(end, start); + ++start; // Skip ','. + result->ParseAddress(start); +} + + +static void Pass2CodeCreation(char* start, char* end, ParseLogResult* result) { + Address addr = result->ConsumeAddress(&start); + CHECK_GT(end, start); + ++start; // Skip ','. + + size_t idx = result->GetIndexForAddress(addr); + result->entities_map[idx] = -1; + for (int i = 0; i < result->max_entities; ++i) { + // Find an empty slot and fill it. + if (result->entities[i] == NULL) { + result->entities[i] = start; + result->entities_map[idx] = i; + break; + } + } + // Make sure that a slot was found. + CHECK_GE(result->entities_map[idx], 0); +} + + +static void Pass2CodeDelete(char* start, char* end, ParseLogResult* result) { + Address addr = result->ConsumeAddress(&start); + size_t idx = result->GetIndexForAddress(addr); + // There can be code deletes that are not related to JS code. + if (result->entities_map[idx] >= 0) { + result->entities[result->entities_map[idx]] = NULL; + result->entities_map[idx] = -1; + } +} + + +static void Pass2CodeMove(char* start, char* end, ParseLogResult* result) { + Address from_addr = result->ConsumeAddress(&start); + CHECK_GT(end, start); + ++start; // Skip ','. + Address to_addr = result->ConsumeAddress(&start); + CHECK_GT(end, start); + + size_t from_idx = result->GetIndexForAddress(from_addr); + size_t to_idx = result->GetIndexForAddress(to_addr); + // There can be code moves that are not related to JS code. + if (from_idx != to_idx && result->entities_map[from_idx] >= 0) { + CHECK_EQ(-1, result->entities_map[to_idx]); + result->entities_map[to_idx] = result->entities_map[from_idx]; + result->entities_map[from_idx] = -1; + }; +} + + +static void ParseLog(char* start, char* end, ParseLogResult* result) { + // Pass 1: Calculate boundaries of addresses and entities count. + ParserCycle(start, end, result, + Pass1CodeCreation, Pass1CodeDelete, Pass1CodeMove); + + printf("min_addr: %p, max_addr: %p, entities: %d\n", + result->bounds.GetMinAddr(), result->bounds.GetMaxAddr(), + result->max_entities); + + result->AllocateEntities(); + + // Pass 2: Fill in code entries data. + ParserCycle(start, end, result, + Pass2CodeCreation, Pass2CodeDelete, Pass2CodeMove); +} + + +static inline void PrintCodeEntityInfo(CodeEntityInfo entity) { + const int max_len = 50; + if (entity != NULL) { + char* eol = strchr(entity, '\n'); + int len = eol - entity; + len = len <= max_len ? len : max_len; + printf("%-*.*s ", max_len, len, entity); + } else { + printf("%*s", max_len + 1, ""); + } +} + + +static void PrintCodeEntitiesInfo( + bool is_equal, Address addr, + CodeEntityInfo l_entity, CodeEntityInfo r_entity) { + printf("%c %p ", is_equal ? ' ' : '*', addr); + PrintCodeEntityInfo(l_entity); + PrintCodeEntityInfo(r_entity); + printf("\n"); +} + + +static inline int StrChrLen(const char* s, char c) { + return strchr(s, c) - s; +} + + +static bool AreFuncSizesEqual(CodeEntityInfo ref_s, CodeEntityInfo new_s) { + int ref_len = StrChrLen(ref_s, ','); + int new_len = StrChrLen(new_s, ','); + return ref_len == new_len && strncmp(ref_s, new_s, ref_len) == 0; +} + + +static bool AreFuncNamesEqual(CodeEntityInfo ref_s, CodeEntityInfo new_s) { + // Skip size. + ref_s = strchr(ref_s, ',') + 1; + new_s = strchr(new_s, ',') + 1; + int ref_len = StrChrLen(ref_s, '\n'); + int new_len = StrChrLen(new_s, '\n'); + // If reference is anonymous (""), it's OK to have anything in new. + if (ref_len == 2) return true; + // A special case for ErrorPrototype. Haven't yet figured out why they + // are different. + const char* error_prototype = "\"ErrorPrototype"; + if (IsStringEqualTo(error_prototype, ref_s) + && IsStringEqualTo(error_prototype, new_s)) { + return true; + } + // Built-in objects have problems too. + const char* built_ins[] = { + "\"Boolean\"", "\"Function\"", "\"Number\"", + "\"Object\"", "\"Script\"", "\"String\"" + }; + for (size_t i = 0; i < sizeof(built_ins) / sizeof(*built_ins); ++i) { + if (IsStringEqualTo(built_ins[i], new_s)) { + return true; + } + } + return ref_len == new_len && strncmp(ref_s, new_s, ref_len) == 0; +} + + +static bool AreEntitiesEqual(CodeEntityInfo ref_e, CodeEntityInfo new_e) { + if (ref_e == NULL && new_e != NULL) return true; + if (ref_e != NULL && new_e != NULL) { + return AreFuncSizesEqual(ref_e, new_e) && AreFuncNamesEqual(ref_e, new_e); + } + if (ref_e != NULL && new_e == NULL) { + // args_count entities (argument adapters) are not found by heap traversal, + // but they are not needed because they doesn't contain any code. + ref_e = strchr(ref_e, ',') + 1; + const char* args_count = "\"args_count:"; + return IsStringEqualTo(args_count, ref_e); + } + return false; +} + + +// Test that logging of code create / move / delete events +// is equivalent to traversal of a resulting heap. +TEST(EquivalenceOfLoggingAndTraversal) { + // This test needs to be run on a "clean" V8 to ensure that snapshot log + // is loaded. This is always true when running using tools/test.py because + // it launches a new cctest instance for every test. To be sure that launching + // cctest manually also works, please be sure that no tests below + // are using V8. + // + // P.S. No, V8 can't be re-initialized after disposal, see include/v8.h. + CHECK(!i::V8::IsRunning()); + + i::FLAG_logfile = "*"; + i::FLAG_log = true; + i::FLAG_log_code = true; + + // Make sure objects move. + bool saved_always_compact = i::FLAG_always_compact; + if (!i::FLAG_never_compact) { + i::FLAG_always_compact = true; + } + + v8::HandleScope scope; + v8::Handle<v8::Value> global_object = v8::Handle<v8::Value>(); + v8::Handle<v8::Context> env = v8::Context::New( + 0, v8::Handle<v8::ObjectTemplate>(), global_object); + env->Enter(); + + // Compile and run a function that creates other functions. + CompileAndRunScript( + "(function f(obj) {\n" + " obj.test =\n" + " (function a(j) { return function b() { return j; } })(100);\n" + "})(this);"); + i::Heap::CollectAllGarbage(); + + EmbeddedVector<char, 204800> buffer; + int log_size; + ParseLogResult ref_result; + + // Retrieve the log. + { + // Make sure that no GCs occur prior to LogCompiledFunctions call. + i::AssertNoAllocation no_alloc; + + log_size = GetLogLines(0, &buffer); + CHECK_GT(log_size, 0); + CHECK_GT(buffer.length(), log_size); + + // Fill a map of compiled code objects. + ParseLog(buffer.start(), buffer.start() + log_size, &ref_result); + } + + // Iterate heap to find compiled functions, will write to log. + i::Logger::LogCompiledFunctions(); + char* new_log_start = buffer.start() + log_size; + const int new_log_size = Logger::GetLogLines( + log_size, new_log_start, buffer.length() - log_size); + CHECK_GT(new_log_size, 0); + CHECK_GT(buffer.length(), log_size + new_log_size); + + // Fill an equivalent map of compiled code objects. + ParseLogResult new_result; + ParseLog(new_log_start, new_log_start + new_log_size, &new_result); + + // Test their actual equivalence. + Interval combined; + combined.CloneFrom(&ref_result.bounds); + combined.CloneFrom(&new_result.bounds); + Interval* iter = &combined; + bool results_equal = true; + + while (iter != NULL) { + for (Address addr = iter->raw_min_addr(); + addr <= iter->raw_max_addr(); ++addr) { + CodeEntityInfo ref_entity = ref_result.GetEntity(addr); + CodeEntityInfo new_entity = new_result.GetEntity(addr); + if (ref_entity != NULL || new_entity != NULL) { + const bool equal = AreEntitiesEqual(ref_entity, new_entity); + if (!equal) results_equal = false; + PrintCodeEntitiesInfo(equal, addr, ref_entity, new_entity); + } + } + iter = iter->get_next(); + } + // Make sure that all log data is written prior crash due to CHECK failure. + fflush(stdout); + CHECK(results_equal); + + env->Exit(); + Logger::TearDown(); + i::FLAG_always_compact = saved_always_compact; +} + #endif // ENABLE_LOGGING_AND_PROFILING diff --git a/deps/v8/test/cctest/test-utils.cc b/deps/v8/test/cctest/test-utils.cc index acb5d3ca6..23b3254c2 100644 --- a/deps/v8/test/cctest/test-utils.cc +++ b/deps/v8/test/cctest/test-utils.cc @@ -152,6 +152,13 @@ TEST(Utils1) { CHECK_EQ(0, FastD2I(0.345)); CHECK_EQ(1, FastD2I(1.234)); CHECK_EQ(1000000, FastD2I(1000000.123)); + // Check that >> is implemented as arithmetic shift right. + // If this is not true, then ArithmeticShiftRight() must be changed, + // There are also documented right shifts in assembler.cc of + // int8_t and intptr_t signed integers. + CHECK_EQ(-2, -8 >> 2); + CHECK_EQ(-2, static_cast<int8_t>(-8) >> 2); + CHECK_EQ(-2, static_cast<intptr_t>(-8) >> 2); } diff --git a/deps/v8/test/cctest/test-version.cc b/deps/v8/test/cctest/test-version.cc index 0b93fbf8c..6d2685596 100644 --- a/deps/v8/test/cctest/test-version.cc +++ b/deps/v8/test/cctest/test-version.cc @@ -33,7 +33,8 @@ using namespace v8::internal; -namespace v8 { namespace internal { +namespace v8 { +namespace internal { void SetVersion(int major, int minor, int build, int patch, bool candidate, const char* soname) { diff --git a/deps/v8/test/mjsunit/codegen_coverage.js b/deps/v8/test/mjsunit/codegen-coverage.js index d5e7769d7..d5e7769d7 100644 --- a/deps/v8/test/mjsunit/codegen_coverage.js +++ b/deps/v8/test/mjsunit/codegen-coverage.js diff --git a/deps/v8/test/mjsunit/debug-backtrace.js b/deps/v8/test/mjsunit/debug-backtrace.js index f08f6390e..1d2bb9af9 100644 --- a/deps/v8/test/mjsunit/debug-backtrace.js +++ b/deps/v8/test/mjsunit/debug-backtrace.js @@ -37,7 +37,7 @@ var m = function() { }; function g() { - m(); + m(); }; @@ -80,8 +80,9 @@ function listener(event, exec_state, event_data, data) { { // The expected backtrace is // 0: f - // 1: g - // 2: [anonymous] + // 1: m + // 2: g + // 3: [anonymous] var response; var backtrace; @@ -133,6 +134,23 @@ function listener(event, exec_state, event_data, data) { assertEquals(2, frames[1].index); assertEquals("g", response.lookup(frames[1].func.ref).name); + // Get backtrace with bottom two frames. + json = '{"seq":0,"type":"request","command":"backtrace","arguments":{"fromFrame":0,"toFrame":2, "bottom":true}}' + response = new ParsedResponse(dcp.processDebugJSONRequest(json)); + backtrace = response.body(); + assertEquals(2, backtrace.fromFrame); + assertEquals(4, backtrace.toFrame); + assertEquals(4, backtrace.totalFrames); + var frames = backtrace.frames; + assertEquals(2, frames.length); + for (var i = 0; i < frames.length; i++) { + assertEquals('frame', frames[i].type); + } + assertEquals(2, frames[0].index); + assertEquals("g", response.lookup(frames[0].func.ref).name); + assertEquals(3, frames[1].index); + assertEquals("", response.lookup(frames[1].func.ref).name); + // Get the individual frames. json = '{"seq":0,"type":"request","command":"frame"}' response = new ParsedResponse(dcp.processDebugJSONRequest(json)); diff --git a/deps/v8/test/mjsunit/debug-compile-event.js b/deps/v8/test/mjsunit/debug-compile-event.js index 035e36c30..c346f76e8 100644 --- a/deps/v8/test/mjsunit/debug-compile-event.js +++ b/deps/v8/test/mjsunit/debug-compile-event.js @@ -32,8 +32,11 @@ Debug = debug.Debug var exception = false; // Exception in debug event listener. var before_compile_count = 0; var after_compile_count = 0; -var current_source = ''; // Current source compiled. -var source_count = 0; // Total number of scource sompiled. +var current_source = ''; // Current source being compiled. +var source_count = 0; // Total number of scources compiled. +var host_compilations = 0; // Number of scources compiled through the API. +var eval_compilations = 0; // Number of scources compiled through eval. +var json_compilations = 0; // Number of scources compiled through JSON.parse. function compileSource(source) { @@ -52,19 +55,41 @@ function listener(event, exec_state, event_data, data) { before_compile_count++; } else { after_compile_count++; + switch (event_data.script().compilationType()) { + case Debug.ScriptCompilationType.Host: + host_compilations++; + break; + case Debug.ScriptCompilationType.Eval: + eval_compilations++; + break; + case Debug.ScriptCompilationType.JSON: + json_compilations++; + break; + } } - + // If the compiled source contains 'eval' there will be additional compile // events for the source inside eval. if (current_source.indexOf('eval') == 0) { // For source with 'eval' there will be compile events with substrings // as well as with with the exact source. assertTrue(current_source.indexOf(event_data.script().source()) >= 0); + } else if (current_source.indexOf('JSON.parse') == 0) { + // For JSON the JSON source will be in parentheses. + var s = event_data.script().source(); + if (s[0] == '(') { + s = s.substring(1, s.length - 2); + } + assertTrue(current_source.indexOf(s) >= 0); } else { // For source without 'eval' there will be a compile events with the // exact source. assertEquals(current_source, event_data.script().source()); } + // Check that script context is included into the event message. + var json = event_data.toJSONProtocol(); + var msg = eval('(' + json + ')'); + assertTrue('context' in msg.body.script); } } catch (e) { exception = e @@ -82,6 +107,8 @@ compileSource('eval("a=2")'); source_count++; // Using eval causes additional compilation event. compileSource('eval("eval(\'function(){return a;}\')")'); source_count += 2; // Using eval causes additional compilation event. +compileSource('JSON.parse("{a:1,b:2}")'); +source_count++; // Using JSON.parse causes additional compilation event. // Make sure that the debug event listener was invoked. assertFalse(exception, "exception in listener") @@ -89,7 +116,11 @@ assertFalse(exception, "exception in listener") // Number of before and after compile events should be the same. assertEquals(before_compile_count, after_compile_count); -// Check the actual number of events. +// Check the actual number of events (no compilation through the API as all +// source compiled through eval except for one JSON.parse call). assertEquals(source_count, after_compile_count); +assertEquals(0, host_compilations); +assertEquals(source_count - 1, eval_compilations); +assertEquals(1, json_compilations); Debug.setListener(null); diff --git a/deps/v8/test/mjsunit/debug-references.js b/deps/v8/test/mjsunit/debug-references.js index dedc3eff4..1fde1ac74 100644 --- a/deps/v8/test/mjsunit/debug-references.js +++ b/deps/v8/test/mjsunit/debug-references.js @@ -113,6 +113,6 @@ q = new Point(1,2); // Enter debugger causing the event listener to be called.
debugger;
-// Make sure that the debug event listener vas invoked.
+// Make sure that the debug event listener was invoked.
assertFalse(exception, "exception in listener")
assertTrue(listenerComplete, "listener did not run to completion");
diff --git a/deps/v8/test/mjsunit/debug-scripts-request.js b/deps/v8/test/mjsunit/debug-scripts-request.js index cf1615bb7..80b3bce59 100644 --- a/deps/v8/test/mjsunit/debug-scripts-request.js +++ b/deps/v8/test/mjsunit/debug-scripts-request.js @@ -66,9 +66,6 @@ function listener(event, exec_state, event_data, data) { testArguments(dcp, '{"types":"xx"}', false); // Test legal scripts requests. - var request = '{' + base_request + '}' - var response = safeEval(dcp.processDebugJSONRequest(request)); - assertTrue(response.success); testArguments(dcp, '{}', true); testArguments(dcp, '{"types":1}', true); testArguments(dcp, '{"types":2}', true); @@ -76,6 +73,21 @@ function listener(event, exec_state, event_data, data) { testArguments(dcp, '{"types":7}', true); testArguments(dcp, '{"types":0xFF}', true); + // Test request for all scripts. + var request = '{' + base_request + '}' + var response = safeEval(dcp.processDebugJSONRequest(request)); + assertTrue(response.success); + + // Test filtering by id. + assertEquals(2, response.body.length); + var script = response.body[0]; + var request = '{' + base_request + ',"arguments":{"ids":[' + + script.id + ']}}'; + var response = safeEval(dcp.processDebugJSONRequest(request)); + assertTrue(response.success); + assertEquals(1, response.body.length); + assertEquals(script.id, response.body[0].id); + // Indicate that all was processed. listenerComplete = true; } @@ -91,5 +103,6 @@ Debug.setListener(listener); debugger; // Make sure that the debug event listener vas invoked with no exceptions. -assertTrue(listenerComplete, "listener did not run to completion"); +assertTrue(listenerComplete, + "listener did not run to completion, exception: " + exception); assertFalse(exception, "exception in listener") diff --git a/deps/v8/test/mjsunit/mirror-array.js b/deps/v8/test/mjsunit/mirror-array.js index 1873d1eb6..eb8f72a8c 100644 --- a/deps/v8/test/mjsunit/mirror-array.js +++ b/deps/v8/test/mjsunit/mirror-array.js @@ -44,8 +44,9 @@ function testArrayMirror(a, names) { // Create mirror and JSON representation. var mirror = debug.MakeMirror(a); var serializer = debug.MakeMirrorSerializer(); - var json = serializer.serializeValue(mirror); - var refs = new MirrorRefCache(serializer.serializeReferencedObjects()); + var json = JSON.stringify(serializer.serializeValue(mirror)); + var refs = new MirrorRefCache( + JSON.stringify(serializer.serializeReferencedObjects())); // Check the mirror hierachy. assertTrue(mirror instanceof debug.Mirror, 'Unexpected mirror hierachy'); diff --git a/deps/v8/test/mjsunit/mirror-boolean.js b/deps/v8/test/mjsunit/mirror-boolean.js index 4f9308924..311c78176 100644 --- a/deps/v8/test/mjsunit/mirror-boolean.js +++ b/deps/v8/test/mjsunit/mirror-boolean.js @@ -32,7 +32,7 @@ function testBooleanMirror(b) { // Create mirror and JSON representation. var mirror = debug.MakeMirror(b); var serializer = debug.MakeMirrorSerializer(); - var json = serializer.serializeValue(mirror); + var json = JSON.stringify(serializer.serializeValue(mirror)); // Check the mirror hierachy. assertTrue(mirror instanceof debug.Mirror); diff --git a/deps/v8/test/mjsunit/mirror-date.js b/deps/v8/test/mjsunit/mirror-date.js index a6334d0f1..6b6a3ad40 100644 --- a/deps/v8/test/mjsunit/mirror-date.js +++ b/deps/v8/test/mjsunit/mirror-date.js @@ -32,7 +32,7 @@ function testDateMirror(d, iso8601) { // Create mirror and JSON representation. var mirror = debug.MakeMirror(d); var serializer = debug.MakeMirrorSerializer(); - var json = serializer.serializeValue(mirror); + var json = JSON.stringify(serializer.serializeValue(mirror)); // Check the mirror hierachy. assertTrue(mirror instanceof debug.Mirror); @@ -55,9 +55,9 @@ function testDateMirror(d, iso8601) { assertEquals(iso8601, fromJSON.value); } - // Test Date values. -testDateMirror(new Date(Date.parse("Dec 25, 1995 1:30 UTC")), "1995-12-25T01:30:00.000Z"); +testDateMirror(new Date(Date.parse("Dec 25, 1995 1:30 UTC")), + "1995-12-25T01:30:00Z"); d = new Date(); d.setUTCFullYear(1967); d.setUTCMonth(0); // January. @@ -66,10 +66,10 @@ d.setUTCHours(9); d.setUTCMinutes(22); d.setUTCSeconds(59); d.setUTCMilliseconds(0); -testDateMirror(d, "1967-01-17T09:22:59.000Z"); +testDateMirror(d, "1967-01-17T09:22:59Z"); d.setUTCMilliseconds(1); -testDateMirror(d, "1967-01-17T09:22:59.001Z"); -d.setUTCMilliseconds(12); -testDateMirror(d, "1967-01-17T09:22:59.012Z"); -d.setUTCMilliseconds(123); -testDateMirror(d, "1967-01-17T09:22:59.123Z"); +testDateMirror(d, "1967-01-17T09:22:59Z"); +d.setUTCSeconds(12); +testDateMirror(d, "1967-01-17T09:22:12Z"); +d.setUTCSeconds(36); +testDateMirror(d, "1967-01-17T09:22:36Z"); diff --git a/deps/v8/test/mjsunit/mirror-error.js b/deps/v8/test/mjsunit/mirror-error.js index 37ec46c86..4ed8c1b42 100644 --- a/deps/v8/test/mjsunit/mirror-error.js +++ b/deps/v8/test/mjsunit/mirror-error.js @@ -44,8 +44,9 @@ function testErrorMirror(e) { // Create mirror and JSON representation. var mirror = debug.MakeMirror(e); var serializer = debug.MakeMirrorSerializer(); - var json = serializer.serializeValue(mirror); - var refs = new MirrorRefCache(serializer.serializeReferencedObjects()); + var json = JSON.stringify(serializer.serializeValue(mirror)); + var refs = new MirrorRefCache( + JSON.stringify(serializer.serializeReferencedObjects())); // Check the mirror hierachy. assertTrue(mirror instanceof debug.Mirror); diff --git a/deps/v8/test/mjsunit/mirror-function.js b/deps/v8/test/mjsunit/mirror-function.js index 59d9e7862..58aee3dae 100644 --- a/deps/v8/test/mjsunit/mirror-function.js +++ b/deps/v8/test/mjsunit/mirror-function.js @@ -44,8 +44,9 @@ function testFunctionMirror(f) { // Create mirror and JSON representation. var mirror = debug.MakeMirror(f); var serializer = debug.MakeMirrorSerializer(); - var json = serializer.serializeValue(mirror); - var refs = new MirrorRefCache(serializer.serializeReferencedObjects()); + var json = JSON.stringify(serializer.serializeValue(mirror)); + var refs = new MirrorRefCache( + JSON.stringify(serializer.serializeReferencedObjects())); // Check the mirror hierachy. assertTrue(mirror instanceof debug.Mirror); diff --git a/deps/v8/test/mjsunit/mirror-null.js b/deps/v8/test/mjsunit/mirror-null.js index db68966a4..1ee555b7b 100644 --- a/deps/v8/test/mjsunit/mirror-null.js +++ b/deps/v8/test/mjsunit/mirror-null.js @@ -31,7 +31,7 @@ // Create mirror and JSON representation. var mirror = debug.MakeMirror(null); var serializer = debug.MakeMirrorSerializer(); -var json = serializer.serializeValue(mirror); +var json = JSON.stringify(serializer.serializeValue(mirror)); // Check the mirror hierachy. assertTrue(mirror instanceof debug.Mirror); diff --git a/deps/v8/test/mjsunit/mirror-number.js b/deps/v8/test/mjsunit/mirror-number.js index 68eb0d7e7..2db5df439 100644 --- a/deps/v8/test/mjsunit/mirror-number.js +++ b/deps/v8/test/mjsunit/mirror-number.js @@ -32,7 +32,7 @@ function testNumberMirror(n) { // Create mirror and JSON representation. var mirror = debug.MakeMirror(n); var serializer = debug.MakeMirrorSerializer(); - var json = serializer.serializeValue(mirror); + var json = JSON.stringify(serializer.serializeValue(mirror)); // Check the mirror hierachy. assertTrue(mirror instanceof debug.Mirror); diff --git a/deps/v8/test/mjsunit/mirror-object.js b/deps/v8/test/mjsunit/mirror-object.js index e829a0e22..ad7add8ae 100644 --- a/deps/v8/test/mjsunit/mirror-object.js +++ b/deps/v8/test/mjsunit/mirror-object.js @@ -44,8 +44,9 @@ function testObjectMirror(obj, cls_name, ctor_name, hasSpecialProperties) { // Create mirror and JSON representation. var mirror = debug.MakeMirror(obj); var serializer = debug.MakeMirrorSerializer(); - var json = serializer.serializeValue(mirror); - var refs = new MirrorRefCache(serializer.serializeReferencedObjects()); + var json = JSON.stringify(serializer.serializeValue(mirror)); + var refs = new MirrorRefCache( + JSON.stringify(serializer.serializeReferencedObjects())); // Check the mirror hierachy. assertTrue(mirror instanceof debug.Mirror, 'Unexpected mirror hierachy'); @@ -105,7 +106,7 @@ function testObjectMirror(obj, cls_name, ctor_name, hasSpecialProperties) { assertEquals(names.length, fromJSON.properties.length, 'Some properties missing in JSON'); for (var i = 0; i < fromJSON.properties.length; i++) { var name = fromJSON.properties[i].name; - if (!name) name = fromJSON.properties[i].index; + if (typeof name == 'undefined') name = fromJSON.properties[i].index; var found = false; for (var j = 0; j < names.length; j++) { if (names[j] == name) { @@ -157,7 +158,6 @@ function Point(x,y) { this.y_ = y; } - // Test a number of different objects. testObjectMirror({}, 'Object', 'Object'); testObjectMirror({'a':1,'b':2}, 'Object', 'Object'); diff --git a/deps/v8/test/mjsunit/mirror-regexp.js b/deps/v8/test/mjsunit/mirror-regexp.js index 0490b17f0..8c834bf38 100644 --- a/deps/v8/test/mjsunit/mirror-regexp.js +++ b/deps/v8/test/mjsunit/mirror-regexp.js @@ -55,8 +55,9 @@ function testRegExpMirror(r) { // Create mirror and JSON representation. var mirror = debug.MakeMirror(r); var serializer = debug.MakeMirrorSerializer(); - var json = serializer.serializeValue(mirror); - var refs = new MirrorRefCache(serializer.serializeReferencedObjects()); + var json = JSON.stringify(serializer.serializeValue(mirror)); + var refs = new MirrorRefCache( + JSON.stringify(serializer.serializeReferencedObjects())); // Check the mirror hierachy. assertTrue(mirror instanceof debug.Mirror); diff --git a/deps/v8/test/mjsunit/mirror-script.js b/deps/v8/test/mjsunit/mirror-script.js index 61f0f3aa6..9b67b9ba0 100644 --- a/deps/v8/test/mjsunit/mirror-script.js +++ b/deps/v8/test/mjsunit/mirror-script.js @@ -25,14 +25,15 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// Flags: --expose-debug-as debug +// Flags: --expose-debug-as debug --allow-natives-syntax // Test the mirror object for scripts. -function testScriptMirror(f, file_name, file_lines, script_type, script_source) { +function testScriptMirror(f, file_name, file_lines, type, compilation_type, + source, eval_from_line) { // Create mirror and JSON representation. var mirror = debug.MakeMirror(f).script(); var serializer = debug.MakeMirrorSerializer(); - var json = serializer.serializeValue(mirror); + var json = JSON.stringify(serializer.serializeValue(mirror)); // Check the mirror hierachy. assertTrue(mirror instanceof debug.Mirror); @@ -53,13 +54,17 @@ function testScriptMirror(f, file_name, file_lines, script_type, script_source) if (file_lines > 0) { assertEquals(file_lines, mirror.lineCount()); } - assertEquals(script_type, mirror.scriptType()); - if (script_source) { - assertEquals(script_source, mirror.source()); + assertEquals(type, mirror.scriptType()); + assertEquals(compilation_type, mirror.compilationType(), "compilation type"); + if (source) { + assertEquals(source, mirror.source()); + } + if (eval_from_line) { + assertEquals(eval_from_line, mirror.evalFromLocation().line); } // Parse JSON representation and check. - var fromJSON = eval('(' + json + ')'); + var fromJSON = JSON.parse(json); assertEquals('script', fromJSON.type); name = fromJSON.name; if (name) { @@ -72,15 +77,18 @@ function testScriptMirror(f, file_name, file_lines, script_type, script_source) if (file_lines > 0) { assertEquals(file_lines, fromJSON.lineCount); } - assertEquals(script_type, fromJSON.scriptType); + assertEquals(type, fromJSON.scriptType); + assertEquals(compilation_type, fromJSON.compilationType); } // Test the script mirror for different functions. -testScriptMirror(function(){}, 'mirror-script.js', 92, 2); -testScriptMirror(Math.sin, 'native math.js', -1, 0); -testScriptMirror(eval('function(){}'), null, 1, 2, 'function(){}'); -testScriptMirror(eval('function(){\n }'), null, 2, 2, 'function(){\n }'); +testScriptMirror(function(){}, 'mirror-script.js', 100, 2, 0); +testScriptMirror(Math.sin, 'native math.js', -1, 0, 0); +testScriptMirror(eval('function(){}'), null, 1, 2, 1, 'function(){}', 87); +testScriptMirror(eval('function(){\n }'), null, 2, 2, 1, 'function(){\n }', 88); +testScriptMirror(%CompileString("({a:1,b:2})", true), null, 1, 2, 2, '({a:1,b:2})'); +testScriptMirror(%CompileString("({a:1,\n b:2})", true), null, 2, 2, 2, '({a:1,\n b:2})'); // Test taking slices of source. var mirror = debug.MakeMirror(eval('function(){\n 1;\n}')).script(); diff --git a/deps/v8/test/mjsunit/mirror-string.js b/deps/v8/test/mjsunit/mirror-string.js index eeabc5fe2..c241849d8 100644 --- a/deps/v8/test/mjsunit/mirror-string.js +++ b/deps/v8/test/mjsunit/mirror-string.js @@ -34,7 +34,7 @@ function testStringMirror(s) { // Create mirror and JSON representation. var mirror = debug.MakeMirror(s); var serializer = debug.MakeMirrorSerializer(); - var json = serializer.serializeValue(mirror); + var json = JSON.stringify(serializer.serializeValue(mirror)); // Check the mirror hierachy. assertTrue(mirror instanceof debug.Mirror); diff --git a/deps/v8/test/mjsunit/mirror-undefined.js b/deps/v8/test/mjsunit/mirror-undefined.js index 2b5b84c6a..7f63239e5 100644 --- a/deps/v8/test/mjsunit/mirror-undefined.js +++ b/deps/v8/test/mjsunit/mirror-undefined.js @@ -31,7 +31,7 @@ // Create mirror and JSON representation. var mirror = debug.MakeMirror(void 0); var serializer = debug.MakeMirrorSerializer(); -var json = serializer.serializeValue(mirror); +var json = JSON.stringify(serializer.serializeValue(mirror)); // Check the mirror hierachy. assertTrue(mirror instanceof debug.Mirror); diff --git a/deps/v8/test/mjsunit/mirror-unresolved-function.js b/deps/v8/test/mjsunit/mirror-unresolved-function.js index 8d8ca37f8..c1fe4a3ef 100644 --- a/deps/v8/test/mjsunit/mirror-unresolved-function.js +++ b/deps/v8/test/mjsunit/mirror-unresolved-function.js @@ -42,8 +42,9 @@ MirrorRefCache.prototype.lookup = function(handle) { var mirror = new debug.UnresolvedFunctionMirror("f"); var serializer = debug.MakeMirrorSerializer(); -var json = serializer.serializeValue(mirror); -var refs = new MirrorRefCache(serializer.serializeReferencedObjects()); +var json = JSON.stringify(serializer.serializeValue(mirror)); +var refs = new MirrorRefCache( + JSON.stringify(serializer.serializeReferencedObjects())); // Check the mirror hierachy for unresolved functions. assertTrue(mirror instanceof debug.Mirror); diff --git a/deps/v8/test/mjsunit/regexp.js b/deps/v8/test/mjsunit/regexp.js index e562df090..0a23d00ac 100644 --- a/deps/v8/test/mjsunit/regexp.js +++ b/deps/v8/test/mjsunit/regexp.js @@ -375,3 +375,16 @@ assertFalse(/x([0-7]%%%x|[0-6]%%%y)/.test('x7%%%y'), 'qt8'); // Don't hang on this one. /[^\xfe-\xff]*/.test(""); + + +var long = "a"; +for (var i = 0; i < 100000; i++) { + long = "a?" + long; +} +// Don't crash on this one, but maybe throw an exception. +try { + RegExp(long).exec("a"); +} catch (e) { + assertTrue(String(e).indexOf("Stack overflow") >= 0, "overflow"); +} + diff --git a/deps/v8/test/mjsunit/bugs/bug-334.js b/deps/v8/test/mjsunit/regress/regress-334.js index 024fc9e85..024fc9e85 100644 --- a/deps/v8/test/mjsunit/bugs/bug-334.js +++ b/deps/v8/test/mjsunit/regress/regress-334.js diff --git a/deps/v8/test/mjsunit/regress/regress-341.js b/deps/v8/test/mjsunit/regress/regress-341.js new file mode 100644 index 000000000..4db6bc696 --- /dev/null +++ b/deps/v8/test/mjsunit/regress/regress-341.js @@ -0,0 +1,36 @@ +// Copyright 2009 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Should not crash. +// See http://code.google.com/p/v8/issues/detail?id=341 + +function F() {} + +F.prototype = 1; +var o = {}; + +assertThrows("o instanceof F"); diff --git a/deps/v8/test/mjsunit/regress/regress-349.js b/deps/v8/test/mjsunit/regress/regress-349.js new file mode 100644 index 000000000..1a60e3e12 --- /dev/null +++ b/deps/v8/test/mjsunit/regress/regress-349.js @@ -0,0 +1,32 @@ +// Copyright 2009 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Should not crash. +// See http://code.google.com/p/v8/issues/detail?id=349 + +var str = "bbaabbbbbbbbabbaaaabbaaabbbaaaabbaaabbabaaabb"; +assertEquals(str, str.replace(/aabab/g, "foo")); diff --git a/deps/v8/test/mjsunit/regress/regress-351.js b/deps/v8/test/mjsunit/regress/regress-351.js new file mode 100644 index 000000000..44470db32 --- /dev/null +++ b/deps/v8/test/mjsunit/regress/regress-351.js @@ -0,0 +1,31 @@ +// Copyright 2009 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Should use index of 0 if provided index is negative. +// See http://code.google.com/p/v8/issues/detail?id=351 + +assertEquals(0, "test".lastIndexOf("test", -1)); diff --git a/deps/v8/test/mjsunit/string-lastindexof.js b/deps/v8/test/mjsunit/string-lastindexof.js index bf46666ee..27378f789 100644 --- a/deps/v8/test/mjsunit/string-lastindexof.js +++ b/deps/v8/test/mjsunit/string-lastindexof.js @@ -27,25 +27,62 @@ var s = "test test test"; -assertEquals(5, s.lastIndexOf("test", 5)); -assertEquals(5, s.lastIndexOf("test", 6)); -assertEquals(0, s.lastIndexOf("test", 4)); -assertEquals(0, s.lastIndexOf("test", 0)); -assertEquals(-1, s.lastIndexOf("test", -1)); -assertEquals(10, s.lastIndexOf("test")); -assertEquals(-1, s.lastIndexOf("notpresent")); -assertEquals(-1, s.lastIndexOf()); -assertEquals(10, s.lastIndexOf("test", "not a number")); +var MAX_DOUBLE = 1.7976931348623157e+308; +var MIN_DOUBLE = -MAX_DOUBLE; +var MAX_SMI = Math.pow(2,30)-1; +var MIN_SMI = -Math.pow(2,30); + +assertEquals(10, s.lastIndexOf("test", Infinity), "tinf"); +assertEquals(10, s.lastIndexOf("test", MAX_DOUBLE), "tmaxdouble"); +assertEquals(10, s.lastIndexOf("test", MAX_SMI), "tmaxsmi"); +assertEquals(10, s.lastIndexOf("test", s.length * 2), "t2length"); +assertEquals(10, s.lastIndexOf("test", 15), "t15"); +assertEquals(10, s.lastIndexOf("test", 14), "t14"); +assertEquals(10, s.lastIndexOf("test", 10), "t10"); +assertEquals(5, s.lastIndexOf("test", 9), "t9"); +assertEquals(5, s.lastIndexOf("test", 6), "t6"); +assertEquals(5, s.lastIndexOf("test", 5), "t5"); +assertEquals(0, s.lastIndexOf("test", 4), "t4"); +assertEquals(0, s.lastIndexOf("test", 0), "t0"); +assertEquals(0, s.lastIndexOf("test", -1), "t-1"); +assertEquals(0, s.lastIndexOf("test", -s.length), "t-len"); +assertEquals(0, s.lastIndexOf("test", MIN_SMI), "tminsmi"); +assertEquals(0, s.lastIndexOf("test", MIN_DOUBLE), "tmindouble"); +assertEquals(0, s.lastIndexOf("test", -Infinity), "tneginf"); +assertEquals(10, s.lastIndexOf("test"), "t"); +assertEquals(-1, s.lastIndexOf("notpresent"), "n"); +assertEquals(-1, s.lastIndexOf(), "none"); +assertEquals(10, s.lastIndexOf("test", "not a number"), "nan"); + +var longNonMatch = "overlong string that doesn't match"; +var longAlmostMatch = "test test test!"; +var longAlmostMatch2 = "!test test test"; + + +assertEquals(-1, s.lastIndexOf(longNonMatch), "long"); +assertEquals(-1, s.lastIndexOf(longNonMatch, 10), "longpos"); +assertEquals(-1, s.lastIndexOf(longNonMatch, NaN), "longnan"); +assertEquals(-1, s.lastIndexOf(longAlmostMatch), "tlong"); +assertEquals(-1, s.lastIndexOf(longAlmostMatch, 10), "tlongpos"); +assertEquals(-1, s.lastIndexOf(longAlmostMatch), "tlongnan"); + +var nonInitialMatch = "est"; + +assertEquals(-1, s.lastIndexOf(nonInitialMatch, 0), "noninit"); +assertEquals(-1, s.lastIndexOf(nonInitialMatch, -1), "noninitneg"); +assertEquals(-1, s.lastIndexOf(nonInitialMatch, MIN_SMI), "noninitminsmi"); +assertEquals(-1, s.lastIndexOf(nonInitialMatch, MIN_DOUBLE), "noninitmindbl"); +assertEquals(-1, s.lastIndexOf(nonInitialMatch, -Infinity), "noninitneginf"); for (var i = s.length + 10; i >= 0; i--) { var expected = i < s.length ? i : s.length; - assertEquals(expected, s.lastIndexOf("", i)); + assertEquals(expected, s.lastIndexOf("", i), "empty" + i); } var reString = "asdf[a-z]+(asdf)?"; -assertEquals(4, reString.lastIndexOf("[a-z]+")); -assertEquals(10, reString.lastIndexOf("(asdf)?")); +assertEquals(4, reString.lastIndexOf("[a-z]+"), "r4"); +assertEquals(10, reString.lastIndexOf("(asdf)?"), "r10"); -assertEquals(1, String.prototype.lastIndexOf.length); +assertEquals(1, String.prototype.lastIndexOf.length, "length"); |