diff options
Diffstat (limited to 'src/3rdparty/v8/test/cctest/test-api.cc')
-rw-r--r-- | src/3rdparty/v8/test/cctest/test-api.cc | 18323 |
1 files changed, 0 insertions, 18323 deletions
diff --git a/src/3rdparty/v8/test/cctest/test-api.cc b/src/3rdparty/v8/test/cctest/test-api.cc deleted file mode 100644 index 48d8436..0000000 --- a/src/3rdparty/v8/test/cctest/test-api.cc +++ /dev/null @@ -1,18323 +0,0 @@ -// Copyright 2012 the V8 project authors. All rights reserved. -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following -// disclaimer in the documentation and/or other materials provided -// with the distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived -// from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#include <limits.h> - -#ifndef WIN32 -#include <signal.h> // kill -#include <unistd.h> // getpid -#endif // WIN32 - -#include "v8.h" - -#include "api.h" -#include "isolate.h" -#include "compilation-cache.h" -#include "execution.h" -#include "objects.h" -#include "snapshot.h" -#include "platform.h" -#include "utils.h" -#include "cctest.h" -#include "parser.h" -#include "unicode-inl.h" - -static const bool kLogThreading = false; - -static bool IsNaN(double x) { -#ifdef WIN32 - return _isnan(x); -#else - return isnan(x); -#endif -} - -using ::v8::AccessorInfo; -using ::v8::Arguments; -using ::v8::Context; -using ::v8::Extension; -using ::v8::Function; -using ::v8::FunctionTemplate; -using ::v8::Handle; -using ::v8::HandleScope; -using ::v8::Local; -using ::v8::Message; -using ::v8::MessageCallback; -using ::v8::Object; -using ::v8::ObjectTemplate; -using ::v8::Persistent; -using ::v8::Script; -using ::v8::StackTrace; -using ::v8::String; -using ::v8::TryCatch; -using ::v8::Undefined; -using ::v8::V8; -using ::v8::Value; - - -static void ExpectString(const char* code, const char* expected) { - Local<Value> result = CompileRun(code); - CHECK(result->IsString()); - String::AsciiValue ascii(result); - CHECK_EQ(expected, *ascii); -} - -static void ExpectInt32(const char* code, int expected) { - Local<Value> result = CompileRun(code); - CHECK(result->IsInt32()); - CHECK_EQ(expected, result->Int32Value()); -} - -static void ExpectBoolean(const char* code, bool expected) { - Local<Value> result = CompileRun(code); - CHECK(result->IsBoolean()); - CHECK_EQ(expected, result->BooleanValue()); -} - - -static void ExpectTrue(const char* code) { - ExpectBoolean(code, true); -} - - -static void ExpectFalse(const char* code) { - ExpectBoolean(code, false); -} - - -static void ExpectObject(const char* code, Local<Value> expected) { - Local<Value> result = CompileRun(code); - CHECK(result->Equals(expected)); -} - - -static void ExpectUndefined(const char* code) { - Local<Value> result = CompileRun(code); - CHECK(result->IsUndefined()); -} - - -static int signature_callback_count; -static v8::Handle<Value> IncrementingSignatureCallback( - const v8::Arguments& args) { - ApiTestFuzzer::Fuzz(); - signature_callback_count++; - v8::Handle<v8::Array> result = v8::Array::New(args.Length()); - for (int i = 0; i < args.Length(); i++) - result->Set(v8::Integer::New(i), args[i]); - return result; -} - - -static v8::Handle<Value> SignatureCallback(const v8::Arguments& args) { - ApiTestFuzzer::Fuzz(); - v8::Handle<v8::Array> result = v8::Array::New(args.Length()); - for (int i = 0; i < args.Length(); i++) { - result->Set(v8::Integer::New(i), args[i]); - } - return result; -} - - -THREADED_TEST(Handles) { - v8::HandleScope scope; - Local<Context> local_env; - { - LocalContext env; - local_env = env.local(); - } - - // Local context should still be live. - CHECK(!local_env.IsEmpty()); - local_env->Enter(); - - v8::Handle<v8::Primitive> undef = v8::Undefined(); - CHECK(!undef.IsEmpty()); - CHECK(undef->IsUndefined()); - - const char* c_source = "1 + 2 + 3"; - Local<String> source = String::New(c_source); - Local<Script> script = Script::Compile(source); - CHECK_EQ(6, script->Run()->Int32Value()); - - local_env->Exit(); -} - - -THREADED_TEST(IsolateOfContext) { - v8::HandleScope scope; - v8::Persistent<Context> env = Context::New(); - - CHECK(!env->InContext()); - CHECK(env->GetIsolate() == v8::Isolate::GetCurrent()); - env->Enter(); - CHECK(env->InContext()); - CHECK(env->GetIsolate() == v8::Isolate::GetCurrent()); - env->Exit(); - CHECK(!env->InContext()); - CHECK(env->GetIsolate() == v8::Isolate::GetCurrent()); - - env.Dispose(env->GetIsolate()); -} - - -THREADED_TEST(ReceiverSignature) { - v8::HandleScope scope; - LocalContext env; - v8::Handle<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(); - v8::Handle<v8::Signature> sig = v8::Signature::New(fun); - fun->PrototypeTemplate()->Set( - v8_str("m"), - v8::FunctionTemplate::New(IncrementingSignatureCallback, - v8::Handle<Value>(), - sig)); - env->Global()->Set(v8_str("Fun"), fun->GetFunction()); - signature_callback_count = 0; - CompileRun( - "var o = new Fun();" - "o.m();"); - CHECK_EQ(1, signature_callback_count); - v8::Handle<v8::FunctionTemplate> sub_fun = v8::FunctionTemplate::New(); - sub_fun->Inherit(fun); - env->Global()->Set(v8_str("SubFun"), sub_fun->GetFunction()); - CompileRun( - "var o = new SubFun();" - "o.m();"); - CHECK_EQ(2, signature_callback_count); - - v8::TryCatch try_catch; - CompileRun( - "var o = { };" - "o.m = Fun.prototype.m;" - "o.m();"); - CHECK_EQ(2, signature_callback_count); - CHECK(try_catch.HasCaught()); - try_catch.Reset(); - v8::Handle<v8::FunctionTemplate> unrel_fun = v8::FunctionTemplate::New(); - sub_fun->Inherit(fun); - env->Global()->Set(v8_str("UnrelFun"), unrel_fun->GetFunction()); - CompileRun( - "var o = new UnrelFun();" - "o.m = Fun.prototype.m;" - "o.m();"); - CHECK_EQ(2, signature_callback_count); - CHECK(try_catch.HasCaught()); -} - - -THREADED_TEST(ArgumentSignature) { - v8::HandleScope scope; - LocalContext env; - v8::Handle<v8::FunctionTemplate> cons = v8::FunctionTemplate::New(); - cons->SetClassName(v8_str("Cons")); - v8::Handle<v8::Signature> sig = - v8::Signature::New(v8::Handle<v8::FunctionTemplate>(), 1, &cons); - v8::Handle<v8::FunctionTemplate> fun = - v8::FunctionTemplate::New(SignatureCallback, v8::Handle<Value>(), sig); - env->Global()->Set(v8_str("Cons"), cons->GetFunction()); - env->Global()->Set(v8_str("Fun1"), fun->GetFunction()); - - v8::Handle<Value> value1 = CompileRun("Fun1(4) == '';"); - CHECK(value1->IsTrue()); - - v8::Handle<Value> value2 = CompileRun("Fun1(new Cons()) == '[object Cons]';"); - CHECK(value2->IsTrue()); - - v8::Handle<Value> value3 = CompileRun("Fun1() == '';"); - CHECK(value3->IsTrue()); - - v8::Handle<v8::FunctionTemplate> cons1 = v8::FunctionTemplate::New(); - cons1->SetClassName(v8_str("Cons1")); - v8::Handle<v8::FunctionTemplate> cons2 = v8::FunctionTemplate::New(); - cons2->SetClassName(v8_str("Cons2")); - v8::Handle<v8::FunctionTemplate> cons3 = v8::FunctionTemplate::New(); - cons3->SetClassName(v8_str("Cons3")); - - v8::Handle<v8::FunctionTemplate> args[3] = { cons1, cons2, cons3 }; - v8::Handle<v8::Signature> wsig = - v8::Signature::New(v8::Handle<v8::FunctionTemplate>(), 3, args); - v8::Handle<v8::FunctionTemplate> fun2 = - v8::FunctionTemplate::New(SignatureCallback, v8::Handle<Value>(), wsig); - - env->Global()->Set(v8_str("Cons1"), cons1->GetFunction()); - env->Global()->Set(v8_str("Cons2"), cons2->GetFunction()); - env->Global()->Set(v8_str("Cons3"), cons3->GetFunction()); - env->Global()->Set(v8_str("Fun2"), fun2->GetFunction()); - v8::Handle<Value> value4 = CompileRun( - "Fun2(new Cons1(), new Cons2(), new Cons3()) ==" - "'[object Cons1],[object Cons2],[object Cons3]'"); - CHECK(value4->IsTrue()); - - v8::Handle<Value> value5 = CompileRun( - "Fun2(new Cons1(), new Cons2(), 5) == '[object Cons1],[object Cons2],'"); - CHECK(value5->IsTrue()); - - v8::Handle<Value> value6 = CompileRun( - "Fun2(new Cons3(), new Cons2(), new Cons1()) == ',[object Cons2],'"); - CHECK(value6->IsTrue()); - - v8::Handle<Value> value7 = CompileRun( - "Fun2(new Cons1(), new Cons2(), new Cons3(), 'd') == " - "'[object Cons1],[object Cons2],[object Cons3],d';"); - CHECK(value7->IsTrue()); - - v8::Handle<Value> value8 = CompileRun( - "Fun2(new Cons1(), new Cons2()) == '[object Cons1],[object Cons2]'"); - CHECK(value8->IsTrue()); -} - - -THREADED_TEST(HulIgennem) { - v8::HandleScope scope; - LocalContext env; - v8::Handle<v8::Primitive> undef = v8::Undefined(); - Local<String> undef_str = undef->ToString(); - char* value = i::NewArray<char>(undef_str->Length() + 1); - undef_str->WriteAscii(value); - CHECK_EQ(0, strcmp(value, "undefined")); - i::DeleteArray(value); -} - - -THREADED_TEST(Access) { - v8::HandleScope scope; - LocalContext env; - Local<v8::Object> obj = v8::Object::New(); - Local<Value> foo_before = obj->Get(v8_str("foo")); - CHECK(foo_before->IsUndefined()); - Local<String> bar_str = v8_str("bar"); - obj->Set(v8_str("foo"), bar_str); - Local<Value> foo_after = obj->Get(v8_str("foo")); - CHECK(!foo_after->IsUndefined()); - CHECK(foo_after->IsString()); - CHECK_EQ(bar_str, foo_after); -} - - -THREADED_TEST(AccessElement) { - v8::HandleScope scope; - LocalContext env; - Local<v8::Object> obj = v8::Object::New(); - Local<Value> before = obj->Get(1); - CHECK(before->IsUndefined()); - Local<String> bar_str = v8_str("bar"); - obj->Set(1, bar_str); - Local<Value> after = obj->Get(1); - CHECK(!after->IsUndefined()); - CHECK(after->IsString()); - CHECK_EQ(bar_str, after); - - Local<v8::Array> value = CompileRun("[\"a\", \"b\"]").As<v8::Array>(); - CHECK_EQ(v8_str("a"), value->Get(0)); - CHECK_EQ(v8_str("b"), value->Get(1)); -} - - -THREADED_TEST(Script) { - v8::HandleScope scope; - LocalContext env; - const char* c_source = "1 + 2 + 3"; - Local<String> source = String::New(c_source); - Local<Script> script = Script::Compile(source); - CHECK_EQ(6, script->Run()->Int32Value()); -} - - -static uint16_t* AsciiToTwoByteString(const char* source) { - int array_length = i::StrLength(source) + 1; - uint16_t* converted = i::NewArray<uint16_t>(array_length); - for (int i = 0; i < array_length; i++) converted[i] = source[i]; - return converted; -} - - -class TestResource: public String::ExternalStringResource { - public: - explicit TestResource(uint16_t* data, int* counter = NULL) - : data_(data), length_(0), counter_(counter) { - while (data[length_]) ++length_; - } - - ~TestResource() { - i::DeleteArray(data_); - if (counter_ != NULL) ++*counter_; - } - - const uint16_t* data() const { - return data_; - } - - size_t length() const { - return length_; - } - private: - uint16_t* data_; - size_t length_; - int* counter_; -}; - - -class TestAsciiResource: public String::ExternalAsciiStringResource { - public: - explicit TestAsciiResource(const char* data, int* counter = NULL) - : data_(data), length_(strlen(data)), counter_(counter) { } - - ~TestAsciiResource() { - i::DeleteArray(data_); - if (counter_ != NULL) ++*counter_; - } - - const char* data() const { - return data_; - } - - size_t length() const { - return length_; - } - private: - const char* data_; - size_t length_; - int* counter_; -}; - - -THREADED_TEST(ScriptUsingStringResource) { - int dispose_count = 0; - const char* c_source = "1 + 2 * 3"; - uint16_t* two_byte_source = AsciiToTwoByteString(c_source); - { - v8::HandleScope scope; - LocalContext env; - TestResource* resource = new TestResource(two_byte_source, &dispose_count); - Local<String> source = String::NewExternal(resource); - Local<Script> script = Script::Compile(source); - Local<Value> value = script->Run(); - CHECK(value->IsNumber()); - CHECK_EQ(7, value->Int32Value()); - CHECK(source->IsExternal()); - CHECK_EQ(resource, - static_cast<TestResource*>(source->GetExternalStringResource())); - String::Encoding encoding = String::UNKNOWN_ENCODING; - CHECK_EQ(static_cast<const String::ExternalStringResourceBase*>(resource), - source->GetExternalStringResourceBase(&encoding)); - CHECK_EQ(String::TWO_BYTE_ENCODING, encoding); - HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); - CHECK_EQ(0, dispose_count); - } - v8::internal::Isolate::Current()->compilation_cache()->Clear(); - HEAP->CollectAllAvailableGarbage(); - CHECK_EQ(1, dispose_count); -} - - -THREADED_TEST(ScriptUsingAsciiStringResource) { - int dispose_count = 0; - const char* c_source = "1 + 2 * 3"; - { - v8::HandleScope scope; - LocalContext env; - TestAsciiResource* resource = new TestAsciiResource(i::StrDup(c_source), - &dispose_count); - Local<String> source = String::NewExternal(resource); - CHECK(source->IsExternalAscii()); - CHECK_EQ(static_cast<const String::ExternalStringResourceBase*>(resource), - source->GetExternalAsciiStringResource()); - String::Encoding encoding = String::UNKNOWN_ENCODING; - CHECK_EQ(static_cast<const String::ExternalStringResourceBase*>(resource), - source->GetExternalStringResourceBase(&encoding)); - CHECK_EQ(String::ASCII_ENCODING, encoding); - Local<Script> script = Script::Compile(source); - Local<Value> value = script->Run(); - CHECK(value->IsNumber()); - CHECK_EQ(7, value->Int32Value()); - HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); - CHECK_EQ(0, dispose_count); - } - i::Isolate::Current()->compilation_cache()->Clear(); - HEAP->CollectAllAvailableGarbage(); - CHECK_EQ(1, dispose_count); -} - - -THREADED_TEST(ScriptMakingExternalString) { - int dispose_count = 0; - uint16_t* two_byte_source = AsciiToTwoByteString("1 + 2 * 3"); - { - v8::HandleScope scope; - LocalContext env; - Local<String> source = String::New(two_byte_source); - // Trigger GCs so that the newly allocated string moves to old gen. - HEAP->CollectGarbage(i::NEW_SPACE); // in survivor space now - HEAP->CollectGarbage(i::NEW_SPACE); // in old gen now - CHECK_EQ(source->IsExternal(), false); - CHECK_EQ(source->IsExternalAscii(), false); - String::Encoding encoding = String::UNKNOWN_ENCODING; - CHECK_EQ(NULL, source->GetExternalStringResourceBase(&encoding)); - CHECK_EQ(String::ASCII_ENCODING, encoding); - bool success = source->MakeExternal(new TestResource(two_byte_source, - &dispose_count)); - CHECK(success); - Local<Script> script = Script::Compile(source); - Local<Value> value = script->Run(); - CHECK(value->IsNumber()); - CHECK_EQ(7, value->Int32Value()); - HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); - CHECK_EQ(0, dispose_count); - } - i::Isolate::Current()->compilation_cache()->Clear(); - HEAP->CollectAllGarbage(i::Heap::kAbortIncrementalMarkingMask); - CHECK_EQ(1, dispose_count); -} - - -THREADED_TEST(ScriptMakingExternalAsciiString) { - int dispose_count = 0; - const char* c_source = "1 + 2 * 3"; - { - v8::HandleScope scope; - LocalContext env; - Local<String> source = v8_str(c_source); - // Trigger GCs so that the newly allocated string moves to old gen. - HEAP->CollectGarbage(i::NEW_SPACE); // in survivor space now - HEAP->CollectGarbage(i::NEW_SPACE); // in old gen now - bool success = source->MakeExternal( - new TestAsciiResource(i::StrDup(c_source), &dispose_count)); - CHECK(success); - Local<Script> script = Script::Compile(source); - Local<Value> value = script->Run(); - CHECK(value->IsNumber()); - CHECK_EQ(7, value->Int32Value()); - HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); - CHECK_EQ(0, dispose_count); - } - i::Isolate::Current()->compilation_cache()->Clear(); - HEAP->CollectAllGarbage(i::Heap::kAbortIncrementalMarkingMask); - CHECK_EQ(1, dispose_count); -} - - -TEST(MakingExternalStringConditions) { - v8::HandleScope scope; - LocalContext env; - - // Free some space in the new space so that we can check freshness. - HEAP->CollectGarbage(i::NEW_SPACE); - HEAP->CollectGarbage(i::NEW_SPACE); - - uint16_t* two_byte_string = AsciiToTwoByteString("s1"); - Local<String> small_string = String::New(two_byte_string); - i::DeleteArray(two_byte_string); - - // We should refuse to externalize newly created small string. - CHECK(!small_string->CanMakeExternal()); - // Trigger GCs so that the newly allocated string moves to old gen. - HEAP->CollectGarbage(i::NEW_SPACE); // in survivor space now - HEAP->CollectGarbage(i::NEW_SPACE); // in old gen now - // Old space strings should be accepted. - CHECK(small_string->CanMakeExternal()); - - two_byte_string = AsciiToTwoByteString("small string 2"); - small_string = String::New(two_byte_string); - i::DeleteArray(two_byte_string); - - // We should refuse externalizing newly created small string. - CHECK(!small_string->CanMakeExternal()); - for (int i = 0; i < 100; i++) { - String::Value value(small_string); - } - // Frequently used strings should be accepted. - CHECK(small_string->CanMakeExternal()); - - const int buf_size = 10 * 1024; - char* buf = i::NewArray<char>(buf_size); - memset(buf, 'a', buf_size); - buf[buf_size - 1] = '\0'; - - two_byte_string = AsciiToTwoByteString(buf); - Local<String> large_string = String::New(two_byte_string); - i::DeleteArray(buf); - i::DeleteArray(two_byte_string); - // Large strings should be immediately accepted. - CHECK(large_string->CanMakeExternal()); -} - - -TEST(MakingExternalAsciiStringConditions) { - v8::HandleScope scope; - LocalContext env; - - // Free some space in the new space so that we can check freshness. - HEAP->CollectGarbage(i::NEW_SPACE); - HEAP->CollectGarbage(i::NEW_SPACE); - - Local<String> small_string = String::New("s1"); - // We should refuse to externalize newly created small string. - CHECK(!small_string->CanMakeExternal()); - // Trigger GCs so that the newly allocated string moves to old gen. - HEAP->CollectGarbage(i::NEW_SPACE); // in survivor space now - HEAP->CollectGarbage(i::NEW_SPACE); // in old gen now - // Old space strings should be accepted. - CHECK(small_string->CanMakeExternal()); - - small_string = String::New("small string 2"); - // We should refuse externalizing newly created small string. - CHECK(!small_string->CanMakeExternal()); - for (int i = 0; i < 100; i++) { - String::Value value(small_string); - } - // Frequently used strings should be accepted. - CHECK(small_string->CanMakeExternal()); - - const int buf_size = 10 * 1024; - char* buf = i::NewArray<char>(buf_size); - memset(buf, 'a', buf_size); - buf[buf_size - 1] = '\0'; - Local<String> large_string = String::New(buf); - i::DeleteArray(buf); - // Large strings should be immediately accepted. - CHECK(large_string->CanMakeExternal()); -} - - -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. - HEAP->CollectGarbage(i::NEW_SPACE); // in survivor space now - HEAP->CollectGarbage(i::NEW_SPACE); // in old gen now - i::Handle<i::String> isymbol = - FACTORY->InternalizedStringFromString(istring); - CHECK(isymbol->IsInternalizedString()); - } - HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); - HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); -} - - -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. - HEAP->CollectGarbage(i::NEW_SPACE); // in survivor space now - HEAP->CollectGarbage(i::NEW_SPACE); // in old gen now - i::Handle<i::String> isymbol = - FACTORY->InternalizedStringFromString(istring); - CHECK(isymbol->IsInternalizedString()); - } - HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); - HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); -} - - -THREADED_TEST(ScavengeExternalString) { - i::FLAG_stress_compaction = false; - i::FLAG_gc_global = false; - int dispose_count = 0; - bool in_new_space = false; - { - v8::HandleScope scope; - uint16_t* two_byte_string = AsciiToTwoByteString("test string"); - Local<String> string = - String::NewExternal(new TestResource(two_byte_string, - &dispose_count)); - i::Handle<i::String> istring = v8::Utils::OpenHandle(*string); - HEAP->CollectGarbage(i::NEW_SPACE); - in_new_space = HEAP->InNewSpace(*istring); - CHECK(in_new_space || HEAP->old_data_space()->Contains(*istring)); - CHECK_EQ(0, dispose_count); - } - HEAP->CollectGarbage(in_new_space ? i::NEW_SPACE : i::OLD_DATA_SPACE); - CHECK_EQ(1, dispose_count); -} - - -THREADED_TEST(ScavengeExternalAsciiString) { - i::FLAG_stress_compaction = false; - i::FLAG_gc_global = false; - int dispose_count = 0; - bool in_new_space = false; - { - v8::HandleScope scope; - const char* one_byte_string = "test string"; - Local<String> string = String::NewExternal( - new TestAsciiResource(i::StrDup(one_byte_string), &dispose_count)); - i::Handle<i::String> istring = v8::Utils::OpenHandle(*string); - HEAP->CollectGarbage(i::NEW_SPACE); - in_new_space = HEAP->InNewSpace(*istring); - CHECK(in_new_space || HEAP->old_data_space()->Contains(*istring)); - CHECK_EQ(0, dispose_count); - } - HEAP->CollectGarbage(in_new_space ? i::NEW_SPACE : i::OLD_DATA_SPACE); - CHECK_EQ(1, dispose_count); -} - - -class TestAsciiResourceWithDisposeControl: public TestAsciiResource { - public: - // Only used by non-threaded tests, so it can use static fields. - static int dispose_calls; - static int dispose_count; - - TestAsciiResourceWithDisposeControl(const char* data, bool dispose) - : TestAsciiResource(data, &dispose_count), - dispose_(dispose) { } - - void Dispose() { - ++dispose_calls; - if (dispose_) delete this; - } - private: - bool dispose_; -}; - - -int TestAsciiResourceWithDisposeControl::dispose_count = 0; -int TestAsciiResourceWithDisposeControl::dispose_calls = 0; - - -TEST(ExternalStringWithDisposeHandling) { - const char* c_source = "1 + 2 * 3"; - - // Use a stack allocated external string resource allocated object. - TestAsciiResourceWithDisposeControl::dispose_count = 0; - TestAsciiResourceWithDisposeControl::dispose_calls = 0; - TestAsciiResourceWithDisposeControl res_stack(i::StrDup(c_source), false); - { - v8::HandleScope scope; - LocalContext env; - Local<String> source = String::NewExternal(&res_stack); - Local<Script> script = Script::Compile(source); - Local<Value> value = script->Run(); - CHECK(value->IsNumber()); - CHECK_EQ(7, value->Int32Value()); - HEAP->CollectAllAvailableGarbage(); - CHECK_EQ(0, TestAsciiResourceWithDisposeControl::dispose_count); - } - i::Isolate::Current()->compilation_cache()->Clear(); - HEAP->CollectAllAvailableGarbage(); - CHECK_EQ(1, TestAsciiResourceWithDisposeControl::dispose_calls); - CHECK_EQ(0, TestAsciiResourceWithDisposeControl::dispose_count); - - // Use a heap allocated external string resource allocated object. - TestAsciiResourceWithDisposeControl::dispose_count = 0; - TestAsciiResourceWithDisposeControl::dispose_calls = 0; - TestAsciiResource* res_heap = - new TestAsciiResourceWithDisposeControl(i::StrDup(c_source), true); - { - v8::HandleScope scope; - LocalContext env; - Local<String> source = String::NewExternal(res_heap); - Local<Script> script = Script::Compile(source); - Local<Value> value = script->Run(); - CHECK(value->IsNumber()); - CHECK_EQ(7, value->Int32Value()); - HEAP->CollectAllAvailableGarbage(); - CHECK_EQ(0, TestAsciiResourceWithDisposeControl::dispose_count); - } - i::Isolate::Current()->compilation_cache()->Clear(); - HEAP->CollectAllAvailableGarbage(); - CHECK_EQ(1, TestAsciiResourceWithDisposeControl::dispose_calls); - CHECK_EQ(1, TestAsciiResourceWithDisposeControl::dispose_count); -} - - -THREADED_TEST(StringConcat) { - { - v8::HandleScope scope; - LocalContext env; - const char* one_byte_string_1 = "function a_times_t"; - const char* two_byte_string_1 = "wo_plus_b(a, b) {return "; - const char* one_byte_extern_1 = "a * 2 + b;} a_times_two_plus_b(4, 8) + "; - const char* two_byte_extern_1 = "a_times_two_plus_b(4, 8) + "; - const char* one_byte_string_2 = "a_times_two_plus_b(4, 8) + "; - const char* two_byte_string_2 = "a_times_two_plus_b(4, 8) + "; - const char* two_byte_extern_2 = "a_times_two_plus_b(1, 2);"; - Local<String> left = v8_str(one_byte_string_1); - - uint16_t* two_byte_source = AsciiToTwoByteString(two_byte_string_1); - Local<String> right = String::New(two_byte_source); - i::DeleteArray(two_byte_source); - - Local<String> source = String::Concat(left, right); - right = String::NewExternal( - new TestAsciiResource(i::StrDup(one_byte_extern_1))); - source = String::Concat(source, right); - right = String::NewExternal( - new TestResource(AsciiToTwoByteString(two_byte_extern_1))); - source = String::Concat(source, right); - right = v8_str(one_byte_string_2); - source = String::Concat(source, right); - - two_byte_source = AsciiToTwoByteString(two_byte_string_2); - right = String::New(two_byte_source); - i::DeleteArray(two_byte_source); - - source = String::Concat(source, right); - right = String::NewExternal( - new TestResource(AsciiToTwoByteString(two_byte_extern_2))); - source = String::Concat(source, right); - Local<Script> script = Script::Compile(source); - Local<Value> value = script->Run(); - CHECK(value->IsNumber()); - CHECK_EQ(68, value->Int32Value()); - } - i::Isolate::Current()->compilation_cache()->Clear(); - HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); - HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); -} - - -THREADED_TEST(GlobalProperties) { - v8::HandleScope scope; - LocalContext env; - v8::Handle<v8::Object> global = env->Global(); - global->Set(v8_str("pi"), v8_num(3.1415926)); - Local<Value> pi = global->Get(v8_str("pi")); - CHECK_EQ(3.1415926, pi->NumberValue()); -} - - -static v8::Handle<Value> handle_call(const v8::Arguments& args) { - ApiTestFuzzer::Fuzz(); - return v8_num(102); -} - - -static v8::Handle<Value> construct_call(const v8::Arguments& args) { - ApiTestFuzzer::Fuzz(); - args.This()->Set(v8_str("x"), v8_num(1)); - args.This()->Set(v8_str("y"), v8_num(2)); - return args.This(); -} - -static v8::Handle<Value> Return239(Local<String> name, const AccessorInfo&) { - ApiTestFuzzer::Fuzz(); - return v8_num(239); -} - - -THREADED_TEST(FunctionTemplate) { - v8::HandleScope scope; - LocalContext env; - { - Local<v8::FunctionTemplate> fun_templ = - v8::FunctionTemplate::New(handle_call); - Local<Function> fun = fun_templ->GetFunction(); - env->Global()->Set(v8_str("obj"), fun); - Local<Script> script = v8_compile("obj()"); - CHECK_EQ(102, script->Run()->Int32Value()); - } - // Use SetCallHandler to initialize a function template, should work like the - // previous one. - { - Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); - fun_templ->SetCallHandler(handle_call); - Local<Function> fun = fun_templ->GetFunction(); - env->Global()->Set(v8_str("obj"), fun); - Local<Script> script = v8_compile("obj()"); - CHECK_EQ(102, script->Run()->Int32Value()); - } - // Test constructor calls. - { - Local<v8::FunctionTemplate> fun_templ = - v8::FunctionTemplate::New(construct_call); - fun_templ->SetClassName(v8_str("funky")); - fun_templ->InstanceTemplate()->SetAccessor(v8_str("m"), Return239); - Local<Function> fun = fun_templ->GetFunction(); - env->Global()->Set(v8_str("obj"), fun); - Local<Script> script = v8_compile("var s = new obj(); s.x"); - CHECK_EQ(1, script->Run()->Int32Value()); - - Local<Value> result = v8_compile("(new obj()).toString()")->Run(); - CHECK_EQ(v8_str("[object funky]"), result); - - result = v8_compile("(new obj()).m")->Run(); - CHECK_EQ(239, result->Int32Value()); - } -} - - -THREADED_TEST(FunctionTemplateSetLength) { - v8::HandleScope scope; - LocalContext env; - { - Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New( - handle_call, Handle<v8::Value>(), Handle<v8::Signature>(), 23); - Local<Function> fun = fun_templ->GetFunction(); - env->Global()->Set(v8_str("obj"), fun); - Local<Script> script = v8_compile("obj.length"); - CHECK_EQ(23, script->Run()->Int32Value()); - } - { - Local<v8::FunctionTemplate> fun_templ = - v8::FunctionTemplate::New(handle_call); - fun_templ->SetLength(22); - Local<Function> fun = fun_templ->GetFunction(); - env->Global()->Set(v8_str("obj"), fun); - Local<Script> script = v8_compile("obj.length"); - CHECK_EQ(22, script->Run()->Int32Value()); - } - { - // Without setting length it defaults to 0. - Local<v8::FunctionTemplate> fun_templ = - v8::FunctionTemplate::New(handle_call); - Local<Function> fun = fun_templ->GetFunction(); - env->Global()->Set(v8_str("obj"), fun); - Local<Script> script = v8_compile("obj.length"); - CHECK_EQ(0, script->Run()->Int32Value()); - } -} - - -static void* expected_ptr; -static v8::Handle<v8::Value> callback(const v8::Arguments& args) { - void* ptr = v8::External::Cast(*args.Data())->Value(); - CHECK_EQ(expected_ptr, ptr); - return v8::True(); -} - - -static void TestExternalPointerWrapping() { - v8::HandleScope scope; - LocalContext env; - - v8::Handle<v8::Value> data = v8::External::New(expected_ptr); - - v8::Handle<v8::Object> obj = v8::Object::New(); - obj->Set(v8_str("func"), - v8::FunctionTemplate::New(callback, data)->GetFunction()); - env->Global()->Set(v8_str("obj"), obj); - - CHECK(CompileRun( - "function foo() {\n" - " for (var i = 0; i < 13; i++) obj.func();\n" - "}\n" - "foo(), true")->BooleanValue()); -} - - -THREADED_TEST(ExternalWrap) { - // Check heap allocated object. - int* ptr = new int; - expected_ptr = ptr; - TestExternalPointerWrapping(); - delete ptr; - - // Check stack allocated object. - int foo; - expected_ptr = &foo; - TestExternalPointerWrapping(); - - // Check not aligned addresses. - const int n = 100; - char* s = new char[n]; - for (int i = 0; i < n; i++) { - expected_ptr = s + i; - TestExternalPointerWrapping(); - } - - delete[] s; - - // Check several invalid addresses. - expected_ptr = reinterpret_cast<void*>(1); - TestExternalPointerWrapping(); - - expected_ptr = reinterpret_cast<void*>(0xdeadbeef); - TestExternalPointerWrapping(); - - expected_ptr = reinterpret_cast<void*>(0xdeadbeef + 1); - TestExternalPointerWrapping(); - -#if defined(V8_HOST_ARCH_X64) - // Check a value with a leading 1 bit in x64 Smi encoding. - expected_ptr = reinterpret_cast<void*>(0x400000000); - TestExternalPointerWrapping(); - - expected_ptr = reinterpret_cast<void*>(0xdeadbeefdeadbeef); - TestExternalPointerWrapping(); - - expected_ptr = reinterpret_cast<void*>(0xdeadbeefdeadbeef + 1); - TestExternalPointerWrapping(); -#endif -} - - -THREADED_TEST(FindInstanceInPrototypeChain) { - v8::HandleScope scope; - LocalContext env; - - Local<v8::FunctionTemplate> base = v8::FunctionTemplate::New(); - Local<v8::FunctionTemplate> derived = v8::FunctionTemplate::New(); - Local<v8::FunctionTemplate> other = v8::FunctionTemplate::New(); - derived->Inherit(base); - - Local<v8::Function> base_function = base->GetFunction(); - Local<v8::Function> derived_function = derived->GetFunction(); - Local<v8::Function> other_function = other->GetFunction(); - - Local<v8::Object> base_instance = base_function->NewInstance(); - Local<v8::Object> derived_instance = derived_function->NewInstance(); - Local<v8::Object> derived_instance2 = derived_function->NewInstance(); - Local<v8::Object> other_instance = other_function->NewInstance(); - derived_instance2->Set(v8_str("__proto__"), derived_instance); - other_instance->Set(v8_str("__proto__"), derived_instance2); - - // base_instance is only an instance of base. - CHECK_EQ(base_instance, - base_instance->FindInstanceInPrototypeChain(base)); - CHECK(base_instance->FindInstanceInPrototypeChain(derived).IsEmpty()); - CHECK(base_instance->FindInstanceInPrototypeChain(other).IsEmpty()); - - // derived_instance is an instance of base and derived. - CHECK_EQ(derived_instance, - derived_instance->FindInstanceInPrototypeChain(base)); - CHECK_EQ(derived_instance, - derived_instance->FindInstanceInPrototypeChain(derived)); - CHECK(derived_instance->FindInstanceInPrototypeChain(other).IsEmpty()); - - // other_instance is an instance of other and its immediate - // prototype derived_instance2 is an instance of base and derived. - // Note, derived_instance is an instance of base and derived too, - // but it comes after derived_instance2 in the prototype chain of - // other_instance. - CHECK_EQ(derived_instance2, - other_instance->FindInstanceInPrototypeChain(base)); - CHECK_EQ(derived_instance2, - other_instance->FindInstanceInPrototypeChain(derived)); - CHECK_EQ(other_instance, - other_instance->FindInstanceInPrototypeChain(other)); -} - - -THREADED_TEST(TinyInteger) { - v8::HandleScope scope; - LocalContext env; - v8::Isolate* isolate = v8::Isolate::GetCurrent(); - - int32_t value = 239; - Local<v8::Integer> value_obj = v8::Integer::New(value); - CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); - - value_obj = v8::Integer::New(value, isolate); - CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); -} - - -THREADED_TEST(BigSmiInteger) { - v8::HandleScope scope; - LocalContext env; - v8::Isolate* isolate = v8::Isolate::GetCurrent(); - - int32_t value = i::Smi::kMaxValue; - // We cannot add one to a Smi::kMaxValue without wrapping. - if (i::kSmiValueSize < 32) { - CHECK(i::Smi::IsValid(value)); - CHECK(!i::Smi::IsValid(value + 1)); - - Local<v8::Integer> value_obj = v8::Integer::New(value); - CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); - - value_obj = v8::Integer::New(value, isolate); - CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); - } -} - - -THREADED_TEST(BigInteger) { - v8::HandleScope scope; - LocalContext env; - v8::Isolate* isolate = v8::Isolate::GetCurrent(); - - // We cannot add one to a Smi::kMaxValue without wrapping. - if (i::kSmiValueSize < 32) { - // The casts allow this to compile, even if Smi::kMaxValue is 2^31-1. - // The code will not be run in that case, due to the "if" guard. - int32_t value = - static_cast<int32_t>(static_cast<uint32_t>(i::Smi::kMaxValue) + 1); - CHECK(value > i::Smi::kMaxValue); - CHECK(!i::Smi::IsValid(value)); - - Local<v8::Integer> value_obj = v8::Integer::New(value); - CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); - - value_obj = v8::Integer::New(value, isolate); - CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); - } -} - - -THREADED_TEST(TinyUnsignedInteger) { - v8::HandleScope scope; - LocalContext env; - v8::Isolate* isolate = v8::Isolate::GetCurrent(); - - uint32_t value = 239; - - Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value); - CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); - - value_obj = v8::Integer::NewFromUnsigned(value, isolate); - CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); -} - - -THREADED_TEST(BigUnsignedSmiInteger) { - v8::HandleScope scope; - LocalContext env; - v8::Isolate* isolate = v8::Isolate::GetCurrent(); - - uint32_t value = static_cast<uint32_t>(i::Smi::kMaxValue); - CHECK(i::Smi::IsValid(value)); - CHECK(!i::Smi::IsValid(value + 1)); - - Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value); - CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); - - value_obj = v8::Integer::NewFromUnsigned(value, isolate); - CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); -} - - -THREADED_TEST(BigUnsignedInteger) { - v8::HandleScope scope; - LocalContext env; - v8::Isolate* isolate = v8::Isolate::GetCurrent(); - - uint32_t value = static_cast<uint32_t>(i::Smi::kMaxValue) + 1; - CHECK(value > static_cast<uint32_t>(i::Smi::kMaxValue)); - CHECK(!i::Smi::IsValid(value)); - - Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value); - CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); - - value_obj = v8::Integer::NewFromUnsigned(value, isolate); - CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); -} - - -THREADED_TEST(OutOfSignedRangeUnsignedInteger) { - v8::HandleScope scope; - LocalContext env; - v8::Isolate* isolate = v8::Isolate::GetCurrent(); - - uint32_t INT32_MAX_AS_UINT = (1U << 31) - 1; - uint32_t value = INT32_MAX_AS_UINT + 1; - CHECK(value > INT32_MAX_AS_UINT); // No overflow. - - Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value); - CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); - - value_obj = v8::Integer::NewFromUnsigned(value, isolate); - CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); -} - - -THREADED_TEST(IsNativeError) { - v8::HandleScope scope; - LocalContext env; - v8::Handle<Value> syntax_error = CompileRun( - "var out = 0; try { eval(\"#\"); } catch(x) { out = x; } out; "); - CHECK(syntax_error->IsNativeError()); - v8::Handle<Value> not_error = CompileRun("{a:42}"); - CHECK(!not_error->IsNativeError()); - v8::Handle<Value> not_object = CompileRun("42"); - CHECK(!not_object->IsNativeError()); -} - - -THREADED_TEST(StringObject) { - v8::HandleScope scope; - LocalContext env; - v8::Handle<Value> boxed_string = CompileRun("new String(\"test\")"); - CHECK(boxed_string->IsStringObject()); - v8::Handle<Value> unboxed_string = CompileRun("\"test\""); - CHECK(!unboxed_string->IsStringObject()); - v8::Handle<Value> boxed_not_string = CompileRun("new Number(42)"); - CHECK(!boxed_not_string->IsStringObject()); - v8::Handle<Value> not_object = CompileRun("0"); - CHECK(!not_object->IsStringObject()); - v8::Handle<v8::StringObject> as_boxed = boxed_string.As<v8::StringObject>(); - CHECK(!as_boxed.IsEmpty()); - Local<v8::String> the_string = as_boxed->StringValue(); - CHECK(!the_string.IsEmpty()); - ExpectObject("\"test\"", the_string); - v8::Handle<v8::Value> new_boxed_string = v8::StringObject::New(the_string); - CHECK(new_boxed_string->IsStringObject()); - as_boxed = new_boxed_string.As<v8::StringObject>(); - the_string = as_boxed->StringValue(); - CHECK(!the_string.IsEmpty()); - ExpectObject("\"test\"", the_string); -} - - -THREADED_TEST(NumberObject) { - v8::HandleScope scope; - LocalContext env; - v8::Handle<Value> boxed_number = CompileRun("new Number(42)"); - CHECK(boxed_number->IsNumberObject()); - v8::Handle<Value> unboxed_number = CompileRun("42"); - CHECK(!unboxed_number->IsNumberObject()); - v8::Handle<Value> boxed_not_number = CompileRun("new Boolean(false)"); - CHECK(!boxed_not_number->IsNumberObject()); - v8::Handle<v8::NumberObject> as_boxed = boxed_number.As<v8::NumberObject>(); - CHECK(!as_boxed.IsEmpty()); - double the_number = as_boxed->NumberValue(); - CHECK_EQ(42.0, the_number); - v8::Handle<v8::Value> new_boxed_number = v8::NumberObject::New(43); - CHECK(new_boxed_number->IsNumberObject()); - as_boxed = new_boxed_number.As<v8::NumberObject>(); - the_number = as_boxed->NumberValue(); - CHECK_EQ(43.0, the_number); -} - - -THREADED_TEST(BooleanObject) { - v8::HandleScope scope; - LocalContext env; - v8::Handle<Value> boxed_boolean = CompileRun("new Boolean(true)"); - CHECK(boxed_boolean->IsBooleanObject()); - v8::Handle<Value> unboxed_boolean = CompileRun("true"); - CHECK(!unboxed_boolean->IsBooleanObject()); - v8::Handle<Value> boxed_not_boolean = CompileRun("new Number(42)"); - CHECK(!boxed_not_boolean->IsBooleanObject()); - v8::Handle<v8::BooleanObject> as_boxed = - boxed_boolean.As<v8::BooleanObject>(); - CHECK(!as_boxed.IsEmpty()); - bool the_boolean = as_boxed->BooleanValue(); - CHECK_EQ(true, the_boolean); - v8::Handle<v8::Value> boxed_true = v8::BooleanObject::New(true); - v8::Handle<v8::Value> boxed_false = v8::BooleanObject::New(false); - CHECK(boxed_true->IsBooleanObject()); - CHECK(boxed_false->IsBooleanObject()); - as_boxed = boxed_true.As<v8::BooleanObject>(); - CHECK_EQ(true, as_boxed->BooleanValue()); - as_boxed = boxed_false.As<v8::BooleanObject>(); - CHECK_EQ(false, as_boxed->BooleanValue()); -} - - -THREADED_TEST(Number) { - v8::HandleScope scope; - LocalContext env; - double PI = 3.1415926; - Local<v8::Number> pi_obj = v8::Number::New(PI); - CHECK_EQ(PI, pi_obj->NumberValue()); -} - - -THREADED_TEST(ToNumber) { - v8::HandleScope scope; - LocalContext env; - Local<String> str = v8_str("3.1415926"); - CHECK_EQ(3.1415926, str->NumberValue()); - v8::Handle<v8::Boolean> t = v8::True(); - CHECK_EQ(1.0, t->NumberValue()); - v8::Handle<v8::Boolean> f = v8::False(); - CHECK_EQ(0.0, f->NumberValue()); -} - - -THREADED_TEST(Date) { - v8::HandleScope scope; - LocalContext env; - double PI = 3.1415926; - Local<Value> date = v8::Date::New(PI); - CHECK_EQ(3.0, date->NumberValue()); - date.As<v8::Date>()->Set(v8_str("property"), v8::Integer::New(42)); - CHECK_EQ(42, date.As<v8::Date>()->Get(v8_str("property"))->Int32Value()); -} - - -THREADED_TEST(Boolean) { - v8::HandleScope scope; - LocalContext env; - v8::Handle<v8::Boolean> t = v8::True(); - CHECK(t->Value()); - v8::Handle<v8::Boolean> f = v8::False(); - CHECK(!f->Value()); - v8::Handle<v8::Primitive> u = v8::Undefined(); - CHECK(!u->BooleanValue()); - v8::Handle<v8::Primitive> n = v8::Null(); - CHECK(!n->BooleanValue()); - v8::Handle<String> str1 = v8_str(""); - CHECK(!str1->BooleanValue()); - v8::Handle<String> str2 = v8_str("x"); - CHECK(str2->BooleanValue()); - CHECK(!v8::Number::New(0)->BooleanValue()); - CHECK(v8::Number::New(-1)->BooleanValue()); - CHECK(v8::Number::New(1)->BooleanValue()); - CHECK(v8::Number::New(42)->BooleanValue()); - CHECK(!v8_compile("NaN")->Run()->BooleanValue()); -} - - -static v8::Handle<Value> DummyCallHandler(const v8::Arguments& args) { - ApiTestFuzzer::Fuzz(); - return v8_num(13.4); -} - - -static v8::Handle<Value> GetM(Local<String> name, const AccessorInfo&) { - ApiTestFuzzer::Fuzz(); - return v8_num(876); -} - - -THREADED_TEST(GlobalPrototype) { - v8::HandleScope scope; - v8::Handle<v8::FunctionTemplate> func_templ = v8::FunctionTemplate::New(); - func_templ->PrototypeTemplate()->Set( - "dummy", - v8::FunctionTemplate::New(DummyCallHandler)); - v8::Handle<ObjectTemplate> templ = func_templ->InstanceTemplate(); - templ->Set("x", v8_num(200)); - templ->SetAccessor(v8_str("m"), GetM); - LocalContext env(0, templ); - v8::Handle<Script> script(v8_compile("dummy()")); - v8::Handle<Value> result(script->Run()); - CHECK_EQ(13.4, result->NumberValue()); - CHECK_EQ(200, v8_compile("x")->Run()->Int32Value()); - CHECK_EQ(876, v8_compile("m")->Run()->Int32Value()); -} - - -THREADED_TEST(ObjectTemplate) { - v8::HandleScope scope; - Local<ObjectTemplate> templ1 = ObjectTemplate::New(); - templ1->Set("x", v8_num(10)); - templ1->Set("y", v8_num(13)); - LocalContext env; - Local<v8::Object> instance1 = templ1->NewInstance(); - env->Global()->Set(v8_str("p"), instance1); - CHECK(v8_compile("(p.x == 10)")->Run()->BooleanValue()); - CHECK(v8_compile("(p.y == 13)")->Run()->BooleanValue()); - Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(); - fun->PrototypeTemplate()->Set("nirk", v8_num(123)); - Local<ObjectTemplate> templ2 = fun->InstanceTemplate(); - templ2->Set("a", v8_num(12)); - templ2->Set("b", templ1); - Local<v8::Object> instance2 = templ2->NewInstance(); - env->Global()->Set(v8_str("q"), instance2); - CHECK(v8_compile("(q.nirk == 123)")->Run()->BooleanValue()); - CHECK(v8_compile("(q.a == 12)")->Run()->BooleanValue()); - CHECK(v8_compile("(q.b.x == 10)")->Run()->BooleanValue()); - CHECK(v8_compile("(q.b.y == 13)")->Run()->BooleanValue()); -} - - -static v8::Handle<Value> GetFlabby(const v8::Arguments& args) { - ApiTestFuzzer::Fuzz(); - return v8_num(17.2); -} - - -static v8::Handle<Value> GetKnurd(Local<String> property, const AccessorInfo&) { - ApiTestFuzzer::Fuzz(); - return v8_num(15.2); -} - - -THREADED_TEST(DescriptorInheritance) { - v8::HandleScope scope; - v8::Handle<v8::FunctionTemplate> super = v8::FunctionTemplate::New(); - super->PrototypeTemplate()->Set("flabby", - v8::FunctionTemplate::New(GetFlabby)); - super->PrototypeTemplate()->Set("PI", v8_num(3.14)); - - super->InstanceTemplate()->SetAccessor(v8_str("knurd"), GetKnurd); - - v8::Handle<v8::FunctionTemplate> base1 = v8::FunctionTemplate::New(); - base1->Inherit(super); - base1->PrototypeTemplate()->Set("v1", v8_num(20.1)); - - v8::Handle<v8::FunctionTemplate> base2 = v8::FunctionTemplate::New(); - base2->Inherit(super); - base2->PrototypeTemplate()->Set("v2", v8_num(10.1)); - - LocalContext env; - - env->Global()->Set(v8_str("s"), super->GetFunction()); - env->Global()->Set(v8_str("base1"), base1->GetFunction()); - env->Global()->Set(v8_str("base2"), base2->GetFunction()); - - // Checks right __proto__ chain. - CHECK(CompileRun("base1.prototype.__proto__ == s.prototype")->BooleanValue()); - CHECK(CompileRun("base2.prototype.__proto__ == s.prototype")->BooleanValue()); - - CHECK(v8_compile("s.prototype.PI == 3.14")->Run()->BooleanValue()); - - // Instance accessor should not be visible on function object or its prototype - CHECK(CompileRun("s.knurd == undefined")->BooleanValue()); - CHECK(CompileRun("s.prototype.knurd == undefined")->BooleanValue()); - CHECK(CompileRun("base1.prototype.knurd == undefined")->BooleanValue()); - - env->Global()->Set(v8_str("obj"), - base1->GetFunction()->NewInstance()); - CHECK_EQ(17.2, v8_compile("obj.flabby()")->Run()->NumberValue()); - CHECK(v8_compile("'flabby' in obj")->Run()->BooleanValue()); - CHECK_EQ(15.2, v8_compile("obj.knurd")->Run()->NumberValue()); - CHECK(v8_compile("'knurd' in obj")->Run()->BooleanValue()); - CHECK_EQ(20.1, v8_compile("obj.v1")->Run()->NumberValue()); - - env->Global()->Set(v8_str("obj2"), - base2->GetFunction()->NewInstance()); - CHECK_EQ(17.2, v8_compile("obj2.flabby()")->Run()->NumberValue()); - CHECK(v8_compile("'flabby' in obj2")->Run()->BooleanValue()); - CHECK_EQ(15.2, v8_compile("obj2.knurd")->Run()->NumberValue()); - CHECK(v8_compile("'knurd' in obj2")->Run()->BooleanValue()); - CHECK_EQ(10.1, v8_compile("obj2.v2")->Run()->NumberValue()); - - // base1 and base2 cannot cross reference to each's prototype - CHECK(v8_compile("obj.v2")->Run()->IsUndefined()); - CHECK(v8_compile("obj2.v1")->Run()->IsUndefined()); -} - - -int echo_named_call_count; - - -static v8::Handle<Value> EchoNamedProperty(Local<String> name, - const AccessorInfo& info) { - ApiTestFuzzer::Fuzz(); - CHECK_EQ(v8_str("data"), info.Data()); - echo_named_call_count++; - return name; -} - -// Helper functions for Interceptor/Accessor interaction tests - -Handle<Value> SimpleAccessorGetter(Local<String> name, - const AccessorInfo& info) { - Handle<Object> self = info.This(); - return self->Get(String::Concat(v8_str("accessor_"), name)); -} - -void SimpleAccessorSetter(Local<String> name, Local<Value> value, - const AccessorInfo& info) { - Handle<Object> self = info.This(); - self->Set(String::Concat(v8_str("accessor_"), name), value); -} - -Handle<Value> EmptyInterceptorGetter(Local<String> name, - const AccessorInfo& info) { - return Handle<Value>(); -} - -Handle<Value> EmptyInterceptorSetter(Local<String> name, - Local<Value> value, - const AccessorInfo& info) { - return Handle<Value>(); -} - -Handle<Value> InterceptorGetter(Local<String> name, - const AccessorInfo& info) { - // Intercept names that start with 'interceptor_'. - String::AsciiValue ascii(name); - char* name_str = *ascii; - char prefix[] = "interceptor_"; - int i; - for (i = 0; name_str[i] && prefix[i]; ++i) { - if (name_str[i] != prefix[i]) return Handle<Value>(); - } - Handle<Object> self = info.This(); - return self->GetHiddenValue(v8_str(name_str + i)); -} - -Handle<Value> InterceptorSetter(Local<String> name, - Local<Value> value, - const AccessorInfo& info) { - // Intercept accesses that set certain integer values. - if (value->IsInt32() && value->Int32Value() < 10000) { - Handle<Object> self = info.This(); - self->SetHiddenValue(name, value); - return value; - } - return Handle<Value>(); -} - -void AddAccessor(Handle<FunctionTemplate> templ, - Handle<String> name, - v8::AccessorGetter getter, - v8::AccessorSetter setter) { - templ->PrototypeTemplate()->SetAccessor(name, getter, setter); -} - -void AddInterceptor(Handle<FunctionTemplate> templ, - v8::NamedPropertyGetter getter, - v8::NamedPropertySetter setter) { - templ->InstanceTemplate()->SetNamedPropertyHandler(getter, setter); -} - -THREADED_TEST(EmptyInterceptorDoesNotShadowAccessors) { - v8::HandleScope scope; - Handle<FunctionTemplate> parent = FunctionTemplate::New(); - Handle<FunctionTemplate> child = FunctionTemplate::New(); - child->Inherit(parent); - AddAccessor(parent, v8_str("age"), - SimpleAccessorGetter, SimpleAccessorSetter); - AddInterceptor(child, EmptyInterceptorGetter, EmptyInterceptorSetter); - LocalContext env; - env->Global()->Set(v8_str("Child"), child->GetFunction()); - CompileRun("var child = new Child;" - "child.age = 10;"); - ExpectBoolean("child.hasOwnProperty('age')", false); - ExpectInt32("child.age", 10); - ExpectInt32("child.accessor_age", 10); -} - -THREADED_TEST(EmptyInterceptorDoesNotShadowJSAccessors) { - v8::HandleScope scope; - Handle<FunctionTemplate> parent = FunctionTemplate::New(); - Handle<FunctionTemplate> child = FunctionTemplate::New(); - child->Inherit(parent); - AddInterceptor(child, EmptyInterceptorGetter, EmptyInterceptorSetter); - LocalContext env; - env->Global()->Set(v8_str("Child"), child->GetFunction()); - CompileRun("var child = new Child;" - "var parent = child.__proto__;" - "Object.defineProperty(parent, 'age', " - " {get: function(){ return this.accessor_age; }, " - " set: function(v){ this.accessor_age = v; }, " - " enumerable: true, configurable: true});" - "child.age = 10;"); - ExpectBoolean("child.hasOwnProperty('age')", false); - ExpectInt32("child.age", 10); - ExpectInt32("child.accessor_age", 10); -} - -THREADED_TEST(EmptyInterceptorDoesNotAffectJSProperties) { - v8::HandleScope scope; - Handle<FunctionTemplate> parent = FunctionTemplate::New(); - Handle<FunctionTemplate> child = FunctionTemplate::New(); - child->Inherit(parent); - AddInterceptor(child, EmptyInterceptorGetter, EmptyInterceptorSetter); - LocalContext env; - env->Global()->Set(v8_str("Child"), child->GetFunction()); - CompileRun("var child = new Child;" - "var parent = child.__proto__;" - "parent.name = 'Alice';"); - ExpectBoolean("child.hasOwnProperty('name')", false); - ExpectString("child.name", "Alice"); - CompileRun("child.name = 'Bob';"); - ExpectString("child.name", "Bob"); - ExpectBoolean("child.hasOwnProperty('name')", true); - ExpectString("parent.name", "Alice"); -} - -THREADED_TEST(SwitchFromInterceptorToAccessor) { - v8::HandleScope scope; - Handle<FunctionTemplate> templ = FunctionTemplate::New(); - AddAccessor(templ, v8_str("age"), - SimpleAccessorGetter, SimpleAccessorSetter); - AddInterceptor(templ, InterceptorGetter, InterceptorSetter); - LocalContext env; - env->Global()->Set(v8_str("Obj"), templ->GetFunction()); - CompileRun("var obj = new Obj;" - "function setAge(i){ obj.age = i; };" - "for(var i = 0; i <= 10000; i++) setAge(i);"); - // All i < 10000 go to the interceptor. - ExpectInt32("obj.interceptor_age", 9999); - // The last i goes to the accessor. - ExpectInt32("obj.accessor_age", 10000); -} - -THREADED_TEST(SwitchFromAccessorToInterceptor) { - v8::HandleScope scope; - Handle<FunctionTemplate> templ = FunctionTemplate::New(); - AddAccessor(templ, v8_str("age"), - SimpleAccessorGetter, SimpleAccessorSetter); - AddInterceptor(templ, InterceptorGetter, InterceptorSetter); - LocalContext env; - env->Global()->Set(v8_str("Obj"), templ->GetFunction()); - CompileRun("var obj = new Obj;" - "function setAge(i){ obj.age = i; };" - "for(var i = 20000; i >= 9999; i--) setAge(i);"); - // All i >= 10000 go to the accessor. - ExpectInt32("obj.accessor_age", 10000); - // The last i goes to the interceptor. - ExpectInt32("obj.interceptor_age", 9999); -} - -THREADED_TEST(SwitchFromInterceptorToAccessorWithInheritance) { - v8::HandleScope scope; - Handle<FunctionTemplate> parent = FunctionTemplate::New(); - Handle<FunctionTemplate> child = FunctionTemplate::New(); - child->Inherit(parent); - AddAccessor(parent, v8_str("age"), - SimpleAccessorGetter, SimpleAccessorSetter); - AddInterceptor(child, InterceptorGetter, InterceptorSetter); - LocalContext env; - env->Global()->Set(v8_str("Child"), child->GetFunction()); - CompileRun("var child = new Child;" - "function setAge(i){ child.age = i; };" - "for(var i = 0; i <= 10000; i++) setAge(i);"); - // All i < 10000 go to the interceptor. - ExpectInt32("child.interceptor_age", 9999); - // The last i goes to the accessor. - ExpectInt32("child.accessor_age", 10000); -} - -THREADED_TEST(SwitchFromAccessorToInterceptorWithInheritance) { - v8::HandleScope scope; - Handle<FunctionTemplate> parent = FunctionTemplate::New(); - Handle<FunctionTemplate> child = FunctionTemplate::New(); - child->Inherit(parent); - AddAccessor(parent, v8_str("age"), - SimpleAccessorGetter, SimpleAccessorSetter); - AddInterceptor(child, InterceptorGetter, InterceptorSetter); - LocalContext env; - env->Global()->Set(v8_str("Child"), child->GetFunction()); - CompileRun("var child = new Child;" - "function setAge(i){ child.age = i; };" - "for(var i = 20000; i >= 9999; i--) setAge(i);"); - // All i >= 10000 go to the accessor. - ExpectInt32("child.accessor_age", 10000); - // The last i goes to the interceptor. - ExpectInt32("child.interceptor_age", 9999); -} - -THREADED_TEST(SwitchFromInterceptorToJSAccessor) { - v8::HandleScope scope; - Handle<FunctionTemplate> templ = FunctionTemplate::New(); - AddInterceptor(templ, InterceptorGetter, InterceptorSetter); - LocalContext env; - env->Global()->Set(v8_str("Obj"), templ->GetFunction()); - CompileRun("var obj = new Obj;" - "function setter(i) { this.accessor_age = i; };" - "function getter() { return this.accessor_age; };" - "function setAge(i) { obj.age = i; };" - "Object.defineProperty(obj, 'age', { get:getter, set:setter });" - "for(var i = 0; i <= 10000; i++) setAge(i);"); - // All i < 10000 go to the interceptor. - ExpectInt32("obj.interceptor_age", 9999); - // The last i goes to the JavaScript accessor. - ExpectInt32("obj.accessor_age", 10000); - // The installed JavaScript getter is still intact. - // This last part is a regression test for issue 1651 and relies on the fact - // that both interceptor and accessor are being installed on the same object. - ExpectInt32("obj.age", 10000); - ExpectBoolean("obj.hasOwnProperty('age')", true); - ExpectUndefined("Object.getOwnPropertyDescriptor(obj, 'age').value"); -} - -THREADED_TEST(SwitchFromJSAccessorToInterceptor) { - v8::HandleScope scope; - Handle<FunctionTemplate> templ = FunctionTemplate::New(); - AddInterceptor(templ, InterceptorGetter, InterceptorSetter); - LocalContext env; - env->Global()->Set(v8_str("Obj"), templ->GetFunction()); - CompileRun("var obj = new Obj;" - "function setter(i) { this.accessor_age = i; };" - "function getter() { return this.accessor_age; };" - "function setAge(i) { obj.age = i; };" - "Object.defineProperty(obj, 'age', { get:getter, set:setter });" - "for(var i = 20000; i >= 9999; i--) setAge(i);"); - // All i >= 10000 go to the accessor. - ExpectInt32("obj.accessor_age", 10000); - // The last i goes to the interceptor. - ExpectInt32("obj.interceptor_age", 9999); - // The installed JavaScript getter is still intact. - // This last part is a regression test for issue 1651 and relies on the fact - // that both interceptor and accessor are being installed on the same object. - ExpectInt32("obj.age", 10000); - ExpectBoolean("obj.hasOwnProperty('age')", true); - ExpectUndefined("Object.getOwnPropertyDescriptor(obj, 'age').value"); -} - -THREADED_TEST(SwitchFromInterceptorToProperty) { - v8::HandleScope scope; - Handle<FunctionTemplate> parent = FunctionTemplate::New(); - Handle<FunctionTemplate> child = FunctionTemplate::New(); - child->Inherit(parent); - AddInterceptor(child, InterceptorGetter, InterceptorSetter); - LocalContext env; - env->Global()->Set(v8_str("Child"), child->GetFunction()); - CompileRun("var child = new Child;" - "function setAge(i){ child.age = i; };" - "for(var i = 0; i <= 10000; i++) setAge(i);"); - // All i < 10000 go to the interceptor. - ExpectInt32("child.interceptor_age", 9999); - // The last i goes to child's own property. - ExpectInt32("child.age", 10000); -} - -THREADED_TEST(SwitchFromPropertyToInterceptor) { - v8::HandleScope scope; - Handle<FunctionTemplate> parent = FunctionTemplate::New(); - Handle<FunctionTemplate> child = FunctionTemplate::New(); - child->Inherit(parent); - AddInterceptor(child, InterceptorGetter, InterceptorSetter); - LocalContext env; - env->Global()->Set(v8_str("Child"), child->GetFunction()); - CompileRun("var child = new Child;" - "function setAge(i){ child.age = i; };" - "for(var i = 20000; i >= 9999; i--) setAge(i);"); - // All i >= 10000 go to child's own property. - ExpectInt32("child.age", 10000); - // The last i goes to the interceptor. - ExpectInt32("child.interceptor_age", 9999); -} - -THREADED_TEST(NamedPropertyHandlerGetter) { - echo_named_call_count = 0; - v8::HandleScope scope; - v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); - templ->InstanceTemplate()->SetNamedPropertyHandler(EchoNamedProperty, - 0, 0, 0, 0, - v8_str("data")); - LocalContext env; - env->Global()->Set(v8_str("obj"), - templ->GetFunction()->NewInstance()); - CHECK_EQ(echo_named_call_count, 0); - v8_compile("obj.x")->Run(); - CHECK_EQ(echo_named_call_count, 1); - const char* code = "var str = 'oddle'; obj[str] + obj.poddle;"; - v8::Handle<Value> str = CompileRun(code); - String::AsciiValue value(str); - CHECK_EQ(*value, "oddlepoddle"); - // Check default behavior - CHECK_EQ(v8_compile("obj.flob = 10;")->Run()->Int32Value(), 10); - CHECK(v8_compile("'myProperty' in obj")->Run()->BooleanValue()); - CHECK(v8_compile("delete obj.myProperty")->Run()->BooleanValue()); -} - - -int echo_indexed_call_count = 0; - - -static v8::Handle<Value> EchoIndexedProperty(uint32_t index, - const AccessorInfo& info) { - ApiTestFuzzer::Fuzz(); - CHECK_EQ(v8_num(637), info.Data()); - echo_indexed_call_count++; - return v8_num(index); -} - - -THREADED_TEST(IndexedPropertyHandlerGetter) { - v8::HandleScope scope; - v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); - templ->InstanceTemplate()->SetIndexedPropertyHandler(EchoIndexedProperty, - 0, 0, 0, 0, - v8_num(637)); - LocalContext env; - env->Global()->Set(v8_str("obj"), - templ->GetFunction()->NewInstance()); - Local<Script> script = v8_compile("obj[900]"); - CHECK_EQ(script->Run()->Int32Value(), 900); -} - - -v8::Handle<v8::Object> bottom; - -static v8::Handle<Value> CheckThisIndexedPropertyHandler( - uint32_t index, - const AccessorInfo& info) { - ApiTestFuzzer::Fuzz(); - CHECK(info.This()->Equals(bottom)); - return v8::Handle<Value>(); -} - -static v8::Handle<Value> CheckThisNamedPropertyHandler( - Local<String> name, - const AccessorInfo& info) { - ApiTestFuzzer::Fuzz(); - CHECK(info.This()->Equals(bottom)); - return v8::Handle<Value>(); -} - - -v8::Handle<Value> CheckThisIndexedPropertySetter(uint32_t index, - Local<Value> value, - const AccessorInfo& info) { - ApiTestFuzzer::Fuzz(); - CHECK(info.This()->Equals(bottom)); - return v8::Handle<Value>(); -} - - -v8::Handle<Value> CheckThisNamedPropertySetter(Local<String> property, - Local<Value> value, - const AccessorInfo& info) { - ApiTestFuzzer::Fuzz(); - CHECK(info.This()->Equals(bottom)); - return v8::Handle<Value>(); -} - -v8::Handle<v8::Integer> CheckThisIndexedPropertyQuery( - uint32_t index, - const AccessorInfo& info) { - ApiTestFuzzer::Fuzz(); - CHECK(info.This()->Equals(bottom)); - return v8::Handle<v8::Integer>(); -} - - -v8::Handle<v8::Integer> CheckThisNamedPropertyQuery(Local<String> property, - const AccessorInfo& info) { - ApiTestFuzzer::Fuzz(); - CHECK(info.This()->Equals(bottom)); - return v8::Handle<v8::Integer>(); -} - - -v8::Handle<v8::Boolean> CheckThisIndexedPropertyDeleter( - uint32_t index, - const AccessorInfo& info) { - ApiTestFuzzer::Fuzz(); - CHECK(info.This()->Equals(bottom)); - return v8::Handle<v8::Boolean>(); -} - - -v8::Handle<v8::Boolean> CheckThisNamedPropertyDeleter( - Local<String> property, - const AccessorInfo& info) { - ApiTestFuzzer::Fuzz(); - CHECK(info.This()->Equals(bottom)); - return v8::Handle<v8::Boolean>(); -} - - -v8::Handle<v8::Array> CheckThisIndexedPropertyEnumerator( - const AccessorInfo& info) { - ApiTestFuzzer::Fuzz(); - CHECK(info.This()->Equals(bottom)); - return v8::Handle<v8::Array>(); -} - - -v8::Handle<v8::Array> CheckThisNamedPropertyEnumerator( - const AccessorInfo& info) { - ApiTestFuzzer::Fuzz(); - CHECK(info.This()->Equals(bottom)); - return v8::Handle<v8::Array>(); -} - - -THREADED_TEST(PropertyHandlerInPrototype) { - v8::HandleScope scope; - LocalContext env; - - // Set up a prototype chain with three interceptors. - v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); - templ->InstanceTemplate()->SetIndexedPropertyHandler( - CheckThisIndexedPropertyHandler, - CheckThisIndexedPropertySetter, - CheckThisIndexedPropertyQuery, - CheckThisIndexedPropertyDeleter, - CheckThisIndexedPropertyEnumerator); - - templ->InstanceTemplate()->SetNamedPropertyHandler( - CheckThisNamedPropertyHandler, - CheckThisNamedPropertySetter, - CheckThisNamedPropertyQuery, - CheckThisNamedPropertyDeleter, - CheckThisNamedPropertyEnumerator); - - bottom = templ->GetFunction()->NewInstance(); - Local<v8::Object> top = templ->GetFunction()->NewInstance(); - Local<v8::Object> middle = templ->GetFunction()->NewInstance(); - - bottom->SetPrototype(middle); - middle->SetPrototype(top); - env->Global()->Set(v8_str("obj"), bottom); - - // Indexed and named get. - Script::Compile(v8_str("obj[0]"))->Run(); - Script::Compile(v8_str("obj.x"))->Run(); - - // Indexed and named set. - Script::Compile(v8_str("obj[1] = 42"))->Run(); - Script::Compile(v8_str("obj.y = 42"))->Run(); - - // Indexed and named query. - Script::Compile(v8_str("0 in obj"))->Run(); - Script::Compile(v8_str("'x' in obj"))->Run(); - - // Indexed and named deleter. - Script::Compile(v8_str("delete obj[0]"))->Run(); - Script::Compile(v8_str("delete obj.x"))->Run(); - - // Enumerators. - Script::Compile(v8_str("for (var p in obj) ;"))->Run(); -} - - -static v8::Handle<Value> PrePropertyHandlerGet(Local<String> key, - const AccessorInfo& info) { - ApiTestFuzzer::Fuzz(); - if (v8_str("pre")->Equals(key)) { - return v8_str("PrePropertyHandler: pre"); - } - return v8::Handle<String>(); -} - - -static v8::Handle<v8::Integer> PrePropertyHandlerQuery(Local<String> key, - const AccessorInfo&) { - if (v8_str("pre")->Equals(key)) { - return v8::Integer::New(v8::None); - } - - return v8::Handle<v8::Integer>(); // do not intercept the call -} - - -THREADED_TEST(PrePropertyHandler) { - v8::HandleScope scope; - v8::Handle<v8::FunctionTemplate> desc = v8::FunctionTemplate::New(); - desc->InstanceTemplate()->SetNamedPropertyHandler(PrePropertyHandlerGet, - 0, - PrePropertyHandlerQuery); - LocalContext env(NULL, desc->InstanceTemplate()); - Script::Compile(v8_str( - "var pre = 'Object: pre'; var on = 'Object: on';"))->Run(); - v8::Handle<Value> result_pre = Script::Compile(v8_str("pre"))->Run(); - CHECK_EQ(v8_str("PrePropertyHandler: pre"), result_pre); - v8::Handle<Value> result_on = Script::Compile(v8_str("on"))->Run(); - CHECK_EQ(v8_str("Object: on"), result_on); - v8::Handle<Value> result_post = Script::Compile(v8_str("post"))->Run(); - CHECK(result_post.IsEmpty()); -} - - -THREADED_TEST(UndefinedIsNotEnumerable) { - v8::HandleScope scope; - LocalContext env; - v8::Handle<Value> result = Script::Compile(v8_str( - "this.propertyIsEnumerable(undefined)"))->Run(); - CHECK(result->IsFalse()); -} - - -v8::Handle<Script> call_recursively_script; -static const int kTargetRecursionDepth = 200; // near maximum - - -static v8::Handle<Value> CallScriptRecursivelyCall(const v8::Arguments& args) { - ApiTestFuzzer::Fuzz(); - int depth = args.This()->Get(v8_str("depth"))->Int32Value(); - if (depth == kTargetRecursionDepth) return v8::Undefined(); - args.This()->Set(v8_str("depth"), v8::Integer::New(depth + 1)); - return call_recursively_script->Run(); -} - - -static v8::Handle<Value> CallFunctionRecursivelyCall( - const v8::Arguments& args) { - ApiTestFuzzer::Fuzz(); - int depth = args.This()->Get(v8_str("depth"))->Int32Value(); - if (depth == kTargetRecursionDepth) { - printf("[depth = %d]\n", depth); - return v8::Undefined(); - } - args.This()->Set(v8_str("depth"), v8::Integer::New(depth + 1)); - v8::Handle<Value> function = - args.This()->Get(v8_str("callFunctionRecursively")); - return function.As<Function>()->Call(args.This(), 0, NULL); -} - - -THREADED_TEST(DeepCrossLanguageRecursion) { - v8::HandleScope scope; - v8::Handle<v8::ObjectTemplate> global = ObjectTemplate::New(); - global->Set(v8_str("callScriptRecursively"), - v8::FunctionTemplate::New(CallScriptRecursivelyCall)); - global->Set(v8_str("callFunctionRecursively"), - v8::FunctionTemplate::New(CallFunctionRecursivelyCall)); - LocalContext env(NULL, global); - - env->Global()->Set(v8_str("depth"), v8::Integer::New(0)); - call_recursively_script = v8_compile("callScriptRecursively()"); - call_recursively_script->Run(); - call_recursively_script = v8::Handle<Script>(); - - env->Global()->Set(v8_str("depth"), v8::Integer::New(0)); - Script::Compile(v8_str("callFunctionRecursively()"))->Run(); -} - - -static v8::Handle<Value> - ThrowingPropertyHandlerGet(Local<String> key, const AccessorInfo&) { - ApiTestFuzzer::Fuzz(); - return v8::ThrowException(key); -} - - -static v8::Handle<Value> ThrowingPropertyHandlerSet(Local<String> key, - Local<Value>, - const AccessorInfo&) { - v8::ThrowException(key); - return v8::Undefined(); // not the same as v8::Handle<v8::Value>() -} - - -THREADED_TEST(CallbackExceptionRegression) { - v8::HandleScope scope; - v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); - obj->SetNamedPropertyHandler(ThrowingPropertyHandlerGet, - ThrowingPropertyHandlerSet); - LocalContext env; - env->Global()->Set(v8_str("obj"), obj->NewInstance()); - v8::Handle<Value> otto = Script::Compile(v8_str( - "try { with (obj) { otto; } } catch (e) { e; }"))->Run(); - CHECK_EQ(v8_str("otto"), otto); - v8::Handle<Value> netto = Script::Compile(v8_str( - "try { with (obj) { netto = 4; } } catch (e) { e; }"))->Run(); - CHECK_EQ(v8_str("netto"), netto); -} - - -THREADED_TEST(FunctionPrototype) { - v8::HandleScope scope; - Local<v8::FunctionTemplate> Foo = v8::FunctionTemplate::New(); - Foo->PrototypeTemplate()->Set(v8_str("plak"), v8_num(321)); - LocalContext env; - env->Global()->Set(v8_str("Foo"), Foo->GetFunction()); - Local<Script> script = Script::Compile(v8_str("Foo.prototype.plak")); - CHECK_EQ(script->Run()->Int32Value(), 321); -} - - -THREADED_TEST(InternalFields) { - v8::HandleScope scope; - LocalContext env; - - Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); - Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate(); - instance_templ->SetInternalFieldCount(1); - Local<v8::Object> obj = templ->GetFunction()->NewInstance(); - CHECK_EQ(1, obj->InternalFieldCount()); - CHECK(obj->GetInternalField(0)->IsUndefined()); - obj->SetInternalField(0, v8_num(17)); - CHECK_EQ(17, obj->GetInternalField(0)->Int32Value()); -} - - -THREADED_TEST(GlobalObjectInternalFields) { - v8::HandleScope scope; - Local<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); - global_template->SetInternalFieldCount(1); - LocalContext env(NULL, global_template); - v8::Handle<v8::Object> global_proxy = env->Global(); - v8::Handle<v8::Object> global = global_proxy->GetPrototype().As<v8::Object>(); - CHECK_EQ(1, global->InternalFieldCount()); - CHECK(global->GetInternalField(0)->IsUndefined()); - global->SetInternalField(0, v8_num(17)); - CHECK_EQ(17, global->GetInternalField(0)->Int32Value()); -} - - -static void CheckAlignedPointerInInternalField(Handle<v8::Object> obj, - void* value) { - CHECK_EQ(0, static_cast<int>(reinterpret_cast<uintptr_t>(value) & 0x1)); - obj->SetAlignedPointerInInternalField(0, value); - HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); - CHECK_EQ(value, obj->GetAlignedPointerFromInternalField(0)); -} - - -THREADED_TEST(InternalFieldsAlignedPointers) { - v8::HandleScope scope; - LocalContext env; - - Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); - Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate(); - instance_templ->SetInternalFieldCount(1); - Local<v8::Object> obj = templ->GetFunction()->NewInstance(); - CHECK_EQ(1, obj->InternalFieldCount()); - - CheckAlignedPointerInInternalField(obj, NULL); - - int* heap_allocated = new int[100]; - CheckAlignedPointerInInternalField(obj, heap_allocated); - delete[] heap_allocated; - - int stack_allocated[100]; - CheckAlignedPointerInInternalField(obj, stack_allocated); - - void* huge = reinterpret_cast<void*>(~static_cast<uintptr_t>(1)); - CheckAlignedPointerInInternalField(obj, huge); -} - - -static void CheckAlignedPointerInEmbedderData(LocalContext* env, - int index, - void* value) { - CHECK_EQ(0, static_cast<int>(reinterpret_cast<uintptr_t>(value) & 0x1)); - (*env)->SetAlignedPointerInEmbedderData(index, value); - HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); - CHECK_EQ(value, (*env)->GetAlignedPointerFromEmbedderData(index)); -} - - -static void* AlignedTestPointer(int i) { - return reinterpret_cast<void*>(i * 1234); -} - - -THREADED_TEST(EmbedderDataAlignedPointers) { - v8::HandleScope scope; - LocalContext env; - - CheckAlignedPointerInEmbedderData(&env, 0, NULL); - - int* heap_allocated = new int[100]; - CheckAlignedPointerInEmbedderData(&env, 1, heap_allocated); - delete[] heap_allocated; - - int stack_allocated[100]; - CheckAlignedPointerInEmbedderData(&env, 2, stack_allocated); - - void* huge = reinterpret_cast<void*>(~static_cast<uintptr_t>(1)); - CheckAlignedPointerInEmbedderData(&env, 3, huge); - - // Test growing of the embedder data's backing store. - for (int i = 0; i < 100; i++) { - env->SetAlignedPointerInEmbedderData(i, AlignedTestPointer(i)); - } - HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); - for (int i = 0; i < 100; i++) { - CHECK_EQ(AlignedTestPointer(i), env->GetAlignedPointerFromEmbedderData(i)); - } -} - - -static void CheckEmbedderData(LocalContext* env, - int index, - v8::Handle<Value> data) { - (*env)->SetEmbedderData(index, data); - CHECK((*env)->GetEmbedderData(index)->StrictEquals(data)); -} - -THREADED_TEST(EmbedderData) { - v8::HandleScope scope; - LocalContext env; - - CheckEmbedderData(&env, 3, v8::String::New("The quick brown fox jumps")); - CheckEmbedderData(&env, 2, v8::String::New("over the lazy dog.")); - CheckEmbedderData(&env, 1, v8::Number::New(1.2345)); - CheckEmbedderData(&env, 0, v8::Boolean::New(true)); -} - - -THREADED_TEST(IdentityHash) { - v8::HandleScope scope; - LocalContext env; - - // Ensure that the test starts with an fresh heap to test whether the hash - // code is based on the address. - HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); - Local<v8::Object> obj = v8::Object::New(); - int hash = obj->GetIdentityHash(); - int hash1 = obj->GetIdentityHash(); - CHECK_EQ(hash, hash1); - int hash2 = v8::Object::New()->GetIdentityHash(); - // Since the identity hash is essentially a random number two consecutive - // objects should not be assigned the same hash code. If the test below fails - // the random number generator should be evaluated. - CHECK_NE(hash, hash2); - HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); - int hash3 = v8::Object::New()->GetIdentityHash(); - // Make sure that the identity hash is not based on the initial address of - // the object alone. If the test below fails the random number generator - // should be evaluated. - CHECK_NE(hash, hash3); - int hash4 = obj->GetIdentityHash(); - CHECK_EQ(hash, hash4); - - // Check identity hashes behaviour in the presence of JS accessors. - // Put a getter for 'v8::IdentityHash' on the Object's prototype: - { - CompileRun("Object.prototype['v8::IdentityHash'] = 42;\n"); - Local<v8::Object> o1 = v8::Object::New(); - Local<v8::Object> o2 = v8::Object::New(); - CHECK_NE(o1->GetIdentityHash(), o2->GetIdentityHash()); - } - { - CompileRun( - "function cnst() { return 42; };\n" - "Object.prototype.__defineGetter__('v8::IdentityHash', cnst);\n"); - Local<v8::Object> o1 = v8::Object::New(); - Local<v8::Object> o2 = v8::Object::New(); - CHECK_NE(o1->GetIdentityHash(), o2->GetIdentityHash()); - } -} - - -THREADED_TEST(HiddenProperties) { - v8::HandleScope scope; - LocalContext env; - - v8::Local<v8::Object> obj = v8::Object::New(); - v8::Local<v8::String> key = v8_str("api-test::hidden-key"); - v8::Local<v8::String> empty = v8_str(""); - v8::Local<v8::String> prop_name = v8_str("prop_name"); - - HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); - - // Make sure delete of a non-existent hidden value works - CHECK(obj->DeleteHiddenValue(key)); - - CHECK(obj->SetHiddenValue(key, v8::Integer::New(1503))); - CHECK_EQ(1503, obj->GetHiddenValue(key)->Int32Value()); - CHECK(obj->SetHiddenValue(key, v8::Integer::New(2002))); - CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); - - HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); - - // Make sure we do not find the hidden property. - CHECK(!obj->Has(empty)); - CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); - CHECK(obj->Get(empty)->IsUndefined()); - CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); - CHECK(obj->Set(empty, v8::Integer::New(2003))); - CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); - CHECK_EQ(2003, obj->Get(empty)->Int32Value()); - - HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); - - // Add another property and delete it afterwards to force the object in - // slow case. - CHECK(obj->Set(prop_name, v8::Integer::New(2008))); - CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); - CHECK_EQ(2008, obj->Get(prop_name)->Int32Value()); - CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); - CHECK(obj->Delete(prop_name)); - CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); - - HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); - - CHECK(obj->SetHiddenValue(key, Handle<Value>())); - CHECK(obj->GetHiddenValue(key).IsEmpty()); - - CHECK(obj->SetHiddenValue(key, v8::Integer::New(2002))); - CHECK(obj->DeleteHiddenValue(key)); - CHECK(obj->GetHiddenValue(key).IsEmpty()); -} - - -THREADED_TEST(Regress97784) { - // Regression test for crbug.com/97784 - // Messing with the Object.prototype should not have effect on - // hidden properties. - v8::HandleScope scope; - LocalContext env; - - v8::Local<v8::Object> obj = v8::Object::New(); - v8::Local<v8::String> key = v8_str("hidden"); - - CompileRun( - "set_called = false;" - "Object.defineProperty(" - " Object.prototype," - " 'hidden'," - " {get: function() { return 45; }," - " set: function() { set_called = true; }})"); - - CHECK(obj->GetHiddenValue(key).IsEmpty()); - // Make sure that the getter and setter from Object.prototype is not invoked. - // If it did we would have full access to the hidden properties in - // the accessor. - CHECK(obj->SetHiddenValue(key, v8::Integer::New(42))); - ExpectFalse("set_called"); - CHECK_EQ(42, obj->GetHiddenValue(key)->Int32Value()); -} - - -static bool interceptor_for_hidden_properties_called; -static v8::Handle<Value> InterceptorForHiddenProperties( - Local<String> name, const AccessorInfo& info) { - interceptor_for_hidden_properties_called = true; - return v8::Handle<Value>(); -} - - -THREADED_TEST(HiddenPropertiesWithInterceptors) { - v8::HandleScope scope; - LocalContext context; - - interceptor_for_hidden_properties_called = false; - - 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()); - CHECK(!interceptor_for_hidden_properties_called); -} - - -THREADED_TEST(External) { - v8::HandleScope scope; - int x = 3; - Local<v8::External> ext = v8::External::New(&x); - LocalContext env; - env->Global()->Set(v8_str("ext"), ext); - Local<Value> reext_obj = Script::Compile(v8_str("this.ext"))->Run(); - v8::Handle<v8::External> reext = reext_obj.As<v8::External>(); - int* ptr = static_cast<int*>(reext->Value()); - CHECK_EQ(x, 3); - *ptr = 10; - CHECK_EQ(x, 10); - - // Make sure unaligned pointers are wrapped properly. - char* data = i::StrDup("0123456789"); - Local<v8::Value> zero = v8::External::New(&data[0]); - Local<v8::Value> one = v8::External::New(&data[1]); - Local<v8::Value> two = v8::External::New(&data[2]); - Local<v8::Value> three = v8::External::New(&data[3]); - - char* char_ptr = reinterpret_cast<char*>(v8::External::Cast(*zero)->Value()); - CHECK_EQ('0', *char_ptr); - char_ptr = reinterpret_cast<char*>(v8::External::Cast(*one)->Value()); - CHECK_EQ('1', *char_ptr); - char_ptr = reinterpret_cast<char*>(v8::External::Cast(*two)->Value()); - CHECK_EQ('2', *char_ptr); - char_ptr = reinterpret_cast<char*>(v8::External::Cast(*three)->Value()); - CHECK_EQ('3', *char_ptr); - i::DeleteArray(data); -} - - -THREADED_TEST(GlobalHandle) { - v8::Isolate* isolate = v8::Isolate::GetCurrent(); - v8::Persistent<String> global; - { - v8::HandleScope scope; - Local<String> str = v8_str("str"); - global = v8::Persistent<String>::New(isolate, str); - } - CHECK_EQ(global->Length(), 3); - global.Dispose(isolate); - - { - v8::HandleScope scope; - Local<String> str = v8_str("str"); - global = v8::Persistent<String>::New(isolate, str); - } - CHECK_EQ(global->Length(), 3); - global.Dispose(isolate); -} - - -THREADED_TEST(LocalHandle) { - v8::HandleScope scope; - v8::Local<String> local = v8::Local<String>::New(v8_str("str")); - CHECK_EQ(local->Length(), 3); - - local = v8::Local<String>::New(v8::Isolate::GetCurrent(), v8_str("str")); - CHECK_EQ(local->Length(), 3); -} - - -class WeakCallCounter { - public: - explicit WeakCallCounter(int id) : id_(id), number_of_weak_calls_(0) { } - int id() { return id_; } - void increment() { number_of_weak_calls_++; } - int NumberOfWeakCalls() { return number_of_weak_calls_; } - private: - int id_; - int number_of_weak_calls_; -}; - - -static void WeakPointerCallback(v8::Isolate* isolate, - Persistent<Value> handle, - void* id) { - WeakCallCounter* counter = reinterpret_cast<WeakCallCounter*>(id); - CHECK_EQ(1234, counter->id()); - counter->increment(); - handle.Dispose(isolate); -} - - -THREADED_TEST(ApiObjectGroups) { - HandleScope scope; - LocalContext env; - v8::Isolate* iso = env->GetIsolate(); - - Persistent<Object> g1s1; - Persistent<Object> g1s2; - Persistent<Object> g1c1; - Persistent<Object> g2s1; - Persistent<Object> g2s2; - Persistent<Object> g2c1; - - WeakCallCounter counter(1234); - - { - HandleScope scope; - g1s1 = Persistent<Object>::New(iso, Object::New()); - g1s2 = Persistent<Object>::New(iso, Object::New()); - g1c1 = Persistent<Object>::New(iso, Object::New()); - g1s1.MakeWeak(iso, reinterpret_cast<void*>(&counter), &WeakPointerCallback); - g1s2.MakeWeak(iso, reinterpret_cast<void*>(&counter), &WeakPointerCallback); - g1c1.MakeWeak(iso, reinterpret_cast<void*>(&counter), &WeakPointerCallback); - - g2s1 = Persistent<Object>::New(iso, Object::New()); - g2s2 = Persistent<Object>::New(iso, Object::New()); - g2c1 = Persistent<Object>::New(iso, Object::New()); - g2s1.MakeWeak(iso, reinterpret_cast<void*>(&counter), &WeakPointerCallback); - g2s2.MakeWeak(iso, reinterpret_cast<void*>(&counter), &WeakPointerCallback); - g2c1.MakeWeak(iso, reinterpret_cast<void*>(&counter), &WeakPointerCallback); - } - - Persistent<Object> root = Persistent<Object>::New(iso, g1s1); // make a root. - - // Connect group 1 and 2, make a cycle. - CHECK(g1s2->Set(0, g2s2)); - CHECK(g2s1->Set(0, g1s1)); - - { - Persistent<Value> g1_objects[] = { g1s1, g1s2 }; - Persistent<Value> g1_children[] = { g1c1 }; - Persistent<Value> g2_objects[] = { g2s1, g2s2 }; - Persistent<Value> g2_children[] = { g2c1 }; - V8::AddObjectGroup(g1_objects, 2); - V8::AddImplicitReferences(g1s1, g1_children, 1); - V8::AddObjectGroup(g2_objects, 2); - V8::AddImplicitReferences(g2s2, g2_children, 1); - } - // Do a single full GC, ensure incremental marking is stopped. - HEAP->CollectAllGarbage(i::Heap::kAbortIncrementalMarkingMask); - - // All object should be alive. - CHECK_EQ(0, counter.NumberOfWeakCalls()); - - // Weaken the root. - root.MakeWeak(iso, reinterpret_cast<void*>(&counter), &WeakPointerCallback); - // But make children strong roots---all the objects (except for children) - // should be collectable now. - g1c1.ClearWeak(iso); - g2c1.ClearWeak(iso); - - // Groups are deleted, rebuild groups. - { - Persistent<Value> g1_objects[] = { g1s1, g1s2 }; - Persistent<Value> g1_children[] = { g1c1 }; - Persistent<Value> g2_objects[] = { g2s1, g2s2 }; - Persistent<Value> g2_children[] = { g2c1 }; - V8::AddObjectGroup(g1_objects, 2); - V8::AddImplicitReferences(g1s1, g1_children, 1); - V8::AddObjectGroup(g2_objects, 2); - V8::AddImplicitReferences(g2s2, g2_children, 1); - } - - HEAP->CollectAllGarbage(i::Heap::kAbortIncrementalMarkingMask); - - // All objects should be gone. 5 global handles in total. - CHECK_EQ(5, counter.NumberOfWeakCalls()); - - // And now make children weak again and collect them. - g1c1.MakeWeak(iso, reinterpret_cast<void*>(&counter), &WeakPointerCallback); - g2c1.MakeWeak(iso, reinterpret_cast<void*>(&counter), &WeakPointerCallback); - - HEAP->CollectAllGarbage(i::Heap::kAbortIncrementalMarkingMask); - CHECK_EQ(7, counter.NumberOfWeakCalls()); -} - - -THREADED_TEST(ApiObjectGroupsCycle) { - HandleScope scope; - LocalContext env; - v8::Isolate* iso = env->GetIsolate(); - - WeakCallCounter counter(1234); - - Persistent<Object> g1s1; - Persistent<Object> g1s2; - Persistent<Object> g2s1; - Persistent<Object> g2s2; - Persistent<Object> g3s1; - Persistent<Object> g3s2; - Persistent<Object> g4s1; - Persistent<Object> g4s2; - - { - HandleScope scope; - g1s1 = Persistent<Object>::New(iso, Object::New()); - g1s2 = Persistent<Object>::New(iso, Object::New()); - g1s1.MakeWeak(iso, reinterpret_cast<void*>(&counter), &WeakPointerCallback); - g1s2.MakeWeak(iso, reinterpret_cast<void*>(&counter), &WeakPointerCallback); - CHECK(g1s1.IsWeak(iso)); - CHECK(g1s2.IsWeak(iso)); - - g2s1 = Persistent<Object>::New(iso, Object::New()); - g2s2 = Persistent<Object>::New(iso, Object::New()); - g2s1.MakeWeak(iso, reinterpret_cast<void*>(&counter), &WeakPointerCallback); - g2s2.MakeWeak(iso, reinterpret_cast<void*>(&counter), &WeakPointerCallback); - CHECK(g2s1.IsWeak(iso)); - CHECK(g2s2.IsWeak(iso)); - - g3s1 = Persistent<Object>::New(iso, Object::New()); - g3s2 = Persistent<Object>::New(iso, Object::New()); - g3s1.MakeWeak(iso, reinterpret_cast<void*>(&counter), &WeakPointerCallback); - g3s2.MakeWeak(iso, reinterpret_cast<void*>(&counter), &WeakPointerCallback); - CHECK(g3s1.IsWeak(iso)); - CHECK(g3s2.IsWeak(iso)); - - g4s1 = Persistent<Object>::New(iso, Object::New()); - g4s2 = Persistent<Object>::New(iso, Object::New()); - g4s1.MakeWeak(iso, reinterpret_cast<void*>(&counter), &WeakPointerCallback); - g4s2.MakeWeak(iso, reinterpret_cast<void*>(&counter), &WeakPointerCallback); - CHECK(g4s1.IsWeak(iso)); - CHECK(g4s2.IsWeak(iso)); - } - - Persistent<Object> root = Persistent<Object>::New(iso, g1s1); // make a root. - - // Connect groups. We're building the following cycle: - // G1: { g1s1, g2s1 }, g1s1 implicitly references g2s1, ditto for other - // groups. - { - Persistent<Value> g1_objects[] = { g1s1, g1s2 }; - Persistent<Value> g1_children[] = { g2s1 }; - Persistent<Value> g2_objects[] = { g2s1, g2s2 }; - Persistent<Value> g2_children[] = { g3s1 }; - Persistent<Value> g3_objects[] = { g3s1, g3s2 }; - Persistent<Value> g3_children[] = { g4s1 }; - Persistent<Value> g4_objects[] = { g4s1, g4s2 }; - Persistent<Value> g4_children[] = { g1s1 }; - V8::AddObjectGroup(g1_objects, 2); - V8::AddImplicitReferences(g1s1, g1_children, 1); - V8::AddObjectGroup(g2_objects, 2); - V8::AddImplicitReferences(g2s1, g2_children, 1); - V8::AddObjectGroup(g3_objects, 2); - V8::AddImplicitReferences(g3s1, g3_children, 1); - V8::AddObjectGroup(iso, g4_objects, 2); - V8::AddImplicitReferences(g4s1, g4_children, 1); - } - // Do a single full GC - HEAP->CollectAllGarbage(i::Heap::kAbortIncrementalMarkingMask); - - // All object should be alive. - CHECK_EQ(0, counter.NumberOfWeakCalls()); - - // Weaken the root. - root.MakeWeak(iso, reinterpret_cast<void*>(&counter), &WeakPointerCallback); - - // Groups are deleted, rebuild groups. - { - Persistent<Value> g1_objects[] = { g1s1, g1s2 }; - Persistent<Value> g1_children[] = { g2s1 }; - Persistent<Value> g2_objects[] = { g2s1, g2s2 }; - Persistent<Value> g2_children[] = { g3s1 }; - Persistent<Value> g3_objects[] = { g3s1, g3s2 }; - Persistent<Value> g3_children[] = { g4s1 }; - Persistent<Value> g4_objects[] = { g4s1, g4s2 }; - Persistent<Value> g4_children[] = { g1s1 }; - V8::AddObjectGroup(g1_objects, 2); - V8::AddImplicitReferences(g1s1, g1_children, 1); - V8::AddObjectGroup(g2_objects, 2); - V8::AddImplicitReferences(g2s1, g2_children, 1); - V8::AddObjectGroup(g3_objects, 2); - V8::AddImplicitReferences(g3s1, g3_children, 1); - V8::AddObjectGroup(g4_objects, 2); - V8::AddImplicitReferences(g4s1, g4_children, 1); - } - - HEAP->CollectAllGarbage(i::Heap::kAbortIncrementalMarkingMask); - - // All objects should be gone. 9 global handles in total. - CHECK_EQ(9, counter.NumberOfWeakCalls()); -} - - -// TODO(mstarzinger): This should be a THREADED_TEST but causes failures -// on the buildbots, so was made non-threaded for the time being. -TEST(ApiObjectGroupsCycleForScavenger) { - i::FLAG_stress_compaction = false; - i::FLAG_gc_global = false; - HandleScope scope; - LocalContext env; - v8::Isolate* iso = env->GetIsolate(); - - WeakCallCounter counter(1234); - - Persistent<Object> g1s1; - Persistent<Object> g1s2; - Persistent<Object> g2s1; - Persistent<Object> g2s2; - Persistent<Object> g3s1; - Persistent<Object> g3s2; - - { - HandleScope scope; - g1s1 = Persistent<Object>::New(iso, Object::New()); - g1s2 = Persistent<Object>::New(iso, Object::New()); - g1s1.MakeWeak(iso, reinterpret_cast<void*>(&counter), &WeakPointerCallback); - g1s2.MakeWeak(iso, reinterpret_cast<void*>(&counter), &WeakPointerCallback); - - g2s1 = Persistent<Object>::New(iso, Object::New()); - g2s2 = Persistent<Object>::New(iso, Object::New()); - g2s1.MakeWeak(iso, reinterpret_cast<void*>(&counter), &WeakPointerCallback); - g2s2.MakeWeak(iso, reinterpret_cast<void*>(&counter), &WeakPointerCallback); - - g3s1 = Persistent<Object>::New(iso, Object::New()); - g3s2 = Persistent<Object>::New(iso, Object::New()); - g3s1.MakeWeak(iso, reinterpret_cast<void*>(&counter), &WeakPointerCallback); - g3s2.MakeWeak(iso, reinterpret_cast<void*>(&counter), &WeakPointerCallback); - } - - // Make a root. - Persistent<Object> root = Persistent<Object>::New(iso, g1s1); - root.MarkPartiallyDependent(iso); - - // Connect groups. We're building the following cycle: - // G1: { g1s1, g2s1 }, g1s1 implicitly references g2s1, ditto for other - // groups. - { - g1s1.MarkPartiallyDependent(iso); - g1s2.MarkPartiallyDependent(iso); - g2s1.MarkPartiallyDependent(iso); - g2s2.MarkPartiallyDependent(iso); - g3s1.MarkPartiallyDependent(iso); - g3s2.MarkPartiallyDependent(iso); - Persistent<Value> g1_objects[] = { g1s1, g1s2 }; - Persistent<Value> g2_objects[] = { g2s1, g2s2 }; - Persistent<Value> g3_objects[] = { g3s1, g3s2 }; - V8::AddObjectGroup(g1_objects, 2); - g1s1->Set(v8_str("x"), g2s1); - V8::AddObjectGroup(g2_objects, 2); - g2s1->Set(v8_str("x"), g3s1); - V8::AddObjectGroup(g3_objects, 2); - g3s1->Set(v8_str("x"), g1s1); - } - - HEAP->CollectGarbage(i::NEW_SPACE); - - // All objects should be alive. - CHECK_EQ(0, counter.NumberOfWeakCalls()); - - // Weaken the root. - root.MakeWeak(iso, reinterpret_cast<void*>(&counter), &WeakPointerCallback); - root.MarkPartiallyDependent(iso); - - v8::Isolate* isolate = v8::Isolate::GetCurrent(); - // Groups are deleted, rebuild groups. - { - g1s1.MarkPartiallyDependent(isolate); - g1s2.MarkPartiallyDependent(isolate); - g2s1.MarkPartiallyDependent(isolate); - g2s2.MarkPartiallyDependent(isolate); - g3s1.MarkPartiallyDependent(isolate); - g3s2.MarkPartiallyDependent(isolate); - Persistent<Value> g1_objects[] = { g1s1, g1s2 }; - Persistent<Value> g2_objects[] = { g2s1, g2s2 }; - Persistent<Value> g3_objects[] = { g3s1, g3s2 }; - V8::AddObjectGroup(g1_objects, 2); - g1s1->Set(v8_str("x"), g2s1); - V8::AddObjectGroup(g2_objects, 2); - g2s1->Set(v8_str("x"), g3s1); - V8::AddObjectGroup(g3_objects, 2); - g3s1->Set(v8_str("x"), g1s1); - } - - HEAP->CollectGarbage(i::NEW_SPACE); - - // All objects should be gone. 7 global handles in total. - CHECK_EQ(7, counter.NumberOfWeakCalls()); -} - - -THREADED_TEST(ScriptException) { - v8::HandleScope scope; - LocalContext env; - Local<Script> script = Script::Compile(v8_str("throw 'panama!';")); - v8::TryCatch try_catch; - Local<Value> result = script->Run(); - CHECK(result.IsEmpty()); - CHECK(try_catch.HasCaught()); - String::AsciiValue exception_value(try_catch.Exception()); - CHECK_EQ(*exception_value, "panama!"); -} - - -TEST(TryCatchCustomException) { - v8::HandleScope scope; - LocalContext env; - v8::TryCatch try_catch; - CompileRun("function CustomError() { this.a = 'b'; }" - "(function f() { throw new CustomError(); })();"); - CHECK(try_catch.HasCaught()); - CHECK(try_catch.Exception()->ToObject()-> - Get(v8_str("a"))->Equals(v8_str("b"))); -} - - -bool message_received; - - -static void check_message_0(v8::Handle<v8::Message> message, - v8::Handle<Value> data) { - CHECK_EQ(5.76, data->NumberValue()); - CHECK_EQ(6.75, message->GetScriptResourceName()->NumberValue()); - CHECK_EQ(7.56, message->GetScriptData()->NumberValue()); - message_received = true; -} - - -THREADED_TEST(MessageHandler0) { - message_received = false; - v8::HandleScope scope; - CHECK(!message_received); - v8::V8::AddMessageListener(check_message_0, v8_num(5.76)); - LocalContext context; - v8::ScriptOrigin origin = - v8::ScriptOrigin(v8_str("6.75")); - v8::Handle<v8::Script> script = Script::Compile(v8_str("throw 'error'"), - &origin); - script->SetData(v8_str("7.56")); - script->Run(); - CHECK(message_received); - // clear out the message listener - v8::V8::RemoveMessageListeners(check_message_0); -} - - -static void check_message_1(v8::Handle<v8::Message> message, - v8::Handle<Value> data) { - CHECK(data->IsNumber()); - CHECK_EQ(1337, data->Int32Value()); - message_received = true; -} - - -TEST(MessageHandler1) { - message_received = false; - v8::HandleScope scope; - CHECK(!message_received); - v8::V8::AddMessageListener(check_message_1); - LocalContext context; - CompileRun("throw 1337;"); - CHECK(message_received); - // clear out the message listener - v8::V8::RemoveMessageListeners(check_message_1); -} - - -static void check_message_2(v8::Handle<v8::Message> message, - v8::Handle<Value> data) { - LocalContext context; - CHECK(data->IsObject()); - v8::Local<v8::Value> hidden_property = - v8::Object::Cast(*data)->GetHiddenValue(v8_str("hidden key")); - CHECK(v8_str("hidden value")->Equals(hidden_property)); - message_received = true; -} - - -TEST(MessageHandler2) { - message_received = false; - v8::HandleScope scope; - CHECK(!message_received); - v8::V8::AddMessageListener(check_message_2); - LocalContext context; - v8::Local<v8::Value> error = v8::Exception::Error(v8_str("custom error")); - v8::Object::Cast(*error)->SetHiddenValue(v8_str("hidden key"), - v8_str("hidden value")); - context->Global()->Set(v8_str("error"), error); - CompileRun("throw error;"); - CHECK(message_received); - // clear out the message listener - v8::V8::RemoveMessageListeners(check_message_2); -} - - -THREADED_TEST(GetSetProperty) { - v8::HandleScope scope; - LocalContext context; - context->Global()->Set(v8_str("foo"), v8_num(14)); - context->Global()->Set(v8_str("12"), v8_num(92)); - context->Global()->Set(v8::Integer::New(16), v8_num(32)); - context->Global()->Set(v8_num(13), v8_num(56)); - Local<Value> foo = Script::Compile(v8_str("this.foo"))->Run(); - CHECK_EQ(14, foo->Int32Value()); - Local<Value> twelve = Script::Compile(v8_str("this[12]"))->Run(); - CHECK_EQ(92, twelve->Int32Value()); - Local<Value> sixteen = Script::Compile(v8_str("this[16]"))->Run(); - CHECK_EQ(32, sixteen->Int32Value()); - Local<Value> thirteen = Script::Compile(v8_str("this[13]"))->Run(); - CHECK_EQ(56, thirteen->Int32Value()); - CHECK_EQ(92, context->Global()->Get(v8::Integer::New(12))->Int32Value()); - CHECK_EQ(92, context->Global()->Get(v8_str("12"))->Int32Value()); - CHECK_EQ(92, context->Global()->Get(v8_num(12))->Int32Value()); - CHECK_EQ(32, context->Global()->Get(v8::Integer::New(16))->Int32Value()); - CHECK_EQ(32, context->Global()->Get(v8_str("16"))->Int32Value()); - CHECK_EQ(32, context->Global()->Get(v8_num(16))->Int32Value()); - CHECK_EQ(56, context->Global()->Get(v8::Integer::New(13))->Int32Value()); - CHECK_EQ(56, context->Global()->Get(v8_str("13"))->Int32Value()); - CHECK_EQ(56, context->Global()->Get(v8_num(13))->Int32Value()); -} - - -THREADED_TEST(PropertyAttributes) { - v8::HandleScope scope; - LocalContext context; - // none - Local<String> prop = v8_str("none"); - context->Global()->Set(prop, v8_num(7)); - CHECK_EQ(v8::None, context->Global()->GetPropertyAttributes(prop)); - // read-only - prop = v8_str("read_only"); - context->Global()->Set(prop, v8_num(7), v8::ReadOnly); - CHECK_EQ(7, context->Global()->Get(prop)->Int32Value()); - CHECK_EQ(v8::ReadOnly, context->Global()->GetPropertyAttributes(prop)); - Script::Compile(v8_str("read_only = 9"))->Run(); - CHECK_EQ(7, context->Global()->Get(prop)->Int32Value()); - context->Global()->Set(prop, v8_num(10)); - CHECK_EQ(7, context->Global()->Get(prop)->Int32Value()); - // dont-delete - prop = v8_str("dont_delete"); - context->Global()->Set(prop, v8_num(13), v8::DontDelete); - CHECK_EQ(13, context->Global()->Get(prop)->Int32Value()); - Script::Compile(v8_str("delete dont_delete"))->Run(); - CHECK_EQ(13, context->Global()->Get(prop)->Int32Value()); - CHECK_EQ(v8::DontDelete, context->Global()->GetPropertyAttributes(prop)); - // dont-enum - prop = v8_str("dont_enum"); - context->Global()->Set(prop, v8_num(28), v8::DontEnum); - CHECK_EQ(v8::DontEnum, context->Global()->GetPropertyAttributes(prop)); - // absent - prop = v8_str("absent"); - CHECK_EQ(v8::None, context->Global()->GetPropertyAttributes(prop)); - Local<Value> fake_prop = v8_num(1); - CHECK_EQ(v8::None, context->Global()->GetPropertyAttributes(fake_prop)); - // exception - TryCatch try_catch; - Local<Value> exception = - CompileRun("({ toString: function() { throw 'exception';} })"); - CHECK_EQ(v8::None, context->Global()->GetPropertyAttributes(exception)); - CHECK(try_catch.HasCaught()); - String::AsciiValue exception_value(try_catch.Exception()); - CHECK_EQ("exception", *exception_value); - try_catch.Reset(); -} - - -THREADED_TEST(Array) { - v8::HandleScope scope; - LocalContext context; - Local<v8::Array> array = v8::Array::New(); - CHECK_EQ(0, array->Length()); - CHECK(array->Get(0)->IsUndefined()); - CHECK(!array->Has(0)); - CHECK(array->Get(100)->IsUndefined()); - CHECK(!array->Has(100)); - array->Set(2, v8_num(7)); - CHECK_EQ(3, array->Length()); - CHECK(!array->Has(0)); - CHECK(!array->Has(1)); - CHECK(array->Has(2)); - CHECK_EQ(7, array->Get(2)->Int32Value()); - Local<Value> obj = Script::Compile(v8_str("[1, 2, 3]"))->Run(); - Local<v8::Array> arr = obj.As<v8::Array>(); - CHECK_EQ(3, arr->Length()); - CHECK_EQ(1, arr->Get(0)->Int32Value()); - CHECK_EQ(2, arr->Get(1)->Int32Value()); - CHECK_EQ(3, arr->Get(2)->Int32Value()); - array = v8::Array::New(27); - CHECK_EQ(27, array->Length()); - array = v8::Array::New(-27); - CHECK_EQ(0, array->Length()); -} - - -v8::Handle<Value> HandleF(const v8::Arguments& args) { - v8::HandleScope scope; - ApiTestFuzzer::Fuzz(); - Local<v8::Array> result = v8::Array::New(args.Length()); - for (int i = 0; i < args.Length(); i++) - result->Set(i, args[i]); - return scope.Close(result); -} - - -THREADED_TEST(Vector) { - v8::HandleScope scope; - Local<ObjectTemplate> global = ObjectTemplate::New(); - global->Set(v8_str("f"), v8::FunctionTemplate::New(HandleF)); - LocalContext context(0, global); - - const char* fun = "f()"; - Local<v8::Array> a0 = CompileRun(fun).As<v8::Array>(); - CHECK_EQ(0, a0->Length()); - - const char* fun2 = "f(11)"; - Local<v8::Array> a1 = CompileRun(fun2).As<v8::Array>(); - CHECK_EQ(1, a1->Length()); - CHECK_EQ(11, a1->Get(0)->Int32Value()); - - const char* fun3 = "f(12, 13)"; - Local<v8::Array> a2 = CompileRun(fun3).As<v8::Array>(); - CHECK_EQ(2, a2->Length()); - CHECK_EQ(12, a2->Get(0)->Int32Value()); - CHECK_EQ(13, a2->Get(1)->Int32Value()); - - const char* fun4 = "f(14, 15, 16)"; - Local<v8::Array> a3 = CompileRun(fun4).As<v8::Array>(); - CHECK_EQ(3, a3->Length()); - CHECK_EQ(14, a3->Get(0)->Int32Value()); - CHECK_EQ(15, a3->Get(1)->Int32Value()); - CHECK_EQ(16, a3->Get(2)->Int32Value()); - - const char* fun5 = "f(17, 18, 19, 20)"; - Local<v8::Array> a4 = CompileRun(fun5).As<v8::Array>(); - CHECK_EQ(4, a4->Length()); - CHECK_EQ(17, a4->Get(0)->Int32Value()); - CHECK_EQ(18, a4->Get(1)->Int32Value()); - CHECK_EQ(19, a4->Get(2)->Int32Value()); - CHECK_EQ(20, a4->Get(3)->Int32Value()); -} - - -THREADED_TEST(FunctionCall) { - v8::HandleScope scope; - LocalContext context; - CompileRun( - "function Foo() {" - " var result = [];" - " for (var i = 0; i < arguments.length; i++) {" - " result.push(arguments[i]);" - " }" - " return result;" - "}"); - Local<Function> Foo = - Local<Function>::Cast(context->Global()->Get(v8_str("Foo"))); - - v8::Handle<Value>* args0 = NULL; - Local<v8::Array> a0 = Local<v8::Array>::Cast(Foo->Call(Foo, 0, args0)); - CHECK_EQ(0, a0->Length()); - - v8::Handle<Value> args1[] = { v8_num(1.1) }; - Local<v8::Array> a1 = Local<v8::Array>::Cast(Foo->Call(Foo, 1, args1)); - CHECK_EQ(1, a1->Length()); - CHECK_EQ(1.1, a1->Get(v8::Integer::New(0))->NumberValue()); - - v8::Handle<Value> args2[] = { v8_num(2.2), - v8_num(3.3) }; - Local<v8::Array> a2 = Local<v8::Array>::Cast(Foo->Call(Foo, 2, args2)); - CHECK_EQ(2, a2->Length()); - CHECK_EQ(2.2, a2->Get(v8::Integer::New(0))->NumberValue()); - CHECK_EQ(3.3, a2->Get(v8::Integer::New(1))->NumberValue()); - - v8::Handle<Value> args3[] = { v8_num(4.4), - v8_num(5.5), - v8_num(6.6) }; - Local<v8::Array> a3 = Local<v8::Array>::Cast(Foo->Call(Foo, 3, args3)); - CHECK_EQ(3, a3->Length()); - CHECK_EQ(4.4, a3->Get(v8::Integer::New(0))->NumberValue()); - CHECK_EQ(5.5, a3->Get(v8::Integer::New(1))->NumberValue()); - CHECK_EQ(6.6, a3->Get(v8::Integer::New(2))->NumberValue()); - - v8::Handle<Value> args4[] = { v8_num(7.7), - v8_num(8.8), - v8_num(9.9), - v8_num(10.11) }; - Local<v8::Array> a4 = Local<v8::Array>::Cast(Foo->Call(Foo, 4, args4)); - CHECK_EQ(4, a4->Length()); - CHECK_EQ(7.7, a4->Get(v8::Integer::New(0))->NumberValue()); - CHECK_EQ(8.8, a4->Get(v8::Integer::New(1))->NumberValue()); - CHECK_EQ(9.9, a4->Get(v8::Integer::New(2))->NumberValue()); - CHECK_EQ(10.11, a4->Get(v8::Integer::New(3))->NumberValue()); -} - - -static const char* js_code_causing_out_of_memory = - "var a = new Array(); while(true) a.push(a);"; - - -// These tests run for a long time and prevent us from running tests -// that come after them so they cannot run in parallel. -TEST(OutOfMemory) { - // It's not possible to read a snapshot into a heap with different dimensions. - if (i::Snapshot::IsEnabled()) return; - // Set heap limits. - static const int K = 1024; - v8::ResourceConstraints constraints; - constraints.set_max_young_space_size(256 * K); - constraints.set_max_old_space_size(4 * K * K); - v8::SetResourceConstraints(&constraints); - - // Execute a script that causes out of memory. - v8::HandleScope scope; - LocalContext context; - v8::V8::IgnoreOutOfMemoryException(); - Local<Script> script = - Script::Compile(String::New(js_code_causing_out_of_memory)); - Local<Value> result = script->Run(); - - // Check for out of memory state. - CHECK(result.IsEmpty()); - CHECK(context->HasOutOfMemoryException()); -} - - -v8::Handle<Value> ProvokeOutOfMemory(const v8::Arguments& args) { - ApiTestFuzzer::Fuzz(); - - v8::HandleScope scope; - LocalContext context; - Local<Script> script = - Script::Compile(String::New(js_code_causing_out_of_memory)); - Local<Value> result = script->Run(); - - // Check for out of memory state. - CHECK(result.IsEmpty()); - CHECK(context->HasOutOfMemoryException()); - - return result; -} - - -TEST(OutOfMemoryNested) { - // It's not possible to read a snapshot into a heap with different dimensions. - if (i::Snapshot::IsEnabled()) return; - // Set heap limits. - static const int K = 1024; - v8::ResourceConstraints constraints; - constraints.set_max_young_space_size(256 * K); - constraints.set_max_old_space_size(4 * K * K); - v8::SetResourceConstraints(&constraints); - - v8::HandleScope scope; - Local<ObjectTemplate> templ = ObjectTemplate::New(); - templ->Set(v8_str("ProvokeOutOfMemory"), - v8::FunctionTemplate::New(ProvokeOutOfMemory)); - LocalContext context(0, templ); - v8::V8::IgnoreOutOfMemoryException(); - Local<Value> result = CompileRun( - "var thrown = false;" - "try {" - " ProvokeOutOfMemory();" - "} catch (e) {" - " thrown = true;" - "}"); - // Check for out of memory state. - CHECK(result.IsEmpty()); - CHECK(context->HasOutOfMemoryException()); -} - - -TEST(HugeConsStringOutOfMemory) { - // It's not possible to read a snapshot into a heap with different dimensions. - if (i::Snapshot::IsEnabled()) return; - // Set heap limits. - static const int K = 1024; - v8::ResourceConstraints constraints; - constraints.set_max_young_space_size(256 * K); - constraints.set_max_old_space_size(3 * K * K); - v8::SetResourceConstraints(&constraints); - - // Execute a script that causes out of memory. - v8::V8::IgnoreOutOfMemoryException(); - - v8::HandleScope scope; - LocalContext context; - - // Build huge string. This should fail with out of memory exception. - Local<Value> result = CompileRun( - "var str = Array.prototype.join.call({length: 513}, \"A\").toUpperCase();" - "for (var i = 0; i < 22; i++) { str = str + str; }"); - - // Check for out of memory state. - CHECK(result.IsEmpty()); - CHECK(context->HasOutOfMemoryException()); -} - - -THREADED_TEST(ConstructCall) { - v8::HandleScope scope; - LocalContext context; - CompileRun( - "function Foo() {" - " var result = [];" - " for (var i = 0; i < arguments.length; i++) {" - " result.push(arguments[i]);" - " }" - " return result;" - "}"); - Local<Function> Foo = - Local<Function>::Cast(context->Global()->Get(v8_str("Foo"))); - - v8::Handle<Value>* args0 = NULL; - Local<v8::Array> a0 = Local<v8::Array>::Cast(Foo->NewInstance(0, args0)); - CHECK_EQ(0, a0->Length()); - - v8::Handle<Value> args1[] = { v8_num(1.1) }; - Local<v8::Array> a1 = Local<v8::Array>::Cast(Foo->NewInstance(1, args1)); - CHECK_EQ(1, a1->Length()); - CHECK_EQ(1.1, a1->Get(v8::Integer::New(0))->NumberValue()); - - v8::Handle<Value> args2[] = { v8_num(2.2), - v8_num(3.3) }; - Local<v8::Array> a2 = Local<v8::Array>::Cast(Foo->NewInstance(2, args2)); - CHECK_EQ(2, a2->Length()); - CHECK_EQ(2.2, a2->Get(v8::Integer::New(0))->NumberValue()); - CHECK_EQ(3.3, a2->Get(v8::Integer::New(1))->NumberValue()); - - v8::Handle<Value> args3[] = { v8_num(4.4), - v8_num(5.5), - v8_num(6.6) }; - Local<v8::Array> a3 = Local<v8::Array>::Cast(Foo->NewInstance(3, args3)); - CHECK_EQ(3, a3->Length()); - CHECK_EQ(4.4, a3->Get(v8::Integer::New(0))->NumberValue()); - CHECK_EQ(5.5, a3->Get(v8::Integer::New(1))->NumberValue()); - CHECK_EQ(6.6, a3->Get(v8::Integer::New(2))->NumberValue()); - - v8::Handle<Value> args4[] = { v8_num(7.7), - v8_num(8.8), - v8_num(9.9), - v8_num(10.11) }; - Local<v8::Array> a4 = Local<v8::Array>::Cast(Foo->NewInstance(4, args4)); - CHECK_EQ(4, a4->Length()); - CHECK_EQ(7.7, a4->Get(v8::Integer::New(0))->NumberValue()); - CHECK_EQ(8.8, a4->Get(v8::Integer::New(1))->NumberValue()); - CHECK_EQ(9.9, a4->Get(v8::Integer::New(2))->NumberValue()); - CHECK_EQ(10.11, a4->Get(v8::Integer::New(3))->NumberValue()); -} - - -static void CheckUncle(v8::TryCatch* try_catch) { - CHECK(try_catch->HasCaught()); - String::AsciiValue str_value(try_catch->Exception()); - CHECK_EQ(*str_value, "uncle?"); - try_catch->Reset(); -} - - -THREADED_TEST(ConversionNumber) { - v8::HandleScope scope; - LocalContext env; - // Very large number. - CompileRun("var obj = Math.pow(2,32) * 1237;"); - Local<Value> obj = env->Global()->Get(v8_str("obj")); - CHECK_EQ(5312874545152.0, obj->ToNumber()->Value()); - CHECK_EQ(0, obj->ToInt32()->Value()); - CHECK(0u == obj->ToUint32()->Value()); // NOLINT - no CHECK_EQ for unsigned. - // Large number. - CompileRun("var obj = -1234567890123;"); - obj = env->Global()->Get(v8_str("obj")); - CHECK_EQ(-1234567890123.0, obj->ToNumber()->Value()); - CHECK_EQ(-1912276171, obj->ToInt32()->Value()); - CHECK(2382691125u == obj->ToUint32()->Value()); // NOLINT - // Small positive integer. - CompileRun("var obj = 42;"); - obj = env->Global()->Get(v8_str("obj")); - CHECK_EQ(42.0, obj->ToNumber()->Value()); - CHECK_EQ(42, obj->ToInt32()->Value()); - CHECK(42u == obj->ToUint32()->Value()); // NOLINT - // Negative integer. - CompileRun("var obj = -37;"); - obj = env->Global()->Get(v8_str("obj")); - CHECK_EQ(-37.0, obj->ToNumber()->Value()); - CHECK_EQ(-37, obj->ToInt32()->Value()); - CHECK(4294967259u == obj->ToUint32()->Value()); // NOLINT - // Positive non-int32 integer. - CompileRun("var obj = 0x81234567;"); - obj = env->Global()->Get(v8_str("obj")); - CHECK_EQ(2166572391.0, obj->ToNumber()->Value()); - CHECK_EQ(-2128394905, obj->ToInt32()->Value()); - CHECK(2166572391u == obj->ToUint32()->Value()); // NOLINT - // Fraction. - CompileRun("var obj = 42.3;"); - obj = env->Global()->Get(v8_str("obj")); - CHECK_EQ(42.3, obj->ToNumber()->Value()); - CHECK_EQ(42, obj->ToInt32()->Value()); - CHECK(42u == obj->ToUint32()->Value()); // NOLINT - // Large negative fraction. - CompileRun("var obj = -5726623061.75;"); - obj = env->Global()->Get(v8_str("obj")); - CHECK_EQ(-5726623061.75, obj->ToNumber()->Value()); - CHECK_EQ(-1431655765, obj->ToInt32()->Value()); - CHECK(2863311531u == obj->ToUint32()->Value()); // NOLINT -} - - -THREADED_TEST(isNumberType) { - v8::HandleScope scope; - LocalContext env; - // Very large number. - CompileRun("var obj = Math.pow(2,32) * 1237;"); - Local<Value> obj = env->Global()->Get(v8_str("obj")); - CHECK(!obj->IsInt32()); - CHECK(!obj->IsUint32()); - // Large negative number. - CompileRun("var obj = -1234567890123;"); - obj = env->Global()->Get(v8_str("obj")); - CHECK(!obj->IsInt32()); - CHECK(!obj->IsUint32()); - // Small positive integer. - CompileRun("var obj = 42;"); - obj = env->Global()->Get(v8_str("obj")); - CHECK(obj->IsInt32()); - CHECK(obj->IsUint32()); - // Negative integer. - CompileRun("var obj = -37;"); - obj = env->Global()->Get(v8_str("obj")); - CHECK(obj->IsInt32()); - CHECK(!obj->IsUint32()); - // Positive non-int32 integer. - CompileRun("var obj = 0x81234567;"); - obj = env->Global()->Get(v8_str("obj")); - CHECK(!obj->IsInt32()); - CHECK(obj->IsUint32()); - // Fraction. - CompileRun("var obj = 42.3;"); - obj = env->Global()->Get(v8_str("obj")); - CHECK(!obj->IsInt32()); - CHECK(!obj->IsUint32()); - // Large negative fraction. - CompileRun("var obj = -5726623061.75;"); - obj = env->Global()->Get(v8_str("obj")); - CHECK(!obj->IsInt32()); - CHECK(!obj->IsUint32()); - // Positive zero - CompileRun("var obj = 0.0;"); - obj = env->Global()->Get(v8_str("obj")); - CHECK(obj->IsInt32()); - CHECK(obj->IsUint32()); - // Positive zero - CompileRun("var obj = -0.0;"); - obj = env->Global()->Get(v8_str("obj")); - CHECK(!obj->IsInt32()); - CHECK(!obj->IsUint32()); -} - - -THREADED_TEST(ConversionException) { - v8::HandleScope scope; - LocalContext env; - CompileRun( - "function TestClass() { };" - "TestClass.prototype.toString = function () { throw 'uncle?'; };" - "var obj = new TestClass();"); - Local<Value> obj = env->Global()->Get(v8_str("obj")); - - v8::TryCatch try_catch; - - Local<Value> to_string_result = obj->ToString(); - CHECK(to_string_result.IsEmpty()); - CheckUncle(&try_catch); - - Local<Value> to_number_result = obj->ToNumber(); - CHECK(to_number_result.IsEmpty()); - CheckUncle(&try_catch); - - Local<Value> to_integer_result = obj->ToInteger(); - CHECK(to_integer_result.IsEmpty()); - CheckUncle(&try_catch); - - Local<Value> to_uint32_result = obj->ToUint32(); - CHECK(to_uint32_result.IsEmpty()); - CheckUncle(&try_catch); - - Local<Value> to_int32_result = obj->ToInt32(); - CHECK(to_int32_result.IsEmpty()); - CheckUncle(&try_catch); - - Local<Value> to_object_result = v8::Undefined()->ToObject(); - CHECK(to_object_result.IsEmpty()); - CHECK(try_catch.HasCaught()); - try_catch.Reset(); - - int32_t int32_value = obj->Int32Value(); - CHECK_EQ(0, int32_value); - CheckUncle(&try_catch); - - uint32_t uint32_value = obj->Uint32Value(); - CHECK_EQ(0, uint32_value); - CheckUncle(&try_catch); - - double number_value = obj->NumberValue(); - CHECK_NE(0, IsNaN(number_value)); - CheckUncle(&try_catch); - - int64_t integer_value = obj->IntegerValue(); - CHECK_EQ(0.0, static_cast<double>(integer_value)); - CheckUncle(&try_catch); -} - - -v8::Handle<Value> ThrowFromC(const v8::Arguments& args) { - ApiTestFuzzer::Fuzz(); - return v8::ThrowException(v8_str("konto")); -} - - -v8::Handle<Value> CCatcher(const v8::Arguments& args) { - if (args.Length() < 1) return v8::False(); - v8::HandleScope scope; - v8::TryCatch try_catch; - Local<Value> result = v8::Script::Compile(args[0]->ToString())->Run(); - CHECK(!try_catch.HasCaught() || result.IsEmpty()); - return v8::Boolean::New(try_catch.HasCaught()); -} - - -THREADED_TEST(APICatch) { - v8::HandleScope scope; - Local<ObjectTemplate> templ = ObjectTemplate::New(); - templ->Set(v8_str("ThrowFromC"), - v8::FunctionTemplate::New(ThrowFromC)); - LocalContext context(0, templ); - CompileRun( - "var thrown = false;" - "try {" - " ThrowFromC();" - "} catch (e) {" - " thrown = true;" - "}"); - Local<Value> thrown = context->Global()->Get(v8_str("thrown")); - CHECK(thrown->BooleanValue()); -} - - -THREADED_TEST(APIThrowTryCatch) { - v8::HandleScope scope; - Local<ObjectTemplate> templ = ObjectTemplate::New(); - templ->Set(v8_str("ThrowFromC"), - v8::FunctionTemplate::New(ThrowFromC)); - LocalContext context(0, templ); - v8::TryCatch try_catch; - CompileRun("ThrowFromC();"); - CHECK(try_catch.HasCaught()); -} - - -// Test that a try-finally block doesn't shadow a try-catch block -// when setting up an external handler. -// -// BUG(271): Some of the exception propagation does not work on the -// ARM simulator because the simulator separates the C++ stack and the -// JS stack. This test therefore fails on the simulator. The test is -// not threaded to allow the threading tests to run on the simulator. -TEST(TryCatchInTryFinally) { - v8::HandleScope scope; - Local<ObjectTemplate> templ = ObjectTemplate::New(); - templ->Set(v8_str("CCatcher"), - v8::FunctionTemplate::New(CCatcher)); - LocalContext context(0, templ); - Local<Value> result = CompileRun("try {" - " try {" - " CCatcher('throw 7;');" - " } finally {" - " }" - "} catch (e) {" - "}"); - CHECK(result->IsTrue()); -} - - -static void check_reference_error_message( - v8::Handle<v8::Message> message, - v8::Handle<v8::Value> data) { - const char* reference_error = "Uncaught ReferenceError: asdf is not defined"; - CHECK(message->Get()->Equals(v8_str(reference_error))); -} - - -static v8::Handle<Value> Fail(const v8::Arguments& args) { - ApiTestFuzzer::Fuzz(); - CHECK(false); - return v8::Undefined(); -} - - -// Test that overwritten methods are not invoked on uncaught exception -// formatting. However, they are invoked when performing normal error -// string conversions. -TEST(APIThrowMessageOverwrittenToString) { - v8::HandleScope scope; - v8::V8::AddMessageListener(check_reference_error_message); - Local<ObjectTemplate> templ = ObjectTemplate::New(); - templ->Set(v8_str("fail"), v8::FunctionTemplate::New(Fail)); - LocalContext context(NULL, templ); - CompileRun("asdf;"); - CompileRun("var limit = {};" - "limit.valueOf = fail;" - "Error.stackTraceLimit = limit;"); - CompileRun("asdf"); - CompileRun("Array.prototype.pop = fail;"); - CompileRun("Object.prototype.hasOwnProperty = fail;"); - CompileRun("Object.prototype.toString = function f() { return 'Yikes'; }"); - CompileRun("Number.prototype.toString = function f() { return 'Yikes'; }"); - CompileRun("String.prototype.toString = function f() { return 'Yikes'; }"); - CompileRun("ReferenceError.prototype.toString =" - " function() { return 'Whoops' }"); - CompileRun("asdf;"); - CompileRun("ReferenceError.prototype.constructor.name = void 0;"); - CompileRun("asdf;"); - CompileRun("ReferenceError.prototype.constructor = void 0;"); - CompileRun("asdf;"); - CompileRun("ReferenceError.prototype.__proto__ = new Object();"); - CompileRun("asdf;"); - CompileRun("ReferenceError.prototype = new Object();"); - CompileRun("asdf;"); - v8::Handle<Value> string = CompileRun("try { asdf; } catch(e) { e + ''; }"); - CHECK(string->Equals(v8_str("Whoops"))); - CompileRun("ReferenceError.prototype.constructor = new Object();" - "ReferenceError.prototype.constructor.name = 1;" - "Number.prototype.toString = function() { return 'Whoops'; };" - "ReferenceError.prototype.toString = Object.prototype.toString;"); - CompileRun("asdf;"); - v8::V8::RemoveMessageListeners(check_reference_error_message); -} - - -static void check_custom_error_message( - v8::Handle<v8::Message> message, - v8::Handle<v8::Value> data) { - const char* uncaught_error = "Uncaught MyError toString"; - CHECK(message->Get()->Equals(v8_str(uncaught_error))); -} - - -TEST(CustomErrorToString) { - v8::HandleScope scope; - v8::V8::AddMessageListener(check_custom_error_message); - LocalContext context; - CompileRun( - "function MyError(name, message) { " - " this.name = name; " - " this.message = message; " - "} " - "MyError.prototype = Object.create(Error.prototype); " - "MyError.prototype.toString = function() { " - " return 'MyError toString'; " - "}; " - "throw new MyError('my name', 'my message'); "); - v8::V8::RemoveMessageListeners(check_custom_error_message); -} - - -static void receive_message(v8::Handle<v8::Message> message, - v8::Handle<v8::Value> data) { - message->Get(); - message_received = true; -} - - -TEST(APIThrowMessage) { - message_received = false; - v8::HandleScope scope; - v8::V8::AddMessageListener(receive_message); - Local<ObjectTemplate> templ = ObjectTemplate::New(); - templ->Set(v8_str("ThrowFromC"), - v8::FunctionTemplate::New(ThrowFromC)); - LocalContext context(0, templ); - CompileRun("ThrowFromC();"); - CHECK(message_received); - v8::V8::RemoveMessageListeners(receive_message); -} - - -TEST(APIThrowMessageAndVerboseTryCatch) { - message_received = false; - v8::HandleScope scope; - v8::V8::AddMessageListener(receive_message); - Local<ObjectTemplate> templ = ObjectTemplate::New(); - templ->Set(v8_str("ThrowFromC"), - v8::FunctionTemplate::New(ThrowFromC)); - LocalContext context(0, templ); - v8::TryCatch try_catch; - try_catch.SetVerbose(true); - Local<Value> result = CompileRun("ThrowFromC();"); - CHECK(try_catch.HasCaught()); - CHECK(result.IsEmpty()); - CHECK(message_received); - v8::V8::RemoveMessageListeners(receive_message); -} - - -TEST(APIStackOverflowAndVerboseTryCatch) { - message_received = false; - v8::HandleScope scope; - v8::V8::AddMessageListener(receive_message); - LocalContext context; - v8::TryCatch try_catch; - try_catch.SetVerbose(true); - Local<Value> result = CompileRun("function foo() { foo(); } foo();"); - CHECK(try_catch.HasCaught()); - CHECK(result.IsEmpty()); - CHECK(message_received); - v8::V8::RemoveMessageListeners(receive_message); -} - - -THREADED_TEST(ExternalScriptException) { - v8::HandleScope scope; - Local<ObjectTemplate> templ = ObjectTemplate::New(); - templ->Set(v8_str("ThrowFromC"), - v8::FunctionTemplate::New(ThrowFromC)); - LocalContext context(0, templ); - - v8::TryCatch try_catch; - Local<Script> script - = Script::Compile(v8_str("ThrowFromC(); throw 'panama';")); - Local<Value> result = script->Run(); - CHECK(result.IsEmpty()); - CHECK(try_catch.HasCaught()); - String::AsciiValue exception_value(try_catch.Exception()); - CHECK_EQ("konto", *exception_value); -} - - - -v8::Handle<Value> CThrowCountDown(const v8::Arguments& args) { - ApiTestFuzzer::Fuzz(); - CHECK_EQ(4, args.Length()); - int count = args[0]->Int32Value(); - int cInterval = args[2]->Int32Value(); - if (count == 0) { - return v8::ThrowException(v8_str("FromC")); - } else { - Local<v8::Object> global = Context::GetCurrent()->Global(); - Local<Value> fun = global->Get(v8_str("JSThrowCountDown")); - v8::Handle<Value> argv[] = { v8_num(count - 1), - args[1], - args[2], - args[3] }; - if (count % cInterval == 0) { - v8::TryCatch try_catch; - Local<Value> result = fun.As<Function>()->Call(global, 4, argv); - int expected = args[3]->Int32Value(); - if (try_catch.HasCaught()) { - CHECK_EQ(expected, count); - CHECK(result.IsEmpty()); - CHECK(!i::Isolate::Current()->has_scheduled_exception()); - } else { - CHECK_NE(expected, count); - } - return result; - } else { - return fun.As<Function>()->Call(global, 4, argv); - } - } -} - - -v8::Handle<Value> JSCheck(const v8::Arguments& args) { - ApiTestFuzzer::Fuzz(); - CHECK_EQ(3, args.Length()); - bool equality = args[0]->BooleanValue(); - int count = args[1]->Int32Value(); - int expected = args[2]->Int32Value(); - if (equality) { - CHECK_EQ(count, expected); - } else { - CHECK_NE(count, expected); - } - return v8::Undefined(); -} - - -THREADED_TEST(EvalInTryFinally) { - v8::HandleScope scope; - LocalContext context; - v8::TryCatch try_catch; - CompileRun("(function() {" - " try {" - " eval('asldkf (*&^&*^');" - " } finally {" - " return;" - " }" - "})()"); - CHECK(!try_catch.HasCaught()); -} - - -// This test works by making a stack of alternating JavaScript and C -// activations. These activations set up exception handlers with regular -// intervals, one interval for C activations and another for JavaScript -// activations. When enough activations have been created an exception is -// thrown and we check that the right activation catches the exception and that -// no other activations do. The right activation is always the topmost one with -// a handler, regardless of whether it is in JavaScript or C. -// -// The notation used to describe a test case looks like this: -// -// *JS[4] *C[3] @JS[2] C[1] JS[0] -// -// Each entry is an activation, either JS or C. The index is the count at that -// level. Stars identify activations with exception handlers, the @ identifies -// the exception handler that should catch the exception. -// -// BUG(271): Some of the exception propagation does not work on the -// ARM simulator because the simulator separates the C++ stack and the -// JS stack. This test therefore fails on the simulator. The test is -// not threaded to allow the threading tests to run on the simulator. -TEST(ExceptionOrder) { - v8::HandleScope scope; - Local<ObjectTemplate> templ = ObjectTemplate::New(); - templ->Set(v8_str("check"), v8::FunctionTemplate::New(JSCheck)); - templ->Set(v8_str("CThrowCountDown"), - v8::FunctionTemplate::New(CThrowCountDown)); - LocalContext context(0, templ); - CompileRun( - "function JSThrowCountDown(count, jsInterval, cInterval, expected) {" - " if (count == 0) throw 'FromJS';" - " if (count % jsInterval == 0) {" - " try {" - " var value = CThrowCountDown(count - 1," - " jsInterval," - " cInterval," - " expected);" - " check(false, count, expected);" - " return value;" - " } catch (e) {" - " check(true, count, expected);" - " }" - " } else {" - " return CThrowCountDown(count - 1, jsInterval, cInterval, expected);" - " }" - "}"); - Local<Function> fun = - Local<Function>::Cast(context->Global()->Get(v8_str("JSThrowCountDown"))); - - const int argc = 4; - // count jsInterval cInterval expected - - // *JS[4] *C[3] @JS[2] C[1] JS[0] - v8::Handle<Value> a0[argc] = { v8_num(4), v8_num(2), v8_num(3), v8_num(2) }; - fun->Call(fun, argc, a0); - - // JS[5] *C[4] JS[3] @C[2] JS[1] C[0] - v8::Handle<Value> a1[argc] = { v8_num(5), v8_num(6), v8_num(1), v8_num(2) }; - fun->Call(fun, argc, a1); - - // JS[6] @C[5] JS[4] C[3] JS[2] C[1] JS[0] - v8::Handle<Value> a2[argc] = { v8_num(6), v8_num(7), v8_num(5), v8_num(5) }; - fun->Call(fun, argc, a2); - - // @JS[6] C[5] JS[4] C[3] JS[2] C[1] JS[0] - v8::Handle<Value> a3[argc] = { v8_num(6), v8_num(6), v8_num(7), v8_num(6) }; - fun->Call(fun, argc, a3); - - // JS[6] *C[5] @JS[4] C[3] JS[2] C[1] JS[0] - v8::Handle<Value> a4[argc] = { v8_num(6), v8_num(4), v8_num(5), v8_num(4) }; - fun->Call(fun, argc, a4); - - // JS[6] C[5] *JS[4] @C[3] JS[2] C[1] JS[0] - v8::Handle<Value> a5[argc] = { v8_num(6), v8_num(4), v8_num(3), v8_num(3) }; - fun->Call(fun, argc, a5); -} - - -v8::Handle<Value> ThrowValue(const v8::Arguments& args) { - ApiTestFuzzer::Fuzz(); - CHECK_EQ(1, args.Length()); - return v8::ThrowException(args[0]); -} - - -THREADED_TEST(ThrowValues) { - v8::HandleScope scope; - Local<ObjectTemplate> templ = ObjectTemplate::New(); - templ->Set(v8_str("Throw"), v8::FunctionTemplate::New(ThrowValue)); - LocalContext context(0, templ); - v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun( - "function Run(obj) {" - " try {" - " Throw(obj);" - " } catch (e) {" - " return e;" - " }" - " return 'no exception';" - "}" - "[Run('str'), Run(1), Run(0), Run(null), Run(void 0)];")); - CHECK_EQ(5, result->Length()); - CHECK(result->Get(v8::Integer::New(0))->IsString()); - CHECK(result->Get(v8::Integer::New(1))->IsNumber()); - CHECK_EQ(1, result->Get(v8::Integer::New(1))->Int32Value()); - CHECK(result->Get(v8::Integer::New(2))->IsNumber()); - CHECK_EQ(0, result->Get(v8::Integer::New(2))->Int32Value()); - CHECK(result->Get(v8::Integer::New(3))->IsNull()); - CHECK(result->Get(v8::Integer::New(4))->IsUndefined()); -} - - -THREADED_TEST(CatchZero) { - v8::HandleScope scope; - LocalContext context; - v8::TryCatch try_catch; - CHECK(!try_catch.HasCaught()); - Script::Compile(v8_str("throw 10"))->Run(); - CHECK(try_catch.HasCaught()); - CHECK_EQ(10, try_catch.Exception()->Int32Value()); - try_catch.Reset(); - CHECK(!try_catch.HasCaught()); - Script::Compile(v8_str("throw 0"))->Run(); - CHECK(try_catch.HasCaught()); - CHECK_EQ(0, try_catch.Exception()->Int32Value()); -} - - -THREADED_TEST(CatchExceptionFromWith) { - v8::HandleScope scope; - LocalContext context; - v8::TryCatch try_catch; - CHECK(!try_catch.HasCaught()); - Script::Compile(v8_str("var o = {}; with (o) { throw 42; }"))->Run(); - CHECK(try_catch.HasCaught()); -} - - -THREADED_TEST(TryCatchAndFinallyHidingException) { - v8::HandleScope scope; - LocalContext context; - v8::TryCatch try_catch; - CHECK(!try_catch.HasCaught()); - CompileRun("function f(k) { try { this[k]; } finally { return 0; } };"); - CompileRun("f({toString: function() { throw 42; }});"); - CHECK(!try_catch.HasCaught()); -} - - -v8::Handle<v8::Value> WithTryCatch(const v8::Arguments& args) { - v8::TryCatch try_catch; - return v8::Undefined(); -} - - -THREADED_TEST(TryCatchAndFinally) { - v8::HandleScope scope; - LocalContext context; - context->Global()->Set( - v8_str("native_with_try_catch"), - v8::FunctionTemplate::New(WithTryCatch)->GetFunction()); - v8::TryCatch try_catch; - CHECK(!try_catch.HasCaught()); - CompileRun( - "try {\n" - " throw new Error('a');\n" - "} finally {\n" - " native_with_try_catch();\n" - "}\n"); - CHECK(try_catch.HasCaught()); -} - - -static void TryCatchNestedHelper(int depth) { - if (depth > 0) { - v8::TryCatch try_catch; - try_catch.SetVerbose(true); - TryCatchNestedHelper(depth - 1); - CHECK(try_catch.HasCaught()); - try_catch.ReThrow(); - } else { - v8::ThrowException(v8_str("back")); - } -} - - -TEST(TryCatchNested) { - v8::V8::Initialize(); - v8::HandleScope scope; - LocalContext context; - v8::TryCatch try_catch; - TryCatchNestedHelper(5); - CHECK(try_catch.HasCaught()); - CHECK_EQ(0, strcmp(*v8::String::Utf8Value(try_catch.Exception()), "back")); -} - - -THREADED_TEST(Equality) { - v8::HandleScope scope; - LocalContext context; - v8::Isolate* isolate = context->GetIsolate(); - // Check that equality works at all before relying on CHECK_EQ - CHECK(v8_str("a")->Equals(v8_str("a"))); - CHECK(!v8_str("a")->Equals(v8_str("b"))); - - CHECK_EQ(v8_str("a"), v8_str("a")); - CHECK_NE(v8_str("a"), v8_str("b")); - CHECK_EQ(v8_num(1), v8_num(1)); - CHECK_EQ(v8_num(1.00), v8_num(1)); - CHECK_NE(v8_num(1), v8_num(2)); - - // Assume String is not internalized. - CHECK(v8_str("a")->StrictEquals(v8_str("a"))); - CHECK(!v8_str("a")->StrictEquals(v8_str("b"))); - CHECK(!v8_str("5")->StrictEquals(v8_num(5))); - CHECK(v8_num(1)->StrictEquals(v8_num(1))); - CHECK(!v8_num(1)->StrictEquals(v8_num(2))); - CHECK(v8_num(0)->StrictEquals(v8_num(-0))); - Local<Value> not_a_number = v8_num(i::OS::nan_value()); - CHECK(!not_a_number->StrictEquals(not_a_number)); - CHECK(v8::False()->StrictEquals(v8::False())); - CHECK(!v8::False()->StrictEquals(v8::Undefined())); - - v8::Handle<v8::Object> obj = v8::Object::New(); - v8::Persistent<v8::Object> alias = - v8::Persistent<v8::Object>::New(isolate, obj); - CHECK(alias->StrictEquals(obj)); - alias.Dispose(isolate); -} - - -THREADED_TEST(MultiRun) { - v8::HandleScope scope; - LocalContext context; - Local<Script> script = Script::Compile(v8_str("x")); - for (int i = 0; i < 10; i++) - script->Run(); -} - - -static v8::Handle<Value> GetXValue(Local<String> name, - const AccessorInfo& info) { - ApiTestFuzzer::Fuzz(); - CHECK_EQ(info.Data(), v8_str("donut")); - CHECK_EQ(name, v8_str("x")); - return name; -} - - -THREADED_TEST(SimplePropertyRead) { - v8::HandleScope scope; - Local<ObjectTemplate> templ = ObjectTemplate::New(); - templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")); - LocalContext context; - context->Global()->Set(v8_str("obj"), templ->NewInstance()); - Local<Script> script = Script::Compile(v8_str("obj.x")); - for (int i = 0; i < 10; i++) { - Local<Value> result = script->Run(); - CHECK_EQ(result, v8_str("x")); - } -} - -THREADED_TEST(DefinePropertyOnAPIAccessor) { - v8::HandleScope scope; - Local<ObjectTemplate> templ = ObjectTemplate::New(); - templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")); - LocalContext context; - context->Global()->Set(v8_str("obj"), templ->NewInstance()); - - // Uses getOwnPropertyDescriptor to check the configurable status - Local<Script> script_desc - = Script::Compile(v8_str("var prop = Object.getOwnPropertyDescriptor( " - "obj, 'x');" - "prop.configurable;")); - Local<Value> result = script_desc->Run(); - CHECK_EQ(result->BooleanValue(), true); - - // Redefine get - but still configurable - Local<Script> script_define - = Script::Compile(v8_str("var desc = { get: function(){return 42; }," - " configurable: true };" - "Object.defineProperty(obj, 'x', desc);" - "obj.x")); - result = script_define->Run(); - CHECK_EQ(result, v8_num(42)); - - // Check that the accessor is still configurable - result = script_desc->Run(); - CHECK_EQ(result->BooleanValue(), true); - - // Redefine to a non-configurable - script_define - = Script::Compile(v8_str("var desc = { get: function(){return 43; }," - " configurable: false };" - "Object.defineProperty(obj, 'x', desc);" - "obj.x")); - result = script_define->Run(); - CHECK_EQ(result, v8_num(43)); - result = script_desc->Run(); - CHECK_EQ(result->BooleanValue(), false); - - // Make sure that it is not possible to redefine again - v8::TryCatch try_catch; - result = script_define->Run(); - CHECK(try_catch.HasCaught()); - String::AsciiValue exception_value(try_catch.Exception()); - CHECK_EQ(*exception_value, "TypeError: Cannot redefine property: x"); -} - -THREADED_TEST(DefinePropertyOnDefineGetterSetter) { - v8::HandleScope scope; - Local<ObjectTemplate> templ = ObjectTemplate::New(); - templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")); - LocalContext context; - context->Global()->Set(v8_str("obj"), templ->NewInstance()); - - Local<Script> script_desc = Script::Compile(v8_str("var prop =" - "Object.getOwnPropertyDescriptor( " - "obj, 'x');" - "prop.configurable;")); - Local<Value> result = script_desc->Run(); - CHECK_EQ(result->BooleanValue(), true); - - Local<Script> script_define = - Script::Compile(v8_str("var desc = {get: function(){return 42; }," - " configurable: true };" - "Object.defineProperty(obj, 'x', desc);" - "obj.x")); - result = script_define->Run(); - CHECK_EQ(result, v8_num(42)); - - - result = script_desc->Run(); - CHECK_EQ(result->BooleanValue(), true); - - - script_define = - Script::Compile(v8_str("var desc = {get: function(){return 43; }," - " configurable: false };" - "Object.defineProperty(obj, 'x', desc);" - "obj.x")); - result = script_define->Run(); - CHECK_EQ(result, v8_num(43)); - result = script_desc->Run(); - - CHECK_EQ(result->BooleanValue(), false); - - v8::TryCatch try_catch; - result = script_define->Run(); - CHECK(try_catch.HasCaught()); - String::AsciiValue exception_value(try_catch.Exception()); - CHECK_EQ(*exception_value, "TypeError: Cannot redefine property: x"); -} - - -static v8::Handle<v8::Object> GetGlobalProperty(LocalContext* context, - char const* name) { - return v8::Handle<v8::Object>::Cast((*context)->Global()->Get(v8_str(name))); -} - - -THREADED_TEST(DefineAPIAccessorOnObject) { - v8::HandleScope scope; - Local<ObjectTemplate> templ = ObjectTemplate::New(); - LocalContext context; - - context->Global()->Set(v8_str("obj1"), templ->NewInstance()); - CompileRun("var obj2 = {};"); - - CHECK(CompileRun("obj1.x")->IsUndefined()); - CHECK(CompileRun("obj2.x")->IsUndefined()); - - CHECK(GetGlobalProperty(&context, "obj1")-> - SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); - - ExpectString("obj1.x", "x"); - CHECK(CompileRun("obj2.x")->IsUndefined()); - - CHECK(GetGlobalProperty(&context, "obj2")-> - SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); - - ExpectString("obj1.x", "x"); - ExpectString("obj2.x", "x"); - - ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable"); - ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable"); - - CompileRun("Object.defineProperty(obj1, 'x'," - "{ get: function() { return 'y'; }, configurable: true })"); - - ExpectString("obj1.x", "y"); - ExpectString("obj2.x", "x"); - - CompileRun("Object.defineProperty(obj2, 'x'," - "{ get: function() { return 'y'; }, configurable: true })"); - - ExpectString("obj1.x", "y"); - ExpectString("obj2.x", "y"); - - ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable"); - ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable"); - - CHECK(GetGlobalProperty(&context, "obj1")-> - SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); - CHECK(GetGlobalProperty(&context, "obj2")-> - SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); - - ExpectString("obj1.x", "x"); - ExpectString("obj2.x", "x"); - - ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable"); - ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable"); - - // Define getters/setters, but now make them not configurable. - CompileRun("Object.defineProperty(obj1, 'x'," - "{ get: function() { return 'z'; }, configurable: false })"); - CompileRun("Object.defineProperty(obj2, 'x'," - "{ get: function() { return 'z'; }, configurable: false })"); - - ExpectTrue("!Object.getOwnPropertyDescriptor(obj1, 'x').configurable"); - ExpectTrue("!Object.getOwnPropertyDescriptor(obj2, 'x').configurable"); - - ExpectString("obj1.x", "z"); - ExpectString("obj2.x", "z"); - - CHECK(!GetGlobalProperty(&context, "obj1")-> - SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); - CHECK(!GetGlobalProperty(&context, "obj2")-> - SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); - - ExpectString("obj1.x", "z"); - ExpectString("obj2.x", "z"); -} - - -THREADED_TEST(DontDeleteAPIAccessorsCannotBeOverriden) { - v8::HandleScope scope; - Local<ObjectTemplate> templ = ObjectTemplate::New(); - LocalContext context; - - context->Global()->Set(v8_str("obj1"), templ->NewInstance()); - CompileRun("var obj2 = {};"); - - CHECK(GetGlobalProperty(&context, "obj1")->SetAccessor( - v8_str("x"), - GetXValue, NULL, - v8_str("donut"), v8::DEFAULT, v8::DontDelete)); - CHECK(GetGlobalProperty(&context, "obj2")->SetAccessor( - v8_str("x"), - GetXValue, NULL, - v8_str("donut"), v8::DEFAULT, v8::DontDelete)); - - ExpectString("obj1.x", "x"); - ExpectString("obj2.x", "x"); - - ExpectTrue("!Object.getOwnPropertyDescriptor(obj1, 'x').configurable"); - ExpectTrue("!Object.getOwnPropertyDescriptor(obj2, 'x').configurable"); - - CHECK(!GetGlobalProperty(&context, "obj1")-> - SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); - CHECK(!GetGlobalProperty(&context, "obj2")-> - SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); - - { - v8::TryCatch try_catch; - CompileRun("Object.defineProperty(obj1, 'x'," - "{get: function() { return 'func'; }})"); - CHECK(try_catch.HasCaught()); - String::AsciiValue exception_value(try_catch.Exception()); - CHECK_EQ(*exception_value, "TypeError: Cannot redefine property: x"); - } - { - v8::TryCatch try_catch; - CompileRun("Object.defineProperty(obj2, 'x'," - "{get: function() { return 'func'; }})"); - CHECK(try_catch.HasCaught()); - String::AsciiValue exception_value(try_catch.Exception()); - CHECK_EQ(*exception_value, "TypeError: Cannot redefine property: x"); - } -} - - -static v8::Handle<Value> Get239Value(Local<String> name, - const AccessorInfo& info) { - ApiTestFuzzer::Fuzz(); - CHECK_EQ(info.Data(), v8_str("donut")); - CHECK_EQ(name, v8_str("239")); - return name; -} - - -THREADED_TEST(ElementAPIAccessor) { - v8::HandleScope scope; - Local<ObjectTemplate> templ = ObjectTemplate::New(); - LocalContext context; - - context->Global()->Set(v8_str("obj1"), templ->NewInstance()); - CompileRun("var obj2 = {};"); - - CHECK(GetGlobalProperty(&context, "obj1")->SetAccessor( - v8_str("239"), - Get239Value, NULL, - v8_str("donut"))); - CHECK(GetGlobalProperty(&context, "obj2")->SetAccessor( - v8_str("239"), - Get239Value, NULL, - v8_str("donut"))); - - ExpectString("obj1[239]", "239"); - ExpectString("obj2[239]", "239"); - ExpectString("obj1['239']", "239"); - ExpectString("obj2['239']", "239"); -} - - -v8::Persistent<Value> xValue; - - -static void SetXValue(Local<String> name, - Local<Value> value, - const AccessorInfo& info) { - CHECK_EQ(value, v8_num(4)); - CHECK_EQ(info.Data(), v8_str("donut")); - CHECK_EQ(name, v8_str("x")); - CHECK(xValue.IsEmpty()); - xValue = v8::Persistent<Value>::New(info.GetIsolate(), value); -} - - -THREADED_TEST(SimplePropertyWrite) { - v8::HandleScope scope; - Local<ObjectTemplate> templ = ObjectTemplate::New(); - templ->SetAccessor(v8_str("x"), GetXValue, SetXValue, v8_str("donut")); - LocalContext context; - context->Global()->Set(v8_str("obj"), templ->NewInstance()); - Local<Script> script = Script::Compile(v8_str("obj.x = 4")); - for (int i = 0; i < 10; i++) { - CHECK(xValue.IsEmpty()); - script->Run(); - CHECK_EQ(v8_num(4), xValue); - xValue.Dispose(context->GetIsolate()); - xValue = v8::Persistent<Value>(); - } -} - - -THREADED_TEST(SetterOnly) { - v8::HandleScope scope; - Local<ObjectTemplate> templ = ObjectTemplate::New(); - templ->SetAccessor(v8_str("x"), NULL, SetXValue, v8_str("donut")); - LocalContext context; - context->Global()->Set(v8_str("obj"), templ->NewInstance()); - Local<Script> script = Script::Compile(v8_str("obj.x = 4; obj.x")); - for (int i = 0; i < 10; i++) { - CHECK(xValue.IsEmpty()); - script->Run(); - CHECK_EQ(v8_num(4), xValue); - xValue.Dispose(context->GetIsolate()); - xValue = v8::Persistent<Value>(); - } -} - - -THREADED_TEST(NoAccessors) { - v8::HandleScope scope; - Local<ObjectTemplate> templ = ObjectTemplate::New(); - templ->SetAccessor(v8_str("x"), NULL, NULL, v8_str("donut")); - LocalContext context; - context->Global()->Set(v8_str("obj"), templ->NewInstance()); - Local<Script> script = Script::Compile(v8_str("obj.x = 4; obj.x")); - for (int i = 0; i < 10; i++) { - script->Run(); - } -} - - -static v8::Handle<Value> XPropertyGetter(Local<String> property, - const AccessorInfo& info) { - ApiTestFuzzer::Fuzz(); - CHECK(info.Data()->IsUndefined()); - return property; -} - - -THREADED_TEST(NamedInterceptorPropertyRead) { - v8::HandleScope scope; - Local<ObjectTemplate> templ = ObjectTemplate::New(); - templ->SetNamedPropertyHandler(XPropertyGetter); - LocalContext context; - context->Global()->Set(v8_str("obj"), templ->NewInstance()); - Local<Script> script = Script::Compile(v8_str("obj.x")); - for (int i = 0; i < 10; i++) { - Local<Value> result = script->Run(); - CHECK_EQ(result, v8_str("x")); - } -} - - -THREADED_TEST(NamedInterceptorDictionaryIC) { - v8::HandleScope scope; - Local<ObjectTemplate> templ = ObjectTemplate::New(); - templ->SetNamedPropertyHandler(XPropertyGetter); - LocalContext context; - // Create an object with a named interceptor. - context->Global()->Set(v8_str("interceptor_obj"), templ->NewInstance()); - Local<Script> script = Script::Compile(v8_str("interceptor_obj.x")); - for (int i = 0; i < 10; i++) { - Local<Value> result = script->Run(); - CHECK_EQ(result, v8_str("x")); - } - // Create a slow case object and a function accessing a property in - // that slow case object (with dictionary probing in generated - // code). Then force object with a named interceptor into slow-case, - // pass it to the function, and check that the interceptor is called - // instead of accessing the local property. - Local<Value> result = - CompileRun("function get_x(o) { return o.x; };" - "var obj = { x : 42, y : 0 };" - "delete obj.y;" - "for (var i = 0; i < 10; i++) get_x(obj);" - "interceptor_obj.x = 42;" - "interceptor_obj.y = 10;" - "delete interceptor_obj.y;" - "get_x(interceptor_obj)"); - CHECK_EQ(result, v8_str("x")); -} - - -THREADED_TEST(NamedInterceptorDictionaryICMultipleContext) { - v8::HandleScope scope; - - v8::Persistent<Context> context1 = Context::New(); - - context1->Enter(); - Local<ObjectTemplate> templ = ObjectTemplate::New(); - templ->SetNamedPropertyHandler(XPropertyGetter); - // Create an object with a named interceptor. - v8::Local<v8::Object> object = templ->NewInstance(); - context1->Global()->Set(v8_str("interceptor_obj"), object); - - // Force the object into the slow case. - CompileRun("interceptor_obj.y = 0;" - "delete interceptor_obj.y;"); - context1->Exit(); - - { - // Introduce the object into a different context. - // Repeat named loads to exercise ICs. - LocalContext context2; - context2->Global()->Set(v8_str("interceptor_obj"), object); - Local<Value> result = - CompileRun("function get_x(o) { return o.x; }" - "interceptor_obj.x = 42;" - "for (var i=0; i != 10; i++) {" - " get_x(interceptor_obj);" - "}" - "get_x(interceptor_obj)"); - // Check that the interceptor was actually invoked. - CHECK_EQ(result, v8_str("x")); - } - - // Return to the original context and force some object to the slow case - // to cause the NormalizedMapCache to verify. - context1->Enter(); - CompileRun("var obj = { x : 0 }; delete obj.x;"); - context1->Exit(); - - context1.Dispose(context1->GetIsolate()); -} - - -static v8::Handle<Value> SetXOnPrototypeGetter(Local<String> property, - const AccessorInfo& info) { - // Set x on the prototype object and do not handle the get request. - v8::Handle<v8::Value> proto = info.Holder()->GetPrototype(); - proto.As<v8::Object>()->Set(v8_str("x"), v8::Integer::New(23)); - return v8::Handle<Value>(); -} - - -// This is a regression test for http://crbug.com/20104. Map -// transitions should not interfere with post interceptor lookup. -THREADED_TEST(NamedInterceptorMapTransitionRead) { - v8::HandleScope scope; - Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(); - Local<v8::ObjectTemplate> instance_template - = function_template->InstanceTemplate(); - instance_template->SetNamedPropertyHandler(SetXOnPrototypeGetter); - LocalContext context; - context->Global()->Set(v8_str("F"), function_template->GetFunction()); - // Create an instance of F and introduce a map transition for x. - CompileRun("var o = new F(); o.x = 23;"); - // Create an instance of F and invoke the getter. The result should be 23. - Local<Value> result = CompileRun("o = new F(); o.x"); - CHECK_EQ(result->Int32Value(), 23); -} - - -static v8::Handle<Value> IndexedPropertyGetter(uint32_t index, - const AccessorInfo& info) { - ApiTestFuzzer::Fuzz(); - if (index == 37) { - return v8::Handle<Value>(v8_num(625)); - } - return v8::Handle<Value>(); -} - - -static v8::Handle<Value> IndexedPropertySetter(uint32_t index, - Local<Value> value, - const AccessorInfo& info) { - ApiTestFuzzer::Fuzz(); - if (index == 39) { - return value; - } - return v8::Handle<Value>(); -} - - -THREADED_TEST(IndexedInterceptorWithIndexedAccessor) { - v8::HandleScope scope; - Local<ObjectTemplate> templ = ObjectTemplate::New(); - templ->SetIndexedPropertyHandler(IndexedPropertyGetter, - IndexedPropertySetter); - LocalContext context; - context->Global()->Set(v8_str("obj"), templ->NewInstance()); - Local<Script> getter_script = Script::Compile(v8_str( - "obj.__defineGetter__(\"3\", function(){return 5;});obj[3];")); - Local<Script> setter_script = Script::Compile(v8_str( - "obj.__defineSetter__(\"17\", function(val){this.foo = val;});" - "obj[17] = 23;" - "obj.foo;")); - Local<Script> interceptor_setter_script = Script::Compile(v8_str( - "obj.__defineSetter__(\"39\", function(val){this.foo = \"hit\";});" - "obj[39] = 47;" - "obj.foo;")); // This setter should not run, due to the interceptor. - Local<Script> interceptor_getter_script = Script::Compile(v8_str( - "obj[37];")); - Local<Value> result = getter_script->Run(); - CHECK_EQ(v8_num(5), result); - result = setter_script->Run(); - CHECK_EQ(v8_num(23), result); - result = interceptor_setter_script->Run(); - CHECK_EQ(v8_num(23), result); - result = interceptor_getter_script->Run(); - CHECK_EQ(v8_num(625), result); -} - - -static v8::Handle<Value> UnboxedDoubleIndexedPropertyGetter( - uint32_t index, - const AccessorInfo& info) { - ApiTestFuzzer::Fuzz(); - if (index < 25) { - return v8::Handle<Value>(v8_num(index)); - } - return v8::Handle<Value>(); -} - - -static v8::Handle<Value> UnboxedDoubleIndexedPropertySetter( - uint32_t index, - Local<Value> value, - const AccessorInfo& info) { - ApiTestFuzzer::Fuzz(); - if (index < 25) { - return v8::Handle<Value>(v8_num(index)); - } - return v8::Handle<Value>(); -} - - -Handle<v8::Array> UnboxedDoubleIndexedPropertyEnumerator( - const AccessorInfo& info) { - // Force the list of returned keys to be stored in a FastDoubleArray. - Local<Script> indexed_property_names_script = Script::Compile(v8_str( - "keys = new Array(); keys[125000] = 1;" - "for(i = 0; i < 80000; i++) { keys[i] = i; };" - "keys.length = 25; keys;")); - Local<Value> result = indexed_property_names_script->Run(); - return Local<v8::Array>(::v8::Array::Cast(*result)); -} - - -// Make sure that the the interceptor code in the runtime properly handles -// merging property name lists for double-array-backed arrays. -THREADED_TEST(IndexedInterceptorUnboxedDoubleWithIndexedAccessor) { - v8::HandleScope scope; - Local<ObjectTemplate> templ = ObjectTemplate::New(); - templ->SetIndexedPropertyHandler(UnboxedDoubleIndexedPropertyGetter, - UnboxedDoubleIndexedPropertySetter, - 0, - 0, - UnboxedDoubleIndexedPropertyEnumerator); - LocalContext context; - context->Global()->Set(v8_str("obj"), templ->NewInstance()); - // When obj is created, force it to be Stored in a FastDoubleArray. - Local<Script> create_unboxed_double_script = Script::Compile(v8_str( - "obj[125000] = 1; for(i = 0; i < 80000; i+=2) { obj[i] = i; } " - "key_count = 0; " - "for (x in obj) {key_count++;};" - "obj;")); - Local<Value> result = create_unboxed_double_script->Run(); - CHECK(result->ToObject()->HasRealIndexedProperty(2000)); - Local<Script> key_count_check = Script::Compile(v8_str( - "key_count;")); - result = key_count_check->Run(); - CHECK_EQ(v8_num(40013), result); -} - - -Handle<v8::Array> NonStrictArgsIndexedPropertyEnumerator( - const AccessorInfo& info) { - // Force the list of returned keys to be stored in a Arguments object. - Local<Script> indexed_property_names_script = Script::Compile(v8_str( - "function f(w,x) {" - " return arguments;" - "}" - "keys = f(0, 1, 2, 3);" - "keys;")); - Local<Value> result = indexed_property_names_script->Run(); - return Local<v8::Array>(static_cast<v8::Array*>(::v8::Object::Cast(*result))); -} - - -static v8::Handle<Value> NonStrictIndexedPropertyGetter( - uint32_t index, - const AccessorInfo& info) { - ApiTestFuzzer::Fuzz(); - if (index < 4) { - return v8::Handle<Value>(v8_num(index)); - } - return v8::Handle<Value>(); -} - - -// Make sure that the the interceptor code in the runtime properly handles -// merging property name lists for non-string arguments arrays. -THREADED_TEST(IndexedInterceptorNonStrictArgsWithIndexedAccessor) { - v8::HandleScope scope; - Local<ObjectTemplate> templ = ObjectTemplate::New(); - templ->SetIndexedPropertyHandler(NonStrictIndexedPropertyGetter, - 0, - 0, - 0, - NonStrictArgsIndexedPropertyEnumerator); - LocalContext context; - context->Global()->Set(v8_str("obj"), templ->NewInstance()); - Local<Script> create_args_script = - Script::Compile(v8_str( - "var key_count = 0;" - "for (x in obj) {key_count++;} key_count;")); - Local<Value> result = create_args_script->Run(); - CHECK_EQ(v8_num(4), result); -} - - -static v8::Handle<Value> IdentityIndexedPropertyGetter( - uint32_t index, - const AccessorInfo& info) { - return v8::Integer::NewFromUnsigned(index); -} - - -THREADED_TEST(IndexedInterceptorWithGetOwnPropertyDescriptor) { - v8::HandleScope scope; - Local<ObjectTemplate> templ = ObjectTemplate::New(); - templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); - - LocalContext context; - context->Global()->Set(v8_str("obj"), templ->NewInstance()); - - // Check fast object case. - const char* fast_case_code = - "Object.getOwnPropertyDescriptor(obj, 0).value.toString()"; - ExpectString(fast_case_code, "0"); - - // Check slow case. - const char* slow_case_code = - "obj.x = 1; delete obj.x;" - "Object.getOwnPropertyDescriptor(obj, 1).value.toString()"; - ExpectString(slow_case_code, "1"); -} - - -THREADED_TEST(IndexedInterceptorWithNoSetter) { - v8::HandleScope scope; - Local<ObjectTemplate> templ = ObjectTemplate::New(); - templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); - - LocalContext context; - context->Global()->Set(v8_str("obj"), templ->NewInstance()); - - const char* code = - "try {" - " obj[0] = 239;" - " for (var i = 0; i < 100; i++) {" - " var v = obj[0];" - " if (v != 0) throw 'Wrong value ' + v + ' at iteration ' + i;" - " }" - " 'PASSED'" - "} catch(e) {" - " e" - "}"; - ExpectString(code, "PASSED"); -} - - -THREADED_TEST(IndexedInterceptorWithAccessorCheck) { - v8::HandleScope scope; - Local<ObjectTemplate> templ = ObjectTemplate::New(); - templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); - - LocalContext context; - Local<v8::Object> obj = templ->NewInstance(); - obj->TurnOnAccessCheck(); - context->Global()->Set(v8_str("obj"), obj); - - const char* code = - "try {" - " for (var i = 0; i < 100; i++) {" - " var v = obj[0];" - " if (v != undefined) throw 'Wrong value ' + v + ' at iteration ' + i;" - " }" - " 'PASSED'" - "} catch(e) {" - " e" - "}"; - ExpectString(code, "PASSED"); -} - - -THREADED_TEST(IndexedInterceptorWithAccessorCheckSwitchedOn) { - i::FLAG_allow_natives_syntax = true; - v8::HandleScope scope; - Local<ObjectTemplate> templ = ObjectTemplate::New(); - templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); - - LocalContext context; - Local<v8::Object> obj = templ->NewInstance(); - context->Global()->Set(v8_str("obj"), obj); - - const char* code = - "try {" - " for (var i = 0; i < 100; i++) {" - " var expected = i;" - " if (i == 5) {" - " %EnableAccessChecks(obj);" - " expected = undefined;" - " }" - " var v = obj[i];" - " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;" - " if (i == 5) %DisableAccessChecks(obj);" - " }" - " 'PASSED'" - "} catch(e) {" - " e" - "}"; - ExpectString(code, "PASSED"); -} - - -THREADED_TEST(IndexedInterceptorWithDifferentIndices) { - v8::HandleScope scope; - Local<ObjectTemplate> templ = ObjectTemplate::New(); - templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); - - LocalContext context; - Local<v8::Object> obj = templ->NewInstance(); - context->Global()->Set(v8_str("obj"), obj); - - const char* code = - "try {" - " for (var i = 0; i < 100; i++) {" - " var v = obj[i];" - " if (v != i) throw 'Wrong value ' + v + ' at iteration ' + i;" - " }" - " 'PASSED'" - "} catch(e) {" - " e" - "}"; - ExpectString(code, "PASSED"); -} - - -THREADED_TEST(IndexedInterceptorWithNegativeIndices) { - v8::HandleScope scope; - Local<ObjectTemplate> templ = ObjectTemplate::New(); - templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); - - LocalContext context; - Local<v8::Object> obj = templ->NewInstance(); - context->Global()->Set(v8_str("obj"), obj); - - const char* code = - "try {" - " for (var i = 0; i < 100; i++) {" - " var expected = i;" - " var key = i;" - " if (i == 25) {" - " key = -1;" - " expected = undefined;" - " }" - " if (i == 50) {" - " /* probe minimal Smi number on 32-bit platforms */" - " key = -(1 << 30);" - " expected = undefined;" - " }" - " if (i == 75) {" - " /* probe minimal Smi number on 64-bit platforms */" - " key = 1 << 31;" - " expected = undefined;" - " }" - " var v = obj[key];" - " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;" - " }" - " 'PASSED'" - "} catch(e) {" - " e" - "}"; - ExpectString(code, "PASSED"); -} - - -THREADED_TEST(IndexedInterceptorWithNotSmiLookup) { - v8::HandleScope scope; - Local<ObjectTemplate> templ = ObjectTemplate::New(); - templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); - - LocalContext context; - Local<v8::Object> obj = templ->NewInstance(); - context->Global()->Set(v8_str("obj"), obj); - - const char* code = - "try {" - " for (var i = 0; i < 100; i++) {" - " var expected = i;" - " var key = i;" - " if (i == 50) {" - " key = 'foobar';" - " expected = undefined;" - " }" - " var v = obj[key];" - " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;" - " }" - " 'PASSED'" - "} catch(e) {" - " e" - "}"; - ExpectString(code, "PASSED"); -} - - -THREADED_TEST(IndexedInterceptorGoingMegamorphic) { - v8::HandleScope scope; - Local<ObjectTemplate> templ = ObjectTemplate::New(); - templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); - - LocalContext context; - Local<v8::Object> obj = templ->NewInstance(); - context->Global()->Set(v8_str("obj"), obj); - - const char* code = - "var original = obj;" - "try {" - " for (var i = 0; i < 100; i++) {" - " var expected = i;" - " if (i == 50) {" - " obj = {50: 'foobar'};" - " expected = 'foobar';" - " }" - " var v = obj[i];" - " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;" - " if (i == 50) obj = original;" - " }" - " 'PASSED'" - "} catch(e) {" - " e" - "}"; - ExpectString(code, "PASSED"); -} - - -THREADED_TEST(IndexedInterceptorReceiverTurningSmi) { - v8::HandleScope scope; - Local<ObjectTemplate> templ = ObjectTemplate::New(); - templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); - - LocalContext context; - Local<v8::Object> obj = templ->NewInstance(); - context->Global()->Set(v8_str("obj"), obj); - - const char* code = - "var original = obj;" - "try {" - " for (var i = 0; i < 100; i++) {" - " var expected = i;" - " if (i == 5) {" - " obj = 239;" - " expected = undefined;" - " }" - " var v = obj[i];" - " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;" - " if (i == 5) obj = original;" - " }" - " 'PASSED'" - "} catch(e) {" - " e" - "}"; - ExpectString(code, "PASSED"); -} - - -THREADED_TEST(IndexedInterceptorOnProto) { - v8::HandleScope scope; - Local<ObjectTemplate> templ = ObjectTemplate::New(); - templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); - - LocalContext context; - Local<v8::Object> obj = templ->NewInstance(); - context->Global()->Set(v8_str("obj"), obj); - - const char* code = - "var o = {__proto__: obj};" - "try {" - " for (var i = 0; i < 100; i++) {" - " var v = o[i];" - " if (v != i) throw 'Wrong value ' + v + ' at iteration ' + i;" - " }" - " 'PASSED'" - "} catch(e) {" - " e" - "}"; - ExpectString(code, "PASSED"); -} - - -THREADED_TEST(MultiContexts) { - v8::HandleScope scope; - v8::Handle<ObjectTemplate> templ = ObjectTemplate::New(); - templ->Set(v8_str("dummy"), v8::FunctionTemplate::New(DummyCallHandler)); - - Local<String> password = v8_str("Password"); - - // Create an environment - LocalContext context0(0, templ); - context0->SetSecurityToken(password); - v8::Handle<v8::Object> global0 = context0->Global(); - global0->Set(v8_str("custom"), v8_num(1234)); - CHECK_EQ(1234, global0->Get(v8_str("custom"))->Int32Value()); - - // Create an independent environment - LocalContext context1(0, templ); - context1->SetSecurityToken(password); - v8::Handle<v8::Object> global1 = context1->Global(); - global1->Set(v8_str("custom"), v8_num(1234)); - CHECK_NE(global0, global1); - CHECK_EQ(1234, global0->Get(v8_str("custom"))->Int32Value()); - CHECK_EQ(1234, global1->Get(v8_str("custom"))->Int32Value()); - - // Now create a new context with the old global - LocalContext context2(0, templ, global1); - context2->SetSecurityToken(password); - v8::Handle<v8::Object> global2 = context2->Global(); - CHECK_EQ(global1, global2); - CHECK_EQ(0, global1->Get(v8_str("custom"))->Int32Value()); - CHECK_EQ(0, global2->Get(v8_str("custom"))->Int32Value()); -} - - -THREADED_TEST(FunctionPrototypeAcrossContexts) { - // Make sure that functions created by cloning boilerplates cannot - // communicate through their __proto__ field. - - v8::HandleScope scope; - - LocalContext env0; - v8::Handle<v8::Object> global0 = - env0->Global(); - v8::Handle<v8::Object> object0 = - global0->Get(v8_str("Object")).As<v8::Object>(); - v8::Handle<v8::Object> tostring0 = - object0->Get(v8_str("toString")).As<v8::Object>(); - v8::Handle<v8::Object> proto0 = - tostring0->Get(v8_str("__proto__")).As<v8::Object>(); - proto0->Set(v8_str("custom"), v8_num(1234)); - - LocalContext env1; - v8::Handle<v8::Object> global1 = - env1->Global(); - v8::Handle<v8::Object> object1 = - global1->Get(v8_str("Object")).As<v8::Object>(); - v8::Handle<v8::Object> tostring1 = - object1->Get(v8_str("toString")).As<v8::Object>(); - v8::Handle<v8::Object> proto1 = - tostring1->Get(v8_str("__proto__")).As<v8::Object>(); - CHECK(!proto1->Has(v8_str("custom"))); -} - - -THREADED_TEST(Regress892105) { - // Make sure that object and array literals created by cloning - // boilerplates cannot communicate through their __proto__ - // field. This is rather difficult to check, but we try to add stuff - // to Object.prototype and Array.prototype and create a new - // environment. This should succeed. - - v8::HandleScope scope; - - Local<String> source = v8_str("Object.prototype.obj = 1234;" - "Array.prototype.arr = 4567;" - "8901"); - - LocalContext env0; - Local<Script> script0 = Script::Compile(source); - CHECK_EQ(8901.0, script0->Run()->NumberValue()); - - LocalContext env1; - Local<Script> script1 = Script::Compile(source); - CHECK_EQ(8901.0, script1->Run()->NumberValue()); -} - - -THREADED_TEST(UndetectableObject) { - v8::HandleScope scope; - LocalContext env; - - Local<v8::FunctionTemplate> desc = - v8::FunctionTemplate::New(0, v8::Handle<Value>()); - desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable - - Local<v8::Object> obj = desc->GetFunction()->NewInstance(); - env->Global()->Set(v8_str("undetectable"), obj); - - ExpectString("undetectable.toString()", "[object Object]"); - ExpectString("typeof undetectable", "undefined"); - ExpectString("typeof(undetectable)", "undefined"); - ExpectBoolean("typeof undetectable == 'undefined'", true); - ExpectBoolean("typeof undetectable == 'object'", false); - ExpectBoolean("if (undetectable) { true; } else { false; }", false); - ExpectBoolean("!undetectable", true); - - ExpectObject("true&&undetectable", obj); - ExpectBoolean("false&&undetectable", false); - ExpectBoolean("true||undetectable", true); - ExpectObject("false||undetectable", obj); - - ExpectObject("undetectable&&true", obj); - ExpectObject("undetectable&&false", obj); - ExpectBoolean("undetectable||true", true); - ExpectBoolean("undetectable||false", false); - - ExpectBoolean("undetectable==null", true); - ExpectBoolean("null==undetectable", true); - ExpectBoolean("undetectable==undefined", true); - ExpectBoolean("undefined==undetectable", true); - ExpectBoolean("undetectable==undetectable", true); - - - ExpectBoolean("undetectable===null", false); - ExpectBoolean("null===undetectable", false); - ExpectBoolean("undetectable===undefined", false); - ExpectBoolean("undefined===undetectable", false); - ExpectBoolean("undetectable===undetectable", true); -} - - -THREADED_TEST(VoidLiteral) { - v8::HandleScope scope; - LocalContext env; - - Local<v8::FunctionTemplate> desc = - v8::FunctionTemplate::New(0, v8::Handle<Value>()); - desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable - - Local<v8::Object> obj = desc->GetFunction()->NewInstance(); - env->Global()->Set(v8_str("undetectable"), obj); - - ExpectBoolean("undefined == void 0", true); - ExpectBoolean("undetectable == void 0", true); - ExpectBoolean("null == void 0", true); - ExpectBoolean("undefined === void 0", true); - ExpectBoolean("undetectable === void 0", false); - ExpectBoolean("null === void 0", false); - - ExpectBoolean("void 0 == undefined", true); - ExpectBoolean("void 0 == undetectable", true); - ExpectBoolean("void 0 == null", true); - ExpectBoolean("void 0 === undefined", true); - ExpectBoolean("void 0 === undetectable", false); - ExpectBoolean("void 0 === null", false); - - ExpectString("(function() {" - " try {" - " return x === void 0;" - " } catch(e) {" - " return e.toString();" - " }" - "})()", - "ReferenceError: x is not defined"); - ExpectString("(function() {" - " try {" - " return void 0 === x;" - " } catch(e) {" - " return e.toString();" - " }" - "})()", - "ReferenceError: x is not defined"); -} - - -THREADED_TEST(ExtensibleOnUndetectable) { - v8::HandleScope scope; - LocalContext env; - - Local<v8::FunctionTemplate> desc = - v8::FunctionTemplate::New(0, v8::Handle<Value>()); - desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable - - Local<v8::Object> obj = desc->GetFunction()->NewInstance(); - env->Global()->Set(v8_str("undetectable"), obj); - - Local<String> source = v8_str("undetectable.x = 42;" - "undetectable.x"); - - Local<Script> script = Script::Compile(source); - - CHECK_EQ(v8::Integer::New(42), script->Run()); - - ExpectBoolean("Object.isExtensible(undetectable)", true); - - source = v8_str("Object.preventExtensions(undetectable);"); - script = Script::Compile(source); - script->Run(); - ExpectBoolean("Object.isExtensible(undetectable)", false); - - source = v8_str("undetectable.y = 2000;"); - script = Script::Compile(source); - script->Run(); - ExpectBoolean("undetectable.y == undefined", true); -} - - - -THREADED_TEST(UndetectableString) { - v8::HandleScope scope; - LocalContext env; - - Local<String> obj = String::NewUndetectable("foo"); - env->Global()->Set(v8_str("undetectable"), obj); - - ExpectString("undetectable", "foo"); - ExpectString("typeof undetectable", "undefined"); - ExpectString("typeof(undetectable)", "undefined"); - ExpectBoolean("typeof undetectable == 'undefined'", true); - ExpectBoolean("typeof undetectable == 'string'", false); - ExpectBoolean("if (undetectable) { true; } else { false; }", false); - ExpectBoolean("!undetectable", true); - - ExpectObject("true&&undetectable", obj); - ExpectBoolean("false&&undetectable", false); - ExpectBoolean("true||undetectable", true); - ExpectObject("false||undetectable", obj); - - ExpectObject("undetectable&&true", obj); - ExpectObject("undetectable&&false", obj); - ExpectBoolean("undetectable||true", true); - ExpectBoolean("undetectable||false", false); - - ExpectBoolean("undetectable==null", true); - ExpectBoolean("null==undetectable", true); - ExpectBoolean("undetectable==undefined", true); - ExpectBoolean("undefined==undetectable", true); - ExpectBoolean("undetectable==undetectable", true); - - - ExpectBoolean("undetectable===null", false); - ExpectBoolean("null===undetectable", false); - ExpectBoolean("undetectable===undefined", false); - ExpectBoolean("undefined===undetectable", false); - ExpectBoolean("undetectable===undetectable", true); -} - - -TEST(UndetectableOptimized) { - i::FLAG_allow_natives_syntax = true; - v8::HandleScope scope; - LocalContext env; - - Local<String> obj = String::NewUndetectable("foo"); - env->Global()->Set(v8_str("undetectable"), obj); - env->Global()->Set(v8_str("detectable"), v8_str("bar")); - - ExpectString( - "function testBranch() {" - " if (!%_IsUndetectableObject(undetectable)) throw 1;" - " if (%_IsUndetectableObject(detectable)) throw 2;" - "}\n" - "function testBool() {" - " var b1 = !%_IsUndetectableObject(undetectable);" - " var b2 = %_IsUndetectableObject(detectable);" - " if (b1) throw 3;" - " if (b2) throw 4;" - " return b1 == b2;" - "}\n" - "%OptimizeFunctionOnNextCall(testBranch);" - "%OptimizeFunctionOnNextCall(testBool);" - "for (var i = 0; i < 10; i++) {" - " testBranch();" - " testBool();" - "}\n" - "\"PASS\"", - "PASS"); -} - - -template <typename T> static void USE(T) { } - - -// This test is not intended to be run, just type checked. -static inline void PersistentHandles(v8::Isolate* isolate) { - USE(PersistentHandles); - Local<String> str = v8_str("foo"); - v8::Persistent<String> p_str = v8::Persistent<String>::New(isolate, str); - USE(p_str); - Local<Script> scr = Script::Compile(v8_str("")); - v8::Persistent<Script> p_scr = v8::Persistent<Script>::New(isolate, scr); - USE(p_scr); - Local<ObjectTemplate> templ = ObjectTemplate::New(); - v8::Persistent<ObjectTemplate> p_templ = - v8::Persistent<ObjectTemplate>::New(isolate, templ); - USE(p_templ); -} - - -static v8::Handle<Value> HandleLogDelegator(const v8::Arguments& args) { - ApiTestFuzzer::Fuzz(); - return v8::Undefined(); -} - - -THREADED_TEST(GlobalObjectTemplate) { - v8::HandleScope handle_scope; - Local<ObjectTemplate> global_template = ObjectTemplate::New(); - global_template->Set(v8_str("JSNI_Log"), - v8::FunctionTemplate::New(HandleLogDelegator)); - v8::Persistent<Context> context = Context::New(0, global_template); - Context::Scope context_scope(context); - Script::Compile(v8_str("JSNI_Log('LOG')"))->Run(); - context.Dispose(context->GetIsolate()); -} - - -static const char* kSimpleExtensionSource = - "function Foo() {" - " return 4;" - "}"; - - -THREADED_TEST(SimpleExtensions) { - v8::HandleScope handle_scope; - v8::RegisterExtension(new Extension("simpletest", kSimpleExtensionSource)); - const char* extension_names[] = { "simpletest" }; - v8::ExtensionConfiguration extensions(1, extension_names); - v8::Handle<Context> context = Context::New(&extensions); - Context::Scope lock(context); - v8::Handle<Value> result = Script::Compile(v8_str("Foo()"))->Run(); - CHECK_EQ(result, v8::Integer::New(4)); -} - - -THREADED_TEST(NullExtensions) { - v8::HandleScope handle_scope; - v8::RegisterExtension(new Extension("nulltest", NULL)); - const char* extension_names[] = { "nulltest" }; - v8::ExtensionConfiguration extensions(1, extension_names); - v8::Handle<Context> context = Context::New(&extensions); - Context::Scope lock(context); - v8::Handle<Value> result = Script::Compile(v8_str("1+3"))->Run(); - CHECK_EQ(result, v8::Integer::New(4)); -} - - -static const char* kEmbeddedExtensionSource = - "function Ret54321(){return 54321;}~~@@$" - "$%% THIS IS A SERIES OF NON-NULL-TERMINATED STRINGS."; -static const int kEmbeddedExtensionSourceValidLen = 34; - - -THREADED_TEST(ExtensionMissingSourceLength) { - v8::HandleScope handle_scope; - v8::RegisterExtension(new Extension("srclentest_fail", - kEmbeddedExtensionSource)); - const char* extension_names[] = { "srclentest_fail" }; - v8::ExtensionConfiguration extensions(1, extension_names); - v8::Handle<Context> context = Context::New(&extensions); - CHECK_EQ(0, *context); -} - - -THREADED_TEST(ExtensionWithSourceLength) { - for (int source_len = kEmbeddedExtensionSourceValidLen - 1; - source_len <= kEmbeddedExtensionSourceValidLen + 1; ++source_len) { - v8::HandleScope handle_scope; - i::ScopedVector<char> extension_name(32); - i::OS::SNPrintF(extension_name, "ext #%d", source_len); - v8::RegisterExtension(new Extension(extension_name.start(), - kEmbeddedExtensionSource, 0, 0, - source_len)); - const char* extension_names[1] = { extension_name.start() }; - v8::ExtensionConfiguration extensions(1, extension_names); - v8::Handle<Context> context = Context::New(&extensions); - if (source_len == kEmbeddedExtensionSourceValidLen) { - Context::Scope lock(context); - v8::Handle<Value> result = Script::Compile(v8_str("Ret54321()"))->Run(); - CHECK_EQ(v8::Integer::New(54321), result); - } else { - // Anything but exactly the right length should fail to compile. - CHECK_EQ(0, *context); - } - } -} - - -static const char* kEvalExtensionSource1 = - "function UseEval1() {" - " var x = 42;" - " return eval('x');" - "}"; - - -static const char* kEvalExtensionSource2 = - "(function() {" - " var x = 42;" - " function e() {" - " return eval('x');" - " }" - " this.UseEval2 = e;" - "})()"; - - -THREADED_TEST(UseEvalFromExtension) { - v8::HandleScope handle_scope; - v8::RegisterExtension(new Extension("evaltest1", kEvalExtensionSource1)); - v8::RegisterExtension(new Extension("evaltest2", kEvalExtensionSource2)); - const char* extension_names[] = { "evaltest1", "evaltest2" }; - v8::ExtensionConfiguration extensions(2, extension_names); - v8::Handle<Context> context = Context::New(&extensions); - Context::Scope lock(context); - v8::Handle<Value> result = Script::Compile(v8_str("UseEval1()"))->Run(); - CHECK_EQ(result, v8::Integer::New(42)); - result = Script::Compile(v8_str("UseEval2()"))->Run(); - CHECK_EQ(result, v8::Integer::New(42)); -} - - -static const char* kWithExtensionSource1 = - "function UseWith1() {" - " var x = 42;" - " with({x:87}) { return x; }" - "}"; - - - -static const char* kWithExtensionSource2 = - "(function() {" - " var x = 42;" - " function e() {" - " with ({x:87}) { return x; }" - " }" - " this.UseWith2 = e;" - "})()"; - - -THREADED_TEST(UseWithFromExtension) { - v8::HandleScope handle_scope; - v8::RegisterExtension(new Extension("withtest1", kWithExtensionSource1)); - v8::RegisterExtension(new Extension("withtest2", kWithExtensionSource2)); - const char* extension_names[] = { "withtest1", "withtest2" }; - v8::ExtensionConfiguration extensions(2, extension_names); - v8::Handle<Context> context = Context::New(&extensions); - Context::Scope lock(context); - v8::Handle<Value> result = Script::Compile(v8_str("UseWith1()"))->Run(); - CHECK_EQ(result, v8::Integer::New(87)); - result = Script::Compile(v8_str("UseWith2()"))->Run(); - CHECK_EQ(result, v8::Integer::New(87)); -} - - -THREADED_TEST(AutoExtensions) { - v8::HandleScope handle_scope; - Extension* extension = new Extension("autotest", kSimpleExtensionSource); - extension->set_auto_enable(true); - v8::RegisterExtension(extension); - v8::Handle<Context> context = Context::New(); - Context::Scope lock(context); - v8::Handle<Value> result = Script::Compile(v8_str("Foo()"))->Run(); - CHECK_EQ(result, v8::Integer::New(4)); -} - - -static const char* kSyntaxErrorInExtensionSource = - "["; - - -// Test that a syntax error in an extension does not cause a fatal -// error but results in an empty context. -THREADED_TEST(SyntaxErrorExtensions) { - v8::HandleScope handle_scope; - v8::RegisterExtension(new Extension("syntaxerror", - kSyntaxErrorInExtensionSource)); - const char* extension_names[] = { "syntaxerror" }; - v8::ExtensionConfiguration extensions(1, extension_names); - v8::Handle<Context> context = Context::New(&extensions); - CHECK(context.IsEmpty()); -} - - -static const char* kExceptionInExtensionSource = - "throw 42"; - - -// Test that an exception when installing an extension does not cause -// a fatal error but results in an empty context. -THREADED_TEST(ExceptionExtensions) { - v8::HandleScope handle_scope; - v8::RegisterExtension(new Extension("exception", - kExceptionInExtensionSource)); - const char* extension_names[] = { "exception" }; - v8::ExtensionConfiguration extensions(1, extension_names); - v8::Handle<Context> context = Context::New(&extensions); - CHECK(context.IsEmpty()); -} - - -static const char* kNativeCallInExtensionSource = - "function call_runtime_last_index_of(x) {" - " return %StringLastIndexOf(x, 'bob', 10);" - "}"; - - -static const char* kNativeCallTest = - "call_runtime_last_index_of('bobbobboellebobboellebobbob');"; - -// Test that a native runtime calls are supported in extensions. -THREADED_TEST(NativeCallInExtensions) { - v8::HandleScope handle_scope; - v8::RegisterExtension(new Extension("nativecall", - kNativeCallInExtensionSource)); - const char* extension_names[] = { "nativecall" }; - v8::ExtensionConfiguration extensions(1, extension_names); - v8::Handle<Context> context = Context::New(&extensions); - Context::Scope lock(context); - v8::Handle<Value> result = Script::Compile(v8_str(kNativeCallTest))->Run(); - CHECK_EQ(result, v8::Integer::New(3)); -} - - -class NativeFunctionExtension : public Extension { - public: - NativeFunctionExtension(const char* name, - const char* source, - v8::InvocationCallback fun = &Echo) - : Extension(name, source), - function_(fun) { } - - virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction( - v8::Handle<v8::String> name) { - return v8::FunctionTemplate::New(function_); - } - - static v8::Handle<v8::Value> Echo(const v8::Arguments& args) { - if (args.Length() >= 1) return (args[0]); - return v8::Undefined(); - } - private: - v8::InvocationCallback function_; -}; - - -THREADED_TEST(NativeFunctionDeclaration) { - v8::HandleScope handle_scope; - const char* name = "nativedecl"; - v8::RegisterExtension(new NativeFunctionExtension(name, - "native function foo();")); - const char* extension_names[] = { name }; - v8::ExtensionConfiguration extensions(1, extension_names); - v8::Handle<Context> context = Context::New(&extensions); - Context::Scope lock(context); - v8::Handle<Value> result = Script::Compile(v8_str("foo(42);"))->Run(); - CHECK_EQ(result, v8::Integer::New(42)); -} - - -THREADED_TEST(NativeFunctionDeclarationError) { - v8::HandleScope handle_scope; - const char* name = "nativedeclerr"; - // Syntax error in extension code. - v8::RegisterExtension(new NativeFunctionExtension(name, - "native\nfunction foo();")); - const char* extension_names[] = { name }; - v8::ExtensionConfiguration extensions(1, extension_names); - v8::Handle<Context> context(Context::New(&extensions)); - CHECK(context.IsEmpty()); -} - - -THREADED_TEST(NativeFunctionDeclarationErrorEscape) { - v8::HandleScope handle_scope; - const char* name = "nativedeclerresc"; - // Syntax error in extension code - escape code in "native" means that - // it's not treated as a keyword. - v8::RegisterExtension(new NativeFunctionExtension( - name, - "nativ\\u0065 function foo();")); - const char* extension_names[] = { name }; - v8::ExtensionConfiguration extensions(1, extension_names); - v8::Handle<Context> context(Context::New(&extensions)); - CHECK(context.IsEmpty()); -} - - -static void CheckDependencies(const char* name, const char* expected) { - v8::HandleScope handle_scope; - v8::ExtensionConfiguration config(1, &name); - LocalContext context(&config); - CHECK_EQ(String::New(expected), context->Global()->Get(v8_str("loaded"))); -} - - -/* - * Configuration: - * - * /-- B <--\ - * A <- -- D <-- E - * \-- C <--/ - */ -THREADED_TEST(ExtensionDependency) { - static const char* kEDeps[] = { "D" }; - v8::RegisterExtension(new Extension("E", "this.loaded += 'E';", 1, kEDeps)); - static const char* kDDeps[] = { "B", "C" }; - v8::RegisterExtension(new Extension("D", "this.loaded += 'D';", 2, kDDeps)); - static const char* kBCDeps[] = { "A" }; - v8::RegisterExtension(new Extension("B", "this.loaded += 'B';", 1, kBCDeps)); - v8::RegisterExtension(new Extension("C", "this.loaded += 'C';", 1, kBCDeps)); - v8::RegisterExtension(new Extension("A", "this.loaded += 'A';")); - CheckDependencies("A", "undefinedA"); - CheckDependencies("B", "undefinedAB"); - CheckDependencies("C", "undefinedAC"); - CheckDependencies("D", "undefinedABCD"); - CheckDependencies("E", "undefinedABCDE"); - v8::HandleScope handle_scope; - static const char* exts[2] = { "C", "E" }; - v8::ExtensionConfiguration config(2, exts); - LocalContext context(&config); - CHECK_EQ(v8_str("undefinedACBDE"), context->Global()->Get(v8_str("loaded"))); -} - - -static const char* kExtensionTestScript = - "native function A();" - "native function B();" - "native function C();" - "function Foo(i) {" - " if (i == 0) return A();" - " if (i == 1) return B();" - " if (i == 2) return C();" - "}"; - - -static v8::Handle<Value> CallFun(const v8::Arguments& args) { - ApiTestFuzzer::Fuzz(); - if (args.IsConstructCall()) { - args.This()->Set(v8_str("data"), args.Data()); - return v8::Null(); - } - return args.Data(); -} - - -class FunctionExtension : public Extension { - public: - FunctionExtension() : Extension("functiontest", kExtensionTestScript) { } - virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction( - v8::Handle<String> name); -}; - - -static int lookup_count = 0; -v8::Handle<v8::FunctionTemplate> FunctionExtension::GetNativeFunction( - v8::Handle<String> name) { - lookup_count++; - if (name->Equals(v8_str("A"))) { - return v8::FunctionTemplate::New(CallFun, v8::Integer::New(8)); - } else if (name->Equals(v8_str("B"))) { - return v8::FunctionTemplate::New(CallFun, v8::Integer::New(7)); - } else if (name->Equals(v8_str("C"))) { - return v8::FunctionTemplate::New(CallFun, v8::Integer::New(6)); - } else { - return v8::Handle<v8::FunctionTemplate>(); - } -} - - -THREADED_TEST(FunctionLookup) { - v8::RegisterExtension(new FunctionExtension()); - v8::HandleScope handle_scope; - static const char* exts[1] = { "functiontest" }; - v8::ExtensionConfiguration config(1, exts); - LocalContext context(&config); - CHECK_EQ(3, lookup_count); - CHECK_EQ(v8::Integer::New(8), Script::Compile(v8_str("Foo(0)"))->Run()); - CHECK_EQ(v8::Integer::New(7), Script::Compile(v8_str("Foo(1)"))->Run()); - CHECK_EQ(v8::Integer::New(6), Script::Compile(v8_str("Foo(2)"))->Run()); -} - - -THREADED_TEST(NativeFunctionConstructCall) { - v8::RegisterExtension(new FunctionExtension()); - v8::HandleScope handle_scope; - static const char* exts[1] = { "functiontest" }; - v8::ExtensionConfiguration config(1, exts); - LocalContext context(&config); - for (int i = 0; i < 10; i++) { - // Run a few times to ensure that allocation of objects doesn't - // change behavior of a constructor function. - CHECK_EQ(v8::Integer::New(8), - Script::Compile(v8_str("(new A()).data"))->Run()); - CHECK_EQ(v8::Integer::New(7), - Script::Compile(v8_str("(new B()).data"))->Run()); - CHECK_EQ(v8::Integer::New(6), - Script::Compile(v8_str("(new C()).data"))->Run()); - } -} - - -static const char* last_location; -static const char* last_message; -void StoringErrorCallback(const char* location, const char* message) { - if (last_location == NULL) { - last_location = location; - last_message = message; - } -} - - -// ErrorReporting creates a circular extensions configuration and -// tests that the fatal error handler gets called. This renders V8 -// unusable and therefore this test cannot be run in parallel. -TEST(ErrorReporting) { - v8::V8::SetFatalErrorHandler(StoringErrorCallback); - static const char* aDeps[] = { "B" }; - v8::RegisterExtension(new Extension("A", "", 1, aDeps)); - static const char* bDeps[] = { "A" }; - v8::RegisterExtension(new Extension("B", "", 1, bDeps)); - last_location = NULL; - v8::ExtensionConfiguration config(1, bDeps); - v8::Handle<Context> context = Context::New(&config); - CHECK(context.IsEmpty()); - CHECK_NE(last_location, NULL); -} - - -static const char* js_code_causing_huge_string_flattening = - "var str = 'X';" - "for (var i = 0; i < 30; i++) {" - " str = str + str;" - "}" - "str.match(/X/);"; - - -void OOMCallback(const char* location, const char* message) { - exit(0); -} - - -TEST(RegexpOutOfMemory) { - // Execute a script that causes out of memory when flattening a string. - v8::HandleScope scope; - v8::V8::SetFatalErrorHandler(OOMCallback); - LocalContext context; - Local<Script> script = - Script::Compile(String::New(js_code_causing_huge_string_flattening)); - last_location = NULL; - script->Run(); - - CHECK(false); // Should not return. -} - - -static void MissingScriptInfoMessageListener(v8::Handle<v8::Message> message, - v8::Handle<Value> data) { - CHECK(message->GetScriptResourceName()->IsUndefined()); - CHECK_EQ(v8::Undefined(), message->GetScriptResourceName()); - message->GetLineNumber(); - message->GetSourceLine(); -} - - -THREADED_TEST(ErrorWithMissingScriptInfo) { - v8::HandleScope scope; - LocalContext context; - v8::V8::AddMessageListener(MissingScriptInfoMessageListener); - Script::Compile(v8_str("throw Error()"))->Run(); - v8::V8::RemoveMessageListeners(MissingScriptInfoMessageListener); -} - - -int global_index = 0; - -class Snorkel { - public: - Snorkel() { index_ = global_index++; } - int index_; -}; - -class Whammy { - public: - explicit Whammy(v8::Isolate* isolate) : cursor_(0), isolate_(isolate) { } - ~Whammy() { script_.Dispose(isolate_); } - v8::Handle<Script> getScript() { - if (script_.IsEmpty()) { - script_ = v8::Persistent<Script>::New(isolate_, - v8_compile("({}).blammo")); - } - return Local<Script>(*script_); - } - - public: - static const int kObjectCount = 256; - int cursor_; - v8::Isolate* isolate_; - v8::Persistent<v8::Object> objects_[kObjectCount]; - v8::Persistent<Script> script_; -}; - -static void HandleWeakReference(v8::Isolate* isolate, - v8::Persistent<v8::Value> obj, - void* data) { - Snorkel* snorkel = reinterpret_cast<Snorkel*>(data); - delete snorkel; - obj.ClearWeak(isolate); -} - -v8::Handle<Value> WhammyPropertyGetter(Local<String> name, - const AccessorInfo& info) { - Whammy* whammy = - static_cast<Whammy*>(v8::Handle<v8::External>::Cast(info.Data())->Value()); - - v8::Persistent<v8::Object> prev = whammy->objects_[whammy->cursor_]; - - v8::Handle<v8::Object> obj = v8::Object::New(); - v8::Persistent<v8::Object> global = - v8::Persistent<v8::Object>::New(info.GetIsolate(), obj); - if (!prev.IsEmpty()) { - prev->Set(v8_str("next"), obj); - prev.MakeWeak(info.GetIsolate(), new Snorkel(), &HandleWeakReference); - whammy->objects_[whammy->cursor_].Clear(); - } - whammy->objects_[whammy->cursor_] = global; - whammy->cursor_ = (whammy->cursor_ + 1) % Whammy::kObjectCount; - return whammy->getScript()->Run(); -} - -THREADED_TEST(WeakReference) { - v8::HandleScope handle_scope; - v8::Handle<v8::ObjectTemplate> templ= v8::ObjectTemplate::New(); - Whammy* whammy = new Whammy(v8::Isolate::GetCurrent()); - templ->SetNamedPropertyHandler(WhammyPropertyGetter, - 0, 0, 0, 0, - v8::External::New(whammy)); - const char* extension_list[] = { "v8/gc" }; - v8::ExtensionConfiguration extensions(1, extension_list); - v8::Persistent<Context> context = Context::New(&extensions); - Context::Scope context_scope(context); - - v8::Handle<v8::Object> interceptor = templ->NewInstance(); - context->Global()->Set(v8_str("whammy"), interceptor); - const char* code = - "var last;" - "for (var i = 0; i < 10000; i++) {" - " var obj = whammy.length;" - " if (last) last.next = obj;" - " last = obj;" - "}" - "gc();" - "4"; - v8::Handle<Value> result = CompileRun(code); - CHECK_EQ(4.0, result->NumberValue()); - delete whammy; - context.Dispose(context->GetIsolate()); -} - - -static void DisposeAndSetFlag(v8::Isolate* isolate, - v8::Persistent<v8::Value> obj, - void* data) { - obj.Dispose(isolate); - obj.Clear(); - *(reinterpret_cast<bool*>(data)) = true; -} - - -THREADED_TEST(IndependentWeakHandle) { - v8::Persistent<Context> context = Context::New(); - v8::Isolate* iso = context->GetIsolate(); - Context::Scope context_scope(context); - - v8::Persistent<v8::Object> object_a, object_b; - - { - v8::HandleScope handle_scope; - object_a = v8::Persistent<v8::Object>::New(iso, v8::Object::New()); - object_b = v8::Persistent<v8::Object>::New(iso, v8::Object::New()); - } - - bool object_a_disposed = false; - bool object_b_disposed = false; - object_a.MakeWeak(iso, &object_a_disposed, &DisposeAndSetFlag); - object_b.MakeWeak(iso, &object_b_disposed, &DisposeAndSetFlag); - CHECK(!object_b.IsIndependent(iso)); - object_a.MarkIndependent(iso); - object_b.MarkIndependent(iso); - CHECK(object_b.IsIndependent(iso)); - HEAP->PerformScavenge(); - CHECK(object_a_disposed); - CHECK(object_b_disposed); -} - - -static void InvokeScavenge() { - HEAP->PerformScavenge(); -} - - -static void InvokeMarkSweep() { - HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); -} - - -static void ForceScavenge(v8::Isolate* isolate, - v8::Persistent<v8::Value> obj, - void* data) { - obj.Dispose(isolate); - obj.Clear(); - *(reinterpret_cast<bool*>(data)) = true; - InvokeScavenge(); -} - - -static void ForceMarkSweep(v8::Isolate* isolate, - v8::Persistent<v8::Value> obj, - void* data) { - obj.Dispose(isolate); - obj.Clear(); - *(reinterpret_cast<bool*>(data)) = true; - InvokeMarkSweep(); -} - - -THREADED_TEST(GCFromWeakCallbacks) { - v8::Persistent<Context> context = Context::New(); - v8::Isolate* isolate = context->GetIsolate(); - Context::Scope context_scope(context); - - static const int kNumberOfGCTypes = 2; - v8::NearDeathCallback gc_forcing_callback[kNumberOfGCTypes] = - {&ForceScavenge, &ForceMarkSweep}; - - typedef void (*GCInvoker)(); - GCInvoker invoke_gc[kNumberOfGCTypes] = {&InvokeScavenge, &InvokeMarkSweep}; - - for (int outer_gc = 0; outer_gc < kNumberOfGCTypes; outer_gc++) { - for (int inner_gc = 0; inner_gc < kNumberOfGCTypes; inner_gc++) { - v8::Persistent<v8::Object> object; - { - v8::HandleScope handle_scope; - object = v8::Persistent<v8::Object>::New(isolate, v8::Object::New()); - } - bool disposed = false; - object.MakeWeak(isolate, &disposed, gc_forcing_callback[inner_gc]); - object.MarkIndependent(isolate); - invoke_gc[outer_gc](); - CHECK(disposed); - } - } -} - - -static void RevivingCallback(v8::Isolate* isolate, - v8::Persistent<v8::Value> obj, - void* data) { - obj.ClearWeak(isolate); - *(reinterpret_cast<bool*>(data)) = true; -} - - -THREADED_TEST(IndependentHandleRevival) { - v8::Persistent<Context> context = Context::New(); - Context::Scope context_scope(context); - v8::Isolate* isolate = context->GetIsolate(); - - v8::Persistent<v8::Object> object; - { - v8::HandleScope handle_scope; - object = v8::Persistent<v8::Object>::New(isolate, v8::Object::New()); - object->Set(v8_str("x"), v8::Integer::New(1)); - v8::Local<String> y_str = v8_str("y"); - object->Set(y_str, y_str); - } - bool revived = false; - object.MakeWeak(isolate, &revived, &RevivingCallback); - object.MarkIndependent(isolate); - HEAP->PerformScavenge(); - CHECK(revived); - HEAP->CollectAllGarbage(i::Heap::kAbortIncrementalMarkingMask); - { - v8::HandleScope handle_scope; - v8::Local<String> y_str = v8_str("y"); - CHECK_EQ(v8::Integer::New(1), object->Get(v8_str("x"))); - CHECK(object->Get(y_str)->Equals(y_str)); - } -} - - -v8::Handle<Function> args_fun; - - -static v8::Handle<Value> ArgumentsTestCallback(const v8::Arguments& args) { - ApiTestFuzzer::Fuzz(); - CHECK_EQ(args_fun, args.Callee()); - CHECK_EQ(3, args.Length()); - CHECK_EQ(v8::Integer::New(1), args[0]); - CHECK_EQ(v8::Integer::New(2), args[1]); - CHECK_EQ(v8::Integer::New(3), args[2]); - CHECK_EQ(v8::Undefined(), args[3]); - v8::HandleScope scope; - HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); - return v8::Undefined(); -} - - -THREADED_TEST(Arguments) { - v8::HandleScope scope; - v8::Handle<v8::ObjectTemplate> global = ObjectTemplate::New(); - global->Set(v8_str("f"), v8::FunctionTemplate::New(ArgumentsTestCallback)); - LocalContext context(NULL, global); - args_fun = context->Global()->Get(v8_str("f")).As<Function>(); - v8_compile("f(1, 2, 3)")->Run(); -} - - -static v8::Handle<Value> NoBlockGetterX(Local<String> name, - const AccessorInfo&) { - return v8::Handle<Value>(); -} - - -static v8::Handle<Value> NoBlockGetterI(uint32_t index, - const AccessorInfo&) { - return v8::Handle<Value>(); -} - - -static v8::Handle<v8::Boolean> PDeleter(Local<String> name, - const AccessorInfo&) { - if (!name->Equals(v8_str("foo"))) { - return v8::Handle<v8::Boolean>(); // not intercepted - } - - return v8::False(); // intercepted, and don't delete the property -} - - -static v8::Handle<v8::Boolean> IDeleter(uint32_t index, const AccessorInfo&) { - if (index != 2) { - return v8::Handle<v8::Boolean>(); // not intercepted - } - - return v8::False(); // intercepted, and don't delete the property -} - - -THREADED_TEST(Deleter) { - v8::HandleScope scope; - v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); - obj->SetNamedPropertyHandler(NoBlockGetterX, NULL, NULL, PDeleter, NULL); - obj->SetIndexedPropertyHandler(NoBlockGetterI, NULL, NULL, IDeleter, NULL); - LocalContext context; - context->Global()->Set(v8_str("k"), obj->NewInstance()); - CompileRun( - "k.foo = 'foo';" - "k.bar = 'bar';" - "k[2] = 2;" - "k[4] = 4;"); - CHECK(v8_compile("delete k.foo")->Run()->IsFalse()); - CHECK(v8_compile("delete k.bar")->Run()->IsTrue()); - - CHECK_EQ(v8_compile("k.foo")->Run(), v8_str("foo")); - CHECK(v8_compile("k.bar")->Run()->IsUndefined()); - - CHECK(v8_compile("delete k[2]")->Run()->IsFalse()); - CHECK(v8_compile("delete k[4]")->Run()->IsTrue()); - - CHECK_EQ(v8_compile("k[2]")->Run(), v8_num(2)); - CHECK(v8_compile("k[4]")->Run()->IsUndefined()); -} - - -static v8::Handle<Value> GetK(Local<String> name, const AccessorInfo&) { - ApiTestFuzzer::Fuzz(); - if (name->Equals(v8_str("foo")) || - name->Equals(v8_str("bar")) || - name->Equals(v8_str("baz"))) { - return v8::Undefined(); - } - return v8::Handle<Value>(); -} - - -static v8::Handle<Value> IndexedGetK(uint32_t index, const AccessorInfo&) { - ApiTestFuzzer::Fuzz(); - if (index == 0 || index == 1) return v8::Undefined(); - return v8::Handle<Value>(); -} - - -static v8::Handle<v8::Array> NamedEnum(const AccessorInfo&) { - ApiTestFuzzer::Fuzz(); - v8::Handle<v8::Array> result = v8::Array::New(3); - result->Set(v8::Integer::New(0), v8_str("foo")); - result->Set(v8::Integer::New(1), v8_str("bar")); - result->Set(v8::Integer::New(2), v8_str("baz")); - return result; -} - - -static v8::Handle<v8::Array> IndexedEnum(const AccessorInfo&) { - ApiTestFuzzer::Fuzz(); - v8::Handle<v8::Array> result = v8::Array::New(2); - result->Set(v8::Integer::New(0), v8_str("0")); - result->Set(v8::Integer::New(1), v8_str("1")); - return result; -} - - -THREADED_TEST(Enumerators) { - v8::HandleScope scope; - v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); - obj->SetNamedPropertyHandler(GetK, NULL, NULL, NULL, NamedEnum); - obj->SetIndexedPropertyHandler(IndexedGetK, NULL, NULL, NULL, IndexedEnum); - LocalContext context; - context->Global()->Set(v8_str("k"), obj->NewInstance()); - v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun( - "k[10] = 0;" - "k.a = 0;" - "k[5] = 0;" - "k.b = 0;" - "k[4294967295] = 0;" - "k.c = 0;" - "k[4294967296] = 0;" - "k.d = 0;" - "k[140000] = 0;" - "k.e = 0;" - "k[30000000000] = 0;" - "k.f = 0;" - "var result = [];" - "for (var prop in k) {" - " result.push(prop);" - "}" - "result")); - // Check that we get all the property names returned including the - // ones from the enumerators in the right order: indexed properties - // in numerical order, indexed interceptor properties, named - // properties in insertion order, named interceptor properties. - // This order is not mandated by the spec, so this test is just - // documenting our behavior. - CHECK_EQ(17, result->Length()); - // Indexed properties in numerical order. - CHECK_EQ(v8_str("5"), result->Get(v8::Integer::New(0))); - CHECK_EQ(v8_str("10"), result->Get(v8::Integer::New(1))); - CHECK_EQ(v8_str("140000"), result->Get(v8::Integer::New(2))); - CHECK_EQ(v8_str("4294967295"), result->Get(v8::Integer::New(3))); - // Indexed interceptor properties in the order they are returned - // from the enumerator interceptor. - CHECK_EQ(v8_str("0"), result->Get(v8::Integer::New(4))); - CHECK_EQ(v8_str("1"), result->Get(v8::Integer::New(5))); - // Named properties in insertion order. - CHECK_EQ(v8_str("a"), result->Get(v8::Integer::New(6))); - CHECK_EQ(v8_str("b"), result->Get(v8::Integer::New(7))); - CHECK_EQ(v8_str("c"), result->Get(v8::Integer::New(8))); - CHECK_EQ(v8_str("4294967296"), result->Get(v8::Integer::New(9))); - CHECK_EQ(v8_str("d"), result->Get(v8::Integer::New(10))); - CHECK_EQ(v8_str("e"), result->Get(v8::Integer::New(11))); - CHECK_EQ(v8_str("30000000000"), result->Get(v8::Integer::New(12))); - CHECK_EQ(v8_str("f"), result->Get(v8::Integer::New(13))); - // Named interceptor properties. - CHECK_EQ(v8_str("foo"), result->Get(v8::Integer::New(14))); - CHECK_EQ(v8_str("bar"), result->Get(v8::Integer::New(15))); - CHECK_EQ(v8_str("baz"), result->Get(v8::Integer::New(16))); -} - - -int p_getter_count; -int p_getter_count2; - - -static v8::Handle<Value> PGetter(Local<String> name, const AccessorInfo& info) { - ApiTestFuzzer::Fuzz(); - p_getter_count++; - v8::Handle<v8::Object> global = Context::GetCurrent()->Global(); - CHECK_EQ(info.Holder(), global->Get(v8_str("o1"))); - if (name->Equals(v8_str("p1"))) { - CHECK_EQ(info.This(), global->Get(v8_str("o1"))); - } else if (name->Equals(v8_str("p2"))) { - CHECK_EQ(info.This(), global->Get(v8_str("o2"))); - } else if (name->Equals(v8_str("p3"))) { - CHECK_EQ(info.This(), global->Get(v8_str("o3"))); - } else if (name->Equals(v8_str("p4"))) { - CHECK_EQ(info.This(), global->Get(v8_str("o4"))); - } - return v8::Undefined(); -} - - -static void RunHolderTest(v8::Handle<v8::ObjectTemplate> obj) { - ApiTestFuzzer::Fuzz(); - LocalContext context; - context->Global()->Set(v8_str("o1"), obj->NewInstance()); - CompileRun( - "o1.__proto__ = { };" - "var o2 = { __proto__: o1 };" - "var o3 = { __proto__: o2 };" - "var o4 = { __proto__: o3 };" - "for (var i = 0; i < 10; i++) o4.p4;" - "for (var i = 0; i < 10; i++) o3.p3;" - "for (var i = 0; i < 10; i++) o2.p2;" - "for (var i = 0; i < 10; i++) o1.p1;"); -} - - -static v8::Handle<Value> PGetter2(Local<String> name, - const AccessorInfo& info) { - ApiTestFuzzer::Fuzz(); - p_getter_count2++; - v8::Handle<v8::Object> global = Context::GetCurrent()->Global(); - CHECK_EQ(info.Holder(), global->Get(v8_str("o1"))); - if (name->Equals(v8_str("p1"))) { - CHECK_EQ(info.This(), global->Get(v8_str("o1"))); - } else if (name->Equals(v8_str("p2"))) { - CHECK_EQ(info.This(), global->Get(v8_str("o2"))); - } else if (name->Equals(v8_str("p3"))) { - CHECK_EQ(info.This(), global->Get(v8_str("o3"))); - } else if (name->Equals(v8_str("p4"))) { - CHECK_EQ(info.This(), global->Get(v8_str("o4"))); - } - return v8::Undefined(); -} - - -THREADED_TEST(GetterHolders) { - v8::HandleScope scope; - v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); - obj->SetAccessor(v8_str("p1"), PGetter); - obj->SetAccessor(v8_str("p2"), PGetter); - obj->SetAccessor(v8_str("p3"), PGetter); - obj->SetAccessor(v8_str("p4"), PGetter); - p_getter_count = 0; - RunHolderTest(obj); - CHECK_EQ(40, p_getter_count); -} - - -THREADED_TEST(PreInterceptorHolders) { - v8::HandleScope scope; - v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); - obj->SetNamedPropertyHandler(PGetter2); - p_getter_count2 = 0; - RunHolderTest(obj); - CHECK_EQ(40, p_getter_count2); -} - - -THREADED_TEST(ObjectInstantiation) { - v8::HandleScope scope; - v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); - templ->SetAccessor(v8_str("t"), PGetter2); - LocalContext context; - context->Global()->Set(v8_str("o"), templ->NewInstance()); - for (int i = 0; i < 100; i++) { - v8::HandleScope inner_scope; - v8::Handle<v8::Object> obj = templ->NewInstance(); - CHECK_NE(obj, context->Global()->Get(v8_str("o"))); - context->Global()->Set(v8_str("o2"), obj); - v8::Handle<Value> value = - Script::Compile(v8_str("o.__proto__ === o2.__proto__"))->Run(); - CHECK_EQ(v8::True(), value); - context->Global()->Set(v8_str("o"), obj); - } -} - - -static int StrCmp16(uint16_t* a, uint16_t* b) { - while (true) { - if (*a == 0 && *b == 0) return 0; - if (*a != *b) return 0 + *a - *b; - a++; - b++; - } -} - - -static int StrNCmp16(uint16_t* a, uint16_t* b, int n) { - while (true) { - if (n-- == 0) return 0; - if (*a == 0 && *b == 0) return 0; - if (*a != *b) return 0 + *a - *b; - a++; - b++; - } -} - - -int GetUtf8Length(Handle<String> str) { - int len = str->Utf8Length(); - if (len < 0) { - i::Handle<i::String> istr(v8::Utils::OpenHandle(*str)); - i::FlattenString(istr); - len = str->Utf8Length(); - } - return len; -} - - -THREADED_TEST(StringWrite) { - LocalContext context; - v8::HandleScope scope; - v8::Handle<String> str = v8_str("abcde"); - // abc<Icelandic eth><Unicode snowman>. - v8::Handle<String> str2 = v8_str("abc\303\260\342\230\203"); - v8::Handle<String> str3 = v8::String::New("abc\0def", 7); - const int kStride = 4; // Must match stride in for loops in JS below. - CompileRun( - "var left = '';" - "for (var i = 0; i < 0xd800; i += 4) {" - " left = left + String.fromCharCode(i);" - "}"); - CompileRun( - "var right = '';" - "for (var i = 0; i < 0xd800; i += 4) {" - " right = String.fromCharCode(i) + right;" - "}"); - v8::Handle<v8::Object> global = Context::GetCurrent()->Global(); - Handle<String> left_tree = global->Get(v8_str("left")).As<String>(); - Handle<String> right_tree = global->Get(v8_str("right")).As<String>(); - - CHECK_EQ(5, str2->Length()); - CHECK_EQ(0xd800 / kStride, left_tree->Length()); - CHECK_EQ(0xd800 / kStride, right_tree->Length()); - - char buf[100]; - char utf8buf[0xd800 * 3]; - uint16_t wbuf[100]; - int len; - int charlen; - - memset(utf8buf, 0x1, 1000); - len = str2->WriteUtf8(utf8buf, sizeof(utf8buf), &charlen); - CHECK_EQ(9, len); - CHECK_EQ(5, charlen); - CHECK_EQ(0, strcmp(utf8buf, "abc\303\260\342\230\203")); - - memset(utf8buf, 0x1, 1000); - len = str2->WriteUtf8(utf8buf, 8, &charlen); - CHECK_EQ(8, len); - CHECK_EQ(5, charlen); - CHECK_EQ(0, strncmp(utf8buf, "abc\303\260\342\230\203\1", 9)); - - memset(utf8buf, 0x1, 1000); - len = str2->WriteUtf8(utf8buf, 7, &charlen); - CHECK_EQ(5, len); - CHECK_EQ(4, charlen); - CHECK_EQ(0, strncmp(utf8buf, "abc\303\260\1", 5)); - - memset(utf8buf, 0x1, 1000); - len = str2->WriteUtf8(utf8buf, 6, &charlen); - CHECK_EQ(5, len); - CHECK_EQ(4, charlen); - CHECK_EQ(0, strncmp(utf8buf, "abc\303\260\1", 5)); - - memset(utf8buf, 0x1, 1000); - len = str2->WriteUtf8(utf8buf, 5, &charlen); - CHECK_EQ(5, len); - CHECK_EQ(4, charlen); - CHECK_EQ(0, strncmp(utf8buf, "abc\303\260\1", 5)); - - memset(utf8buf, 0x1, 1000); - len = str2->WriteUtf8(utf8buf, 4, &charlen); - CHECK_EQ(3, len); - CHECK_EQ(3, charlen); - CHECK_EQ(0, strncmp(utf8buf, "abc\1", 4)); - - memset(utf8buf, 0x1, 1000); - len = str2->WriteUtf8(utf8buf, 3, &charlen); - CHECK_EQ(3, len); - CHECK_EQ(3, charlen); - CHECK_EQ(0, strncmp(utf8buf, "abc\1", 4)); - - memset(utf8buf, 0x1, 1000); - len = str2->WriteUtf8(utf8buf, 2, &charlen); - CHECK_EQ(2, len); - CHECK_EQ(2, charlen); - CHECK_EQ(0, strncmp(utf8buf, "ab\1", 3)); - - memset(utf8buf, 0x1, sizeof(utf8buf)); - len = GetUtf8Length(left_tree); - int utf8_expected = - (0x80 + (0x800 - 0x80) * 2 + (0xd800 - 0x800) * 3) / kStride; - CHECK_EQ(utf8_expected, len); - len = left_tree->WriteUtf8(utf8buf, utf8_expected, &charlen); - CHECK_EQ(utf8_expected, len); - CHECK_EQ(0xd800 / kStride, charlen); - CHECK_EQ(0xed, static_cast<unsigned char>(utf8buf[utf8_expected - 3])); - CHECK_EQ(0x9f, static_cast<unsigned char>(utf8buf[utf8_expected - 2])); - CHECK_EQ(0xc0 - kStride, - static_cast<unsigned char>(utf8buf[utf8_expected - 1])); - CHECK_EQ(1, utf8buf[utf8_expected]); - - memset(utf8buf, 0x1, sizeof(utf8buf)); - len = GetUtf8Length(right_tree); - CHECK_EQ(utf8_expected, len); - len = right_tree->WriteUtf8(utf8buf, utf8_expected, &charlen); - CHECK_EQ(utf8_expected, len); - CHECK_EQ(0xd800 / kStride, charlen); - CHECK_EQ(0xed, static_cast<unsigned char>(utf8buf[0])); - CHECK_EQ(0x9f, static_cast<unsigned char>(utf8buf[1])); - CHECK_EQ(0xc0 - kStride, static_cast<unsigned char>(utf8buf[2])); - CHECK_EQ(1, utf8buf[utf8_expected]); - - memset(buf, 0x1, sizeof(buf)); - memset(wbuf, 0x1, sizeof(wbuf)); - len = str->WriteAscii(buf); - CHECK_EQ(5, len); - len = str->Write(wbuf); - CHECK_EQ(5, len); - CHECK_EQ(0, strcmp("abcde", buf)); - uint16_t answer1[] = {'a', 'b', 'c', 'd', 'e', '\0'}; - CHECK_EQ(0, StrCmp16(answer1, wbuf)); - - memset(buf, 0x1, sizeof(buf)); - memset(wbuf, 0x1, sizeof(wbuf)); - len = str->WriteAscii(buf, 0, 4); - CHECK_EQ(4, len); - len = str->Write(wbuf, 0, 4); - CHECK_EQ(4, len); - CHECK_EQ(0, strncmp("abcd\1", buf, 5)); - uint16_t answer2[] = {'a', 'b', 'c', 'd', 0x101}; - CHECK_EQ(0, StrNCmp16(answer2, wbuf, 5)); - - memset(buf, 0x1, sizeof(buf)); - memset(wbuf, 0x1, sizeof(wbuf)); - len = str->WriteAscii(buf, 0, 5); - CHECK_EQ(5, len); - len = str->Write(wbuf, 0, 5); - CHECK_EQ(5, len); - CHECK_EQ(0, strncmp("abcde\1", buf, 6)); - uint16_t answer3[] = {'a', 'b', 'c', 'd', 'e', 0x101}; - CHECK_EQ(0, StrNCmp16(answer3, wbuf, 6)); - - memset(buf, 0x1, sizeof(buf)); - memset(wbuf, 0x1, sizeof(wbuf)); - len = str->WriteAscii(buf, 0, 6); - CHECK_EQ(5, len); - len = str->Write(wbuf, 0, 6); - CHECK_EQ(5, len); - CHECK_EQ(0, strcmp("abcde", buf)); - uint16_t answer4[] = {'a', 'b', 'c', 'd', 'e', '\0'}; - CHECK_EQ(0, StrCmp16(answer4, wbuf)); - - memset(buf, 0x1, sizeof(buf)); - memset(wbuf, 0x1, sizeof(wbuf)); - len = str->WriteAscii(buf, 4, -1); - CHECK_EQ(1, len); - len = str->Write(wbuf, 4, -1); - CHECK_EQ(1, len); - CHECK_EQ(0, strcmp("e", buf)); - uint16_t answer5[] = {'e', '\0'}; - CHECK_EQ(0, StrCmp16(answer5, wbuf)); - - memset(buf, 0x1, sizeof(buf)); - memset(wbuf, 0x1, sizeof(wbuf)); - len = str->WriteAscii(buf, 4, 6); - CHECK_EQ(1, len); - len = str->Write(wbuf, 4, 6); - CHECK_EQ(1, len); - CHECK_EQ(0, strcmp("e", buf)); - CHECK_EQ(0, StrCmp16(answer5, wbuf)); - - memset(buf, 0x1, sizeof(buf)); - memset(wbuf, 0x1, sizeof(wbuf)); - len = str->WriteAscii(buf, 4, 1); - CHECK_EQ(1, len); - len = str->Write(wbuf, 4, 1); - CHECK_EQ(1, len); - CHECK_EQ(0, strncmp("e\1", buf, 2)); - uint16_t answer6[] = {'e', 0x101}; - CHECK_EQ(0, StrNCmp16(answer6, wbuf, 2)); - - memset(buf, 0x1, sizeof(buf)); - memset(wbuf, 0x1, sizeof(wbuf)); - len = str->WriteAscii(buf, 3, 1); - CHECK_EQ(1, len); - len = str->Write(wbuf, 3, 1); - CHECK_EQ(1, len); - CHECK_EQ(0, strncmp("d\1", buf, 2)); - uint16_t answer7[] = {'d', 0x101}; - CHECK_EQ(0, StrNCmp16(answer7, wbuf, 2)); - - memset(wbuf, 0x1, sizeof(wbuf)); - wbuf[5] = 'X'; - len = str->Write(wbuf, 0, 6, String::NO_NULL_TERMINATION); - CHECK_EQ(5, len); - CHECK_EQ('X', wbuf[5]); - uint16_t answer8a[] = {'a', 'b', 'c', 'd', 'e'}; - uint16_t answer8b[] = {'a', 'b', 'c', 'd', 'e', '\0'}; - CHECK_EQ(0, StrNCmp16(answer8a, wbuf, 5)); - CHECK_NE(0, StrCmp16(answer8b, wbuf)); - wbuf[5] = '\0'; - CHECK_EQ(0, StrCmp16(answer8b, wbuf)); - - memset(buf, 0x1, sizeof(buf)); - buf[5] = 'X'; - len = str->WriteAscii(buf, 0, 6, String::NO_NULL_TERMINATION); - CHECK_EQ(5, len); - CHECK_EQ('X', buf[5]); - CHECK_EQ(0, strncmp("abcde", buf, 5)); - CHECK_NE(0, strcmp("abcde", buf)); - buf[5] = '\0'; - CHECK_EQ(0, strcmp("abcde", buf)); - - memset(utf8buf, 0x1, sizeof(utf8buf)); - utf8buf[8] = 'X'; - len = str2->WriteUtf8(utf8buf, sizeof(utf8buf), &charlen, - String::NO_NULL_TERMINATION); - CHECK_EQ(8, len); - CHECK_EQ('X', utf8buf[8]); - CHECK_EQ(5, charlen); - CHECK_EQ(0, strncmp(utf8buf, "abc\303\260\342\230\203", 8)); - CHECK_NE(0, strcmp(utf8buf, "abc\303\260\342\230\203")); - utf8buf[8] = '\0'; - CHECK_EQ(0, strcmp(utf8buf, "abc\303\260\342\230\203")); - - memset(utf8buf, 0x1, sizeof(utf8buf)); - utf8buf[5] = 'X'; - len = str->WriteUtf8(utf8buf, sizeof(utf8buf), &charlen, - String::NO_NULL_TERMINATION); - CHECK_EQ(5, len); - CHECK_EQ('X', utf8buf[5]); // Test that the sixth character is untouched. - CHECK_EQ(5, charlen); - utf8buf[5] = '\0'; - CHECK_EQ(0, strcmp(utf8buf, "abcde")); - - memset(buf, 0x1, sizeof(buf)); - len = str3->WriteAscii(buf); - CHECK_EQ(7, len); - CHECK_EQ(0, strcmp("abc def", buf)); - - memset(buf, 0x1, sizeof(buf)); - len = str3->WriteAscii(buf, 0, -1, String::PRESERVE_ASCII_NULL); - CHECK_EQ(7, len); - CHECK_EQ(0, strcmp("abc", buf)); - CHECK_EQ(0, buf[3]); - CHECK_EQ(0, strcmp("def", buf + 4)); - - CHECK_EQ(0, str->WriteAscii(NULL, 0, 0, String::NO_NULL_TERMINATION)); - CHECK_EQ(0, str->WriteUtf8(NULL, 0, 0, String::NO_NULL_TERMINATION)); - CHECK_EQ(0, str->Write(NULL, 0, 0, String::NO_NULL_TERMINATION)); -} - - -static void Utf16Helper( - LocalContext& context, - const char* name, - const char* lengths_name, - int len) { - Local<v8::Array> a = - Local<v8::Array>::Cast(context->Global()->Get(v8_str(name))); - Local<v8::Array> alens = - Local<v8::Array>::Cast(context->Global()->Get(v8_str(lengths_name))); - for (int i = 0; i < len; i++) { - Local<v8::String> string = - Local<v8::String>::Cast(a->Get(i)); - Local<v8::Number> expected_len = - Local<v8::Number>::Cast(alens->Get(i)); -#ifndef ENABLE_LATIN_1 - CHECK_EQ(expected_len->Value() != string->Length(), - string->MayContainNonAscii()); -#endif - int length = GetUtf8Length(string); - CHECK_EQ(static_cast<int>(expected_len->Value()), length); - } -} - - -static uint16_t StringGet(Handle<String> str, int index) { - i::Handle<i::String> istring = - v8::Utils::OpenHandle(String::Cast(*str)); - return istring->Get(index); -} - - -static void WriteUtf8Helper( - LocalContext& context, - const char* name, - const char* lengths_name, - int len) { - Local<v8::Array> b = - Local<v8::Array>::Cast(context->Global()->Get(v8_str(name))); - Local<v8::Array> alens = - Local<v8::Array>::Cast(context->Global()->Get(v8_str(lengths_name))); - char buffer[1000]; - char buffer2[1000]; - for (int i = 0; i < len; i++) { - Local<v8::String> string = - Local<v8::String>::Cast(b->Get(i)); - Local<v8::Number> expected_len = - Local<v8::Number>::Cast(alens->Get(i)); - int utf8_length = static_cast<int>(expected_len->Value()); - for (int j = utf8_length + 1; j >= 0; j--) { - memset(reinterpret_cast<void*>(&buffer), 42, sizeof(buffer)); - memset(reinterpret_cast<void*>(&buffer2), 42, sizeof(buffer2)); - int nchars; - int utf8_written = - string->WriteUtf8(buffer, j, &nchars, String::NO_OPTIONS); - int utf8_written2 = - string->WriteUtf8(buffer2, j, &nchars, String::NO_NULL_TERMINATION); - CHECK_GE(utf8_length + 1, utf8_written); - CHECK_GE(utf8_length, utf8_written2); - for (int k = 0; k < utf8_written2; k++) { - CHECK_EQ(buffer[k], buffer2[k]); - } - CHECK(nchars * 3 >= utf8_written - 1); - CHECK(nchars <= utf8_written); - if (j == utf8_length + 1) { - CHECK_EQ(utf8_written2, utf8_length); - CHECK_EQ(utf8_written2 + 1, utf8_written); - } - CHECK_EQ(buffer[utf8_written], 42); - if (j > utf8_length) { - if (utf8_written != 0) CHECK_EQ(buffer[utf8_written - 1], 0); - if (utf8_written > 1) CHECK_NE(buffer[utf8_written - 2], 42); - Handle<String> roundtrip = v8_str(buffer); - CHECK(roundtrip->Equals(string)); - } else { - if (utf8_written != 0) CHECK_NE(buffer[utf8_written - 1], 42); - } - if (utf8_written2 != 0) CHECK_NE(buffer[utf8_written - 1], 42); - if (nchars >= 2) { - uint16_t trail = StringGet(string, nchars - 1); - uint16_t lead = StringGet(string, nchars - 2); - if (((lead & 0xfc00) == 0xd800) && - ((trail & 0xfc00) == 0xdc00)) { - unsigned char u1 = buffer2[utf8_written2 - 4]; - unsigned char u2 = buffer2[utf8_written2 - 3]; - unsigned char u3 = buffer2[utf8_written2 - 2]; - unsigned char u4 = buffer2[utf8_written2 - 1]; - CHECK_EQ((u1 & 0xf8), 0xf0); - CHECK_EQ((u2 & 0xc0), 0x80); - CHECK_EQ((u3 & 0xc0), 0x80); - CHECK_EQ((u4 & 0xc0), 0x80); - uint32_t c = 0x10000 + ((lead & 0x3ff) << 10) + (trail & 0x3ff); - CHECK_EQ((u4 & 0x3f), (c & 0x3f)); - CHECK_EQ((u3 & 0x3f), ((c >> 6) & 0x3f)); - CHECK_EQ((u2 & 0x3f), ((c >> 12) & 0x3f)); - CHECK_EQ((u1 & 0x3), c >> 18); - } - } - } - } -} - - -THREADED_TEST(Utf16) { - LocalContext context; - v8::HandleScope scope; - CompileRun( - "var pad = '01234567890123456789';" - "var p = [];" - "var plens = [20, 3, 3];" - "p.push('01234567890123456789');" - "var lead = 0xd800;" - "var trail = 0xdc00;" - "p.push(String.fromCharCode(0xd800));" - "p.push(String.fromCharCode(0xdc00));" - "var a = [];" - "var b = [];" - "var c = [];" - "var alens = [];" - "for (var i = 0; i < 3; i++) {" - " p[1] = String.fromCharCode(lead++);" - " for (var j = 0; j < 3; j++) {" - " p[2] = String.fromCharCode(trail++);" - " a.push(p[i] + p[j]);" - " b.push(p[i] + p[j]);" - " c.push(p[i] + p[j]);" - " alens.push(plens[i] + plens[j]);" - " }" - "}" - "alens[5] -= 2;" // Here the surrogate pairs match up. - "var a2 = [];" - "var b2 = [];" - "var c2 = [];" - "var a2lens = [];" - "for (var m = 0; m < 9; m++) {" - " for (var n = 0; n < 9; n++) {" - " a2.push(a[m] + a[n]);" - " b2.push(b[m] + b[n]);" - " var newc = 'x' + c[m] + c[n] + 'y';" - " c2.push(newc.substring(1, newc.length - 1));" - " var utf = alens[m] + alens[n];" // And here. - // The 'n's that start with 0xdc.. are 6-8 - // The 'm's that end with 0xd8.. are 1, 4 and 7 - " if ((m % 3) == 1 && n >= 6) utf -= 2;" - " a2lens.push(utf);" - " }" - "}"); - Utf16Helper(context, "a", "alens", 9); - Utf16Helper(context, "a2", "a2lens", 81); - WriteUtf8Helper(context, "b", "alens", 9); - WriteUtf8Helper(context, "b2", "a2lens", 81); - WriteUtf8Helper(context, "c2", "a2lens", 81); -} - - -static bool SameSymbol(Handle<String> s1, Handle<String> s2) { - i::Handle<i::String> is1(v8::Utils::OpenHandle(*s1)); - i::Handle<i::String> is2(v8::Utils::OpenHandle(*s2)); - return *is1 == *is2; -} - - -static void SameSymbolHelper(const char* a, const char* b) { - Handle<String> symbol1 = v8::String::NewSymbol(a); - Handle<String> symbol2 = v8::String::NewSymbol(b); - CHECK(SameSymbol(symbol1, symbol2)); -} - - -THREADED_TEST(Utf16Symbol) { - LocalContext context; - v8::HandleScope scope; - - Handle<String> symbol1 = v8::String::NewSymbol("abc"); - Handle<String> symbol2 = v8::String::NewSymbol("abc"); - CHECK(SameSymbol(symbol1, symbol2)); - - SameSymbolHelper("\360\220\220\205", // 4 byte encoding. - "\355\240\201\355\260\205"); // 2 3-byte surrogates. - SameSymbolHelper("\355\240\201\355\260\206", // 2 3-byte surrogates. - "\360\220\220\206"); // 4 byte encoding. - SameSymbolHelper("x\360\220\220\205", // 4 byte encoding. - "x\355\240\201\355\260\205"); // 2 3-byte surrogates. - SameSymbolHelper("x\355\240\201\355\260\206", // 2 3-byte surrogates. - "x\360\220\220\206"); // 4 byte encoding. - CompileRun( - "var sym0 = 'benedictus';" - "var sym0b = 'S\303\270ren';" - "var sym1 = '\355\240\201\355\260\207';" - "var sym2 = '\360\220\220\210';" - "var sym3 = 'x\355\240\201\355\260\207';" - "var sym4 = 'x\360\220\220\210';" - "if (sym1.length != 2) throw sym1;" - "if (sym1.charCodeAt(1) != 0xdc07) throw sym1.charCodeAt(1);" - "if (sym2.length != 2) throw sym2;" - "if (sym2.charCodeAt(1) != 0xdc08) throw sym2.charCodeAt(2);" - "if (sym3.length != 3) throw sym3;" - "if (sym3.charCodeAt(2) != 0xdc07) throw sym1.charCodeAt(2);" - "if (sym4.length != 3) throw sym4;" - "if (sym4.charCodeAt(2) != 0xdc08) throw sym2.charCodeAt(2);"); - Handle<String> sym0 = v8::String::NewSymbol("benedictus"); - Handle<String> sym0b = v8::String::NewSymbol("S\303\270ren"); - Handle<String> sym1 = v8::String::NewSymbol("\355\240\201\355\260\207"); - Handle<String> sym2 = v8::String::NewSymbol("\360\220\220\210"); - Handle<String> sym3 = v8::String::NewSymbol("x\355\240\201\355\260\207"); - Handle<String> sym4 = v8::String::NewSymbol("x\360\220\220\210"); - v8::Local<v8::Object> global = context->Global(); - Local<Value> s0 = global->Get(v8_str("sym0")); - Local<Value> s0b = global->Get(v8_str("sym0b")); - Local<Value> s1 = global->Get(v8_str("sym1")); - Local<Value> s2 = global->Get(v8_str("sym2")); - Local<Value> s3 = global->Get(v8_str("sym3")); - Local<Value> s4 = global->Get(v8_str("sym4")); - CHECK(SameSymbol(sym0, Handle<String>(String::Cast(*s0)))); - CHECK(SameSymbol(sym0b, Handle<String>(String::Cast(*s0b)))); - CHECK(SameSymbol(sym1, Handle<String>(String::Cast(*s1)))); - CHECK(SameSymbol(sym2, Handle<String>(String::Cast(*s2)))); - CHECK(SameSymbol(sym3, Handle<String>(String::Cast(*s3)))); - CHECK(SameSymbol(sym4, Handle<String>(String::Cast(*s4)))); -} - - -THREADED_TEST(ToArrayIndex) { - v8::HandleScope scope; - LocalContext context; - - v8::Handle<String> str = v8_str("42"); - v8::Handle<v8::Uint32> index = str->ToArrayIndex(); - CHECK(!index.IsEmpty()); - CHECK_EQ(42.0, index->Uint32Value()); - str = v8_str("42asdf"); - index = str->ToArrayIndex(); - CHECK(index.IsEmpty()); - str = v8_str("-42"); - index = str->ToArrayIndex(); - CHECK(index.IsEmpty()); - str = v8_str("4294967295"); - index = str->ToArrayIndex(); - CHECK(!index.IsEmpty()); - CHECK_EQ(4294967295.0, index->Uint32Value()); - v8::Handle<v8::Number> num = v8::Number::New(1); - index = num->ToArrayIndex(); - CHECK(!index.IsEmpty()); - CHECK_EQ(1.0, index->Uint32Value()); - num = v8::Number::New(-1); - index = num->ToArrayIndex(); - CHECK(index.IsEmpty()); - v8::Handle<v8::Object> obj = v8::Object::New(); - index = obj->ToArrayIndex(); - CHECK(index.IsEmpty()); -} - - -THREADED_TEST(ErrorConstruction) { - v8::HandleScope scope; - LocalContext context; - - v8::Handle<String> foo = v8_str("foo"); - v8::Handle<String> message = v8_str("message"); - v8::Handle<Value> range_error = v8::Exception::RangeError(foo); - CHECK(range_error->IsObject()); - CHECK(range_error.As<v8::Object>()->Get(message)->Equals(foo)); - v8::Handle<Value> reference_error = v8::Exception::ReferenceError(foo); - CHECK(reference_error->IsObject()); - CHECK(reference_error.As<v8::Object>()->Get(message)->Equals(foo)); - v8::Handle<Value> syntax_error = v8::Exception::SyntaxError(foo); - CHECK(syntax_error->IsObject()); - CHECK(syntax_error.As<v8::Object>()->Get(message)->Equals(foo)); - v8::Handle<Value> type_error = v8::Exception::TypeError(foo); - CHECK(type_error->IsObject()); - CHECK(type_error.As<v8::Object>()->Get(message)->Equals(foo)); - v8::Handle<Value> error = v8::Exception::Error(foo); - CHECK(error->IsObject()); - CHECK(error.As<v8::Object>()->Get(message)->Equals(foo)); -} - - -static v8::Handle<Value> YGetter(Local<String> name, const AccessorInfo& info) { - ApiTestFuzzer::Fuzz(); - return v8_num(10); -} - - -static void YSetter(Local<String> name, - Local<Value> value, - const AccessorInfo& info) { - if (info.This()->Has(name)) { - info.This()->Delete(name); - } - info.This()->Set(name, value); -} - - -THREADED_TEST(DeleteAccessor) { - v8::HandleScope scope; - v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); - obj->SetAccessor(v8_str("y"), YGetter, YSetter); - LocalContext context; - v8::Handle<v8::Object> holder = obj->NewInstance(); - context->Global()->Set(v8_str("holder"), holder); - v8::Handle<Value> result = CompileRun( - "holder.y = 11; holder.y = 12; holder.y"); - CHECK_EQ(12, result->Uint32Value()); -} - - -THREADED_TEST(TypeSwitch) { - v8::HandleScope scope; - v8::Handle<v8::FunctionTemplate> templ1 = v8::FunctionTemplate::New(); - v8::Handle<v8::FunctionTemplate> templ2 = v8::FunctionTemplate::New(); - v8::Handle<v8::FunctionTemplate> templ3 = v8::FunctionTemplate::New(); - v8::Handle<v8::FunctionTemplate> templs[3] = { templ1, templ2, templ3 }; - v8::Handle<v8::TypeSwitch> type_switch = v8::TypeSwitch::New(3, templs); - LocalContext context; - v8::Handle<v8::Object> obj0 = v8::Object::New(); - v8::Handle<v8::Object> obj1 = templ1->GetFunction()->NewInstance(); - v8::Handle<v8::Object> obj2 = templ2->GetFunction()->NewInstance(); - v8::Handle<v8::Object> obj3 = templ3->GetFunction()->NewInstance(); - for (int i = 0; i < 10; i++) { - CHECK_EQ(0, type_switch->match(obj0)); - CHECK_EQ(1, type_switch->match(obj1)); - CHECK_EQ(2, type_switch->match(obj2)); - CHECK_EQ(3, type_switch->match(obj3)); - CHECK_EQ(3, type_switch->match(obj3)); - CHECK_EQ(2, type_switch->match(obj2)); - CHECK_EQ(1, type_switch->match(obj1)); - CHECK_EQ(0, type_switch->match(obj0)); - } -} - - -// For use within the TestSecurityHandler() test. -static bool g_security_callback_result = false; -static bool NamedSecurityTestCallback(Local<v8::Object> global, - Local<Value> name, - v8::AccessType type, - Local<Value> data) { - // Always allow read access. - if (type == v8::ACCESS_GET) - return true; - - // Sometimes allow other access. - return g_security_callback_result; -} - - -static bool IndexedSecurityTestCallback(Local<v8::Object> global, - uint32_t key, - v8::AccessType type, - Local<Value> data) { - // Always allow read access. - if (type == v8::ACCESS_GET) - return true; - - // Sometimes allow other access. - return g_security_callback_result; -} - - -static int trouble_nesting = 0; -static v8::Handle<Value> TroubleCallback(const v8::Arguments& args) { - ApiTestFuzzer::Fuzz(); - trouble_nesting++; - - // Call a JS function that throws an uncaught exception. - Local<v8::Object> arg_this = Context::GetCurrent()->Global(); - Local<Value> trouble_callee = (trouble_nesting == 3) ? - arg_this->Get(v8_str("trouble_callee")) : - arg_this->Get(v8_str("trouble_caller")); - CHECK(trouble_callee->IsFunction()); - return Function::Cast(*trouble_callee)->Call(arg_this, 0, NULL); -} - - -static int report_count = 0; -static void ApiUncaughtExceptionTestListener(v8::Handle<v8::Message>, - v8::Handle<Value>) { - report_count++; -} - - -// Counts uncaught exceptions, but other tests running in parallel -// also have uncaught exceptions. -TEST(ApiUncaughtException) { - report_count = 0; - v8::HandleScope scope; - LocalContext env; - v8::V8::AddMessageListener(ApiUncaughtExceptionTestListener); - - Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(TroubleCallback); - v8::Local<v8::Object> global = env->Global(); - global->Set(v8_str("trouble"), fun->GetFunction()); - - Script::Compile(v8_str("function trouble_callee() {" - " var x = null;" - " return x.foo;" - "};" - "function trouble_caller() {" - " trouble();" - "};"))->Run(); - Local<Value> trouble = global->Get(v8_str("trouble")); - CHECK(trouble->IsFunction()); - Local<Value> trouble_callee = global->Get(v8_str("trouble_callee")); - CHECK(trouble_callee->IsFunction()); - Local<Value> trouble_caller = global->Get(v8_str("trouble_caller")); - CHECK(trouble_caller->IsFunction()); - Function::Cast(*trouble_caller)->Call(global, 0, NULL); - CHECK_EQ(1, report_count); - v8::V8::RemoveMessageListeners(ApiUncaughtExceptionTestListener); -} - -static const char* script_resource_name = "ExceptionInNativeScript.js"; -static void ExceptionInNativeScriptTestListener(v8::Handle<v8::Message> message, - v8::Handle<Value>) { - v8::Handle<v8::Value> name_val = message->GetScriptResourceName(); - CHECK(!name_val.IsEmpty() && name_val->IsString()); - v8::String::AsciiValue name(message->GetScriptResourceName()); - CHECK_EQ(script_resource_name, *name); - CHECK_EQ(3, message->GetLineNumber()); - v8::String::AsciiValue source_line(message->GetSourceLine()); - CHECK_EQ(" new o.foo();", *source_line); -} - -TEST(ExceptionInNativeScript) { - v8::HandleScope scope; - LocalContext env; - v8::V8::AddMessageListener(ExceptionInNativeScriptTestListener); - - Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(TroubleCallback); - v8::Local<v8::Object> global = env->Global(); - global->Set(v8_str("trouble"), fun->GetFunction()); - - Script::Compile(v8_str("function trouble() {\n" - " var o = {};\n" - " new o.foo();\n" - "};"), v8::String::New(script_resource_name))->Run(); - Local<Value> trouble = global->Get(v8_str("trouble")); - CHECK(trouble->IsFunction()); - Function::Cast(*trouble)->Call(global, 0, NULL); - v8::V8::RemoveMessageListeners(ExceptionInNativeScriptTestListener); -} - - -TEST(CompilationErrorUsingTryCatchHandler) { - v8::HandleScope scope; - LocalContext env; - v8::TryCatch try_catch; - Script::Compile(v8_str("This doesn't &*&@#$&*^ compile.")); - CHECK_NE(NULL, *try_catch.Exception()); - CHECK(try_catch.HasCaught()); -} - - -TEST(TryCatchFinallyUsingTryCatchHandler) { - v8::HandleScope scope; - LocalContext env; - v8::TryCatch try_catch; - Script::Compile(v8_str("try { throw ''; } catch (e) {}"))->Run(); - CHECK(!try_catch.HasCaught()); - Script::Compile(v8_str("try { throw ''; } finally {}"))->Run(); - CHECK(try_catch.HasCaught()); - try_catch.Reset(); - Script::Compile(v8_str("(function() {" - "try { throw ''; } finally { return; }" - "})()"))->Run(); - CHECK(!try_catch.HasCaught()); - Script::Compile(v8_str("(function()" - " { try { throw ''; } finally { throw 0; }" - "})()"))->Run(); - CHECK(try_catch.HasCaught()); -} - - -// SecurityHandler can't be run twice -TEST(SecurityHandler) { - v8::HandleScope scope0; - v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); - global_template->SetAccessCheckCallbacks(NamedSecurityTestCallback, - IndexedSecurityTestCallback); - // Create an environment - v8::Persistent<Context> context0 = - Context::New(NULL, global_template); - context0->Enter(); - - v8::Handle<v8::Object> global0 = context0->Global(); - v8::Handle<Script> script0 = v8_compile("foo = 111"); - script0->Run(); - global0->Set(v8_str("0"), v8_num(999)); - v8::Handle<Value> foo0 = global0->Get(v8_str("foo")); - CHECK_EQ(111, foo0->Int32Value()); - v8::Handle<Value> z0 = global0->Get(v8_str("0")); - CHECK_EQ(999, z0->Int32Value()); - - // Create another environment, should fail security checks. - v8::HandleScope scope1; - - v8::Persistent<Context> context1 = - Context::New(NULL, global_template); - context1->Enter(); - - v8::Handle<v8::Object> global1 = context1->Global(); - global1->Set(v8_str("othercontext"), global0); - // This set will fail the security check. - v8::Handle<Script> script1 = - v8_compile("othercontext.foo = 222; othercontext[0] = 888;"); - script1->Run(); - // This read will pass the security check. - v8::Handle<Value> foo1 = global0->Get(v8_str("foo")); - CHECK_EQ(111, foo1->Int32Value()); - // This read will pass the security check. - v8::Handle<Value> z1 = global0->Get(v8_str("0")); - CHECK_EQ(999, z1->Int32Value()); - - // Create another environment, should pass security checks. - { g_security_callback_result = true; // allow security handler to pass. - v8::HandleScope scope2; - LocalContext context2; - v8::Handle<v8::Object> global2 = context2->Global(); - global2->Set(v8_str("othercontext"), global0); - v8::Handle<Script> script2 = - v8_compile("othercontext.foo = 333; othercontext[0] = 888;"); - script2->Run(); - v8::Handle<Value> foo2 = global0->Get(v8_str("foo")); - CHECK_EQ(333, foo2->Int32Value()); - v8::Handle<Value> z2 = global0->Get(v8_str("0")); - CHECK_EQ(888, z2->Int32Value()); - } - - context1->Exit(); - context1.Dispose(context1->GetIsolate()); - - context0->Exit(); - context0.Dispose(context0->GetIsolate()); -} - - -THREADED_TEST(SecurityChecks) { - v8::HandleScope handle_scope; - LocalContext env1; - v8::Persistent<Context> env2 = Context::New(); - - Local<Value> foo = v8_str("foo"); - Local<Value> bar = v8_str("bar"); - - // Set to the same domain. - env1->SetSecurityToken(foo); - - // Create a function in env1. - Script::Compile(v8_str("spy=function(){return spy;}"))->Run(); - Local<Value> spy = env1->Global()->Get(v8_str("spy")); - CHECK(spy->IsFunction()); - - // Create another function accessing global objects. - Script::Compile(v8_str("spy2=function(){return new this.Array();}"))->Run(); - Local<Value> spy2 = env1->Global()->Get(v8_str("spy2")); - CHECK(spy2->IsFunction()); - - // Switch to env2 in the same domain and invoke spy on env2. - { - env2->SetSecurityToken(foo); - // Enter env2 - Context::Scope scope_env2(env2); - Local<Value> result = Function::Cast(*spy)->Call(env2->Global(), 0, NULL); - CHECK(result->IsFunction()); - } - - { - env2->SetSecurityToken(bar); - Context::Scope scope_env2(env2); - - // Call cross_domain_call, it should throw an exception - v8::TryCatch try_catch; - Function::Cast(*spy2)->Call(env2->Global(), 0, NULL); - CHECK(try_catch.HasCaught()); - } - - env2.Dispose(env2->GetIsolate()); -} - - -// Regression test case for issue 1183439. -THREADED_TEST(SecurityChecksForPrototypeChain) { - v8::HandleScope scope; - LocalContext current; - v8::Persistent<Context> other = Context::New(); - - // Change context to be able to get to the Object function in the - // other context without hitting the security checks. - v8::Local<Value> other_object; - { Context::Scope scope(other); - other_object = other->Global()->Get(v8_str("Object")); - other->Global()->Set(v8_num(42), v8_num(87)); - } - - current->Global()->Set(v8_str("other"), other->Global()); - CHECK(v8_compile("other")->Run()->Equals(other->Global())); - - // Make sure the security check fails here and we get an undefined - // result instead of getting the Object function. Repeat in a loop - // to make sure to exercise the IC code. - v8::Local<Script> access_other0 = v8_compile("other.Object"); - v8::Local<Script> access_other1 = v8_compile("other[42]"); - for (int i = 0; i < 5; i++) { - CHECK(!access_other0->Run()->Equals(other_object)); - CHECK(access_other0->Run()->IsUndefined()); - CHECK(!access_other1->Run()->Equals(v8_num(87))); - CHECK(access_other1->Run()->IsUndefined()); - } - - // Create an object that has 'other' in its prototype chain and make - // sure we cannot access the Object function indirectly through - // that. Repeat in a loop to make sure to exercise the IC code. - v8_compile("function F() { };" - "F.prototype = other;" - "var f = new F();")->Run(); - v8::Local<Script> access_f0 = v8_compile("f.Object"); - v8::Local<Script> access_f1 = v8_compile("f[42]"); - for (int j = 0; j < 5; j++) { - CHECK(!access_f0->Run()->Equals(other_object)); - CHECK(access_f0->Run()->IsUndefined()); - CHECK(!access_f1->Run()->Equals(v8_num(87))); - CHECK(access_f1->Run()->IsUndefined()); - } - - // Now it gets hairy: Set the prototype for the other global object - // to be the current global object. The prototype chain for 'f' now - // goes through 'other' but ends up in the current global object. - { Context::Scope scope(other); - other->Global()->Set(v8_str("__proto__"), current->Global()); - } - // Set a named and an index property on the current global - // object. To force the lookup to go through the other global object, - // the properties must not exist in the other global object. - current->Global()->Set(v8_str("foo"), v8_num(100)); - current->Global()->Set(v8_num(99), v8_num(101)); - // Try to read the properties from f and make sure that the access - // gets stopped by the security checks on the other global object. - Local<Script> access_f2 = v8_compile("f.foo"); - Local<Script> access_f3 = v8_compile("f[99]"); - for (int k = 0; k < 5; k++) { - CHECK(!access_f2->Run()->Equals(v8_num(100))); - CHECK(access_f2->Run()->IsUndefined()); - CHECK(!access_f3->Run()->Equals(v8_num(101))); - CHECK(access_f3->Run()->IsUndefined()); - } - other.Dispose(other->GetIsolate()); -} - - -THREADED_TEST(CrossDomainDelete) { - v8::HandleScope handle_scope; - LocalContext env1; - v8::Persistent<Context> env2 = Context::New(); - - Local<Value> foo = v8_str("foo"); - Local<Value> bar = v8_str("bar"); - - // Set to the same domain. - env1->SetSecurityToken(foo); - env2->SetSecurityToken(foo); - - env1->Global()->Set(v8_str("prop"), v8_num(3)); - env2->Global()->Set(v8_str("env1"), env1->Global()); - - // Change env2 to a different domain and delete env1.prop. - env2->SetSecurityToken(bar); - { - Context::Scope scope_env2(env2); - Local<Value> result = - Script::Compile(v8_str("delete env1.prop"))->Run(); - CHECK(result->IsFalse()); - } - - // Check that env1.prop still exists. - Local<Value> v = env1->Global()->Get(v8_str("prop")); - CHECK(v->IsNumber()); - CHECK_EQ(3, v->Int32Value()); - - env2.Dispose(env2->GetIsolate()); -} - - -THREADED_TEST(CrossDomainIsPropertyEnumerable) { - v8::HandleScope handle_scope; - LocalContext env1; - v8::Persistent<Context> env2 = Context::New(); - - Local<Value> foo = v8_str("foo"); - Local<Value> bar = v8_str("bar"); - - // Set to the same domain. - env1->SetSecurityToken(foo); - env2->SetSecurityToken(foo); - - env1->Global()->Set(v8_str("prop"), v8_num(3)); - env2->Global()->Set(v8_str("env1"), env1->Global()); - - // env1.prop is enumerable in env2. - Local<String> test = v8_str("propertyIsEnumerable.call(env1, 'prop')"); - { - Context::Scope scope_env2(env2); - Local<Value> result = Script::Compile(test)->Run(); - CHECK(result->IsTrue()); - } - - // Change env2 to a different domain and test again. - env2->SetSecurityToken(bar); - { - Context::Scope scope_env2(env2); - Local<Value> result = Script::Compile(test)->Run(); - CHECK(result->IsFalse()); - } - - env2.Dispose(env2->GetIsolate()); -} - - -THREADED_TEST(CrossDomainForIn) { - v8::HandleScope handle_scope; - LocalContext env1; - v8::Persistent<Context> env2 = Context::New(); - - Local<Value> foo = v8_str("foo"); - Local<Value> bar = v8_str("bar"); - - // Set to the same domain. - env1->SetSecurityToken(foo); - env2->SetSecurityToken(foo); - - env1->Global()->Set(v8_str("prop"), v8_num(3)); - env2->Global()->Set(v8_str("env1"), env1->Global()); - - // Change env2 to a different domain and set env1's global object - // as the __proto__ of an object in env2 and enumerate properties - // in for-in. It shouldn't enumerate properties on env1's global - // object. - env2->SetSecurityToken(bar); - { - Context::Scope scope_env2(env2); - Local<Value> result = - CompileRun("(function(){var obj = {'__proto__':env1};" - "for (var p in obj)" - " if (p == 'prop') return false;" - "return true;})()"); - CHECK(result->IsTrue()); - } - env2.Dispose(env2->GetIsolate()); -} - - -TEST(ContextDetachGlobal) { - v8::HandleScope handle_scope; - LocalContext env1; - v8::Persistent<Context> env2 = Context::New(); - - Local<v8::Object> global1 = env1->Global(); - - Local<Value> foo = v8_str("foo"); - - // Set to the same domain. - env1->SetSecurityToken(foo); - env2->SetSecurityToken(foo); - - // Enter env2 - env2->Enter(); - - // Create a function in env2 and add a reference to it in env1. - Local<v8::Object> global2 = env2->Global(); - global2->Set(v8_str("prop"), v8::Integer::New(1)); - CompileRun("function getProp() {return prop;}"); - - env1->Global()->Set(v8_str("getProp"), - global2->Get(v8_str("getProp"))); - - // Detach env2's global, and reuse the global object of env2 - env2->Exit(); - env2->DetachGlobal(); - // env2 has a new global object. - CHECK(!env2->Global()->Equals(global2)); - - v8::Persistent<Context> env3 = - Context::New(0, v8::Handle<v8::ObjectTemplate>(), global2); - env3->SetSecurityToken(v8_str("bar")); - env3->Enter(); - - Local<v8::Object> global3 = env3->Global(); - CHECK_EQ(global2, global3); - CHECK(global3->Get(v8_str("prop"))->IsUndefined()); - CHECK(global3->Get(v8_str("getProp"))->IsUndefined()); - global3->Set(v8_str("prop"), v8::Integer::New(-1)); - global3->Set(v8_str("prop2"), v8::Integer::New(2)); - env3->Exit(); - - // Call getProp in env1, and it should return the value 1 - { - Local<Value> get_prop = global1->Get(v8_str("getProp")); - CHECK(get_prop->IsFunction()); - v8::TryCatch try_catch; - Local<Value> r = Function::Cast(*get_prop)->Call(global1, 0, NULL); - CHECK(!try_catch.HasCaught()); - CHECK_EQ(1, r->Int32Value()); - } - - // Check that env3 is not accessible from env1 - { - Local<Value> r = global3->Get(v8_str("prop2")); - CHECK(r->IsUndefined()); - } - - env2.Dispose(env2->GetIsolate()); - env3.Dispose(env3->GetIsolate()); -} - - -TEST(DetachAndReattachGlobal) { - v8::HandleScope scope; - LocalContext env1; - - // Create second environment. - v8::Persistent<Context> env2 = Context::New(); - - Local<Value> foo = v8_str("foo"); - - // Set same security token for env1 and env2. - env1->SetSecurityToken(foo); - env2->SetSecurityToken(foo); - - // Create a property on the global object in env2. - { - v8::Context::Scope scope(env2); - env2->Global()->Set(v8_str("p"), v8::Integer::New(42)); - } - - // Create a reference to env2 global from env1 global. - env1->Global()->Set(v8_str("other"), env2->Global()); - - // Check that we have access to other.p in env2 from env1. - Local<Value> result = CompileRun("other.p"); - CHECK(result->IsInt32()); - CHECK_EQ(42, result->Int32Value()); - - // Hold on to global from env2 and detach global from env2. - Local<v8::Object> global2 = env2->Global(); - env2->DetachGlobal(); - - // Check that the global has been detached. No other.p property can - // be found. - result = CompileRun("other.p"); - CHECK(result->IsUndefined()); - - // Reuse global2 for env3. - v8::Persistent<Context> env3 = - Context::New(0, v8::Handle<v8::ObjectTemplate>(), global2); - CHECK_EQ(global2, env3->Global()); - - // Start by using the same security token for env3 as for env1 and env2. - env3->SetSecurityToken(foo); - - // Create a property on the global object in env3. - { - v8::Context::Scope scope(env3); - env3->Global()->Set(v8_str("p"), v8::Integer::New(24)); - } - - // Check that other.p is now the property in env3 and that we have access. - result = CompileRun("other.p"); - CHECK(result->IsInt32()); - CHECK_EQ(24, result->Int32Value()); - - // Change security token for env3 to something different from env1 and env2. - env3->SetSecurityToken(v8_str("bar")); - - // Check that we do not have access to other.p in env1. |other| is now - // the global object for env3 which has a different security token, - // so access should be blocked. - result = CompileRun("other.p"); - CHECK(result->IsUndefined()); - - // Detach the global for env3 and reattach it to env2. - env3->DetachGlobal(); - env2->ReattachGlobal(global2); - - // Check that we have access to other.p again in env1. |other| is now - // the global object for env2 which has the same security token as env1. - result = CompileRun("other.p"); - CHECK(result->IsInt32()); - CHECK_EQ(42, result->Int32Value()); - - env2.Dispose(env2->GetIsolate()); - env3.Dispose(env3->GetIsolate()); -} - - -static bool allowed_access_type[v8::ACCESS_KEYS + 1] = { false }; -static bool NamedAccessBlocker(Local<v8::Object> global, - Local<Value> name, - v8::AccessType type, - Local<Value> data) { - return Context::GetCurrent()->Global()->Equals(global) || - allowed_access_type[type]; -} - - -static bool IndexedAccessBlocker(Local<v8::Object> global, - uint32_t key, - v8::AccessType type, - Local<Value> data) { - return Context::GetCurrent()->Global()->Equals(global) || - allowed_access_type[type]; -} - - -static int g_echo_value = -1; -static v8::Handle<Value> EchoGetter(Local<String> name, - const AccessorInfo& info) { - return v8_num(g_echo_value); -} - - -static void EchoSetter(Local<String> name, - Local<Value> value, - const AccessorInfo&) { - if (value->IsNumber()) - g_echo_value = value->Int32Value(); -} - - -static v8::Handle<Value> UnreachableGetter(Local<String> name, - const AccessorInfo& info) { - CHECK(false); // This function should not be called.. - return v8::Undefined(); -} - - -static void UnreachableSetter(Local<String>, Local<Value>, - const AccessorInfo&) { - CHECK(false); // This function should nto be called. -} - - -TEST(AccessControl) { - v8::HandleScope handle_scope; - v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); - - global_template->SetAccessCheckCallbacks(NamedAccessBlocker, - IndexedAccessBlocker); - - // Add an accessor accessible by cross-domain JS code. - global_template->SetAccessor( - v8_str("accessible_prop"), - EchoGetter, EchoSetter, - v8::Handle<Value>(), - v8::AccessControl(v8::ALL_CAN_READ | v8::ALL_CAN_WRITE)); - - // Add an accessor that is not accessible by cross-domain JS code. - global_template->SetAccessor(v8_str("blocked_prop"), - UnreachableGetter, UnreachableSetter, - v8::Handle<Value>(), - v8::DEFAULT); - - // Create an environment - v8::Persistent<Context> context0 = Context::New(NULL, global_template); - context0->Enter(); - - v8::Handle<v8::Object> global0 = context0->Global(); - - // Define a property with JS getter and setter. - CompileRun( - "function getter() { return 'getter'; };\n" - "function setter() { return 'setter'; }\n" - "Object.defineProperty(this, 'js_accessor_p', {get:getter, set:setter})"); - - Local<Value> getter = global0->Get(v8_str("getter")); - Local<Value> setter = global0->Get(v8_str("setter")); - - // And define normal element. - global0->Set(239, v8_str("239")); - - // Define an element with JS getter and setter. - CompileRun( - "function el_getter() { return 'el_getter'; };\n" - "function el_setter() { return 'el_setter'; };\n" - "Object.defineProperty(this, '42', {get: el_getter, set: el_setter});"); - - Local<Value> el_getter = global0->Get(v8_str("el_getter")); - Local<Value> el_setter = global0->Get(v8_str("el_setter")); - - v8::HandleScope scope1; - - v8::Persistent<Context> context1 = Context::New(); - context1->Enter(); - - v8::Handle<v8::Object> global1 = context1->Global(); - global1->Set(v8_str("other"), global0); - - // Access blocked property. - CompileRun("other.blocked_prop = 1"); - - ExpectUndefined("other.blocked_prop"); - ExpectUndefined( - "Object.getOwnPropertyDescriptor(other, 'blocked_prop')"); - ExpectFalse("propertyIsEnumerable.call(other, 'blocked_prop')"); - - // Enable ACCESS_HAS - allowed_access_type[v8::ACCESS_HAS] = true; - ExpectUndefined("other.blocked_prop"); - // ... and now we can get the descriptor... - ExpectUndefined( - "Object.getOwnPropertyDescriptor(other, 'blocked_prop').value"); - // ... and enumerate the property. - ExpectTrue("propertyIsEnumerable.call(other, 'blocked_prop')"); - allowed_access_type[v8::ACCESS_HAS] = false; - - // Access blocked element. - CompileRun("other[239] = 1"); - - ExpectUndefined("other[239]"); - ExpectUndefined("Object.getOwnPropertyDescriptor(other, '239')"); - ExpectFalse("propertyIsEnumerable.call(other, '239')"); - - // Enable ACCESS_HAS - allowed_access_type[v8::ACCESS_HAS] = true; - ExpectUndefined("other[239]"); - // ... and now we can get the descriptor... - ExpectUndefined("Object.getOwnPropertyDescriptor(other, '239').value"); - // ... and enumerate the property. - ExpectTrue("propertyIsEnumerable.call(other, '239')"); - allowed_access_type[v8::ACCESS_HAS] = false; - - // Access a property with JS accessor. - CompileRun("other.js_accessor_p = 2"); - - ExpectUndefined("other.js_accessor_p"); - ExpectUndefined( - "Object.getOwnPropertyDescriptor(other, 'js_accessor_p')"); - - // Enable ACCESS_HAS. - allowed_access_type[v8::ACCESS_HAS] = true; - ExpectUndefined("other.js_accessor_p"); - ExpectUndefined( - "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').get"); - ExpectUndefined( - "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').set"); - ExpectUndefined( - "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').value"); - allowed_access_type[v8::ACCESS_HAS] = false; - - // Enable both ACCESS_HAS and ACCESS_GET. - allowed_access_type[v8::ACCESS_HAS] = true; - allowed_access_type[v8::ACCESS_GET] = true; - - ExpectString("other.js_accessor_p", "getter"); - ExpectObject( - "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').get", getter); - ExpectUndefined( - "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').set"); - ExpectUndefined( - "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').value"); - - allowed_access_type[v8::ACCESS_GET] = false; - allowed_access_type[v8::ACCESS_HAS] = false; - - // Enable both ACCESS_HAS and ACCESS_SET. - allowed_access_type[v8::ACCESS_HAS] = true; - allowed_access_type[v8::ACCESS_SET] = true; - - ExpectUndefined("other.js_accessor_p"); - ExpectUndefined( - "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').get"); - ExpectObject( - "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').set", setter); - ExpectUndefined( - "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').value"); - - allowed_access_type[v8::ACCESS_SET] = false; - allowed_access_type[v8::ACCESS_HAS] = false; - - // Enable both ACCESS_HAS, ACCESS_GET and ACCESS_SET. - allowed_access_type[v8::ACCESS_HAS] = true; - allowed_access_type[v8::ACCESS_GET] = true; - allowed_access_type[v8::ACCESS_SET] = true; - - ExpectString("other.js_accessor_p", "getter"); - ExpectObject( - "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').get", getter); - ExpectObject( - "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').set", setter); - ExpectUndefined( - "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').value"); - - allowed_access_type[v8::ACCESS_SET] = false; - allowed_access_type[v8::ACCESS_GET] = false; - allowed_access_type[v8::ACCESS_HAS] = false; - - // Access an element with JS accessor. - CompileRun("other[42] = 2"); - - ExpectUndefined("other[42]"); - ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42')"); - - // Enable ACCESS_HAS. - allowed_access_type[v8::ACCESS_HAS] = true; - ExpectUndefined("other[42]"); - ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').get"); - ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').set"); - ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').value"); - allowed_access_type[v8::ACCESS_HAS] = false; - - // Enable both ACCESS_HAS and ACCESS_GET. - allowed_access_type[v8::ACCESS_HAS] = true; - allowed_access_type[v8::ACCESS_GET] = true; - - ExpectString("other[42]", "el_getter"); - ExpectObject("Object.getOwnPropertyDescriptor(other, '42').get", el_getter); - ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').set"); - ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').value"); - - allowed_access_type[v8::ACCESS_GET] = false; - allowed_access_type[v8::ACCESS_HAS] = false; - - // Enable both ACCESS_HAS and ACCESS_SET. - allowed_access_type[v8::ACCESS_HAS] = true; - allowed_access_type[v8::ACCESS_SET] = true; - - ExpectUndefined("other[42]"); - ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').get"); - ExpectObject("Object.getOwnPropertyDescriptor(other, '42').set", el_setter); - ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').value"); - - allowed_access_type[v8::ACCESS_SET] = false; - allowed_access_type[v8::ACCESS_HAS] = false; - - // Enable both ACCESS_HAS, ACCESS_GET and ACCESS_SET. - allowed_access_type[v8::ACCESS_HAS] = true; - allowed_access_type[v8::ACCESS_GET] = true; - allowed_access_type[v8::ACCESS_SET] = true; - - ExpectString("other[42]", "el_getter"); - ExpectObject("Object.getOwnPropertyDescriptor(other, '42').get", el_getter); - ExpectObject("Object.getOwnPropertyDescriptor(other, '42').set", el_setter); - ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').value"); - - allowed_access_type[v8::ACCESS_SET] = false; - allowed_access_type[v8::ACCESS_GET] = false; - allowed_access_type[v8::ACCESS_HAS] = false; - - v8::Handle<Value> value; - - // Access accessible property - value = CompileRun("other.accessible_prop = 3"); - CHECK(value->IsNumber()); - CHECK_EQ(3, value->Int32Value()); - CHECK_EQ(3, g_echo_value); - - value = CompileRun("other.accessible_prop"); - CHECK(value->IsNumber()); - CHECK_EQ(3, value->Int32Value()); - - value = CompileRun( - "Object.getOwnPropertyDescriptor(other, 'accessible_prop').value"); - CHECK(value->IsNumber()); - CHECK_EQ(3, value->Int32Value()); - - value = CompileRun("propertyIsEnumerable.call(other, 'accessible_prop')"); - CHECK(value->IsTrue()); - - // Enumeration doesn't enumerate accessors from inaccessible objects in - // the prototype chain even if the accessors are in themselves accessible. - value = - CompileRun("(function(){var obj = {'__proto__':other};" - "for (var p in obj)" - " if (p == 'accessible_prop' || p == 'blocked_prop') {" - " return false;" - " }" - "return true;})()"); - CHECK(value->IsTrue()); - - context1->Exit(); - context0->Exit(); - context1.Dispose(context1->GetIsolate()); - context0.Dispose(context0->GetIsolate()); -} - - -TEST(AccessControlES5) { - v8::HandleScope handle_scope; - v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); - - global_template->SetAccessCheckCallbacks(NamedAccessBlocker, - IndexedAccessBlocker); - - // Add accessible accessor. - global_template->SetAccessor( - v8_str("accessible_prop"), - EchoGetter, EchoSetter, - v8::Handle<Value>(), - v8::AccessControl(v8::ALL_CAN_READ | v8::ALL_CAN_WRITE)); - - - // Add an accessor that is not accessible by cross-domain JS code. - global_template->SetAccessor(v8_str("blocked_prop"), - UnreachableGetter, UnreachableSetter, - v8::Handle<Value>(), - v8::DEFAULT); - - // Create an environment - v8::Persistent<Context> context0 = Context::New(NULL, global_template); - context0->Enter(); - - v8::Handle<v8::Object> global0 = context0->Global(); - - v8::Persistent<Context> context1 = Context::New(); - context1->Enter(); - v8::Handle<v8::Object> global1 = context1->Global(); - global1->Set(v8_str("other"), global0); - - // Regression test for issue 1154. - ExpectTrue("Object.keys(other).indexOf('blocked_prop') == -1"); - - ExpectUndefined("other.blocked_prop"); - - // Regression test for issue 1027. - CompileRun("Object.defineProperty(\n" - " other, 'blocked_prop', {configurable: false})"); - ExpectUndefined("other.blocked_prop"); - ExpectUndefined( - "Object.getOwnPropertyDescriptor(other, 'blocked_prop')"); - - // Regression test for issue 1171. - ExpectTrue("Object.isExtensible(other)"); - CompileRun("Object.preventExtensions(other)"); - ExpectTrue("Object.isExtensible(other)"); - - // Object.seal and Object.freeze. - CompileRun("Object.freeze(other)"); - ExpectTrue("Object.isExtensible(other)"); - - CompileRun("Object.seal(other)"); - ExpectTrue("Object.isExtensible(other)"); - - // Regression test for issue 1250. - // Make sure that we can set the accessible accessors value using normal - // assignment. - CompileRun("other.accessible_prop = 42"); - CHECK_EQ(42, g_echo_value); - - v8::Handle<Value> value; - // We follow Safari in ignoring assignments to host object accessors. - CompileRun("Object.defineProperty(other, 'accessible_prop', {value: -1})"); - value = CompileRun("other.accessible_prop == 42"); - CHECK(value->IsTrue()); -} - - -static bool GetOwnPropertyNamesNamedBlocker(Local<v8::Object> global, - Local<Value> name, - v8::AccessType type, - Local<Value> data) { - return false; -} - - -static bool GetOwnPropertyNamesIndexedBlocker(Local<v8::Object> global, - uint32_t key, - v8::AccessType type, - Local<Value> data) { - return false; -} - - -THREADED_TEST(AccessControlGetOwnPropertyNames) { - v8::HandleScope handle_scope; - v8::Handle<v8::ObjectTemplate> obj_template = v8::ObjectTemplate::New(); - - obj_template->Set(v8_str("x"), v8::Integer::New(42)); - obj_template->SetAccessCheckCallbacks(GetOwnPropertyNamesNamedBlocker, - GetOwnPropertyNamesIndexedBlocker); - - // Create an environment - v8::Persistent<Context> context0 = Context::New(NULL, obj_template); - context0->Enter(); - - v8::Handle<v8::Object> global0 = context0->Global(); - - v8::HandleScope scope1; - - v8::Persistent<Context> context1 = Context::New(); - context1->Enter(); - - v8::Handle<v8::Object> global1 = context1->Global(); - global1->Set(v8_str("other"), global0); - global1->Set(v8_str("object"), obj_template->NewInstance()); - - v8::Handle<Value> value; - - // Attempt to get the property names of the other global object and - // of an object that requires access checks. Accessing the other - // global object should be blocked by access checks on the global - // proxy object. Accessing the object that requires access checks - // is blocked by the access checks on the object itself. - value = CompileRun("Object.getOwnPropertyNames(other).length == 0"); - CHECK(value->IsTrue()); - - value = CompileRun("Object.getOwnPropertyNames(object).length == 0"); - CHECK(value->IsTrue()); - - context1->Exit(); - context0->Exit(); - context1.Dispose(context1->GetIsolate()); - context0.Dispose(context0->GetIsolate()); -} - - -static v8::Handle<v8::Array> IndexedPropertyEnumerator(const AccessorInfo&) { - v8::Handle<v8::Array> result = v8::Array::New(2); - result->Set(0, v8::Integer::New(7)); - result->Set(1, v8::Object::New()); - return result; -} - - -static v8::Handle<v8::Array> NamedPropertyEnumerator(const AccessorInfo& info) { - v8::Handle<v8::Array> result = v8::Array::New(2); - result->Set(0, v8_str("x")); - result->Set(1, v8::Object::New()); - return result; -} - - -THREADED_TEST(GetOwnPropertyNamesWithInterceptor) { - v8::HandleScope handle_scope; - v8::Handle<v8::ObjectTemplate> obj_template = v8::ObjectTemplate::New(); - - obj_template->Set(v8_str("7"), v8::Integer::New(7)); - obj_template->Set(v8_str("x"), v8::Integer::New(42)); - obj_template->SetIndexedPropertyHandler(NULL, NULL, NULL, NULL, - IndexedPropertyEnumerator); - obj_template->SetNamedPropertyHandler(NULL, NULL, NULL, NULL, - NamedPropertyEnumerator); - - LocalContext context; - v8::Handle<v8::Object> global = context->Global(); - global->Set(v8_str("object"), obj_template->NewInstance()); - - v8::Handle<v8::Value> result = - CompileRun("Object.getOwnPropertyNames(object)"); - CHECK(result->IsArray()); - v8::Handle<v8::Array> result_array = v8::Handle<v8::Array>::Cast(result); - CHECK_EQ(3, result_array->Length()); - CHECK(result_array->Get(0)->IsString()); - CHECK(result_array->Get(1)->IsString()); - CHECK(result_array->Get(2)->IsString()); - CHECK_EQ(v8_str("7"), result_array->Get(0)); - CHECK_EQ(v8_str("[object Object]"), result_array->Get(1)); - CHECK_EQ(v8_str("x"), result_array->Get(2)); -} - - -static v8::Handle<Value> ConstTenGetter(Local<String> name, - const AccessorInfo& info) { - return v8_num(10); -} - - -THREADED_TEST(CrossDomainAccessors) { - v8::HandleScope handle_scope; - - v8::Handle<v8::FunctionTemplate> func_template = v8::FunctionTemplate::New(); - - v8::Handle<v8::ObjectTemplate> global_template = - func_template->InstanceTemplate(); - - v8::Handle<v8::ObjectTemplate> proto_template = - func_template->PrototypeTemplate(); - - // Add an accessor to proto that's accessible by cross-domain JS code. - proto_template->SetAccessor(v8_str("accessible"), - ConstTenGetter, 0, - v8::Handle<Value>(), - v8::ALL_CAN_READ); - - // Add an accessor that is not accessible by cross-domain JS code. - global_template->SetAccessor(v8_str("unreachable"), - UnreachableGetter, 0, - v8::Handle<Value>(), - v8::DEFAULT); - - v8::Persistent<Context> context0 = Context::New(NULL, global_template); - context0->Enter(); - - Local<v8::Object> global = context0->Global(); - // Add a normal property that shadows 'accessible' - global->Set(v8_str("accessible"), v8_num(11)); - - // Enter a new context. - v8::HandleScope scope1; - v8::Persistent<Context> context1 = Context::New(); - context1->Enter(); - - v8::Handle<v8::Object> global1 = context1->Global(); - global1->Set(v8_str("other"), global); - - // Should return 10, instead of 11 - v8::Handle<Value> value = v8_compile("other.accessible")->Run(); - CHECK(value->IsNumber()); - CHECK_EQ(10, value->Int32Value()); - - value = v8_compile("other.unreachable")->Run(); - CHECK(value->IsUndefined()); - - context1->Exit(); - context0->Exit(); - context1.Dispose(context1->GetIsolate()); - context0.Dispose(context0->GetIsolate()); -} - - -static int named_access_count = 0; -static int indexed_access_count = 0; - -static bool NamedAccessCounter(Local<v8::Object> global, - Local<Value> name, - v8::AccessType type, - Local<Value> data) { - named_access_count++; - return true; -} - - -static bool IndexedAccessCounter(Local<v8::Object> global, - uint32_t key, - v8::AccessType type, - Local<Value> data) { - indexed_access_count++; - return true; -} - - -// This one is too easily disturbed by other tests. -TEST(AccessControlIC) { - named_access_count = 0; - indexed_access_count = 0; - - v8::HandleScope handle_scope; - - // Create an environment. - v8::Persistent<Context> context0 = Context::New(); - context0->Enter(); - - // Create an object that requires access-check functions to be - // called for cross-domain access. - v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New(); - object_template->SetAccessCheckCallbacks(NamedAccessCounter, - IndexedAccessCounter); - Local<v8::Object> object = object_template->NewInstance(); - - v8::HandleScope scope1; - - // Create another environment. - v8::Persistent<Context> context1 = Context::New(); - context1->Enter(); - - // Make easy access to the object from the other environment. - v8::Handle<v8::Object> global1 = context1->Global(); - global1->Set(v8_str("obj"), object); - - v8::Handle<Value> value; - - // Check that the named access-control function is called every time. - CompileRun("function testProp(obj) {" - " for (var i = 0; i < 10; i++) obj.prop = 1;" - " for (var j = 0; j < 10; j++) obj.prop;" - " return obj.prop" - "}"); - value = CompileRun("testProp(obj)"); - CHECK(value->IsNumber()); - CHECK_EQ(1, value->Int32Value()); - CHECK_EQ(21, named_access_count); - - // Check that the named access-control function is called every time. - CompileRun("var p = 'prop';" - "function testKeyed(obj) {" - " for (var i = 0; i < 10; i++) obj[p] = 1;" - " for (var j = 0; j < 10; j++) obj[p];" - " return obj[p];" - "}"); - // Use obj which requires access checks. No inline caching is used - // in that case. - value = CompileRun("testKeyed(obj)"); - CHECK(value->IsNumber()); - CHECK_EQ(1, value->Int32Value()); - CHECK_EQ(42, named_access_count); - // Force the inline caches into generic state and try again. - CompileRun("testKeyed({ a: 0 })"); - CompileRun("testKeyed({ b: 0 })"); - value = CompileRun("testKeyed(obj)"); - CHECK(value->IsNumber()); - CHECK_EQ(1, value->Int32Value()); - CHECK_EQ(63, named_access_count); - - // Check that the indexed access-control function is called every time. - CompileRun("function testIndexed(obj) {" - " for (var i = 0; i < 10; i++) obj[0] = 1;" - " for (var j = 0; j < 10; j++) obj[0];" - " return obj[0]" - "}"); - value = CompileRun("testIndexed(obj)"); - CHECK(value->IsNumber()); - CHECK_EQ(1, value->Int32Value()); - CHECK_EQ(21, indexed_access_count); - // Force the inline caches into generic state. - CompileRun("testIndexed(new Array(1))"); - // Test that the indexed access check is called. - value = CompileRun("testIndexed(obj)"); - CHECK(value->IsNumber()); - CHECK_EQ(1, value->Int32Value()); - CHECK_EQ(42, indexed_access_count); - - // Check that the named access check is called when invoking - // functions on an object that requires access checks. - CompileRun("obj.f = function() {}"); - CompileRun("function testCallNormal(obj) {" - " for (var i = 0; i < 10; i++) obj.f();" - "}"); - CompileRun("testCallNormal(obj)"); - CHECK_EQ(74, named_access_count); - - // Force obj into slow case. - value = CompileRun("delete obj.prop"); - CHECK(value->BooleanValue()); - // Force inline caches into dictionary probing mode. - CompileRun("var o = { x: 0 }; delete o.x; testProp(o);"); - // Test that the named access check is called. - value = CompileRun("testProp(obj);"); - CHECK(value->IsNumber()); - CHECK_EQ(1, value->Int32Value()); - CHECK_EQ(96, named_access_count); - - // Force the call inline cache into dictionary probing mode. - CompileRun("o.f = function() {}; testCallNormal(o)"); - // Test that the named access check is still called for each - // invocation of the function. - value = CompileRun("testCallNormal(obj)"); - CHECK_EQ(106, named_access_count); - - context1->Exit(); - context0->Exit(); - context1.Dispose(context1->GetIsolate()); - context0.Dispose(context0->GetIsolate()); -} - - -static bool NamedAccessFlatten(Local<v8::Object> global, - Local<Value> name, - v8::AccessType type, - Local<Value> data) { - char buf[100]; - int len; - - CHECK(name->IsString()); - - memset(buf, 0x1, sizeof(buf)); - len = name.As<String>()->WriteAscii(buf); - CHECK_EQ(4, len); - - uint16_t buf2[100]; - - memset(buf, 0x1, sizeof(buf)); - len = name.As<String>()->Write(buf2); - CHECK_EQ(4, len); - - return true; -} - - -static bool IndexedAccessFlatten(Local<v8::Object> global, - uint32_t key, - v8::AccessType type, - Local<Value> data) { - return true; -} - - -// Regression test. In access checks, operations that may cause -// garbage collection are not allowed. It used to be the case that -// using the Write operation on a string could cause a garbage -// collection due to flattening of the string. This is no longer the -// case. -THREADED_TEST(AccessControlFlatten) { - named_access_count = 0; - indexed_access_count = 0; - - v8::HandleScope handle_scope; - - // Create an environment. - v8::Persistent<Context> context0 = Context::New(); - context0->Enter(); - - // Create an object that requires access-check functions to be - // called for cross-domain access. - v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New(); - object_template->SetAccessCheckCallbacks(NamedAccessFlatten, - IndexedAccessFlatten); - Local<v8::Object> object = object_template->NewInstance(); - - v8::HandleScope scope1; - - // Create another environment. - v8::Persistent<Context> context1 = Context::New(); - context1->Enter(); - - // Make easy access to the object from the other environment. - v8::Handle<v8::Object> global1 = context1->Global(); - global1->Set(v8_str("obj"), object); - - v8::Handle<Value> value; - - value = v8_compile("var p = 'as' + 'df';")->Run(); - value = v8_compile("obj[p];")->Run(); - - context1->Exit(); - context0->Exit(); - context1.Dispose(context1->GetIsolate()); - context0.Dispose(context0->GetIsolate()); -} - - -static v8::Handle<Value> AccessControlNamedGetter( - Local<String>, const AccessorInfo&) { - return v8::Integer::New(42); -} - - -static v8::Handle<Value> AccessControlNamedSetter( - Local<String>, Local<Value> value, const AccessorInfo&) { - return value; -} - - -static v8::Handle<Value> AccessControlIndexedGetter( - uint32_t index, - const AccessorInfo& info) { - return v8_num(42); -} - - -static v8::Handle<Value> AccessControlIndexedSetter( - uint32_t, Local<Value> value, const AccessorInfo&) { - return value; -} - - -THREADED_TEST(AccessControlInterceptorIC) { - named_access_count = 0; - indexed_access_count = 0; - - v8::HandleScope handle_scope; - - // Create an environment. - v8::Persistent<Context> context0 = Context::New(); - context0->Enter(); - - // Create an object that requires access-check functions to be - // called for cross-domain access. The object also has interceptors - // interceptor. - v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New(); - object_template->SetAccessCheckCallbacks(NamedAccessCounter, - IndexedAccessCounter); - object_template->SetNamedPropertyHandler(AccessControlNamedGetter, - AccessControlNamedSetter); - object_template->SetIndexedPropertyHandler(AccessControlIndexedGetter, - AccessControlIndexedSetter); - Local<v8::Object> object = object_template->NewInstance(); - - v8::HandleScope scope1; - - // Create another environment. - v8::Persistent<Context> context1 = Context::New(); - context1->Enter(); - - // Make easy access to the object from the other environment. - v8::Handle<v8::Object> global1 = context1->Global(); - global1->Set(v8_str("obj"), object); - - v8::Handle<Value> value; - - // Check that the named access-control function is called every time - // eventhough there is an interceptor on the object. - value = v8_compile("for (var i = 0; i < 10; i++) obj.x = 1;")->Run(); - value = v8_compile("for (var i = 0; i < 10; i++) obj.x;" - "obj.x")->Run(); - CHECK(value->IsNumber()); - CHECK_EQ(42, value->Int32Value()); - CHECK_EQ(21, named_access_count); - - value = v8_compile("var p = 'x';")->Run(); - value = v8_compile("for (var i = 0; i < 10; i++) obj[p] = 1;")->Run(); - value = v8_compile("for (var i = 0; i < 10; i++) obj[p];" - "obj[p]")->Run(); - CHECK(value->IsNumber()); - CHECK_EQ(42, value->Int32Value()); - CHECK_EQ(42, named_access_count); - - // Check that the indexed access-control function is called every - // time eventhough there is an interceptor on the object. - value = v8_compile("for (var i = 0; i < 10; i++) obj[0] = 1;")->Run(); - value = v8_compile("for (var i = 0; i < 10; i++) obj[0];" - "obj[0]")->Run(); - CHECK(value->IsNumber()); - CHECK_EQ(42, value->Int32Value()); - CHECK_EQ(21, indexed_access_count); - - context1->Exit(); - context0->Exit(); - context1.Dispose(context1->GetIsolate()); - context0.Dispose(context0->GetIsolate()); -} - - -THREADED_TEST(Version) { - v8::V8::GetVersion(); -} - - -static v8::Handle<Value> InstanceFunctionCallback(const v8::Arguments& args) { - ApiTestFuzzer::Fuzz(); - return v8_num(12); -} - - -THREADED_TEST(InstanceProperties) { - v8::HandleScope handle_scope; - LocalContext context; - - Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); - Local<ObjectTemplate> instance = t->InstanceTemplate(); - - instance->Set(v8_str("x"), v8_num(42)); - instance->Set(v8_str("f"), - v8::FunctionTemplate::New(InstanceFunctionCallback)); - - Local<Value> o = t->GetFunction()->NewInstance(); - - context->Global()->Set(v8_str("i"), o); - Local<Value> value = Script::Compile(v8_str("i.x"))->Run(); - CHECK_EQ(42, value->Int32Value()); - - value = Script::Compile(v8_str("i.f()"))->Run(); - CHECK_EQ(12, value->Int32Value()); -} - - -static v8::Handle<Value> -GlobalObjectInstancePropertiesGet(Local<String> key, const AccessorInfo&) { - ApiTestFuzzer::Fuzz(); - return v8::Handle<Value>(); -} - - -THREADED_TEST(GlobalObjectInstanceProperties) { - v8::HandleScope handle_scope; - - Local<Value> global_object; - - Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); - t->InstanceTemplate()->SetNamedPropertyHandler( - GlobalObjectInstancePropertiesGet); - Local<ObjectTemplate> instance_template = t->InstanceTemplate(); - instance_template->Set(v8_str("x"), v8_num(42)); - instance_template->Set(v8_str("f"), - v8::FunctionTemplate::New(InstanceFunctionCallback)); - - // The script to check how Crankshaft compiles missing global function - // invocations. function g is not defined and should throw on call. - const char* script = - "function wrapper(call) {" - " var x = 0, y = 1;" - " for (var i = 0; i < 1000; i++) {" - " x += i * 100;" - " y += i * 100;" - " }" - " if (call) g();" - "}" - "for (var i = 0; i < 17; i++) wrapper(false);" - "var thrown = 0;" - "try { wrapper(true); } catch (e) { thrown = 1; };" - "thrown"; - - { - LocalContext env(NULL, instance_template); - // Hold on to the global object so it can be used again in another - // environment initialization. - global_object = env->Global(); - - Local<Value> value = Script::Compile(v8_str("x"))->Run(); - CHECK_EQ(42, value->Int32Value()); - value = Script::Compile(v8_str("f()"))->Run(); - CHECK_EQ(12, value->Int32Value()); - value = Script::Compile(v8_str(script))->Run(); - CHECK_EQ(1, value->Int32Value()); - } - - { - // Create new environment reusing the global object. - LocalContext env(NULL, instance_template, global_object); - Local<Value> value = Script::Compile(v8_str("x"))->Run(); - CHECK_EQ(42, value->Int32Value()); - value = Script::Compile(v8_str("f()"))->Run(); - CHECK_EQ(12, value->Int32Value()); - value = Script::Compile(v8_str(script))->Run(); - CHECK_EQ(1, value->Int32Value()); - } -} - - -THREADED_TEST(CallKnownGlobalReceiver) { - v8::HandleScope handle_scope; - - Local<Value> global_object; - - Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); - Local<ObjectTemplate> instance_template = t->InstanceTemplate(); - - // The script to check that we leave global object not - // global object proxy on stack when we deoptimize from inside - // arguments evaluation. - // To provoke error we need to both force deoptimization - // from arguments evaluation and to force CallIC to take - // CallIC_Miss code path that can't cope with global proxy. - const char* script = - "function bar(x, y) { try { } finally { } }" - "function baz(x) { try { } finally { } }" - "function bom(x) { try { } finally { } }" - "function foo(x) { bar([x], bom(2)); }" - "for (var i = 0; i < 10000; i++) foo(1);" - "foo"; - - Local<Value> foo; - { - LocalContext env(NULL, instance_template); - // Hold on to the global object so it can be used again in another - // environment initialization. - global_object = env->Global(); - foo = Script::Compile(v8_str(script))->Run(); - } - - { - // Create new environment reusing the global object. - LocalContext env(NULL, instance_template, global_object); - env->Global()->Set(v8_str("foo"), foo); - Script::Compile(v8_str("foo()"))->Run(); - } -} - - -static v8::Handle<Value> ShadowFunctionCallback(const v8::Arguments& args) { - ApiTestFuzzer::Fuzz(); - return v8_num(42); -} - - -static int shadow_y; -static int shadow_y_setter_call_count; -static int shadow_y_getter_call_count; - - -static void ShadowYSetter(Local<String>, Local<Value>, const AccessorInfo&) { - shadow_y_setter_call_count++; - shadow_y = 42; -} - - -static v8::Handle<Value> ShadowYGetter(Local<String> name, - const AccessorInfo& info) { - ApiTestFuzzer::Fuzz(); - shadow_y_getter_call_count++; - return v8_num(shadow_y); -} - - -static v8::Handle<Value> ShadowIndexedGet(uint32_t index, - const AccessorInfo& info) { - return v8::Handle<Value>(); -} - - -static v8::Handle<Value> ShadowNamedGet(Local<String> key, - const AccessorInfo&) { - return v8::Handle<Value>(); -} - - -THREADED_TEST(ShadowObject) { - shadow_y = shadow_y_setter_call_count = shadow_y_getter_call_count = 0; - v8::HandleScope handle_scope; - - Local<ObjectTemplate> global_template = v8::ObjectTemplate::New(); - LocalContext context(NULL, global_template); - - Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); - t->InstanceTemplate()->SetNamedPropertyHandler(ShadowNamedGet); - t->InstanceTemplate()->SetIndexedPropertyHandler(ShadowIndexedGet); - Local<ObjectTemplate> proto = t->PrototypeTemplate(); - Local<ObjectTemplate> instance = t->InstanceTemplate(); - - proto->Set(v8_str("f"), - v8::FunctionTemplate::New(ShadowFunctionCallback, Local<Value>())); - proto->Set(v8_str("x"), v8_num(12)); - - instance->SetAccessor(v8_str("y"), ShadowYGetter, ShadowYSetter); - - Local<Value> o = t->GetFunction()->NewInstance(); - context->Global()->Set(v8_str("__proto__"), o); - - Local<Value> value = - Script::Compile(v8_str("this.propertyIsEnumerable(0)"))->Run(); - CHECK(value->IsBoolean()); - CHECK(!value->BooleanValue()); - - value = Script::Compile(v8_str("x"))->Run(); - CHECK_EQ(12, value->Int32Value()); - - value = Script::Compile(v8_str("f()"))->Run(); - CHECK_EQ(42, value->Int32Value()); - - Script::Compile(v8_str("y = 43"))->Run(); - CHECK_EQ(1, shadow_y_setter_call_count); - value = Script::Compile(v8_str("y"))->Run(); - CHECK_EQ(1, shadow_y_getter_call_count); - CHECK_EQ(42, value->Int32Value()); -} - - -THREADED_TEST(HiddenPrototype) { - v8::HandleScope handle_scope; - LocalContext context; - - Local<v8::FunctionTemplate> t0 = v8::FunctionTemplate::New(); - t0->InstanceTemplate()->Set(v8_str("x"), v8_num(0)); - Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New(); - t1->SetHiddenPrototype(true); - t1->InstanceTemplate()->Set(v8_str("y"), v8_num(1)); - Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New(); - t2->SetHiddenPrototype(true); - t2->InstanceTemplate()->Set(v8_str("z"), v8_num(2)); - Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New(); - t3->InstanceTemplate()->Set(v8_str("u"), v8_num(3)); - - Local<v8::Object> o0 = t0->GetFunction()->NewInstance(); - Local<v8::Object> o1 = t1->GetFunction()->NewInstance(); - Local<v8::Object> o2 = t2->GetFunction()->NewInstance(); - Local<v8::Object> o3 = t3->GetFunction()->NewInstance(); - - // Setting the prototype on an object skips hidden prototypes. - CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); - o0->Set(v8_str("__proto__"), o1); - CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); - CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); - o0->Set(v8_str("__proto__"), o2); - CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); - CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); - CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value()); - o0->Set(v8_str("__proto__"), o3); - CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); - CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); - CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value()); - CHECK_EQ(3, o0->Get(v8_str("u"))->Int32Value()); - - // Getting the prototype of o0 should get the first visible one - // which is o3. Therefore, z should not be defined on the prototype - // object. - Local<Value> proto = o0->Get(v8_str("__proto__")); - CHECK(proto->IsObject()); - CHECK(proto.As<v8::Object>()->Get(v8_str("z"))->IsUndefined()); -} - - -THREADED_TEST(HiddenPrototypeSet) { - v8::HandleScope handle_scope; - LocalContext context; - - Local<v8::FunctionTemplate> ot = v8::FunctionTemplate::New(); - Local<v8::FunctionTemplate> ht = v8::FunctionTemplate::New(); - ht->SetHiddenPrototype(true); - Local<v8::FunctionTemplate> pt = v8::FunctionTemplate::New(); - ht->InstanceTemplate()->Set(v8_str("x"), v8_num(0)); - - Local<v8::Object> o = ot->GetFunction()->NewInstance(); - Local<v8::Object> h = ht->GetFunction()->NewInstance(); - Local<v8::Object> p = pt->GetFunction()->NewInstance(); - o->Set(v8_str("__proto__"), h); - h->Set(v8_str("__proto__"), p); - - // Setting a property that exists on the hidden prototype goes there. - o->Set(v8_str("x"), v8_num(7)); - CHECK_EQ(7, o->Get(v8_str("x"))->Int32Value()); - CHECK_EQ(7, h->Get(v8_str("x"))->Int32Value()); - CHECK(p->Get(v8_str("x"))->IsUndefined()); - - // Setting a new property should not be forwarded to the hidden prototype. - o->Set(v8_str("y"), v8_num(6)); - CHECK_EQ(6, o->Get(v8_str("y"))->Int32Value()); - CHECK(h->Get(v8_str("y"))->IsUndefined()); - CHECK(p->Get(v8_str("y"))->IsUndefined()); - - // Setting a property that only exists on a prototype of the hidden prototype - // is treated normally again. - p->Set(v8_str("z"), v8_num(8)); - CHECK_EQ(8, o->Get(v8_str("z"))->Int32Value()); - CHECK_EQ(8, h->Get(v8_str("z"))->Int32Value()); - CHECK_EQ(8, p->Get(v8_str("z"))->Int32Value()); - o->Set(v8_str("z"), v8_num(9)); - CHECK_EQ(9, o->Get(v8_str("z"))->Int32Value()); - CHECK_EQ(8, h->Get(v8_str("z"))->Int32Value()); - CHECK_EQ(8, p->Get(v8_str("z"))->Int32Value()); -} - - -// Regression test for issue 2457. -THREADED_TEST(HiddenPrototypeIdentityHash) { - v8::HandleScope handle_scope; - LocalContext context; - - Handle<FunctionTemplate> t = FunctionTemplate::New(); - t->SetHiddenPrototype(true); - t->InstanceTemplate()->Set(v8_str("foo"), v8_num(75)); - Handle<Object> p = t->GetFunction()->NewInstance(); - Handle<Object> o = Object::New(); - o->SetPrototype(p); - - int hash = o->GetIdentityHash(); - USE(hash); - o->Set(v8_str("foo"), v8_num(42)); - ASSERT_EQ(hash, o->GetIdentityHash()); -} - - -THREADED_TEST(SetPrototype) { - v8::HandleScope handle_scope; - LocalContext context; - - Local<v8::FunctionTemplate> t0 = v8::FunctionTemplate::New(); - t0->InstanceTemplate()->Set(v8_str("x"), v8_num(0)); - Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New(); - t1->SetHiddenPrototype(true); - t1->InstanceTemplate()->Set(v8_str("y"), v8_num(1)); - Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New(); - t2->SetHiddenPrototype(true); - t2->InstanceTemplate()->Set(v8_str("z"), v8_num(2)); - Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New(); - t3->InstanceTemplate()->Set(v8_str("u"), v8_num(3)); - - Local<v8::Object> o0 = t0->GetFunction()->NewInstance(); - Local<v8::Object> o1 = t1->GetFunction()->NewInstance(); - Local<v8::Object> o2 = t2->GetFunction()->NewInstance(); - Local<v8::Object> o3 = t3->GetFunction()->NewInstance(); - - // Setting the prototype on an object does not skip hidden prototypes. - CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); - CHECK(o0->SetPrototype(o1)); - CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); - CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); - CHECK(o1->SetPrototype(o2)); - CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); - CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); - CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value()); - CHECK(o2->SetPrototype(o3)); - CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); - CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); - CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value()); - CHECK_EQ(3, o0->Get(v8_str("u"))->Int32Value()); - - // Getting the prototype of o0 should get the first visible one - // which is o3. Therefore, z should not be defined on the prototype - // object. - Local<Value> proto = o0->Get(v8_str("__proto__")); - CHECK(proto->IsObject()); - CHECK_EQ(proto.As<v8::Object>(), o3); - - // However, Object::GetPrototype ignores hidden prototype. - Local<Value> proto0 = o0->GetPrototype(); - CHECK(proto0->IsObject()); - CHECK_EQ(proto0.As<v8::Object>(), o1); - - Local<Value> proto1 = o1->GetPrototype(); - CHECK(proto1->IsObject()); - CHECK_EQ(proto1.As<v8::Object>(), o2); - - Local<Value> proto2 = o2->GetPrototype(); - CHECK(proto2->IsObject()); - CHECK_EQ(proto2.As<v8::Object>(), o3); -} - - -// Getting property names of an object with a prototype chain that -// triggers dictionary elements in GetLocalPropertyNames() shouldn't -// crash the runtime. -THREADED_TEST(Regress91517) { - i::FLAG_allow_natives_syntax = true; - v8::HandleScope handle_scope; - LocalContext context; - - Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New(); - t1->SetHiddenPrototype(true); - t1->InstanceTemplate()->Set(v8_str("foo"), v8_num(1)); - Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New(); - t2->SetHiddenPrototype(true); - t2->InstanceTemplate()->Set(v8_str("fuz1"), v8_num(2)); - t2->InstanceTemplate()->Set(v8_str("objects"), v8::Object::New()); - t2->InstanceTemplate()->Set(v8_str("fuz2"), v8_num(2)); - Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New(); - t3->SetHiddenPrototype(true); - t3->InstanceTemplate()->Set(v8_str("boo"), v8_num(3)); - Local<v8::FunctionTemplate> t4 = v8::FunctionTemplate::New(); - t4->InstanceTemplate()->Set(v8_str("baz"), v8_num(4)); - - // Force dictionary-based properties. - i::ScopedVector<char> name_buf(1024); - for (int i = 1; i <= 1000; i++) { - i::OS::SNPrintF(name_buf, "sdf%d", i); - t2->InstanceTemplate()->Set(v8_str(name_buf.start()), v8_num(2)); - } - - Local<v8::Object> o1 = t1->GetFunction()->NewInstance(); - Local<v8::Object> o2 = t2->GetFunction()->NewInstance(); - Local<v8::Object> o3 = t3->GetFunction()->NewInstance(); - Local<v8::Object> o4 = t4->GetFunction()->NewInstance(); - - // Create prototype chain of hidden prototypes. - CHECK(o4->SetPrototype(o3)); - CHECK(o3->SetPrototype(o2)); - CHECK(o2->SetPrototype(o1)); - - // Call the runtime version of GetLocalPropertyNames() on the natively - // created object through JavaScript. - context->Global()->Set(v8_str("obj"), o4); - CompileRun("var names = %GetLocalPropertyNames(obj);"); - - ExpectInt32("names.length", 1006); - ExpectTrue("names.indexOf(\"baz\") >= 0"); - ExpectTrue("names.indexOf(\"boo\") >= 0"); - ExpectTrue("names.indexOf(\"foo\") >= 0"); - ExpectTrue("names.indexOf(\"fuz1\") >= 0"); - ExpectTrue("names.indexOf(\"fuz2\") >= 0"); - ExpectFalse("names[1005] == undefined"); -} - - -THREADED_TEST(FunctionReadOnlyPrototype) { - v8::HandleScope handle_scope; - LocalContext context; - - Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New(); - t1->PrototypeTemplate()->Set(v8_str("x"), v8::Integer::New(42)); - t1->ReadOnlyPrototype(); - context->Global()->Set(v8_str("func1"), t1->GetFunction()); - // Configured value of ReadOnly flag. - CHECK(CompileRun( - "(function() {" - " descriptor = Object.getOwnPropertyDescriptor(func1, 'prototype');" - " return (descriptor['writable'] == false);" - "})()")->BooleanValue()); - CHECK_EQ(42, CompileRun("func1.prototype.x")->Int32Value()); - CHECK_EQ(42, - CompileRun("func1.prototype = {}; func1.prototype.x")->Int32Value()); - - Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New(); - t2->PrototypeTemplate()->Set(v8_str("x"), v8::Integer::New(42)); - context->Global()->Set(v8_str("func2"), t2->GetFunction()); - // Default value of ReadOnly flag. - CHECK(CompileRun( - "(function() {" - " descriptor = Object.getOwnPropertyDescriptor(func2, 'prototype');" - " return (descriptor['writable'] == true);" - "})()")->BooleanValue()); - CHECK_EQ(42, CompileRun("func2.prototype.x")->Int32Value()); -} - - -THREADED_TEST(SetPrototypeThrows) { - v8::HandleScope handle_scope; - LocalContext context; - - Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); - - Local<v8::Object> o0 = t->GetFunction()->NewInstance(); - Local<v8::Object> o1 = t->GetFunction()->NewInstance(); - - CHECK(o0->SetPrototype(o1)); - // If setting the prototype leads to the cycle, SetPrototype should - // return false and keep VM in sane state. - v8::TryCatch try_catch; - CHECK(!o1->SetPrototype(o0)); - CHECK(!try_catch.HasCaught()); - ASSERT(!i::Isolate::Current()->has_pending_exception()); - - CHECK_EQ(42, CompileRun("function f() { return 42; }; f()")->Int32Value()); -} - - -THREADED_TEST(GetterSetterExceptions) { - v8::HandleScope handle_scope; - LocalContext context; - CompileRun( - "function Foo() { };" - "function Throw() { throw 5; };" - "var x = { };" - "x.__defineSetter__('set', Throw);" - "x.__defineGetter__('get', Throw);"); - Local<v8::Object> x = - Local<v8::Object>::Cast(context->Global()->Get(v8_str("x"))); - v8::TryCatch try_catch; - x->Set(v8_str("set"), v8::Integer::New(8)); - x->Get(v8_str("get")); - x->Set(v8_str("set"), v8::Integer::New(8)); - x->Get(v8_str("get")); - x->Set(v8_str("set"), v8::Integer::New(8)); - x->Get(v8_str("get")); - x->Set(v8_str("set"), v8::Integer::New(8)); - x->Get(v8_str("get")); -} - - -THREADED_TEST(Constructor) { - v8::HandleScope handle_scope; - LocalContext context; - Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); - templ->SetClassName(v8_str("Fun")); - Local<Function> cons = templ->GetFunction(); - context->Global()->Set(v8_str("Fun"), cons); - Local<v8::Object> inst = cons->NewInstance(); - i::Handle<i::JSObject> obj(v8::Utils::OpenHandle(*inst)); - CHECK(obj->IsJSObject()); - Local<Value> value = CompileRun("(new Fun()).constructor === Fun"); - CHECK(value->BooleanValue()); -} - - -static Handle<Value> ConstructorCallback(const Arguments& args) { - ApiTestFuzzer::Fuzz(); - Local<Object> This; - - if (args.IsConstructCall()) { - Local<Object> Holder = args.Holder(); - This = Object::New(); - Local<Value> proto = Holder->GetPrototype(); - if (proto->IsObject()) { - This->SetPrototype(proto); - } - } else { - This = args.This(); - } - - This->Set(v8_str("a"), args[0]); - return This; -} - - -static Handle<Value> FakeConstructorCallback(const Arguments& args) { - ApiTestFuzzer::Fuzz(); - return args[0]; -} - - -THREADED_TEST(ConstructorForObject) { - v8::HandleScope handle_scope; - LocalContext context; - - { Local<ObjectTemplate> instance_template = ObjectTemplate::New(); - instance_template->SetCallAsFunctionHandler(ConstructorCallback); - Local<Object> instance = instance_template->NewInstance(); - context->Global()->Set(v8_str("obj"), instance); - v8::TryCatch try_catch; - Local<Value> value; - CHECK(!try_catch.HasCaught()); - - // Call the Object's constructor with a 32-bit signed integer. - value = CompileRun("(function() { var o = new obj(28); return o.a; })()"); - CHECK(!try_catch.HasCaught()); - CHECK(value->IsInt32()); - CHECK_EQ(28, value->Int32Value()); - - Local<Value> args1[] = { v8_num(28) }; - Local<Value> value_obj1 = instance->CallAsConstructor(1, args1); - CHECK(value_obj1->IsObject()); - Local<Object> object1 = Local<Object>::Cast(value_obj1); - value = object1->Get(v8_str("a")); - CHECK(value->IsInt32()); - CHECK(!try_catch.HasCaught()); - CHECK_EQ(28, value->Int32Value()); - - // Call the Object's constructor with a String. - value = CompileRun( - "(function() { var o = new obj('tipli'); return o.a; })()"); - CHECK(!try_catch.HasCaught()); - CHECK(value->IsString()); - String::AsciiValue string_value1(value->ToString()); - CHECK_EQ("tipli", *string_value1); - - Local<Value> args2[] = { v8_str("tipli") }; - Local<Value> value_obj2 = instance->CallAsConstructor(1, args2); - CHECK(value_obj2->IsObject()); - Local<Object> object2 = Local<Object>::Cast(value_obj2); - value = object2->Get(v8_str("a")); - CHECK(!try_catch.HasCaught()); - CHECK(value->IsString()); - String::AsciiValue string_value2(value->ToString()); - CHECK_EQ("tipli", *string_value2); - - // Call the Object's constructor with a Boolean. - value = CompileRun("(function() { var o = new obj(true); return o.a; })()"); - CHECK(!try_catch.HasCaught()); - CHECK(value->IsBoolean()); - CHECK_EQ(true, value->BooleanValue()); - - Handle<Value> args3[] = { v8::True() }; - Local<Value> value_obj3 = instance->CallAsConstructor(1, args3); - CHECK(value_obj3->IsObject()); - Local<Object> object3 = Local<Object>::Cast(value_obj3); - value = object3->Get(v8_str("a")); - CHECK(!try_catch.HasCaught()); - CHECK(value->IsBoolean()); - CHECK_EQ(true, value->BooleanValue()); - - // Call the Object's constructor with undefined. - Handle<Value> args4[] = { v8::Undefined() }; - Local<Value> value_obj4 = instance->CallAsConstructor(1, args4); - CHECK(value_obj4->IsObject()); - Local<Object> object4 = Local<Object>::Cast(value_obj4); - value = object4->Get(v8_str("a")); - CHECK(!try_catch.HasCaught()); - CHECK(value->IsUndefined()); - - // Call the Object's constructor with null. - Handle<Value> args5[] = { v8::Null() }; - Local<Value> value_obj5 = instance->CallAsConstructor(1, args5); - CHECK(value_obj5->IsObject()); - Local<Object> object5 = Local<Object>::Cast(value_obj5); - value = object5->Get(v8_str("a")); - CHECK(!try_catch.HasCaught()); - CHECK(value->IsNull()); - } - - // Check exception handling when there is no constructor set for the Object. - { Local<ObjectTemplate> instance_template = ObjectTemplate::New(); - Local<Object> instance = instance_template->NewInstance(); - context->Global()->Set(v8_str("obj2"), instance); - v8::TryCatch try_catch; - Local<Value> value; - CHECK(!try_catch.HasCaught()); - - value = CompileRun("new obj2(28)"); - CHECK(try_catch.HasCaught()); - String::AsciiValue exception_value1(try_catch.Exception()); - CHECK_EQ("TypeError: object is not a function", *exception_value1); - try_catch.Reset(); - - Local<Value> args[] = { v8_num(29) }; - value = instance->CallAsConstructor(1, args); - CHECK(try_catch.HasCaught()); - String::AsciiValue exception_value2(try_catch.Exception()); - CHECK_EQ("TypeError: #<Object> is not a function", *exception_value2); - try_catch.Reset(); - } - - // Check the case when constructor throws exception. - { Local<ObjectTemplate> instance_template = ObjectTemplate::New(); - instance_template->SetCallAsFunctionHandler(ThrowValue); - Local<Object> instance = instance_template->NewInstance(); - context->Global()->Set(v8_str("obj3"), instance); - v8::TryCatch try_catch; - Local<Value> value; - CHECK(!try_catch.HasCaught()); - - value = CompileRun("new obj3(22)"); - CHECK(try_catch.HasCaught()); - String::AsciiValue exception_value1(try_catch.Exception()); - CHECK_EQ("22", *exception_value1); - try_catch.Reset(); - - Local<Value> args[] = { v8_num(23) }; - value = instance->CallAsConstructor(1, args); - CHECK(try_catch.HasCaught()); - String::AsciiValue exception_value2(try_catch.Exception()); - CHECK_EQ("23", *exception_value2); - try_catch.Reset(); - } - - // Check whether constructor returns with an object or non-object. - { Local<FunctionTemplate> function_template = - FunctionTemplate::New(FakeConstructorCallback); - Local<Function> function = function_template->GetFunction(); - Local<Object> instance1 = function; - context->Global()->Set(v8_str("obj4"), instance1); - v8::TryCatch try_catch; - Local<Value> value; - CHECK(!try_catch.HasCaught()); - - CHECK(instance1->IsObject()); - CHECK(instance1->IsFunction()); - - value = CompileRun("new obj4(28)"); - CHECK(!try_catch.HasCaught()); - CHECK(value->IsObject()); - - Local<Value> args1[] = { v8_num(28) }; - value = instance1->CallAsConstructor(1, args1); - CHECK(!try_catch.HasCaught()); - CHECK(value->IsObject()); - - Local<ObjectTemplate> instance_template = ObjectTemplate::New(); - instance_template->SetCallAsFunctionHandler(FakeConstructorCallback); - Local<Object> instance2 = instance_template->NewInstance(); - context->Global()->Set(v8_str("obj5"), instance2); - CHECK(!try_catch.HasCaught()); - - CHECK(instance2->IsObject()); - CHECK(!instance2->IsFunction()); - - value = CompileRun("new obj5(28)"); - CHECK(!try_catch.HasCaught()); - CHECK(!value->IsObject()); - - Local<Value> args2[] = { v8_num(28) }; - value = instance2->CallAsConstructor(1, args2); - CHECK(!try_catch.HasCaught()); - CHECK(!value->IsObject()); - } -} - - -THREADED_TEST(FunctionDescriptorException) { - v8::HandleScope handle_scope; - LocalContext context; - Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); - templ->SetClassName(v8_str("Fun")); - Local<Function> cons = templ->GetFunction(); - context->Global()->Set(v8_str("Fun"), cons); - Local<Value> value = CompileRun( - "function test() {" - " try {" - " (new Fun()).blah()" - " } catch (e) {" - " var str = String(e);" - " if (str.indexOf('TypeError') == -1) return 1;" - " if (str.indexOf('[object Fun]') != -1) return 2;" - " if (str.indexOf('#<Fun>') == -1) return 3;" - " return 0;" - " }" - " return 4;" - "}" - "test();"); - CHECK_EQ(0, value->Int32Value()); -} - - -THREADED_TEST(EvalAliasedDynamic) { - v8::HandleScope scope; - LocalContext current; - - // Tests where aliased eval can only be resolved dynamically. - Local<Script> script = - Script::Compile(v8_str("function f(x) { " - " var foo = 2;" - " with (x) { return eval('foo'); }" - "}" - "foo = 0;" - "result1 = f(new Object());" - "result2 = f(this);" - "var x = new Object();" - "x.eval = function(x) { return 1; };" - "result3 = f(x);")); - script->Run(); - CHECK_EQ(2, current->Global()->Get(v8_str("result1"))->Int32Value()); - CHECK_EQ(0, current->Global()->Get(v8_str("result2"))->Int32Value()); - CHECK_EQ(1, current->Global()->Get(v8_str("result3"))->Int32Value()); - - v8::TryCatch try_catch; - script = - Script::Compile(v8_str("function f(x) { " - " var bar = 2;" - " with (x) { return eval('bar'); }" - "}" - "result4 = f(this)")); - script->Run(); - CHECK(!try_catch.HasCaught()); - CHECK_EQ(2, current->Global()->Get(v8_str("result4"))->Int32Value()); - - try_catch.Reset(); -} - - -THREADED_TEST(CrossEval) { - v8::HandleScope scope; - LocalContext other; - LocalContext current; - - Local<String> token = v8_str("<security token>"); - other->SetSecurityToken(token); - current->SetSecurityToken(token); - - // Set up reference from current to other. - current->Global()->Set(v8_str("other"), other->Global()); - - // Check that new variables are introduced in other context. - Local<Script> script = - Script::Compile(v8_str("other.eval('var foo = 1234')")); - script->Run(); - Local<Value> foo = other->Global()->Get(v8_str("foo")); - CHECK_EQ(1234, foo->Int32Value()); - CHECK(!current->Global()->Has(v8_str("foo"))); - - // Check that writing to non-existing properties introduces them in - // the other context. - script = - Script::Compile(v8_str("other.eval('na = 1234')")); - script->Run(); - CHECK_EQ(1234, other->Global()->Get(v8_str("na"))->Int32Value()); - CHECK(!current->Global()->Has(v8_str("na"))); - - // Check that global variables in current context are not visible in other - // context. - v8::TryCatch try_catch; - script = - Script::Compile(v8_str("var bar = 42; other.eval('bar');")); - Local<Value> result = script->Run(); - CHECK(try_catch.HasCaught()); - try_catch.Reset(); - - // Check that local variables in current context are not visible in other - // context. - script = - Script::Compile(v8_str("(function() { " - " var baz = 87;" - " return other.eval('baz');" - "})();")); - result = script->Run(); - CHECK(try_catch.HasCaught()); - try_catch.Reset(); - - // Check that global variables in the other environment are visible - // when evaluting code. - other->Global()->Set(v8_str("bis"), v8_num(1234)); - script = Script::Compile(v8_str("other.eval('bis')")); - CHECK_EQ(1234, script->Run()->Int32Value()); - CHECK(!try_catch.HasCaught()); - - // Check that the 'this' pointer points to the global object evaluating - // code. - other->Global()->Set(v8_str("t"), other->Global()); - script = Script::Compile(v8_str("other.eval('this == t')")); - result = script->Run(); - CHECK(result->IsTrue()); - CHECK(!try_catch.HasCaught()); - - // Check that variables introduced in with-statement are not visible in - // other context. - script = - Script::Compile(v8_str("with({x:2}){other.eval('x')}")); - result = script->Run(); - CHECK(try_catch.HasCaught()); - try_catch.Reset(); - - // Check that you cannot use 'eval.call' with another object than the - // current global object. - script = - Script::Compile(v8_str("other.y = 1; eval.call(other, 'y')")); - result = script->Run(); - CHECK(try_catch.HasCaught()); -} - - -// 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(); - - // Set up 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(context1->GetIsolate()); - context0.Dispose(context0->GetIsolate()); -} - - -THREADED_TEST(CrossLazyLoad) { - v8::HandleScope scope; - LocalContext other; - LocalContext current; - - Local<String> token = v8_str("<security token>"); - other->SetSecurityToken(token); - current->SetSecurityToken(token); - - // Set up reference from current to other. - current->Global()->Set(v8_str("other"), other->Global()); - - // Trigger lazy loading in other context. - Local<Script> script = - Script::Compile(v8_str("other.eval('new Date(42)')")); - Local<Value> value = script->Run(); - CHECK_EQ(42.0, value->NumberValue()); -} - - -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]; -} - - -// Test that a call handler can be set for objects which will allow -// non-function objects created through the API to be called as -// functions. -THREADED_TEST(CallAsFunction) { - v8::HandleScope scope; - LocalContext context; - - { Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); - Local<ObjectTemplate> instance_template = t->InstanceTemplate(); - instance_template->SetCallAsFunctionHandler(call_as_function); - Local<v8::Object> instance = t->GetFunction()->NewInstance(); - context->Global()->Set(v8_str("obj"), instance); - v8::TryCatch try_catch; - Local<Value> value; - CHECK(!try_catch.HasCaught()); - - value = CompileRun("obj(42)"); - CHECK(!try_catch.HasCaught()); - CHECK_EQ(42, value->Int32Value()); - - value = CompileRun("(function(o){return o(49)})(obj)"); - CHECK(!try_catch.HasCaught()); - CHECK_EQ(49, value->Int32Value()); - - // test special case of call as function - value = CompileRun("[obj]['0'](45)"); - CHECK(!try_catch.HasCaught()); - CHECK_EQ(45, value->Int32Value()); - - value = CompileRun("obj.call = Function.prototype.call;" - "obj.call(null, 87)"); - CHECK(!try_catch.HasCaught()); - CHECK_EQ(87, value->Int32Value()); - - // Regression tests for bug #1116356: Calling call through call/apply - // must work for non-function receivers. - const char* apply_99 = "Function.prototype.call.apply(obj, [this, 99])"; - value = CompileRun(apply_99); - CHECK(!try_catch.HasCaught()); - CHECK_EQ(99, value->Int32Value()); - - const char* call_17 = "Function.prototype.call.call(obj, this, 17)"; - value = CompileRun(call_17); - CHECK(!try_catch.HasCaught()); - CHECK_EQ(17, value->Int32Value()); - - // Check that the call-as-function handler can be called through - // new. - value = CompileRun("new obj(43)"); - CHECK(!try_catch.HasCaught()); - CHECK_EQ(-43, value->Int32Value()); - - // Check that the call-as-function handler can be called through - // the API. - v8::Handle<Value> args[] = { v8_num(28) }; - value = instance->CallAsFunction(instance, 1, args); - CHECK(!try_catch.HasCaught()); - CHECK_EQ(28, value->Int32Value()); - } - - { Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); - Local<ObjectTemplate> instance_template(t->InstanceTemplate()); - USE(instance_template); - Local<v8::Object> instance = t->GetFunction()->NewInstance(); - context->Global()->Set(v8_str("obj2"), instance); - v8::TryCatch try_catch; - Local<Value> value; - CHECK(!try_catch.HasCaught()); - - // Call an object without call-as-function handler through the JS - value = CompileRun("obj2(28)"); - CHECK(value.IsEmpty()); - CHECK(try_catch.HasCaught()); - String::AsciiValue exception_value1(try_catch.Exception()); - CHECK_EQ("TypeError: Property 'obj2' of object #<Object> is not a function", - *exception_value1); - try_catch.Reset(); - - // Call an object without call-as-function handler through the API - value = CompileRun("obj2(28)"); - v8::Handle<Value> args[] = { v8_num(28) }; - value = instance->CallAsFunction(instance, 1, args); - CHECK(value.IsEmpty()); - CHECK(try_catch.HasCaught()); - String::AsciiValue exception_value2(try_catch.Exception()); - CHECK_EQ("TypeError: [object Object] is not a function", *exception_value2); - try_catch.Reset(); - } - - { Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); - Local<ObjectTemplate> instance_template = t->InstanceTemplate(); - instance_template->SetCallAsFunctionHandler(ThrowValue); - Local<v8::Object> instance = t->GetFunction()->NewInstance(); - context->Global()->Set(v8_str("obj3"), instance); - v8::TryCatch try_catch; - Local<Value> value; - CHECK(!try_catch.HasCaught()); - - // Catch the exception which is thrown by call-as-function handler - value = CompileRun("obj3(22)"); - CHECK(try_catch.HasCaught()); - String::AsciiValue exception_value1(try_catch.Exception()); - CHECK_EQ("22", *exception_value1); - try_catch.Reset(); - - v8::Handle<Value> args[] = { v8_num(23) }; - value = instance->CallAsFunction(instance, 1, args); - CHECK(try_catch.HasCaught()); - String::AsciiValue exception_value2(try_catch.Exception()); - CHECK_EQ("23", *exception_value2); - try_catch.Reset(); - } -} - - -// Check whether a non-function object is callable. -THREADED_TEST(CallableObject) { - v8::HandleScope scope; - LocalContext context; - - { Local<ObjectTemplate> instance_template = ObjectTemplate::New(); - instance_template->SetCallAsFunctionHandler(call_as_function); - Local<Object> instance = instance_template->NewInstance(); - v8::TryCatch try_catch; - - CHECK(instance->IsCallable()); - CHECK(!try_catch.HasCaught()); - } - - { Local<ObjectTemplate> instance_template = ObjectTemplate::New(); - Local<Object> instance = instance_template->NewInstance(); - v8::TryCatch try_catch; - - CHECK(!instance->IsCallable()); - CHECK(!try_catch.HasCaught()); - } - - { Local<FunctionTemplate> function_template = - FunctionTemplate::New(call_as_function); - Local<Function> function = function_template->GetFunction(); - Local<Object> instance = function; - v8::TryCatch try_catch; - - CHECK(instance->IsCallable()); - CHECK(!try_catch.HasCaught()); - } - - { Local<FunctionTemplate> function_template = FunctionTemplate::New(); - Local<Function> function = function_template->GetFunction(); - Local<Object> instance = function; - v8::TryCatch try_catch; - - CHECK(instance->IsCallable()); - CHECK(!try_catch.HasCaught()); - } -} - - -static int CountHandles() { - return v8::HandleScope::NumberOfHandles(); -} - - -static int Recurse(int depth, int iterations) { - v8::HandleScope scope; - if (depth == 0) return CountHandles(); - for (int i = 0; i < iterations; i++) { - Local<v8::Number> n(v8::Integer::New(42)); - } - return Recurse(depth - 1, iterations); -} - - -THREADED_TEST(HandleIteration) { - static const int kIterations = 500; - static const int kNesting = 200; - CHECK_EQ(0, CountHandles()); - { - v8::HandleScope scope1; - CHECK_EQ(0, CountHandles()); - for (int i = 0; i < kIterations; i++) { - Local<v8::Number> n(v8::Integer::New(42)); - CHECK_EQ(i + 1, CountHandles()); - } - - CHECK_EQ(kIterations, CountHandles()); - { - v8::HandleScope scope2; - for (int j = 0; j < kIterations; j++) { - Local<v8::Number> n(v8::Integer::New(42)); - CHECK_EQ(j + 1 + kIterations, CountHandles()); - } - } - CHECK_EQ(kIterations, CountHandles()); - } - CHECK_EQ(0, CountHandles()); - CHECK_EQ(kNesting * kIterations, Recurse(kNesting, kIterations)); -} - - -static v8::Handle<Value> InterceptorHasOwnPropertyGetter( - Local<String> name, - const AccessorInfo& info) { - ApiTestFuzzer::Fuzz(); - return v8::Handle<Value>(); -} - - -THREADED_TEST(InterceptorHasOwnProperty) { - v8::HandleScope scope; - LocalContext context; - Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); - Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate(); - instance_templ->SetNamedPropertyHandler(InterceptorHasOwnPropertyGetter); - Local<Function> function = fun_templ->GetFunction(); - context->Global()->Set(v8_str("constructor"), function); - v8::Handle<Value> value = CompileRun( - "var o = new constructor();" - "o.hasOwnProperty('ostehaps');"); - CHECK_EQ(false, value->BooleanValue()); - value = CompileRun( - "o.ostehaps = 42;" - "o.hasOwnProperty('ostehaps');"); - CHECK_EQ(true, value->BooleanValue()); - value = CompileRun( - "var p = new constructor();" - "p.hasOwnProperty('ostehaps');"); - CHECK_EQ(false, value->BooleanValue()); -} - - -static v8::Handle<Value> InterceptorHasOwnPropertyGetterGC( - Local<String> name, - const AccessorInfo& info) { - ApiTestFuzzer::Fuzz(); - HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); - return v8::Handle<Value>(); -} - - -THREADED_TEST(InterceptorHasOwnPropertyCausingGC) { - v8::HandleScope scope; - LocalContext context; - Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); - Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate(); - instance_templ->SetNamedPropertyHandler(InterceptorHasOwnPropertyGetterGC); - Local<Function> function = fun_templ->GetFunction(); - context->Global()->Set(v8_str("constructor"), function); - // Let's first make some stuff so we can be sure to get a good GC. - CompileRun( - "function makestr(size) {" - " switch (size) {" - " case 1: return 'f';" - " case 2: return 'fo';" - " case 3: return 'foo';" - " }" - " return makestr(size >> 1) + makestr((size + 1) >> 1);" - "}" - "var x = makestr(12345);" - "x = makestr(31415);" - "x = makestr(23456);"); - v8::Handle<Value> value = CompileRun( - "var o = new constructor();" - "o.__proto__ = new String(x);" - "o.hasOwnProperty('ostehaps');"); - CHECK_EQ(false, value->BooleanValue()); -} - - -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, 0, 0, 0, 0, v8_str("data")); - 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(); - v8::Isolate* isolate = v8::Isolate::GetCurrent(); - CHECK_EQ(isolate, info.GetIsolate()); - CHECK_EQ(v8_str("data"), info.Data()); - CHECK_EQ(v8_str("x"), name); - return v8::Integer::New(42); -} - - -// This test should hit the load IC for the interceptor case. -THREADED_TEST(InterceptorLoadIC) { - CheckInterceptorLoadIC(InterceptorLoadICGetter, - "var result = 0;" - "for (var i = 0; i < 1000; i++) {" - " result = o.x;" - "}", - 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); -} - - -// Test the case when we stored field into -// a stub, but interceptor produced value on its own. -THREADED_TEST(InterceptorLoadICFieldNotNeeded) { - CheckInterceptorLoadIC(InterceptorLoadXICGetter, - "proto = new Object();" - "o.__proto__ = proto;" - "proto.x = 239;" - "for (var i = 0; i < 1000; i++) {" - " o.x;" - // Now it should be ICed and keep a reference to x defined on proto - "}" - "var result = 0;" - "for (var i = 0; i < 1000; i++) {" - " result += o.x;" - "}" - "result;", - 42 * 1000); -} - - -// Test the case when we stored field into -// a stub, but it got invalidated later on. -THREADED_TEST(InterceptorLoadICInvalidatedField) { - CheckInterceptorLoadIC(InterceptorLoadXICGetter, - "proto1 = new Object();" - "proto2 = new Object();" - "o.__proto__ = proto1;" - "proto1.__proto__ = proto2;" - "proto2.y = 239;" - "for (var i = 0; i < 1000; i++) {" - " o.y;" - // Now it should be ICed and keep a reference to y defined on proto2 - "}" - "proto1.y = 42;" - "var result = 0;" - "for (var i = 0; i < 1000; i++) {" - " result += o.y;" - "}" - "result;", - 42 * 1000); -} - - -static int interceptor_load_not_handled_calls = 0; -static v8::Handle<Value> InterceptorLoadNotHandled(Local<String> name, - const AccessorInfo& info) { - ++interceptor_load_not_handled_calls; - return v8::Handle<v8::Value>(); -} - - -// Test how post-interceptor lookups are done in the non-cacheable -// case: the interceptor should not be invoked during this lookup. -THREADED_TEST(InterceptorLoadICPostInterceptor) { - interceptor_load_not_handled_calls = 0; - CheckInterceptorLoadIC(InterceptorLoadNotHandled, - "receiver = new Object();" - "receiver.__proto__ = o;" - "proto = new Object();" - "/* Make proto a slow-case object. */" - "for (var i = 0; i < 1000; i++) {" - " proto[\"xxxxxxxx\" + i] = [];" - "}" - "proto.x = 17;" - "o.__proto__ = proto;" - "var result = 0;" - "for (var i = 0; i < 1000; i++) {" - " result += receiver.x;" - "}" - "result;", - 17 * 1000); - CHECK_EQ(1000, interceptor_load_not_handled_calls); -} - - -// Test the case when we stored field into -// a stub, but it got invalidated later on due to override on -// global object which is between interceptor and fields' holders. -THREADED_TEST(InterceptorLoadICInvalidatedFieldViaGlobal) { - CheckInterceptorLoadIC(InterceptorLoadXICGetter, - "o.__proto__ = this;" // set a global to be a proto of o. - "this.__proto__.y = 239;" - "for (var i = 0; i < 10; i++) {" - " if (o.y != 239) throw 'oops: ' + o.y;" - // Now it should be ICed and keep a reference to y defined on field_holder. - "}" - "this.y = 42;" // Assign on a global. - "var result = 0;" - "for (var i = 0; i < 10; i++) {" - " result += o.y;" - "}" - "result;", - 42 * 10); -} - - -static void SetOnThis(Local<String> name, - Local<Value> value, - const AccessorInfo& info) { - info.This()->ForceSet(name, value); -} - - -THREADED_TEST(InterceptorLoadICWithCallbackOnHolder) { - v8::HandleScope scope; - v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); - templ->SetNamedPropertyHandler(InterceptorLoadXICGetter); - templ->SetAccessor(v8_str("y"), Return239); - LocalContext context; - context->Global()->Set(v8_str("o"), templ->NewInstance()); - - // Check the case when receiver and interceptor's holder - // are the same objects. - v8::Handle<Value> value = CompileRun( - "var result = 0;" - "for (var i = 0; i < 7; i++) {" - " result = o.y;" - "}"); - CHECK_EQ(239, value->Int32Value()); - - // Check the case when interceptor's holder is in proto chain - // of receiver. - value = CompileRun( - "r = { __proto__: o };" - "var result = 0;" - "for (var i = 0; i < 7; i++) {" - " result = r.y;" - "}"); - CHECK_EQ(239, value->Int32Value()); -} - - -THREADED_TEST(InterceptorLoadICWithCallbackOnProto) { - v8::HandleScope scope; - v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); - templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter); - v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New(); - templ_p->SetAccessor(v8_str("y"), Return239); - - LocalContext context; - context->Global()->Set(v8_str("o"), templ_o->NewInstance()); - context->Global()->Set(v8_str("p"), templ_p->NewInstance()); - - // Check the case when receiver and interceptor's holder - // are the same objects. - v8::Handle<Value> value = CompileRun( - "o.__proto__ = p;" - "var result = 0;" - "for (var i = 0; i < 7; i++) {" - " result = o.x + o.y;" - "}"); - CHECK_EQ(239 + 42, value->Int32Value()); - - // Check the case when interceptor's holder is in proto chain - // of receiver. - value = CompileRun( - "r = { __proto__: o };" - "var result = 0;" - "for (var i = 0; i < 7; i++) {" - " result = r.x + r.y;" - "}"); - CHECK_EQ(239 + 42, value->Int32Value()); -} - - -THREADED_TEST(InterceptorLoadICForCallbackWithOverride) { - v8::HandleScope scope; - v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); - templ->SetNamedPropertyHandler(InterceptorLoadXICGetter); - templ->SetAccessor(v8_str("y"), Return239); - - LocalContext context; - context->Global()->Set(v8_str("o"), templ->NewInstance()); - - v8::Handle<Value> value = CompileRun( - "fst = new Object(); fst.__proto__ = o;" - "snd = new Object(); snd.__proto__ = fst;" - "var result1 = 0;" - "for (var i = 0; i < 7; i++) {" - " result1 = snd.x;" - "}" - "fst.x = 239;" - "var result = 0;" - "for (var i = 0; i < 7; i++) {" - " result = snd.x;" - "}" - "result + result1"); - CHECK_EQ(239 + 42, value->Int32Value()); -} - - -// Test the case when we stored callback into -// a stub, but interceptor produced value on its own. -THREADED_TEST(InterceptorLoadICCallbackNotNeeded) { - v8::HandleScope scope; - v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); - templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter); - v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New(); - templ_p->SetAccessor(v8_str("y"), Return239); - - LocalContext context; - context->Global()->Set(v8_str("o"), templ_o->NewInstance()); - context->Global()->Set(v8_str("p"), templ_p->NewInstance()); - - v8::Handle<Value> value = CompileRun( - "o.__proto__ = p;" - "for (var i = 0; i < 7; i++) {" - " o.x;" - // Now it should be ICed and keep a reference to x defined on p - "}" - "var result = 0;" - "for (var i = 0; i < 7; i++) {" - " result += o.x;" - "}" - "result"); - CHECK_EQ(42 * 7, value->Int32Value()); -} - - -// Test the case when we stored callback into -// a stub, but it got invalidated later on. -THREADED_TEST(InterceptorLoadICInvalidatedCallback) { - v8::HandleScope scope; - v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); - templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter); - v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New(); - templ_p->SetAccessor(v8_str("y"), Return239, SetOnThis); - - LocalContext context; - context->Global()->Set(v8_str("o"), templ_o->NewInstance()); - context->Global()->Set(v8_str("p"), templ_p->NewInstance()); - - v8::Handle<Value> value = CompileRun( - "inbetween = new Object();" - "o.__proto__ = inbetween;" - "inbetween.__proto__ = p;" - "for (var i = 0; i < 10; i++) {" - " o.y;" - // Now it should be ICed and keep a reference to y defined on p - "}" - "inbetween.y = 42;" - "var result = 0;" - "for (var i = 0; i < 10; i++) {" - " result += o.y;" - "}" - "result"); - CHECK_EQ(42 * 10, value->Int32Value()); -} - - -// Test the case when we stored callback into -// a stub, but it got invalidated later on due to override on -// global object which is between interceptor and callbacks' holders. -THREADED_TEST(InterceptorLoadICInvalidatedCallbackViaGlobal) { - v8::HandleScope scope; - v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); - templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter); - v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New(); - templ_p->SetAccessor(v8_str("y"), Return239, SetOnThis); - - LocalContext context; - context->Global()->Set(v8_str("o"), templ_o->NewInstance()); - context->Global()->Set(v8_str("p"), templ_p->NewInstance()); - - v8::Handle<Value> value = CompileRun( - "o.__proto__ = this;" - "this.__proto__ = p;" - "for (var i = 0; i < 10; i++) {" - " if (o.y != 239) throw 'oops: ' + o.y;" - // Now it should be ICed and keep a reference to y defined on p - "}" - "this.y = 42;" - "var result = 0;" - "for (var i = 0; i < 10; i++) {" - " result += o.y;" - "}" - "result"); - CHECK_EQ(42 * 10, value->Int32Value()); -} - - -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); -} - - -static v8::Handle<Value> InterceptorStoreICSetter( - Local<String> key, Local<Value> value, const AccessorInfo&) { - CHECK(v8_str("x")->Equals(key)); - CHECK_EQ(42, value->Int32Value()); - return value; -} - - -// This test should hit the store IC for the interceptor case. -THREADED_TEST(InterceptorStoreIC) { - v8::HandleScope scope; - v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); - templ->SetNamedPropertyHandler(InterceptorLoadICGetter, - InterceptorStoreICSetter, - 0, 0, 0, v8_str("data")); - LocalContext context; - context->Global()->Set(v8_str("o"), templ->NewInstance()); - CompileRun( - "for (var i = 0; i < 1000; i++) {" - " o.x = 42;" - "}"); -} - - -THREADED_TEST(InterceptorStoreICWithNoSetter) { - v8::HandleScope scope; - v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); - templ->SetNamedPropertyHandler(InterceptorLoadXICGetter); - LocalContext context; - context->Global()->Set(v8_str("o"), templ->NewInstance()); - v8::Handle<Value> value = CompileRun( - "for (var i = 0; i < 1000; i++) {" - " o.y = 239;" - "}" - "42 + o.y"); - CHECK_EQ(239 + 42, value->Int32Value()); -} - - - - -v8::Handle<Value> call_ic_function; -v8::Handle<Value> call_ic_function2; -v8::Handle<Value> call_ic_function3; - -static v8::Handle<Value> InterceptorCallICGetter(Local<String> name, - const AccessorInfo& info) { - ApiTestFuzzer::Fuzz(); - CHECK(v8_str("x")->Equals(name)); - return call_ic_function; -} - - -// This test should hit the call IC for the interceptor case. -THREADED_TEST(InterceptorCallIC) { - v8::HandleScope scope; - v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); - templ->SetNamedPropertyHandler(InterceptorCallICGetter); - LocalContext context; - context->Global()->Set(v8_str("o"), templ->NewInstance()); - call_ic_function = - v8_compile("function f(x) { return x + 1; }; f")->Run(); - v8::Handle<Value> value = CompileRun( - "var result = 0;" - "for (var i = 0; i < 1000; i++) {" - " result = o.x(41);" - "}"); - CHECK_EQ(42, value->Int32Value()); -} - - -// This test checks that if interceptor doesn't provide -// a value, we can fetch regular value. -THREADED_TEST(InterceptorCallICSeesOthers) { - v8::HandleScope scope; - v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); - templ->SetNamedPropertyHandler(NoBlockGetterX); - LocalContext context; - context->Global()->Set(v8_str("o"), templ->NewInstance()); - v8::Handle<Value> value = CompileRun( - "o.x = function f(x) { return x + 1; };" - "var result = 0;" - "for (var i = 0; i < 7; i++) {" - " result = o.x(41);" - "}"); - CHECK_EQ(42, value->Int32Value()); -} - - -static v8::Handle<Value> call_ic_function4; -static v8::Handle<Value> InterceptorCallICGetter4(Local<String> name, - const AccessorInfo& info) { - ApiTestFuzzer::Fuzz(); - CHECK(v8_str("x")->Equals(name)); - return call_ic_function4; -} - - -// This test checks that if interceptor provides a function, -// even if we cached shadowed variant, interceptor's function -// is invoked -THREADED_TEST(InterceptorCallICCacheableNotNeeded) { - v8::HandleScope scope; - v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); - templ->SetNamedPropertyHandler(InterceptorCallICGetter4); - LocalContext context; - context->Global()->Set(v8_str("o"), templ->NewInstance()); - call_ic_function4 = - v8_compile("function f(x) { return x - 1; }; f")->Run(); - v8::Handle<Value> value = CompileRun( - "Object.getPrototypeOf(o).x = function(x) { return x + 1; };" - "var result = 0;" - "for (var i = 0; i < 1000; i++) {" - " result = o.x(42);" - "}"); - CHECK_EQ(41, value->Int32Value()); -} - - -// Test the case when we stored cacheable lookup into -// a stub, but it got invalidated later on -THREADED_TEST(InterceptorCallICInvalidatedCacheable) { - v8::HandleScope scope; - v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); - templ->SetNamedPropertyHandler(NoBlockGetterX); - LocalContext context; - context->Global()->Set(v8_str("o"), templ->NewInstance()); - v8::Handle<Value> value = CompileRun( - "proto1 = new Object();" - "proto2 = new Object();" - "o.__proto__ = proto1;" - "proto1.__proto__ = proto2;" - "proto2.y = function(x) { return x + 1; };" - // Invoke it many times to compile a stub - "for (var i = 0; i < 7; i++) {" - " o.y(42);" - "}" - "proto1.y = function(x) { return x - 1; };" - "var result = 0;" - "for (var i = 0; i < 7; i++) {" - " result += o.y(42);" - "}"); - CHECK_EQ(41 * 7, value->Int32Value()); -} - - -// This test checks that if interceptor doesn't provide a function, -// cached constant function is used -THREADED_TEST(InterceptorCallICConstantFunctionUsed) { - v8::HandleScope scope; - v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); - templ->SetNamedPropertyHandler(NoBlockGetterX); - LocalContext context; - context->Global()->Set(v8_str("o"), templ->NewInstance()); - v8::Handle<Value> value = CompileRun( - "function inc(x) { return x + 1; };" - "inc(1);" - "o.x = inc;" - "var result = 0;" - "for (var i = 0; i < 1000; i++) {" - " result = o.x(42);" - "}"); - CHECK_EQ(43, value->Int32Value()); -} - - -static v8::Handle<Value> call_ic_function5; -static v8::Handle<Value> InterceptorCallICGetter5(Local<String> name, - const AccessorInfo& info) { - ApiTestFuzzer::Fuzz(); - if (v8_str("x")->Equals(name)) - return call_ic_function5; - else - return Local<Value>(); -} - - -// This test checks that if interceptor provides a function, -// even if we cached constant function, interceptor's function -// is invoked -THREADED_TEST(InterceptorCallICConstantFunctionNotNeeded) { - v8::HandleScope scope; - v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); - templ->SetNamedPropertyHandler(InterceptorCallICGetter5); - LocalContext context; - context->Global()->Set(v8_str("o"), templ->NewInstance()); - call_ic_function5 = - v8_compile("function f(x) { return x - 1; }; f")->Run(); - v8::Handle<Value> value = CompileRun( - "function inc(x) { return x + 1; };" - "inc(1);" - "o.x = inc;" - "var result = 0;" - "for (var i = 0; i < 1000; i++) {" - " result = o.x(42);" - "}"); - CHECK_EQ(41, value->Int32Value()); -} - - -static v8::Handle<Value> call_ic_function6; -static v8::Handle<Value> InterceptorCallICGetter6(Local<String> name, - const AccessorInfo& info) { - ApiTestFuzzer::Fuzz(); - if (v8_str("x")->Equals(name)) - return call_ic_function6; - else - return Local<Value>(); -} - - -// Same test as above, except the code is wrapped in a function -// to test the optimized compiler. -THREADED_TEST(InterceptorCallICConstantFunctionNotNeededWrapped) { - i::FLAG_allow_natives_syntax = true; - v8::HandleScope scope; - v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); - templ->SetNamedPropertyHandler(InterceptorCallICGetter6); - LocalContext context; - context->Global()->Set(v8_str("o"), templ->NewInstance()); - call_ic_function6 = - v8_compile("function f(x) { return x - 1; }; f")->Run(); - v8::Handle<Value> value = CompileRun( - "function inc(x) { return x + 1; };" - "inc(1);" - "o.x = inc;" - "function test() {" - " var result = 0;" - " for (var i = 0; i < 1000; i++) {" - " result = o.x(42);" - " }" - " return result;" - "};" - "test();" - "test();" - "test();" - "%OptimizeFunctionOnNextCall(test);" - "test()"); - CHECK_EQ(41, value->Int32Value()); -} - - -// Test the case when we stored constant function into -// a stub, but it got invalidated later on -THREADED_TEST(InterceptorCallICInvalidatedConstantFunction) { - v8::HandleScope scope; - v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); - templ->SetNamedPropertyHandler(NoBlockGetterX); - LocalContext context; - context->Global()->Set(v8_str("o"), templ->NewInstance()); - v8::Handle<Value> value = CompileRun( - "function inc(x) { return x + 1; };" - "inc(1);" - "proto1 = new Object();" - "proto2 = new Object();" - "o.__proto__ = proto1;" - "proto1.__proto__ = proto2;" - "proto2.y = inc;" - // Invoke it many times to compile a stub - "for (var i = 0; i < 7; i++) {" - " o.y(42);" - "}" - "proto1.y = function(x) { return x - 1; };" - "var result = 0;" - "for (var i = 0; i < 7; i++) {" - " result += o.y(42);" - "}"); - CHECK_EQ(41 * 7, value->Int32Value()); -} - - -// Test the case when we stored constant function into -// a stub, but it got invalidated later on due to override on -// global object which is between interceptor and constant function' holders. -THREADED_TEST(InterceptorCallICInvalidatedConstantFunctionViaGlobal) { - v8::HandleScope scope; - v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); - templ->SetNamedPropertyHandler(NoBlockGetterX); - LocalContext context; - context->Global()->Set(v8_str("o"), templ->NewInstance()); - v8::Handle<Value> value = CompileRun( - "function inc(x) { return x + 1; };" - "inc(1);" - "o.__proto__ = this;" - "this.__proto__.y = inc;" - // Invoke it many times to compile a stub - "for (var i = 0; i < 7; i++) {" - " if (o.y(42) != 43) throw 'oops: ' + o.y(42);" - "}" - "this.y = function(x) { return x - 1; };" - "var result = 0;" - "for (var i = 0; i < 7; i++) {" - " result += o.y(42);" - "}"); - CHECK_EQ(41 * 7, value->Int32Value()); -} - - -// Test the case when actual function to call sits on global object. -THREADED_TEST(InterceptorCallICCachedFromGlobal) { - v8::HandleScope scope; - v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); - templ_o->SetNamedPropertyHandler(NoBlockGetterX); - - LocalContext context; - context->Global()->Set(v8_str("o"), templ_o->NewInstance()); - - v8::Handle<Value> value = CompileRun( - "try {" - " o.__proto__ = this;" - " for (var i = 0; i < 10; i++) {" - " var v = o.parseFloat('239');" - " if (v != 239) throw v;" - // Now it should be ICed and keep a reference to parseFloat. - " }" - " var result = 0;" - " for (var i = 0; i < 10; i++) {" - " result += o.parseFloat('239');" - " }" - " result" - "} catch(e) {" - " e" - "};"); - CHECK_EQ(239 * 10, value->Int32Value()); -} - -static v8::Handle<Value> InterceptorCallICFastApi(Local<String> name, - const AccessorInfo& info) { - ApiTestFuzzer::Fuzz(); - int* call_count = - reinterpret_cast<int*>(v8::External::Cast(*info.Data())->Value()); - ++(*call_count); - if ((*call_count) % 20 == 0) { - HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); - } - return v8::Handle<Value>(); -} - -static v8::Handle<Value> FastApiCallback_TrivialSignature( - const v8::Arguments& args) { - ApiTestFuzzer::Fuzz(); - v8::Isolate* isolate = v8::Isolate::GetCurrent(); - CHECK_EQ(isolate, args.GetIsolate()); - CHECK_EQ(args.This(), args.Holder()); - CHECK(args.Data()->Equals(v8_str("method_data"))); - return v8::Integer::New(args[0]->Int32Value() + 1); -} - -static v8::Handle<Value> FastApiCallback_SimpleSignature( - const v8::Arguments& args) { - ApiTestFuzzer::Fuzz(); - v8::Isolate* isolate = v8::Isolate::GetCurrent(); - CHECK_EQ(isolate, args.GetIsolate()); - CHECK_EQ(args.This()->GetPrototype(), args.Holder()); - CHECK(args.Data()->Equals(v8_str("method_data"))); - // Note, we're using HasRealNamedProperty instead of Has to avoid - // invoking the interceptor again. - CHECK(args.Holder()->HasRealNamedProperty(v8_str("foo"))); - return v8::Integer::New(args[0]->Int32Value() + 1); -} - -// Helper to maximize the odds of object moving. -static void GenerateSomeGarbage() { - CompileRun( - "var garbage;" - "for (var i = 0; i < 1000; i++) {" - " garbage = [1/i, \"garbage\" + i, garbage, {foo: garbage}];" - "}" - "garbage = undefined;"); -} - - -v8::Handle<v8::Value> DirectApiCallback(const v8::Arguments& args) { - static int count = 0; - if (count++ % 3 == 0) { - HEAP->CollectAllGarbage(i::Heap::kAbortIncrementalMarkingMask); - // This should move the stub - GenerateSomeGarbage(); // This should ensure the old stub memory is flushed - } - return v8::Handle<v8::Value>(); -} - - -THREADED_TEST(CallICFastApi_DirectCall_GCMoveStub) { - v8::HandleScope scope; - LocalContext context; - v8::Handle<v8::ObjectTemplate> nativeobject_templ = v8::ObjectTemplate::New(); - nativeobject_templ->Set("callback", - v8::FunctionTemplate::New(DirectApiCallback)); - v8::Local<v8::Object> nativeobject_obj = nativeobject_templ->NewInstance(); - context->Global()->Set(v8_str("nativeobject"), nativeobject_obj); - // call the api function multiple times to ensure direct call stub creation. - CompileRun( - "function f() {" - " for (var i = 1; i <= 30; i++) {" - " nativeobject.callback();" - " }" - "}" - "f();"); -} - - -v8::Handle<v8::Value> ThrowingDirectApiCallback(const v8::Arguments& args) { - return v8::ThrowException(v8_str("g")); -} - - -THREADED_TEST(CallICFastApi_DirectCall_Throw) { - v8::HandleScope scope; - LocalContext context; - v8::Handle<v8::ObjectTemplate> nativeobject_templ = v8::ObjectTemplate::New(); - nativeobject_templ->Set("callback", - v8::FunctionTemplate::New(ThrowingDirectApiCallback)); - v8::Local<v8::Object> nativeobject_obj = nativeobject_templ->NewInstance(); - context->Global()->Set(v8_str("nativeobject"), nativeobject_obj); - // call the api function multiple times to ensure direct call stub creation. - v8::Handle<Value> result = CompileRun( - "var result = '';" - "function f() {" - " for (var i = 1; i <= 5; i++) {" - " try { nativeobject.callback(); } catch (e) { result += e; }" - " }" - "}" - "f(); result;"); - CHECK_EQ(v8_str("ggggg"), result); -} - - -v8::Handle<v8::Value> DirectGetterCallback(Local<String> name, - const v8::AccessorInfo& info) { - if (++p_getter_count % 3 == 0) { - HEAP->CollectAllGarbage(i::Heap::kAbortIncrementalMarkingMask); - GenerateSomeGarbage(); - } - return v8::Handle<v8::Value>(); -} - - -THREADED_TEST(LoadICFastApi_DirectCall_GCMoveStub) { - v8::HandleScope scope; - LocalContext context; - v8::Handle<v8::ObjectTemplate> obj = v8::ObjectTemplate::New(); - obj->SetAccessor(v8_str("p1"), DirectGetterCallback); - context->Global()->Set(v8_str("o1"), obj->NewInstance()); - p_getter_count = 0; - CompileRun( - "function f() {" - " for (var i = 0; i < 30; i++) o1.p1;" - "}" - "f();"); - CHECK_EQ(30, p_getter_count); -} - - -v8::Handle<v8::Value> ThrowingDirectGetterCallback( - Local<String> name, const v8::AccessorInfo& info) { - return v8::ThrowException(v8_str("g")); -} - - -THREADED_TEST(LoadICFastApi_DirectCall_Throw) { - v8::HandleScope scope; - LocalContext context; - v8::Handle<v8::ObjectTemplate> obj = v8::ObjectTemplate::New(); - obj->SetAccessor(v8_str("p1"), ThrowingDirectGetterCallback); - context->Global()->Set(v8_str("o1"), obj->NewInstance()); - v8::Handle<Value> result = CompileRun( - "var result = '';" - "for (var i = 0; i < 5; i++) {" - " try { o1.p1; } catch (e) { result += e; }" - "}" - "result;"); - CHECK_EQ(v8_str("ggggg"), result); -} - - -THREADED_TEST(InterceptorCallICFastApi_TrivialSignature) { - int interceptor_call_count = 0; - v8::HandleScope scope; - v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); - v8::Handle<v8::FunctionTemplate> method_templ = - v8::FunctionTemplate::New(FastApiCallback_TrivialSignature, - v8_str("method_data"), - v8::Handle<v8::Signature>()); - v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); - proto_templ->Set(v8_str("method"), method_templ); - v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); - templ->SetNamedPropertyHandler(InterceptorCallICFastApi, - NULL, NULL, NULL, NULL, - v8::External::New(&interceptor_call_count)); - LocalContext context; - v8::Handle<v8::Function> fun = fun_templ->GetFunction(); - GenerateSomeGarbage(); - context->Global()->Set(v8_str("o"), fun->NewInstance()); - CompileRun( - "var result = 0;" - "for (var i = 0; i < 100; i++) {" - " result = o.method(41);" - "}"); - CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value()); - CHECK_EQ(100, interceptor_call_count); -} - -THREADED_TEST(InterceptorCallICFastApi_SimpleSignature) { - int interceptor_call_count = 0; - v8::HandleScope scope; - v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); - v8::Handle<v8::FunctionTemplate> method_templ = - v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, - v8_str("method_data"), - v8::Signature::New(fun_templ)); - v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); - proto_templ->Set(v8_str("method"), method_templ); - fun_templ->SetHiddenPrototype(true); - v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); - templ->SetNamedPropertyHandler(InterceptorCallICFastApi, - NULL, NULL, NULL, NULL, - v8::External::New(&interceptor_call_count)); - LocalContext context; - v8::Handle<v8::Function> fun = fun_templ->GetFunction(); - GenerateSomeGarbage(); - context->Global()->Set(v8_str("o"), fun->NewInstance()); - CompileRun( - "o.foo = 17;" - "var receiver = {};" - "receiver.__proto__ = o;" - "var result = 0;" - "for (var i = 0; i < 100; i++) {" - " result = receiver.method(41);" - "}"); - CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value()); - CHECK_EQ(100, interceptor_call_count); -} - -THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss1) { - int interceptor_call_count = 0; - v8::HandleScope scope; - v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); - v8::Handle<v8::FunctionTemplate> method_templ = - v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, - v8_str("method_data"), - v8::Signature::New(fun_templ)); - v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); - proto_templ->Set(v8_str("method"), method_templ); - fun_templ->SetHiddenPrototype(true); - v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); - templ->SetNamedPropertyHandler(InterceptorCallICFastApi, - NULL, NULL, NULL, NULL, - v8::External::New(&interceptor_call_count)); - LocalContext context; - v8::Handle<v8::Function> fun = fun_templ->GetFunction(); - GenerateSomeGarbage(); - context->Global()->Set(v8_str("o"), fun->NewInstance()); - CompileRun( - "o.foo = 17;" - "var receiver = {};" - "receiver.__proto__ = o;" - "var result = 0;" - "var saved_result = 0;" - "for (var i = 0; i < 100; i++) {" - " result = receiver.method(41);" - " if (i == 50) {" - " saved_result = result;" - " receiver = {method: function(x) { return x - 1 }};" - " }" - "}"); - CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value()); - CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); - CHECK_GE(interceptor_call_count, 50); -} - -THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss2) { - int interceptor_call_count = 0; - v8::HandleScope scope; - v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); - v8::Handle<v8::FunctionTemplate> method_templ = - v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, - v8_str("method_data"), - v8::Signature::New(fun_templ)); - v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); - proto_templ->Set(v8_str("method"), method_templ); - fun_templ->SetHiddenPrototype(true); - v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); - templ->SetNamedPropertyHandler(InterceptorCallICFastApi, - NULL, NULL, NULL, NULL, - v8::External::New(&interceptor_call_count)); - LocalContext context; - v8::Handle<v8::Function> fun = fun_templ->GetFunction(); - GenerateSomeGarbage(); - context->Global()->Set(v8_str("o"), fun->NewInstance()); - CompileRun( - "o.foo = 17;" - "var receiver = {};" - "receiver.__proto__ = o;" - "var result = 0;" - "var saved_result = 0;" - "for (var i = 0; i < 100; i++) {" - " result = receiver.method(41);" - " if (i == 50) {" - " saved_result = result;" - " o.method = function(x) { return x - 1 };" - " }" - "}"); - CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value()); - CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); - CHECK_GE(interceptor_call_count, 50); -} - -THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss3) { - int interceptor_call_count = 0; - v8::HandleScope scope; - v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); - v8::Handle<v8::FunctionTemplate> method_templ = - v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, - v8_str("method_data"), - v8::Signature::New(fun_templ)); - v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); - proto_templ->Set(v8_str("method"), method_templ); - fun_templ->SetHiddenPrototype(true); - v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); - templ->SetNamedPropertyHandler(InterceptorCallICFastApi, - NULL, NULL, NULL, NULL, - v8::External::New(&interceptor_call_count)); - LocalContext context; - v8::Handle<v8::Function> fun = fun_templ->GetFunction(); - GenerateSomeGarbage(); - context->Global()->Set(v8_str("o"), fun->NewInstance()); - v8::TryCatch try_catch; - CompileRun( - "o.foo = 17;" - "var receiver = {};" - "receiver.__proto__ = o;" - "var result = 0;" - "var saved_result = 0;" - "for (var i = 0; i < 100; i++) {" - " result = receiver.method(41);" - " if (i == 50) {" - " saved_result = result;" - " receiver = 333;" - " }" - "}"); - CHECK(try_catch.HasCaught()); - CHECK_EQ(v8_str("TypeError: Object 333 has no method 'method'"), - try_catch.Exception()->ToString()); - CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); - CHECK_GE(interceptor_call_count, 50); -} - -THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_TypeError) { - int interceptor_call_count = 0; - v8::HandleScope scope; - v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); - v8::Handle<v8::FunctionTemplate> method_templ = - v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, - v8_str("method_data"), - v8::Signature::New(fun_templ)); - v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); - proto_templ->Set(v8_str("method"), method_templ); - fun_templ->SetHiddenPrototype(true); - v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); - templ->SetNamedPropertyHandler(InterceptorCallICFastApi, - NULL, NULL, NULL, NULL, - v8::External::New(&interceptor_call_count)); - LocalContext context; - v8::Handle<v8::Function> fun = fun_templ->GetFunction(); - GenerateSomeGarbage(); - context->Global()->Set(v8_str("o"), fun->NewInstance()); - v8::TryCatch try_catch; - CompileRun( - "o.foo = 17;" - "var receiver = {};" - "receiver.__proto__ = o;" - "var result = 0;" - "var saved_result = 0;" - "for (var i = 0; i < 100; i++) {" - " result = receiver.method(41);" - " if (i == 50) {" - " saved_result = result;" - " receiver = {method: receiver.method};" - " }" - "}"); - CHECK(try_catch.HasCaught()); - CHECK_EQ(v8_str("TypeError: Illegal invocation"), - try_catch.Exception()->ToString()); - CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); - CHECK_GE(interceptor_call_count, 50); -} - -THREADED_TEST(CallICFastApi_TrivialSignature) { - v8::HandleScope scope; - v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); - v8::Handle<v8::FunctionTemplate> method_templ = - v8::FunctionTemplate::New(FastApiCallback_TrivialSignature, - v8_str("method_data"), - v8::Handle<v8::Signature>()); - v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); - proto_templ->Set(v8_str("method"), method_templ); - v8::Handle<v8::ObjectTemplate> templ(fun_templ->InstanceTemplate()); - USE(templ); - LocalContext context; - v8::Handle<v8::Function> fun = fun_templ->GetFunction(); - GenerateSomeGarbage(); - context->Global()->Set(v8_str("o"), fun->NewInstance()); - CompileRun( - "var result = 0;" - "for (var i = 0; i < 100; i++) {" - " result = o.method(41);" - "}"); - - CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value()); -} - -THREADED_TEST(CallICFastApi_SimpleSignature) { - v8::HandleScope scope; - v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); - v8::Handle<v8::FunctionTemplate> method_templ = - v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, - v8_str("method_data"), - v8::Signature::New(fun_templ)); - v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); - proto_templ->Set(v8_str("method"), method_templ); - fun_templ->SetHiddenPrototype(true); - v8::Handle<v8::ObjectTemplate> templ(fun_templ->InstanceTemplate()); - CHECK(!templ.IsEmpty()); - LocalContext context; - v8::Handle<v8::Function> fun = fun_templ->GetFunction(); - GenerateSomeGarbage(); - context->Global()->Set(v8_str("o"), fun->NewInstance()); - CompileRun( - "o.foo = 17;" - "var receiver = {};" - "receiver.__proto__ = o;" - "var result = 0;" - "for (var i = 0; i < 100; i++) {" - " result = receiver.method(41);" - "}"); - - CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value()); -} - -THREADED_TEST(CallICFastApi_SimpleSignature_Miss1) { - v8::HandleScope scope; - v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); - v8::Handle<v8::FunctionTemplate> method_templ = - v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, - v8_str("method_data"), - v8::Signature::New(fun_templ)); - v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); - proto_templ->Set(v8_str("method"), method_templ); - fun_templ->SetHiddenPrototype(true); - v8::Handle<v8::ObjectTemplate> templ(fun_templ->InstanceTemplate()); - CHECK(!templ.IsEmpty()); - LocalContext context; - v8::Handle<v8::Function> fun = fun_templ->GetFunction(); - GenerateSomeGarbage(); - context->Global()->Set(v8_str("o"), fun->NewInstance()); - CompileRun( - "o.foo = 17;" - "var receiver = {};" - "receiver.__proto__ = o;" - "var result = 0;" - "var saved_result = 0;" - "for (var i = 0; i < 100; i++) {" - " result = receiver.method(41);" - " if (i == 50) {" - " saved_result = result;" - " receiver = {method: function(x) { return x - 1 }};" - " }" - "}"); - CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value()); - CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); -} - -THREADED_TEST(CallICFastApi_SimpleSignature_Miss2) { - v8::HandleScope scope; - v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); - v8::Handle<v8::FunctionTemplate> method_templ = - v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, - v8_str("method_data"), - v8::Signature::New(fun_templ)); - v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); - proto_templ->Set(v8_str("method"), method_templ); - fun_templ->SetHiddenPrototype(true); - v8::Handle<v8::ObjectTemplate> templ(fun_templ->InstanceTemplate()); - CHECK(!templ.IsEmpty()); - LocalContext context; - v8::Handle<v8::Function> fun = fun_templ->GetFunction(); - GenerateSomeGarbage(); - context->Global()->Set(v8_str("o"), fun->NewInstance()); - v8::TryCatch try_catch; - CompileRun( - "o.foo = 17;" - "var receiver = {};" - "receiver.__proto__ = o;" - "var result = 0;" - "var saved_result = 0;" - "for (var i = 0; i < 100; i++) {" - " result = receiver.method(41);" - " if (i == 50) {" - " saved_result = result;" - " receiver = 333;" - " }" - "}"); - CHECK(try_catch.HasCaught()); - CHECK_EQ(v8_str("TypeError: Object 333 has no method 'method'"), - try_catch.Exception()->ToString()); - CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); -} - -THREADED_TEST(CallICFastApi_SimpleSignature_TypeError) { - v8::HandleScope scope; - v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); - v8::Handle<v8::FunctionTemplate> method_templ = - v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, - v8_str("method_data"), - v8::Signature::New(fun_templ)); - v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); - proto_templ->Set(v8_str("method"), method_templ); - fun_templ->SetHiddenPrototype(true); - v8::Handle<v8::ObjectTemplate> templ(fun_templ->InstanceTemplate()); - CHECK(!templ.IsEmpty()); - LocalContext context; - v8::Handle<v8::Function> fun = fun_templ->GetFunction(); - GenerateSomeGarbage(); - context->Global()->Set(v8_str("o"), fun->NewInstance()); - v8::TryCatch try_catch; - CompileRun( - "o.foo = 17;" - "var receiver = {};" - "receiver.__proto__ = o;" - "var result = 0;" - "var saved_result = 0;" - "for (var i = 0; i < 100; i++) {" - " result = receiver.method(41);" - " if (i == 50) {" - " saved_result = result;" - " receiver = Object.create(receiver);" - " }" - "}"); - CHECK(try_catch.HasCaught()); - CHECK_EQ(v8_str("TypeError: Illegal invocation"), - try_catch.Exception()->ToString()); - CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); -} - - -v8::Handle<Value> keyed_call_ic_function; - -static v8::Handle<Value> InterceptorKeyedCallICGetter( - Local<String> name, const AccessorInfo& info) { - ApiTestFuzzer::Fuzz(); - if (v8_str("x")->Equals(name)) { - return keyed_call_ic_function; - } - return v8::Handle<Value>(); -} - - -// Test the case when we stored cacheable lookup into -// a stub, but the function name changed (to another cacheable function). -THREADED_TEST(InterceptorKeyedCallICKeyChange1) { - v8::HandleScope scope; - v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); - templ->SetNamedPropertyHandler(NoBlockGetterX); - LocalContext context; - context->Global()->Set(v8_str("o"), templ->NewInstance()); - CompileRun( - "proto = new Object();" - "proto.y = function(x) { return x + 1; };" - "proto.z = function(x) { return x - 1; };" - "o.__proto__ = proto;" - "var result = 0;" - "var method = 'y';" - "for (var i = 0; i < 10; i++) {" - " if (i == 5) { method = 'z'; };" - " result += o[method](41);" - "}"); - CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value()); -} - - -// Test the case when we stored cacheable lookup into -// a stub, but the function name changed (and the new function is present -// both before and after the interceptor in the prototype chain). -THREADED_TEST(InterceptorKeyedCallICKeyChange2) { - v8::HandleScope scope; - v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); - templ->SetNamedPropertyHandler(InterceptorKeyedCallICGetter); - LocalContext context; - context->Global()->Set(v8_str("proto1"), templ->NewInstance()); - keyed_call_ic_function = - v8_compile("function f(x) { return x - 1; }; f")->Run(); - CompileRun( - "o = new Object();" - "proto2 = new Object();" - "o.y = function(x) { return x + 1; };" - "proto2.y = function(x) { return x + 2; };" - "o.__proto__ = proto1;" - "proto1.__proto__ = proto2;" - "var result = 0;" - "var method = 'x';" - "for (var i = 0; i < 10; i++) {" - " if (i == 5) { method = 'y'; };" - " result += o[method](41);" - "}"); - CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value()); -} - - -// Same as InterceptorKeyedCallICKeyChange1 only the cacheable function sit -// on the global object. -THREADED_TEST(InterceptorKeyedCallICKeyChangeOnGlobal) { - v8::HandleScope scope; - v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); - templ->SetNamedPropertyHandler(NoBlockGetterX); - LocalContext context; - context->Global()->Set(v8_str("o"), templ->NewInstance()); - CompileRun( - "function inc(x) { return x + 1; };" - "inc(1);" - "function dec(x) { return x - 1; };" - "dec(1);" - "o.__proto__ = this;" - "this.__proto__.x = inc;" - "this.__proto__.y = dec;" - "var result = 0;" - "var method = 'x';" - "for (var i = 0; i < 10; i++) {" - " if (i == 5) { method = 'y'; };" - " result += o[method](41);" - "}"); - CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value()); -} - - -// Test the case when actual function to call sits on global object. -THREADED_TEST(InterceptorKeyedCallICFromGlobal) { - v8::HandleScope scope; - v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); - templ_o->SetNamedPropertyHandler(NoBlockGetterX); - LocalContext context; - context->Global()->Set(v8_str("o"), templ_o->NewInstance()); - - CompileRun( - "function len(x) { return x.length; };" - "o.__proto__ = this;" - "var m = 'parseFloat';" - "var result = 0;" - "for (var i = 0; i < 10; i++) {" - " if (i == 5) {" - " m = 'len';" - " saved_result = result;" - " };" - " result = o[m]('239');" - "}"); - CHECK_EQ(3, context->Global()->Get(v8_str("result"))->Int32Value()); - CHECK_EQ(239, context->Global()->Get(v8_str("saved_result"))->Int32Value()); -} - -// Test the map transition before the interceptor. -THREADED_TEST(InterceptorKeyedCallICMapChangeBefore) { - v8::HandleScope scope; - v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); - templ_o->SetNamedPropertyHandler(NoBlockGetterX); - LocalContext context; - context->Global()->Set(v8_str("proto"), templ_o->NewInstance()); - - CompileRun( - "var o = new Object();" - "o.__proto__ = proto;" - "o.method = function(x) { return x + 1; };" - "var m = 'method';" - "var result = 0;" - "for (var i = 0; i < 10; i++) {" - " if (i == 5) { o.method = function(x) { return x - 1; }; };" - " result += o[m](41);" - "}"); - CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value()); -} - - -// Test the map transition after the interceptor. -THREADED_TEST(InterceptorKeyedCallICMapChangeAfter) { - v8::HandleScope scope; - v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); - templ_o->SetNamedPropertyHandler(NoBlockGetterX); - LocalContext context; - context->Global()->Set(v8_str("o"), templ_o->NewInstance()); - - CompileRun( - "var proto = new Object();" - "o.__proto__ = proto;" - "proto.method = function(x) { return x + 1; };" - "var m = 'method';" - "var result = 0;" - "for (var i = 0; i < 10; i++) {" - " if (i == 5) { proto.method = function(x) { return x - 1; }; };" - " result += o[m](41);" - "}"); - CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value()); -} - - -static int interceptor_call_count = 0; - -static v8::Handle<Value> InterceptorICRefErrorGetter(Local<String> name, - const AccessorInfo& info) { - ApiTestFuzzer::Fuzz(); - if (v8_str("x")->Equals(name) && interceptor_call_count++ < 20) { - return call_ic_function2; - } - return v8::Handle<Value>(); -} - - -// This test should hit load and call ICs for the interceptor case. -// Once in a while, the interceptor will reply that a property was not -// found in which case we should get a reference error. -THREADED_TEST(InterceptorICReferenceErrors) { - v8::HandleScope scope; - v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); - templ->SetNamedPropertyHandler(InterceptorICRefErrorGetter); - LocalContext context(0, templ, v8::Handle<Value>()); - call_ic_function2 = v8_compile("function h(x) { return x; }; h")->Run(); - v8::Handle<Value> value = CompileRun( - "function f() {" - " for (var i = 0; i < 1000; i++) {" - " try { x; } catch(e) { return true; }" - " }" - " return false;" - "};" - "f();"); - CHECK_EQ(true, value->BooleanValue()); - interceptor_call_count = 0; - value = CompileRun( - "function g() {" - " for (var i = 0; i < 1000; i++) {" - " try { x(42); } catch(e) { return true; }" - " }" - " return false;" - "};" - "g();"); - CHECK_EQ(true, value->BooleanValue()); -} - - -static int interceptor_ic_exception_get_count = 0; - -static v8::Handle<Value> InterceptorICExceptionGetter( - Local<String> name, - const AccessorInfo& info) { - ApiTestFuzzer::Fuzz(); - if (v8_str("x")->Equals(name) && ++interceptor_ic_exception_get_count < 20) { - return call_ic_function3; - } - if (interceptor_ic_exception_get_count == 20) { - return v8::ThrowException(v8_num(42)); - } - // Do not handle get for properties other than x. - return v8::Handle<Value>(); -} - -// Test interceptor load/call IC where the interceptor throws an -// exception once in a while. -THREADED_TEST(InterceptorICGetterExceptions) { - interceptor_ic_exception_get_count = 0; - v8::HandleScope scope; - v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); - templ->SetNamedPropertyHandler(InterceptorICExceptionGetter); - LocalContext context(0, templ, v8::Handle<Value>()); - call_ic_function3 = v8_compile("function h(x) { return x; }; h")->Run(); - v8::Handle<Value> value = CompileRun( - "function f() {" - " for (var i = 0; i < 100; i++) {" - " try { x; } catch(e) { return true; }" - " }" - " return false;" - "};" - "f();"); - CHECK_EQ(true, value->BooleanValue()); - interceptor_ic_exception_get_count = 0; - value = CompileRun( - "function f() {" - " for (var i = 0; i < 100; i++) {" - " try { x(42); } catch(e) { return true; }" - " }" - " return false;" - "};" - "f();"); - CHECK_EQ(true, value->BooleanValue()); -} - - -static int interceptor_ic_exception_set_count = 0; - -static v8::Handle<Value> InterceptorICExceptionSetter( - Local<String> key, Local<Value> value, const AccessorInfo&) { - ApiTestFuzzer::Fuzz(); - if (++interceptor_ic_exception_set_count > 20) { - return v8::ThrowException(v8_num(42)); - } - // Do not actually handle setting. - return v8::Handle<Value>(); -} - -// Test interceptor store IC where the interceptor throws an exception -// once in a while. -THREADED_TEST(InterceptorICSetterExceptions) { - interceptor_ic_exception_set_count = 0; - v8::HandleScope scope; - v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); - templ->SetNamedPropertyHandler(0, InterceptorICExceptionSetter); - LocalContext context(0, templ, v8::Handle<Value>()); - v8::Handle<Value> value = CompileRun( - "function f() {" - " for (var i = 0; i < 100; i++) {" - " try { x = 42; } catch(e) { return true; }" - " }" - " return false;" - "};" - "f();"); - CHECK_EQ(true, value->BooleanValue()); -} - - -// Test that we ignore null interceptors. -THREADED_TEST(NullNamedInterceptor) { - v8::HandleScope scope; - v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); - templ->SetNamedPropertyHandler(0); - LocalContext context; - templ->Set("x", v8_num(42)); - v8::Handle<v8::Object> obj = templ->NewInstance(); - context->Global()->Set(v8_str("obj"), obj); - v8::Handle<Value> value = CompileRun("obj.x"); - CHECK(value->IsInt32()); - CHECK_EQ(42, value->Int32Value()); -} - - -// Test that we ignore null interceptors. -THREADED_TEST(NullIndexedInterceptor) { - v8::HandleScope scope; - v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); - templ->SetIndexedPropertyHandler(0); - LocalContext context; - templ->Set("42", v8_num(42)); - v8::Handle<v8::Object> obj = templ->NewInstance(); - context->Global()->Set(v8_str("obj"), obj); - v8::Handle<Value> value = CompileRun("obj[42]"); - CHECK(value->IsInt32()); - CHECK_EQ(42, value->Int32Value()); -} - - -THREADED_TEST(NamedPropertyHandlerGetterAttributes) { - v8::HandleScope scope; - v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); - templ->InstanceTemplate()->SetNamedPropertyHandler(InterceptorLoadXICGetter); - LocalContext env; - env->Global()->Set(v8_str("obj"), - templ->GetFunction()->NewInstance()); - ExpectTrue("obj.x === 42"); - ExpectTrue("!obj.propertyIsEnumerable('x')"); -} - - -static Handle<Value> ThrowingGetter(Local<String> name, - const AccessorInfo& info) { - ApiTestFuzzer::Fuzz(); - ThrowException(Handle<Value>()); - return Undefined(); -} - - -THREADED_TEST(VariousGetPropertiesAndThrowingCallbacks) { - HandleScope scope; - LocalContext context; - - Local<FunctionTemplate> templ = FunctionTemplate::New(); - Local<ObjectTemplate> instance_templ = templ->InstanceTemplate(); - instance_templ->SetAccessor(v8_str("f"), ThrowingGetter); - - Local<Object> instance = templ->GetFunction()->NewInstance(); - - Local<Object> another = Object::New(); - another->SetPrototype(instance); - - Local<Object> with_js_getter = CompileRun( - "o = {};\n" - "o.__defineGetter__('f', function() { throw undefined; });\n" - "o\n").As<Object>(); - CHECK(!with_js_getter.IsEmpty()); - - TryCatch try_catch; - - Local<Value> result = instance->GetRealNamedProperty(v8_str("f")); - CHECK(try_catch.HasCaught()); - try_catch.Reset(); - CHECK(result.IsEmpty()); - - result = another->GetRealNamedProperty(v8_str("f")); - CHECK(try_catch.HasCaught()); - try_catch.Reset(); - CHECK(result.IsEmpty()); - - result = another->GetRealNamedPropertyInPrototypeChain(v8_str("f")); - CHECK(try_catch.HasCaught()); - try_catch.Reset(); - CHECK(result.IsEmpty()); - - result = another->Get(v8_str("f")); - CHECK(try_catch.HasCaught()); - try_catch.Reset(); - CHECK(result.IsEmpty()); - - result = with_js_getter->GetRealNamedProperty(v8_str("f")); - CHECK(try_catch.HasCaught()); - try_catch.Reset(); - CHECK(result.IsEmpty()); - - result = with_js_getter->Get(v8_str("f")); - CHECK(try_catch.HasCaught()); - try_catch.Reset(); - CHECK(result.IsEmpty()); -} - - -static Handle<Value> ThrowingCallbackWithTryCatch(const Arguments& args) { - TryCatch try_catch; - // Verboseness is important: it triggers message delivery which can call into - // external code. - try_catch.SetVerbose(true); - CompileRun("throw 'from JS';"); - CHECK(try_catch.HasCaught()); - CHECK(!i::Isolate::Current()->has_pending_exception()); - CHECK(!i::Isolate::Current()->has_scheduled_exception()); - return Undefined(); -} - - -static int call_depth; - - -static void WithTryCatch(Handle<Message> message, Handle<Value> data) { - TryCatch try_catch; -} - - -static void ThrowFromJS(Handle<Message> message, Handle<Value> data) { - if (--call_depth) CompileRun("throw 'ThrowInJS';"); -} - - -static void ThrowViaApi(Handle<Message> message, Handle<Value> data) { - if (--call_depth) ThrowException(v8_str("ThrowViaApi")); -} - - -static void WebKitLike(Handle<Message> message, Handle<Value> data) { - Handle<String> errorMessageString = message->Get(); - CHECK(!errorMessageString.IsEmpty()); - message->GetStackTrace(); - message->GetScriptResourceName(); -} - -THREADED_TEST(ExceptionsDoNotPropagatePastTryCatch) { - HandleScope scope; - LocalContext context; - - Local<Function> func = - FunctionTemplate::New(ThrowingCallbackWithTryCatch)->GetFunction(); - context->Global()->Set(v8_str("func"), func); - - MessageCallback callbacks[] = - { NULL, WebKitLike, ThrowViaApi, ThrowFromJS, WithTryCatch }; - for (unsigned i = 0; i < sizeof(callbacks)/sizeof(callbacks[0]); i++) { - MessageCallback callback = callbacks[i]; - if (callback != NULL) { - V8::AddMessageListener(callback); - } - // Some small number to control number of times message handler should - // throw an exception. - call_depth = 5; - ExpectFalse( - "var thrown = false;\n" - "try { func(); } catch(e) { thrown = true; }\n" - "thrown\n"); - if (callback != NULL) { - V8::RemoveMessageListeners(callback); - } - } -} - - -static v8::Handle<Value> ParentGetter(Local<String> name, - const AccessorInfo& info) { - ApiTestFuzzer::Fuzz(); - return v8_num(1); -} - - -static v8::Handle<Value> ChildGetter(Local<String> name, - const AccessorInfo& info) { - ApiTestFuzzer::Fuzz(); - return v8_num(42); -} - - -THREADED_TEST(Overriding) { - i::FLAG_es5_readonly = true; - v8::HandleScope scope; - LocalContext context; - - // Parent template. - Local<v8::FunctionTemplate> parent_templ = v8::FunctionTemplate::New(); - Local<ObjectTemplate> parent_instance_templ = - parent_templ->InstanceTemplate(); - parent_instance_templ->SetAccessor(v8_str("f"), ParentGetter); - - // Template that inherits from the parent template. - Local<v8::FunctionTemplate> child_templ = v8::FunctionTemplate::New(); - Local<ObjectTemplate> child_instance_templ = - child_templ->InstanceTemplate(); - child_templ->Inherit(parent_templ); - // Override 'f'. The child version of 'f' should get called for child - // instances. - child_instance_templ->SetAccessor(v8_str("f"), ChildGetter); - // Add 'g' twice. The 'g' added last should get called for instances. - child_instance_templ->SetAccessor(v8_str("g"), ParentGetter); - child_instance_templ->SetAccessor(v8_str("g"), ChildGetter); - - // Add 'h' as an accessor to the proto template with ReadOnly attributes - // so 'h' can be shadowed on the instance object. - Local<ObjectTemplate> child_proto_templ = child_templ->PrototypeTemplate(); - child_proto_templ->SetAccessor(v8_str("h"), ParentGetter, 0, - v8::Handle<Value>(), v8::DEFAULT, v8::ReadOnly); - - // Add 'i' as an accessor to the instance template with ReadOnly attributes - // but the attribute does not have effect because it is duplicated with - // NULL setter. - child_instance_templ->SetAccessor(v8_str("i"), ChildGetter, 0, - v8::Handle<Value>(), v8::DEFAULT, v8::ReadOnly); - - - - // Instantiate the child template. - Local<v8::Object> instance = child_templ->GetFunction()->NewInstance(); - - // Check that the child function overrides the parent one. - context->Global()->Set(v8_str("o"), instance); - Local<Value> value = v8_compile("o.f")->Run(); - // Check that the 'g' that was added last is hit. - CHECK_EQ(42, value->Int32Value()); - value = v8_compile("o.g")->Run(); - CHECK_EQ(42, value->Int32Value()); - - // Check that 'h' cannot be shadowed. - value = v8_compile("o.h = 3; o.h")->Run(); - CHECK_EQ(1, value->Int32Value()); - - // Check that 'i' cannot be shadowed or changed. - value = v8_compile("o.i = 3; o.i")->Run(); - CHECK_EQ(42, value->Int32Value()); -} - - -static v8::Handle<Value> IsConstructHandler(const v8::Arguments& args) { - ApiTestFuzzer::Fuzz(); - return v8::Boolean::New(args.IsConstructCall()); -} - - -THREADED_TEST(IsConstructCall) { - v8::HandleScope scope; - - // Function template with call handler. - Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); - templ->SetCallHandler(IsConstructHandler); - - LocalContext context; - - context->Global()->Set(v8_str("f"), templ->GetFunction()); - Local<Value> value = v8_compile("f()")->Run(); - CHECK(!value->BooleanValue()); - value = v8_compile("new f()")->Run(); - CHECK(value->BooleanValue()); -} - - -THREADED_TEST(ObjectProtoToString) { - v8::HandleScope scope; - Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); - templ->SetClassName(v8_str("MyClass")); - - LocalContext context; - - Local<String> customized_tostring = v8_str("customized toString"); - - // Replace Object.prototype.toString - v8_compile("Object.prototype.toString = function() {" - " return 'customized toString';" - "}")->Run(); - - // Normal ToString call should call replaced Object.prototype.toString - Local<v8::Object> instance = templ->GetFunction()->NewInstance(); - Local<String> value = instance->ToString(); - CHECK(value->IsString() && value->Equals(customized_tostring)); - - // ObjectProtoToString should not call replace toString function. - value = instance->ObjectProtoToString(); - CHECK(value->IsString() && value->Equals(v8_str("[object MyClass]"))); - - // Check global - value = context->Global()->ObjectProtoToString(); - CHECK(value->IsString() && value->Equals(v8_str("[object global]"))); - - // Check ordinary object - Local<Value> object = v8_compile("new Object()")->Run(); - value = object.As<v8::Object>()->ObjectProtoToString(); - CHECK(value->IsString() && value->Equals(v8_str("[object Object]"))); -} - - -THREADED_TEST(ObjectGetConstructorName) { - v8::HandleScope scope; - LocalContext context; - v8_compile("function Parent() {};" - "function Child() {};" - "Child.prototype = new Parent();" - "var outer = { inner: function() { } };" - "var p = new Parent();" - "var c = new Child();" - "var x = new outer.inner();")->Run(); - - Local<v8::Value> p = context->Global()->Get(v8_str("p")); - CHECK(p->IsObject() && p->ToObject()->GetConstructorName()->Equals( - v8_str("Parent"))); - - Local<v8::Value> c = context->Global()->Get(v8_str("c")); - CHECK(c->IsObject() && c->ToObject()->GetConstructorName()->Equals( - v8_str("Child"))); - - Local<v8::Value> x = context->Global()->Get(v8_str("x")); - CHECK(x->IsObject() && x->ToObject()->GetConstructorName()->Equals( - v8_str("outer.inner"))); -} - - -bool ApiTestFuzzer::fuzzing_ = false; -i::Semaphore* ApiTestFuzzer::all_tests_done_= - i::OS::CreateSemaphore(0); -int ApiTestFuzzer::active_tests_; -int ApiTestFuzzer::tests_being_run_; -int ApiTestFuzzer::current_; - - -// We are in a callback and want to switch to another thread (if we -// are currently running the thread fuzzing test). -void ApiTestFuzzer::Fuzz() { - if (!fuzzing_) return; - ApiTestFuzzer* test = RegisterThreadedTest::nth(current_)->fuzzer_; - test->ContextSwitch(); -} - - -// Let the next thread go. Since it is also waiting on the V8 lock it may -// not start immediately. -bool ApiTestFuzzer::NextThread() { - int test_position = GetNextTestNumber(); - const char* test_name = RegisterThreadedTest::nth(current_)->name(); - if (test_position == current_) { - if (kLogThreading) - printf("Stay with %s\n", test_name); - return false; - } - if (kLogThreading) { - printf("Switch from %s to %s\n", - test_name, - RegisterThreadedTest::nth(test_position)->name()); - } - current_ = test_position; - RegisterThreadedTest::nth(current_)->fuzzer_->gate_->Signal(); - return true; -} - - -void ApiTestFuzzer::Run() { - // When it is our turn... - gate_->Wait(); - { - // ... get the V8 lock and start running the test. - v8::Locker locker(CcTest::default_isolate()); - CallTest(); - } - // This test finished. - active_ = false; - active_tests_--; - // If it was the last then signal that fact. - if (active_tests_ == 0) { - all_tests_done_->Signal(); - } else { - // Otherwise select a new test and start that. - NextThread(); - } -} - - -static unsigned linear_congruential_generator; - - -void ApiTestFuzzer::SetUp(PartOfTest part) { - linear_congruential_generator = i::FLAG_testing_prng_seed; - fuzzing_ = true; - int count = RegisterThreadedTest::count(); - int start = count * part / (LAST_PART + 1); - int end = (count * (part + 1) / (LAST_PART + 1)) - 1; - active_tests_ = tests_being_run_ = end - start + 1; - for (int i = 0; i < tests_being_run_; i++) { - RegisterThreadedTest::nth(i)->fuzzer_ = new ApiTestFuzzer(i + start); - } - for (int i = 0; i < active_tests_; i++) { - RegisterThreadedTest::nth(i)->fuzzer_->Start(); - } -} - - -static void CallTestNumber(int test_number) { - (RegisterThreadedTest::nth(test_number)->callback())(); -} - - -void ApiTestFuzzer::RunAllTests() { - // Set off the first test. - current_ = -1; - NextThread(); - // Wait till they are all done. - all_tests_done_->Wait(); -} - - -int ApiTestFuzzer::GetNextTestNumber() { - int next_test; - do { - next_test = (linear_congruential_generator >> 16) % tests_being_run_; - linear_congruential_generator *= 1664525u; - linear_congruential_generator += 1013904223u; - } while (!RegisterThreadedTest::nth(next_test)->fuzzer_->active_); - return next_test; -} - - -void ApiTestFuzzer::ContextSwitch() { - // If the new thread is the same as the current thread there is nothing to do. - if (NextThread()) { - // Now it can start. - v8::Unlocker unlocker(CcTest::default_isolate()); - // Wait till someone starts us again. - gate_->Wait(); - // And we're off. - } -} - - -void ApiTestFuzzer::TearDown() { - fuzzing_ = false; - for (int i = 0; i < RegisterThreadedTest::count(); i++) { - ApiTestFuzzer *fuzzer = RegisterThreadedTest::nth(i)->fuzzer_; - if (fuzzer != NULL) fuzzer->Join(); - } -} - - -// Lets not be needlessly self-referential. -TEST(Threading1) { - // TODO(mstarzinger): Disabled in GC stress mode for now, we should find the - // correct timeout for this an re-enable this test again - if (i::FLAG_stress_compaction) return; - ApiTestFuzzer::SetUp(ApiTestFuzzer::FIRST_PART); - ApiTestFuzzer::RunAllTests(); - ApiTestFuzzer::TearDown(); -} - -TEST(Threading2) { - ApiTestFuzzer::SetUp(ApiTestFuzzer::SECOND_PART); - ApiTestFuzzer::RunAllTests(); - ApiTestFuzzer::TearDown(); -} - -TEST(Threading3) { - ApiTestFuzzer::SetUp(ApiTestFuzzer::THIRD_PART); - ApiTestFuzzer::RunAllTests(); - ApiTestFuzzer::TearDown(); -} - -TEST(Threading4) { - ApiTestFuzzer::SetUp(ApiTestFuzzer::FOURTH_PART); - ApiTestFuzzer::RunAllTests(); - ApiTestFuzzer::TearDown(); -} - -void ApiTestFuzzer::CallTest() { - if (kLogThreading) - printf("Start test %d\n", test_number_); - CallTestNumber(test_number_); - if (kLogThreading) - printf("End test %d\n", test_number_); -} - - -static v8::Handle<Value> ThrowInJS(const v8::Arguments& args) { - CHECK(v8::Locker::IsLocked(CcTest::default_isolate())); - ApiTestFuzzer::Fuzz(); - v8::Unlocker unlocker(CcTest::default_isolate()); - const char* code = "throw 7;"; - { - v8::Locker nested_locker(CcTest::default_isolate()); - v8::HandleScope scope; - v8::Handle<Value> exception; - { v8::TryCatch try_catch; - v8::Handle<Value> value = CompileRun(code); - CHECK(value.IsEmpty()); - CHECK(try_catch.HasCaught()); - // Make sure to wrap the exception in a new handle because - // the handle returned from the TryCatch is destroyed - // when the TryCatch is destroyed. - exception = Local<Value>::New(try_catch.Exception()); - } - return v8::ThrowException(exception); - } -} - - -static v8::Handle<Value> ThrowInJSNoCatch(const v8::Arguments& args) { - CHECK(v8::Locker::IsLocked(CcTest::default_isolate())); - ApiTestFuzzer::Fuzz(); - v8::Unlocker unlocker(CcTest::default_isolate()); - const char* code = "throw 7;"; - { - v8::Locker nested_locker(CcTest::default_isolate()); - v8::HandleScope scope; - v8::Handle<Value> value = CompileRun(code); - CHECK(value.IsEmpty()); - return v8_str("foo"); - } -} - - -// These are locking tests that don't need to be run again -// as part of the locking aggregation tests. -TEST(NestedLockers) { - v8::Locker locker(CcTest::default_isolate()); - CHECK(v8::Locker::IsLocked(CcTest::default_isolate())); - v8::HandleScope scope; - LocalContext env; - Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(ThrowInJS); - Local<Function> fun = fun_templ->GetFunction(); - env->Global()->Set(v8_str("throw_in_js"), fun); - Local<Script> script = v8_compile("(function () {" - " try {" - " throw_in_js();" - " return 42;" - " } catch (e) {" - " return e * 13;" - " }" - "})();"); - CHECK_EQ(91, script->Run()->Int32Value()); -} - - -// These are locking tests that don't need to be run again -// as part of the locking aggregation tests. -TEST(NestedLockersNoTryCatch) { - v8::Locker locker(CcTest::default_isolate()); - v8::HandleScope scope; - LocalContext env; - Local<v8::FunctionTemplate> fun_templ = - v8::FunctionTemplate::New(ThrowInJSNoCatch); - Local<Function> fun = fun_templ->GetFunction(); - env->Global()->Set(v8_str("throw_in_js"), fun); - Local<Script> script = v8_compile("(function () {" - " try {" - " throw_in_js();" - " return 42;" - " } catch (e) {" - " return e * 13;" - " }" - "})();"); - CHECK_EQ(91, script->Run()->Int32Value()); -} - - -THREADED_TEST(RecursiveLocking) { - v8::Locker locker(CcTest::default_isolate()); - { - v8::Locker locker2(CcTest::default_isolate()); - CHECK(v8::Locker::IsLocked(CcTest::default_isolate())); - } -} - - -static v8::Handle<Value> UnlockForAMoment(const v8::Arguments& args) { - ApiTestFuzzer::Fuzz(); - v8::Unlocker unlocker(CcTest::default_isolate()); - return v8::Undefined(); -} - - -THREADED_TEST(LockUnlockLock) { - { - v8::Locker locker(CcTest::default_isolate()); - v8::HandleScope scope; - LocalContext env; - Local<v8::FunctionTemplate> fun_templ = - v8::FunctionTemplate::New(UnlockForAMoment); - Local<Function> fun = fun_templ->GetFunction(); - env->Global()->Set(v8_str("unlock_for_a_moment"), fun); - Local<Script> script = v8_compile("(function () {" - " unlock_for_a_moment();" - " return 42;" - "})();"); - CHECK_EQ(42, script->Run()->Int32Value()); - } - { - v8::Locker locker(CcTest::default_isolate()); - v8::HandleScope scope; - LocalContext env; - Local<v8::FunctionTemplate> fun_templ = - v8::FunctionTemplate::New(UnlockForAMoment); - Local<Function> fun = fun_templ->GetFunction(); - env->Global()->Set(v8_str("unlock_for_a_moment"), fun); - Local<Script> script = v8_compile("(function () {" - " unlock_for_a_moment();" - " return 42;" - "})();"); - CHECK_EQ(42, script->Run()->Int32Value()); - } -} - - -static int GetGlobalObjectsCount() { - i::Isolate::Current()->heap()->EnsureHeapIsIterable(); - int count = 0; - i::HeapIterator it(HEAP); - for (i::HeapObject* object = it.next(); object != NULL; object = it.next()) - if (object->IsJSGlobalObject()) count++; - return count; -} - - -static void CheckSurvivingGlobalObjectsCount(int expected) { - // We need to collect all garbage twice to be sure that everything - // has been collected. This is because inline caches are cleared in - // the first garbage collection but some of the maps have already - // been marked at that point. Therefore some of the maps are not - // collected until the second garbage collection. - HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); - HEAP->CollectAllGarbage(i::Heap::kMakeHeapIterableMask); - int count = GetGlobalObjectsCount(); -#ifdef DEBUG - if (count != expected) HEAP->TracePathToGlobal(); -#endif - CHECK_EQ(expected, count); -} - - -TEST(DontLeakGlobalObjects) { - // Regression test for issues 1139850 and 1174891. - - v8::V8::Initialize(); - - for (int i = 0; i < 5; i++) { - { v8::HandleScope scope; - LocalContext context; - } - v8::V8::ContextDisposedNotification(); - CheckSurvivingGlobalObjectsCount(0); - - { v8::HandleScope scope; - LocalContext context; - v8_compile("Date")->Run(); - } - v8::V8::ContextDisposedNotification(); - CheckSurvivingGlobalObjectsCount(0); - - { v8::HandleScope scope; - LocalContext context; - v8_compile("/aaa/")->Run(); - } - v8::V8::ContextDisposedNotification(); - CheckSurvivingGlobalObjectsCount(0); - - { v8::HandleScope scope; - const char* extension_list[] = { "v8/gc" }; - v8::ExtensionConfiguration extensions(1, extension_list); - LocalContext context(&extensions); - v8_compile("gc();")->Run(); - } - v8::V8::ContextDisposedNotification(); - CheckSurvivingGlobalObjectsCount(0); - } -} - - -v8::Persistent<v8::Object> some_object; -v8::Persistent<v8::Object> bad_handle; - -void NewPersistentHandleCallback(v8::Isolate* isolate, - v8::Persistent<v8::Value> handle, - void*) { - v8::HandleScope scope; - bad_handle = v8::Persistent<v8::Object>::New(isolate, some_object); - handle.Dispose(isolate); -} - - -THREADED_TEST(NewPersistentHandleFromWeakCallback) { - LocalContext context; - v8::Isolate* isolate = context->GetIsolate(); - - v8::Persistent<v8::Object> handle1, handle2; - { - v8::HandleScope scope; - some_object = v8::Persistent<v8::Object>::New(isolate, v8::Object::New()); - handle1 = v8::Persistent<v8::Object>::New(isolate, v8::Object::New()); - handle2 = v8::Persistent<v8::Object>::New(isolate, v8::Object::New()); - } - // Note: order is implementation dependent alas: currently - // global handle nodes are processed by PostGarbageCollectionProcessing - // in reverse allocation order, so if second allocated handle is deleted, - // weak callback of the first handle would be able to 'reallocate' it. - handle1.MakeWeak(isolate, NULL, NewPersistentHandleCallback); - handle2.Dispose(isolate); - HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); -} - - -v8::Persistent<v8::Object> to_be_disposed; - -void DisposeAndForceGcCallback(v8::Isolate* isolate, - v8::Persistent<v8::Value> handle, - void*) { - to_be_disposed.Dispose(isolate); - HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); - handle.Dispose(isolate); -} - - -THREADED_TEST(DoNotUseDeletedNodesInSecondLevelGc) { - LocalContext context; - v8::Isolate* isolate = context->GetIsolate(); - - v8::Persistent<v8::Object> handle1, handle2; - { - v8::HandleScope scope; - handle1 = v8::Persistent<v8::Object>::New(isolate, v8::Object::New()); - handle2 = v8::Persistent<v8::Object>::New(isolate, v8::Object::New()); - } - handle1.MakeWeak(isolate, NULL, DisposeAndForceGcCallback); - to_be_disposed = handle2; - HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); -} - -void DisposingCallback(v8::Isolate* isolate, - v8::Persistent<v8::Value> handle, - void*) { - handle.Dispose(isolate); -} - -void HandleCreatingCallback(v8::Isolate* isolate, - v8::Persistent<v8::Value> handle, - void*) { - v8::HandleScope scope; - v8::Persistent<v8::Object>::New(isolate, v8::Object::New()); - handle.Dispose(isolate); -} - - -THREADED_TEST(NoGlobalHandlesOrphaningDueToWeakCallback) { - LocalContext context; - v8::Isolate* isolate = context->GetIsolate(); - - v8::Persistent<v8::Object> handle1, handle2, handle3; - { - v8::HandleScope scope; - handle3 = v8::Persistent<v8::Object>::New(isolate, v8::Object::New()); - handle2 = v8::Persistent<v8::Object>::New(isolate, v8::Object::New()); - handle1 = v8::Persistent<v8::Object>::New(isolate, v8::Object::New()); - } - handle2.MakeWeak(isolate, NULL, DisposingCallback); - handle3.MakeWeak(isolate, NULL, HandleCreatingCallback); - HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); -} - - -THREADED_TEST(CheckForCrossContextObjectLiterals) { - v8::V8::Initialize(); - - const int nof = 2; - const char* sources[nof] = { - "try { [ 2, 3, 4 ].forEach(5); } catch(e) { e.toString(); }", - "Object()" - }; - - for (int i = 0; i < nof; i++) { - const char* source = sources[i]; - { v8::HandleScope scope; - LocalContext context; - CompileRun(source); - } - { v8::HandleScope scope; - LocalContext context; - CompileRun(source); - } - } -} - - -static v8::Handle<Value> NestedScope(v8::Persistent<Context> env) { - v8::HandleScope inner; - env->Enter(); - v8::Handle<Value> three = v8_num(3); - v8::Handle<Value> value = inner.Close(three); - env->Exit(); - return value; -} - - -THREADED_TEST(NestedHandleScopeAndContexts) { - v8::HandleScope outer; - v8::Persistent<Context> env = Context::New(); - env->Enter(); - v8::Handle<Value> value = NestedScope(env); - v8::Handle<String> str(value->ToString()); - CHECK(!str.IsEmpty()); - env->Exit(); - env.Dispose(env->GetIsolate()); -} - - -static i::Handle<i::JSFunction>* foo_ptr = NULL; -static int foo_count = 0; -static i::Handle<i::JSFunction>* bar_ptr = NULL; -static int bar_count = 0; - - -static void entry_hook(uintptr_t function, - uintptr_t return_addr_location) { - i::Code* code = i::Code::GetCodeFromTargetAddress( - reinterpret_cast<i::Address>(function)); - CHECK(code != NULL); - - if (bar_ptr != NULL && code == (*bar_ptr)->code()) - ++bar_count; - - if (foo_ptr != NULL && code == (*foo_ptr)->code()) - ++foo_count; - - // TODO(siggi): Verify return_addr_location. - // This can be done by capturing JitCodeEvents, but requires an ordered - // collection. -} - - -static void RunLoopInNewEnv() { - bar_ptr = NULL; - foo_ptr = NULL; - - v8::HandleScope outer; - v8::Persistent<Context> env = Context::New(); - env->Enter(); - - const char* script = - "function bar() {" - " var sum = 0;" - " for (i = 0; i < 100; ++i)" - " sum = foo(i);" - " return sum;" - "}" - "function foo(i) { return i * i; }"; - CompileRun(script); - i::Handle<i::JSFunction> bar = - i::Handle<i::JSFunction>::cast( - v8::Utils::OpenHandle(*env->Global()->Get(v8_str("bar")))); - ASSERT(*bar); - - i::Handle<i::JSFunction> foo = - i::Handle<i::JSFunction>::cast( - v8::Utils::OpenHandle(*env->Global()->Get(v8_str("foo")))); - ASSERT(*foo); - - bar_ptr = &bar; - foo_ptr = &foo; - - v8::Handle<v8::Value> value = CompileRun("bar();"); - CHECK(value->IsNumber()); - CHECK_EQ(9801.0, v8::Number::Cast(*value)->Value()); - - // Test the optimized codegen path. - value = CompileRun("%OptimizeFunctionOnNextCall(foo);" - "bar();"); - CHECK(value->IsNumber()); - CHECK_EQ(9801.0, v8::Number::Cast(*value)->Value()); - - env->Exit(); -} - - -TEST(SetFunctionEntryHook) { - i::FLAG_allow_natives_syntax = true; - i::FLAG_use_inlining = false; - - // Test setting and resetting the entry hook. - // Nulling it should always succeed. - CHECK(v8::V8::SetFunctionEntryHook(NULL)); - - CHECK(v8::V8::SetFunctionEntryHook(entry_hook)); - // Setting a hook while one's active should fail. - CHECK_EQ(false, v8::V8::SetFunctionEntryHook(entry_hook)); - - CHECK(v8::V8::SetFunctionEntryHook(NULL)); - - // Reset the entry count to zero and set the entry hook. - bar_count = 0; - foo_count = 0; - CHECK(v8::V8::SetFunctionEntryHook(entry_hook)); - RunLoopInNewEnv(); - - CHECK_EQ(2, bar_count); - CHECK_EQ(200, foo_count); - - // Clear the entry hook and count. - bar_count = 0; - foo_count = 0; - v8::V8::SetFunctionEntryHook(NULL); - - // Clear the compilation cache to make sure we don't reuse the - // functions from the previous invocation. - v8::internal::Isolate::Current()->compilation_cache()->Clear(); - - // Verify that entry hooking is now disabled. - RunLoopInNewEnv(); - CHECK_EQ(0u, bar_count); - CHECK_EQ(0u, foo_count); -} - - -static i::HashMap* code_map = NULL; -static i::HashMap* jitcode_line_info = NULL; -static int saw_bar = 0; -static int move_events = 0; - - -static bool FunctionNameIs(const char* expected, - const v8::JitCodeEvent* event) { - // Log lines for functions are of the general form: - // "LazyCompile:<type><function_name>", where the type is one of - // "*", "~" or "". - static const char kPreamble[] = "LazyCompile:"; - static size_t kPreambleLen = sizeof(kPreamble) - 1; - - if (event->name.len < sizeof(kPreamble) - 1 || - strncmp(kPreamble, event->name.str, kPreambleLen) != 0) { - return false; - } - - const char* tail = event->name.str + kPreambleLen; - size_t tail_len = event->name.len - kPreambleLen; - size_t expected_len = strlen(expected); - if (tail_len == expected_len + 1) { - if (*tail == '*' || *tail == '~') { - --tail_len; - ++tail; - } else { - return false; - } - } - - if (tail_len != expected_len) - return false; - - return strncmp(tail, expected, expected_len) == 0; -} - - -static void event_handler(const v8::JitCodeEvent* event) { - CHECK(event != NULL); - CHECK(code_map != NULL); - CHECK(jitcode_line_info != NULL); - - class DummyJitCodeLineInfo { - }; - - switch (event->type) { - case v8::JitCodeEvent::CODE_ADDED: { - CHECK(event->code_start != NULL); - CHECK_NE(0, static_cast<int>(event->code_len)); - CHECK(event->name.str != NULL); - i::HashMap::Entry* entry = - code_map->Lookup(event->code_start, - i::ComputePointerHash(event->code_start), - true); - entry->value = reinterpret_cast<void*>(event->code_len); - - if (FunctionNameIs("bar", event)) { - ++saw_bar; - } - } - break; - - case v8::JitCodeEvent::CODE_MOVED: { - uint32_t hash = i::ComputePointerHash(event->code_start); - // We would like to never see code move that we haven't seen before, - // but the code creation event does not happen until the line endings - // have been calculated (this is so that we can report the line in the - // script at which the function source is found, see - // Compiler::RecordFunctionCompilation) and the line endings - // calculations can cause a GC, which can move the newly created code - // before its existence can be logged. - i::HashMap::Entry* entry = - code_map->Lookup(event->code_start, hash, false); - if (entry != NULL) { - ++move_events; - - CHECK_EQ(reinterpret_cast<void*>(event->code_len), entry->value); - code_map->Remove(event->code_start, hash); - - entry = code_map->Lookup(event->new_code_start, - i::ComputePointerHash(event->new_code_start), - true); - CHECK(entry != NULL); - entry->value = reinterpret_cast<void*>(event->code_len); - } - } - break; - - case v8::JitCodeEvent::CODE_REMOVED: - // Object/code removal events are currently not dispatched from the GC. - CHECK(false); - break; - - // For CODE_START_LINE_INFO_RECORDING event, we will create one - // DummyJitCodeLineInfo data structure pointed by event->user_dat. We - // record it in jitcode_line_info. - case v8::JitCodeEvent::CODE_START_LINE_INFO_RECORDING: { - DummyJitCodeLineInfo* line_info = new DummyJitCodeLineInfo(); - v8::JitCodeEvent* temp_event = const_cast<v8::JitCodeEvent*>(event); - temp_event->user_data = line_info; - i::HashMap::Entry* entry = - jitcode_line_info->Lookup(line_info, - i::ComputePointerHash(line_info), - true); - entry->value = reinterpret_cast<void*>(line_info); - } - break; - // For these two events, we will check whether the event->user_data - // data structure is created before during CODE_START_LINE_INFO_RECORDING - // event. And delete it in CODE_END_LINE_INFO_RECORDING event handling. - case v8::JitCodeEvent::CODE_END_LINE_INFO_RECORDING: { - CHECK(event->user_data != NULL); - uint32_t hash = i::ComputePointerHash(event->user_data); - i::HashMap::Entry* entry = - jitcode_line_info->Lookup(event->user_data, hash, false); - CHECK(entry != NULL); - delete reinterpret_cast<DummyJitCodeLineInfo*>(event->user_data); - } - break; - - case v8::JitCodeEvent::CODE_ADD_LINE_POS_INFO: { - CHECK(event->user_data != NULL); - uint32_t hash = i::ComputePointerHash(event->user_data); - i::HashMap::Entry* entry = - jitcode_line_info->Lookup(event->user_data, hash, false); - CHECK(entry != NULL); - } - break; - - default: - // Impossible event. - CHECK(false); - break; - } -} - - -static bool MatchPointers(void* key1, void* key2) { - return key1 == key2; -} - - -TEST(SetJitCodeEventHandler) { - i::FLAG_stress_compaction = true; - const char* script = - "function bar() {" - " var sum = 0;" - " for (i = 0; i < 100; ++i)" - " sum = foo(i);" - " return sum;" - "}" - "function foo(i) { return i * i; };" - "bar();"; - - // Run this test in a new isolate to make sure we don't - // have remnants of state from other code. - v8::Isolate* isolate = v8::Isolate::New(); - isolate->Enter(); - - { - v8::HandleScope scope; - i::HashMap code(MatchPointers); - code_map = &code; - - i::HashMap lineinfo(MatchPointers); - jitcode_line_info = &lineinfo; - - saw_bar = 0; - move_events = 0; - - V8::SetJitCodeEventHandler(v8::kJitCodeEventDefault, event_handler); - - // Generate new code objects sparsely distributed across several - // different fragmented code-space pages. - const int kIterations = 10; - for (int i = 0; i < kIterations; ++i) { - LocalContext env; - i::AlwaysAllocateScope always_allocate; - SimulateFullSpace(HEAP->code_space()); - CompileRun(script); - - // Keep a strong reference to the code object in the handle scope. - i::Handle<i::Code> bar_code(i::Handle<i::JSFunction>::cast( - v8::Utils::OpenHandle(*env->Global()->Get(v8_str("bar"))))->code()); - i::Handle<i::Code> foo_code(i::Handle<i::JSFunction>::cast( - v8::Utils::OpenHandle(*env->Global()->Get(v8_str("foo"))))->code()); - - // Clear the compilation cache to get more wastage. - ISOLATE->compilation_cache()->Clear(); - } - - // Force code movement. - HEAP->CollectAllAvailableGarbage("TestSetJitCodeEventHandler"); - - V8::SetJitCodeEventHandler(v8::kJitCodeEventDefault, NULL); - - CHECK_LE(kIterations, saw_bar); - CHECK_LT(0, move_events); - - code_map = NULL; - jitcode_line_info = NULL; - } - - isolate->Exit(); - isolate->Dispose(); - - // Do this in a new isolate. - isolate = v8::Isolate::New(); - isolate->Enter(); - - // Verify that we get callbacks for existing code objects when we - // request enumeration of existing code. - { - v8::HandleScope scope; - LocalContext env; - CompileRun(script); - - // Now get code through initial iteration. - i::HashMap code(MatchPointers); - code_map = &code; - - i::HashMap lineinfo(MatchPointers); - jitcode_line_info = &lineinfo; - - V8::SetJitCodeEventHandler(v8::kJitCodeEventEnumExisting, event_handler); - V8::SetJitCodeEventHandler(v8::kJitCodeEventDefault, NULL); - - jitcode_line_info = NULL; - // We expect that we got some events. Note that if we could get code removal - // notifications, we could compare two collections, one created by listening - // from the time of creation of an isolate, and the other by subscribing - // with EnumExisting. - CHECK_LT(0, code.occupancy()); - - code_map = NULL; - } - - isolate->Exit(); - isolate->Dispose(); -} - - -static int64_t cast(intptr_t x) { return static_cast<int64_t>(x); } - - -THREADED_TEST(ExternalAllocatedMemory) { - v8::HandleScope outer; - v8::Persistent<Context> env(Context::New()); - CHECK(!env.IsEmpty()); - const intptr_t kSize = 1024*1024; - CHECK_EQ(cast(v8::V8::AdjustAmountOfExternalAllocatedMemory(kSize)), - cast(kSize)); - CHECK_EQ(cast(v8::V8::AdjustAmountOfExternalAllocatedMemory(-kSize)), - cast(0)); -} - - -THREADED_TEST(DisposeEnteredContext) { - v8::HandleScope scope; - LocalContext outer; - { v8::Persistent<v8::Context> inner = v8::Context::New(); - inner->Enter(); - inner.Dispose(inner->GetIsolate()); - inner.Clear(); - inner->Exit(); - } -} - - -// Regression test for issue 54, object templates with internal fields -// but no accessors or interceptors did not get their internal field -// count set on instances. -THREADED_TEST(Regress54) { - v8::HandleScope outer; - LocalContext context; - v8::Isolate* isolate = context->GetIsolate(); - static v8::Persistent<v8::ObjectTemplate> templ; - if (templ.IsEmpty()) { - v8::HandleScope inner; - v8::Handle<v8::ObjectTemplate> local = v8::ObjectTemplate::New(); - local->SetInternalFieldCount(1); - templ = - v8::Persistent<v8::ObjectTemplate>::New(isolate, inner.Close(local)); - } - v8::Handle<v8::Object> result = templ->NewInstance(); - CHECK_EQ(1, result->InternalFieldCount()); -} - - -// If part of the threaded tests, this test makes ThreadingTest fail -// on mac. -TEST(CatchStackOverflow) { - v8::HandleScope scope; - LocalContext context; - v8::TryCatch try_catch; - v8::Handle<v8::Script> script = v8::Script::Compile(v8::String::New( - "function f() {" - " return f();" - "}" - "" - "f();")); - v8::Handle<v8::Value> result = script->Run(); - CHECK(result.IsEmpty()); -} - - -static void CheckTryCatchSourceInfo(v8::Handle<v8::Script> script, - const char* resource_name, - int line_offset) { - v8::HandleScope scope; - v8::TryCatch try_catch; - v8::Handle<v8::Value> result = script->Run(); - CHECK(result.IsEmpty()); - CHECK(try_catch.HasCaught()); - v8::Handle<v8::Message> message = try_catch.Message(); - CHECK(!message.IsEmpty()); - CHECK_EQ(10 + line_offset, message->GetLineNumber()); - CHECK_EQ(91, message->GetStartPosition()); - CHECK_EQ(92, message->GetEndPosition()); - CHECK_EQ(2, message->GetStartColumn()); - CHECK_EQ(3, message->GetEndColumn()); - v8::String::AsciiValue line(message->GetSourceLine()); - CHECK_EQ(" throw 'nirk';", *line); - v8::String::AsciiValue name(message->GetScriptResourceName()); - CHECK_EQ(resource_name, *name); -} - - -THREADED_TEST(TryCatchSourceInfo) { - v8::HandleScope scope; - LocalContext context; - v8::Handle<v8::String> source = v8::String::New( - "function Foo() {\n" - " return Bar();\n" - "}\n" - "\n" - "function Bar() {\n" - " return Baz();\n" - "}\n" - "\n" - "function Baz() {\n" - " throw 'nirk';\n" - "}\n" - "\n" - "Foo();\n"); - - const char* resource_name; - v8::Handle<v8::Script> script; - resource_name = "test.js"; - script = v8::Script::Compile(source, v8::String::New(resource_name)); - CheckTryCatchSourceInfo(script, resource_name, 0); - - resource_name = "test1.js"; - v8::ScriptOrigin origin1(v8::String::New(resource_name)); - script = v8::Script::Compile(source, &origin1); - CheckTryCatchSourceInfo(script, resource_name, 0); - - resource_name = "test2.js"; - v8::ScriptOrigin origin2(v8::String::New(resource_name), v8::Integer::New(7)); - script = v8::Script::Compile(source, &origin2); - CheckTryCatchSourceInfo(script, resource_name, 7); -} - - -THREADED_TEST(CompilationCache) { - v8::HandleScope scope; - LocalContext context; - v8::Handle<v8::String> source0 = v8::String::New("1234"); - v8::Handle<v8::String> source1 = v8::String::New("1234"); - v8::Handle<v8::Script> script0 = - v8::Script::Compile(source0, v8::String::New("test.js")); - v8::Handle<v8::Script> script1 = - v8::Script::Compile(source1, v8::String::New("test.js")); - v8::Handle<v8::Script> script2 = - v8::Script::Compile(source0); // different origin - CHECK_EQ(1234, script0->Run()->Int32Value()); - CHECK_EQ(1234, script1->Run()->Int32Value()); - CHECK_EQ(1234, script2->Run()->Int32Value()); -} - - -static v8::Handle<Value> FunctionNameCallback(const v8::Arguments& args) { - ApiTestFuzzer::Fuzz(); - return v8_num(42); -} - - -THREADED_TEST(CallbackFunctionName) { - v8::HandleScope scope; - LocalContext context; - Local<ObjectTemplate> t = ObjectTemplate::New(); - t->Set(v8_str("asdf"), v8::FunctionTemplate::New(FunctionNameCallback)); - context->Global()->Set(v8_str("obj"), t->NewInstance()); - v8::Handle<v8::Value> value = CompileRun("obj.asdf.name"); - CHECK(value->IsString()); - v8::String::AsciiValue name(value); - CHECK_EQ("asdf", *name); -} - - -THREADED_TEST(DateAccess) { - v8::HandleScope scope; - LocalContext context; - v8::Handle<v8::Value> date = v8::Date::New(1224744689038.0); - CHECK(date->IsDate()); - CHECK_EQ(1224744689038.0, date.As<v8::Date>()->NumberValue()); -} - - -void CheckProperties(v8::Handle<v8::Value> val, int elmc, const char* elmv[]) { - v8::Handle<v8::Object> obj = val.As<v8::Object>(); - v8::Handle<v8::Array> props = obj->GetPropertyNames(); - CHECK_EQ(elmc, props->Length()); - for (int i = 0; i < elmc; i++) { - v8::String::Utf8Value elm(props->Get(v8::Integer::New(i))); - CHECK_EQ(elmv[i], *elm); - } -} - - -void CheckOwnProperties(v8::Handle<v8::Value> val, - int elmc, - const char* elmv[]) { - v8::Handle<v8::Object> obj = val.As<v8::Object>(); - v8::Handle<v8::Array> props = obj->GetOwnPropertyNames(); - CHECK_EQ(elmc, props->Length()); - for (int i = 0; i < elmc; i++) { - v8::String::Utf8Value elm(props->Get(v8::Integer::New(i))); - CHECK_EQ(elmv[i], *elm); - } -} - - -THREADED_TEST(PropertyEnumeration) { - v8::HandleScope scope; - LocalContext context; - v8::Handle<v8::Value> obj = v8::Script::Compile(v8::String::New( - "var result = [];" - "result[0] = {};" - "result[1] = {a: 1, b: 2};" - "result[2] = [1, 2, 3];" - "var proto = {x: 1, y: 2, z: 3};" - "var x = { __proto__: proto, w: 0, z: 1 };" - "result[3] = x;" - "result;"))->Run(); - v8::Handle<v8::Array> elms = obj.As<v8::Array>(); - CHECK_EQ(4, elms->Length()); - int elmc0 = 0; - const char** elmv0 = NULL; - CheckProperties(elms->Get(v8::Integer::New(0)), elmc0, elmv0); - CheckOwnProperties(elms->Get(v8::Integer::New(0)), elmc0, elmv0); - int elmc1 = 2; - const char* elmv1[] = {"a", "b"}; - CheckProperties(elms->Get(v8::Integer::New(1)), elmc1, elmv1); - CheckOwnProperties(elms->Get(v8::Integer::New(1)), elmc1, elmv1); - int elmc2 = 3; - const char* elmv2[] = {"0", "1", "2"}; - CheckProperties(elms->Get(v8::Integer::New(2)), elmc2, elmv2); - CheckOwnProperties(elms->Get(v8::Integer::New(2)), elmc2, elmv2); - int elmc3 = 4; - const char* elmv3[] = {"w", "z", "x", "y"}; - CheckProperties(elms->Get(v8::Integer::New(3)), elmc3, elmv3); - int elmc4 = 2; - const char* elmv4[] = {"w", "z"}; - CheckOwnProperties(elms->Get(v8::Integer::New(3)), elmc4, elmv4); -} - -THREADED_TEST(PropertyEnumeration2) { - v8::HandleScope scope; - LocalContext context; - v8::Handle<v8::Value> obj = v8::Script::Compile(v8::String::New( - "var result = [];" - "result[0] = {};" - "result[1] = {a: 1, b: 2};" - "result[2] = [1, 2, 3];" - "var proto = {x: 1, y: 2, z: 3};" - "var x = { __proto__: proto, w: 0, z: 1 };" - "result[3] = x;" - "result;"))->Run(); - v8::Handle<v8::Array> elms = obj.As<v8::Array>(); - CHECK_EQ(4, elms->Length()); - int elmc0 = 0; - const char** elmv0 = NULL; - CheckProperties(elms->Get(v8::Integer::New(0)), elmc0, elmv0); - - v8::Handle<v8::Value> val = elms->Get(v8::Integer::New(0)); - v8::Handle<v8::Array> props = val.As<v8::Object>()->GetPropertyNames(); - CHECK_EQ(0, props->Length()); - for (uint32_t i = 0; i < props->Length(); i++) { - printf("p[%d]\n", i); - } -} - -static bool NamedSetAccessBlocker(Local<v8::Object> obj, - Local<Value> name, - v8::AccessType type, - Local<Value> data) { - return type != v8::ACCESS_SET; -} - - -static bool IndexedSetAccessBlocker(Local<v8::Object> obj, - uint32_t key, - v8::AccessType type, - Local<Value> data) { - return type != v8::ACCESS_SET; -} - - -THREADED_TEST(DisableAccessChecksWhileConfiguring) { - v8::HandleScope scope; - LocalContext context; - Local<ObjectTemplate> templ = ObjectTemplate::New(); - templ->SetAccessCheckCallbacks(NamedSetAccessBlocker, - IndexedSetAccessBlocker); - templ->Set(v8_str("x"), v8::True()); - Local<v8::Object> instance = templ->NewInstance(); - context->Global()->Set(v8_str("obj"), instance); - Local<Value> value = CompileRun("obj.x"); - CHECK(value->BooleanValue()); -} - - -static bool NamedGetAccessBlocker(Local<v8::Object> obj, - Local<Value> name, - v8::AccessType type, - Local<Value> data) { - return false; -} - - -static bool IndexedGetAccessBlocker(Local<v8::Object> obj, - uint32_t key, - v8::AccessType type, - Local<Value> data) { - return false; -} - - - -THREADED_TEST(AccessChecksReenabledCorrectly) { - v8::HandleScope scope; - LocalContext context; - Local<ObjectTemplate> templ = ObjectTemplate::New(); - templ->SetAccessCheckCallbacks(NamedGetAccessBlocker, - IndexedGetAccessBlocker); - templ->Set(v8_str("a"), v8_str("a")); - // Add more than 8 (see kMaxFastProperties) properties - // so that the constructor will force copying map. - // Cannot sprintf, gcc complains unsafety. - char buf[4]; - for (char i = '0'; i <= '9' ; i++) { - buf[0] = i; - for (char j = '0'; j <= '9'; j++) { - buf[1] = j; - for (char k = '0'; k <= '9'; k++) { - buf[2] = k; - buf[3] = 0; - templ->Set(v8_str(buf), v8::Number::New(k)); - } - } - } - - Local<v8::Object> instance_1 = templ->NewInstance(); - context->Global()->Set(v8_str("obj_1"), instance_1); - - Local<Value> value_1 = CompileRun("obj_1.a"); - CHECK(value_1->IsUndefined()); - - Local<v8::Object> instance_2 = templ->NewInstance(); - context->Global()->Set(v8_str("obj_2"), instance_2); - - Local<Value> value_2 = CompileRun("obj_2.a"); - CHECK(value_2->IsUndefined()); -} - - -// This tests that access check information remains on the global -// object template when creating contexts. -THREADED_TEST(AccessControlRepeatedContextCreation) { - v8::HandleScope handle_scope; - v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); - global_template->SetAccessCheckCallbacks(NamedSetAccessBlocker, - IndexedSetAccessBlocker); - i::Handle<i::ObjectTemplateInfo> internal_template = - v8::Utils::OpenHandle(*global_template); - CHECK(!internal_template->constructor()->IsUndefined()); - i::Handle<i::FunctionTemplateInfo> constructor( - i::FunctionTemplateInfo::cast(internal_template->constructor())); - CHECK(!constructor->access_check_info()->IsUndefined()); - v8::Persistent<Context> context0(Context::New(NULL, global_template)); - CHECK(!context0.IsEmpty()); - CHECK(!constructor->access_check_info()->IsUndefined()); -} - - -THREADED_TEST(TurnOnAccessCheck) { - v8::HandleScope handle_scope; - - // Create an environment with access check to the global object disabled by - // default. - v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); - global_template->SetAccessCheckCallbacks(NamedGetAccessBlocker, - IndexedGetAccessBlocker, - v8::Handle<v8::Value>(), - false); - v8::Persistent<Context> context = Context::New(NULL, global_template); - Context::Scope context_scope(context); - - // Set up a property and a number of functions. - context->Global()->Set(v8_str("a"), v8_num(1)); - CompileRun("function f1() {return a;}" - "function f2() {return a;}" - "function g1() {return h();}" - "function g2() {return h();}" - "function h() {return 1;}"); - Local<Function> f1 = - Local<Function>::Cast(context->Global()->Get(v8_str("f1"))); - Local<Function> f2 = - Local<Function>::Cast(context->Global()->Get(v8_str("f2"))); - Local<Function> g1 = - Local<Function>::Cast(context->Global()->Get(v8_str("g1"))); - Local<Function> g2 = - Local<Function>::Cast(context->Global()->Get(v8_str("g2"))); - Local<Function> h = - Local<Function>::Cast(context->Global()->Get(v8_str("h"))); - - // Get the global object. - v8::Handle<v8::Object> global = context->Global(); - - // Call f1 one time and f2 a number of times. This will ensure that f1 still - // uses the runtime system to retreive property a whereas f2 uses global load - // inline cache. - CHECK(f1->Call(global, 0, NULL)->Equals(v8_num(1))); - for (int i = 0; i < 4; i++) { - CHECK(f2->Call(global, 0, NULL)->Equals(v8_num(1))); - } - - // Same for g1 and g2. - CHECK(g1->Call(global, 0, NULL)->Equals(v8_num(1))); - for (int i = 0; i < 4; i++) { - CHECK(g2->Call(global, 0, NULL)->Equals(v8_num(1))); - } - - // Detach the global and turn on access check. - context->DetachGlobal(); - context->Global()->TurnOnAccessCheck(); - - // Failing access check to property get results in undefined. - CHECK(f1->Call(global, 0, NULL)->IsUndefined()); - CHECK(f2->Call(global, 0, NULL)->IsUndefined()); - - // Failing access check to function call results in exception. - CHECK(g1->Call(global, 0, NULL).IsEmpty()); - CHECK(g2->Call(global, 0, NULL).IsEmpty()); - - // No failing access check when just returning a constant. - CHECK(h->Call(global, 0, NULL)->Equals(v8_num(1))); -} - - -static const char* kPropertyA = "a"; -static const char* kPropertyH = "h"; - -static bool NamedGetAccessBlockAandH(Local<v8::Object> obj, - Local<Value> name, - v8::AccessType type, - Local<Value> data) { - if (!name->IsString()) return false; - i::Handle<i::String> name_handle = - v8::Utils::OpenHandle(String::Cast(*name)); - return !name_handle->IsUtf8EqualTo(i::CStrVector(kPropertyA)) - && !name_handle->IsUtf8EqualTo(i::CStrVector(kPropertyH)); -} - - -THREADED_TEST(TurnOnAccessCheckAndRecompile) { - v8::HandleScope handle_scope; - - // Create an environment with access check to the global object disabled by - // default. When the registered access checker will block access to properties - // a and h. - v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); - global_template->SetAccessCheckCallbacks(NamedGetAccessBlockAandH, - IndexedGetAccessBlocker, - v8::Handle<v8::Value>(), - false); - v8::Persistent<Context> context = Context::New(NULL, global_template); - Context::Scope context_scope(context); - - // Set up a property and a number of functions. - context->Global()->Set(v8_str("a"), v8_num(1)); - static const char* source = "function f1() {return a;}" - "function f2() {return a;}" - "function g1() {return h();}" - "function g2() {return h();}" - "function h() {return 1;}"; - - CompileRun(source); - Local<Function> f1; - Local<Function> f2; - Local<Function> g1; - Local<Function> g2; - Local<Function> h; - f1 = Local<Function>::Cast(context->Global()->Get(v8_str("f1"))); - f2 = Local<Function>::Cast(context->Global()->Get(v8_str("f2"))); - g1 = Local<Function>::Cast(context->Global()->Get(v8_str("g1"))); - g2 = Local<Function>::Cast(context->Global()->Get(v8_str("g2"))); - h = Local<Function>::Cast(context->Global()->Get(v8_str("h"))); - - // Get the global object. - v8::Handle<v8::Object> global = context->Global(); - - // Call f1 one time and f2 a number of times. This will ensure that f1 still - // uses the runtime system to retreive property a whereas f2 uses global load - // inline cache. - CHECK(f1->Call(global, 0, NULL)->Equals(v8_num(1))); - for (int i = 0; i < 4; i++) { - CHECK(f2->Call(global, 0, NULL)->Equals(v8_num(1))); - } - - // Same for g1 and g2. - CHECK(g1->Call(global, 0, NULL)->Equals(v8_num(1))); - for (int i = 0; i < 4; i++) { - CHECK(g2->Call(global, 0, NULL)->Equals(v8_num(1))); - } - - // Detach the global and turn on access check now blocking access to property - // a and function h. - context->DetachGlobal(); - context->Global()->TurnOnAccessCheck(); - - // Failing access check to property get results in undefined. - CHECK(f1->Call(global, 0, NULL)->IsUndefined()); - CHECK(f2->Call(global, 0, NULL)->IsUndefined()); - - // Failing access check to function call results in exception. - CHECK(g1->Call(global, 0, NULL).IsEmpty()); - CHECK(g2->Call(global, 0, NULL).IsEmpty()); - - // No failing access check when just returning a constant. - CHECK(h->Call(global, 0, NULL)->Equals(v8_num(1))); - - // Now compile the source again. And get the newly compiled functions, except - // for h for which access is blocked. - CompileRun(source); - f1 = Local<Function>::Cast(context->Global()->Get(v8_str("f1"))); - f2 = Local<Function>::Cast(context->Global()->Get(v8_str("f2"))); - g1 = Local<Function>::Cast(context->Global()->Get(v8_str("g1"))); - g2 = Local<Function>::Cast(context->Global()->Get(v8_str("g2"))); - CHECK(context->Global()->Get(v8_str("h"))->IsUndefined()); - - // Failing access check to property get results in undefined. - CHECK(f1->Call(global, 0, NULL)->IsUndefined()); - CHECK(f2->Call(global, 0, NULL)->IsUndefined()); - - // Failing access check to function call results in exception. - CHECK(g1->Call(global, 0, NULL).IsEmpty()); - CHECK(g2->Call(global, 0, NULL).IsEmpty()); -} - - -// This test verifies that pre-compilation (aka preparsing) can be called -// without initializing the whole VM. Thus we cannot run this test in a -// multi-threaded setup. -TEST(PreCompile) { - // TODO(155): This test would break without the initialization of V8. This is - // a workaround for now to make this test not fail. - v8::V8::Initialize(); - const char* script = "function foo(a) { return a+1; }"; - v8::ScriptData* sd = - v8::ScriptData::PreCompile(script, i::StrLength(script)); - CHECK_NE(sd->Length(), 0); - CHECK_NE(sd->Data(), NULL); - CHECK(!sd->HasError()); - delete sd; -} - - -TEST(PreCompileWithError) { - v8::V8::Initialize(); - const char* script = "function foo(a) { return 1 * * 2; }"; - v8::ScriptData* sd = - v8::ScriptData::PreCompile(script, i::StrLength(script)); - CHECK(sd->HasError()); - delete sd; -} - - -TEST(Regress31661) { - v8::V8::Initialize(); - const char* script = " The Definintive Guide"; - v8::ScriptData* sd = - v8::ScriptData::PreCompile(script, i::StrLength(script)); - CHECK(sd->HasError()); - delete sd; -} - - -// Tests that ScriptData can be serialized and deserialized. -TEST(PreCompileSerialization) { - v8::V8::Initialize(); - const char* script = "function foo(a) { return a+1; }"; - v8::ScriptData* sd = - v8::ScriptData::PreCompile(script, i::StrLength(script)); - - // Serialize. - int serialized_data_length = sd->Length(); - char* serialized_data = i::NewArray<char>(serialized_data_length); - memcpy(serialized_data, sd->Data(), serialized_data_length); - - // Deserialize. - v8::ScriptData* deserialized_sd = - v8::ScriptData::New(serialized_data, serialized_data_length); - - // Verify that the original is the same as the deserialized. - CHECK_EQ(sd->Length(), deserialized_sd->Length()); - CHECK_EQ(0, memcmp(sd->Data(), deserialized_sd->Data(), sd->Length())); - CHECK_EQ(sd->HasError(), deserialized_sd->HasError()); - - delete sd; - delete deserialized_sd; -} - - -// Attempts to deserialize bad data. -TEST(PreCompileDeserializationError) { - v8::V8::Initialize(); - const char* data = "DONT CARE"; - int invalid_size = 3; - v8::ScriptData* sd = v8::ScriptData::New(data, invalid_size); - - CHECK_EQ(0, sd->Length()); - - delete sd; -} - - -// Attempts to deserialize bad data. -TEST(PreCompileInvalidPreparseDataError) { - v8::V8::Initialize(); - v8::HandleScope scope; - LocalContext context; - - const char* script = "function foo(){ return 5;}\n" - "function bar(){ return 6 + 7;} foo();"; - v8::ScriptData* sd = - v8::ScriptData::PreCompile(script, i::StrLength(script)); - CHECK(!sd->HasError()); - // ScriptDataImpl private implementation details - const int kHeaderSize = i::PreparseDataConstants::kHeaderSize; - const int kFunctionEntrySize = i::FunctionEntry::kSize; - const int kFunctionEntryStartOffset = 0; - const int kFunctionEntryEndOffset = 1; - unsigned* sd_data = - reinterpret_cast<unsigned*>(const_cast<char*>(sd->Data())); - - // Overwrite function bar's end position with 0. - sd_data[kHeaderSize + 1 * kFunctionEntrySize + kFunctionEntryEndOffset] = 0; - v8::TryCatch try_catch; - - Local<String> source = String::New(script); - Local<Script> compiled_script = Script::New(source, NULL, sd); - CHECK(try_catch.HasCaught()); - String::AsciiValue exception_value(try_catch.Message()->Get()); - CHECK_EQ("Uncaught SyntaxError: Invalid preparser data for function bar", - *exception_value); - - try_catch.Reset(); - - // Overwrite function bar's start position with 200. The function entry - // will not be found when searching for it by position and we should fall - // back on eager compilation. - sd = v8::ScriptData::PreCompile(script, i::StrLength(script)); - sd_data = reinterpret_cast<unsigned*>(const_cast<char*>(sd->Data())); - sd_data[kHeaderSize + 1 * kFunctionEntrySize + kFunctionEntryStartOffset] = - 200; - compiled_script = Script::New(source, NULL, sd); - CHECK(!try_catch.HasCaught()); - - delete sd; -} - - -// Verifies that the Handle<String> and const char* versions of the API produce -// the same results (at least for one trivial case). -TEST(PreCompileAPIVariationsAreSame) { - v8::V8::Initialize(); - v8::HandleScope scope; - - const char* cstring = "function foo(a) { return a+1; }"; - - v8::ScriptData* sd_from_cstring = - v8::ScriptData::PreCompile(cstring, i::StrLength(cstring)); - - TestAsciiResource* resource = new TestAsciiResource(cstring); - v8::ScriptData* sd_from_external_string = v8::ScriptData::PreCompile( - v8::String::NewExternal(resource)); - - v8::ScriptData* sd_from_string = v8::ScriptData::PreCompile( - v8::String::New(cstring)); - - CHECK_EQ(sd_from_cstring->Length(), sd_from_external_string->Length()); - CHECK_EQ(0, memcmp(sd_from_cstring->Data(), - sd_from_external_string->Data(), - sd_from_cstring->Length())); - - CHECK_EQ(sd_from_cstring->Length(), sd_from_string->Length()); - CHECK_EQ(0, memcmp(sd_from_cstring->Data(), - sd_from_string->Data(), - sd_from_cstring->Length())); - - - delete sd_from_cstring; - delete sd_from_external_string; - delete sd_from_string; -} - - -// This tests that we do not allow dictionary load/call inline caches -// to use functions that have not yet been compiled. The potential -// problem of loading a function that has not yet been compiled can -// arise because we share code between contexts via the compilation -// cache. -THREADED_TEST(DictionaryICLoadedFunction) { - v8::HandleScope scope; - // Test LoadIC. - for (int i = 0; i < 2; i++) { - LocalContext context; - context->Global()->Set(v8_str("tmp"), v8::True()); - context->Global()->Delete(v8_str("tmp")); - CompileRun("for (var j = 0; j < 10; j++) new RegExp('');"); - } - // Test CallIC. - for (int i = 0; i < 2; i++) { - LocalContext context; - context->Global()->Set(v8_str("tmp"), v8::True()); - context->Global()->Delete(v8_str("tmp")); - CompileRun("for (var j = 0; j < 10; j++) RegExp('')"); - } -} - - -// Test that cross-context new calls use the context of the callee to -// create the new JavaScript object. -THREADED_TEST(CrossContextNew) { - v8::HandleScope scope; - v8::Persistent<Context> context0 = Context::New(); - v8::Persistent<Context> context1 = Context::New(); - - // Allow cross-domain access. - Local<String> token = v8_str("<security token>"); - context0->SetSecurityToken(token); - context1->SetSecurityToken(token); - - // Set an 'x' property on the Object prototype and define a - // constructor function in context0. - context0->Enter(); - CompileRun("Object.prototype.x = 42; function C() {};"); - context0->Exit(); - - // Call the constructor function from context0 and check that the - // result has the 'x' property. - context1->Enter(); - context1->Global()->Set(v8_str("other"), context0->Global()); - Local<Value> value = CompileRun("var instance = new other.C(); instance.x"); - CHECK(value->IsInt32()); - CHECK_EQ(42, value->Int32Value()); - context1->Exit(); - - // Dispose the contexts to allow them to be garbage collected. - context0.Dispose(context0->GetIsolate()); - context1.Dispose(context1->GetIsolate()); -} - - -class RegExpInterruptTest { - public: - RegExpInterruptTest() : block_(NULL) {} - ~RegExpInterruptTest() { delete block_; } - void RunTest() { - block_ = i::OS::CreateSemaphore(0); - gc_count_ = 0; - gc_during_regexp_ = 0; - regexp_success_ = false; - gc_success_ = false; - GCThread gc_thread(this); - gc_thread.Start(); - v8::Locker::StartPreemption(1); - - LongRunningRegExp(); - { - v8::Unlocker unlock(CcTest::default_isolate()); - gc_thread.Join(); - } - v8::Locker::StopPreemption(); - CHECK(regexp_success_); - CHECK(gc_success_); - } - - private: - // Number of garbage collections required. - static const int kRequiredGCs = 5; - - class GCThread : public i::Thread { - public: - explicit GCThread(RegExpInterruptTest* test) - : Thread("GCThread"), test_(test) {} - virtual void Run() { - test_->CollectGarbage(); - } - private: - RegExpInterruptTest* test_; - }; - - void CollectGarbage() { - block_->Wait(); - while (gc_during_regexp_ < kRequiredGCs) { - { - v8::Locker lock(CcTest::default_isolate()); - // TODO(lrn): Perhaps create some garbage before collecting. - HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); - gc_count_++; - } - i::OS::Sleep(1); - } - gc_success_ = true; - } - - void LongRunningRegExp() { - block_->Signal(); // Enable garbage collection thread on next preemption. - int rounds = 0; - while (gc_during_regexp_ < kRequiredGCs) { - int gc_before = gc_count_; - { - // Match 15-30 "a"'s against 14 and a "b". - const char* c_source = - "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/" - ".exec('aaaaaaaaaaaaaaab') === null"; - Local<String> source = String::New(c_source); - Local<Script> script = Script::Compile(source); - Local<Value> result = script->Run(); - if (!result->BooleanValue()) { - gc_during_regexp_ = kRequiredGCs; // Allow gc thread to exit. - return; - } - } - { - // Match 15-30 "a"'s against 15 and a "b". - const char* c_source = - "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/" - ".exec('aaaaaaaaaaaaaaaab')[0] === 'aaaaaaaaaaaaaaaa'"; - Local<String> source = String::New(c_source); - Local<Script> script = Script::Compile(source); - Local<Value> result = script->Run(); - if (!result->BooleanValue()) { - gc_during_regexp_ = kRequiredGCs; - return; - } - } - int gc_after = gc_count_; - gc_during_regexp_ += gc_after - gc_before; - rounds++; - i::OS::Sleep(1); - } - regexp_success_ = true; - } - - i::Semaphore* block_; - int gc_count_; - int gc_during_regexp_; - bool regexp_success_; - bool gc_success_; -}; - - -// Test that a regular expression execution can be interrupted and -// survive a garbage collection. -TEST(RegExpInterruption) { - v8::Locker lock(CcTest::default_isolate()); - v8::V8::Initialize(); - v8::HandleScope scope; - Local<Context> local_env; - { - LocalContext env; - local_env = env.local(); - } - - // Local context should still be live. - CHECK(!local_env.IsEmpty()); - local_env->Enter(); - - // Should complete without problems. - RegExpInterruptTest().RunTest(); - - local_env->Exit(); -} - - -class ApplyInterruptTest { - public: - ApplyInterruptTest() : block_(NULL) {} - ~ApplyInterruptTest() { delete block_; } - void RunTest() { - block_ = i::OS::CreateSemaphore(0); - gc_count_ = 0; - gc_during_apply_ = 0; - apply_success_ = false; - gc_success_ = false; - GCThread gc_thread(this); - gc_thread.Start(); - v8::Locker::StartPreemption(1); - - LongRunningApply(); - { - v8::Unlocker unlock(CcTest::default_isolate()); - gc_thread.Join(); - } - v8::Locker::StopPreemption(); - CHECK(apply_success_); - CHECK(gc_success_); - } - - private: - // Number of garbage collections required. - static const int kRequiredGCs = 2; - - class GCThread : public i::Thread { - public: - explicit GCThread(ApplyInterruptTest* test) - : Thread("GCThread"), test_(test) {} - virtual void Run() { - test_->CollectGarbage(); - } - private: - ApplyInterruptTest* test_; - }; - - void CollectGarbage() { - block_->Wait(); - while (gc_during_apply_ < kRequiredGCs) { - { - v8::Locker lock(CcTest::default_isolate()); - HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); - gc_count_++; - } - i::OS::Sleep(1); - } - gc_success_ = true; - } - - void LongRunningApply() { - block_->Signal(); - int rounds = 0; - while (gc_during_apply_ < kRequiredGCs) { - int gc_before = gc_count_; - { - const char* c_source = - "function do_very_little(bar) {" - " this.foo = bar;" - "}" - "for (var i = 0; i < 100000; i++) {" - " do_very_little.apply(this, ['bar']);" - "}"; - Local<String> source = String::New(c_source); - Local<Script> script = Script::Compile(source); - Local<Value> result = script->Run(); - // Check that no exception was thrown. - CHECK(!result.IsEmpty()); - } - int gc_after = gc_count_; - gc_during_apply_ += gc_after - gc_before; - rounds++; - } - apply_success_ = true; - } - - i::Semaphore* block_; - int gc_count_; - int gc_during_apply_; - bool apply_success_; - bool gc_success_; -}; - - -// Test that nothing bad happens if we get a preemption just when we were -// about to do an apply(). -TEST(ApplyInterruption) { - v8::Locker lock(CcTest::default_isolate()); - v8::V8::Initialize(); - v8::HandleScope scope; - Local<Context> local_env; - { - LocalContext env; - local_env = env.local(); - } - - // Local context should still be live. - CHECK(!local_env.IsEmpty()); - local_env->Enter(); - - // Should complete without problems. - ApplyInterruptTest().RunTest(); - - local_env->Exit(); -} - - -// Verify that we can clone an object -TEST(ObjectClone) { - v8::HandleScope scope; - LocalContext env; - - const char* sample = - "var rv = {};" \ - "rv.alpha = 'hello';" \ - "rv.beta = 123;" \ - "rv;"; - - // Create an object, verify basics. - Local<Value> val = CompileRun(sample); - CHECK(val->IsObject()); - Local<v8::Object> obj = val.As<v8::Object>(); - obj->Set(v8_str("gamma"), v8_str("cloneme")); - - CHECK_EQ(v8_str("hello"), obj->Get(v8_str("alpha"))); - CHECK_EQ(v8::Integer::New(123), obj->Get(v8_str("beta"))); - CHECK_EQ(v8_str("cloneme"), obj->Get(v8_str("gamma"))); - - // Clone it. - Local<v8::Object> clone = obj->Clone(); - CHECK_EQ(v8_str("hello"), clone->Get(v8_str("alpha"))); - CHECK_EQ(v8::Integer::New(123), clone->Get(v8_str("beta"))); - CHECK_EQ(v8_str("cloneme"), clone->Get(v8_str("gamma"))); - - // Set a property on the clone, verify each object. - clone->Set(v8_str("beta"), v8::Integer::New(456)); - CHECK_EQ(v8::Integer::New(123), obj->Get(v8_str("beta"))); - CHECK_EQ(v8::Integer::New(456), clone->Get(v8_str("beta"))); -} - - -class AsciiVectorResource : public v8::String::ExternalAsciiStringResource { - public: - explicit AsciiVectorResource(i::Vector<const char> vector) - : data_(vector) {} - virtual ~AsciiVectorResource() {} - virtual size_t length() const { return data_.length(); } - virtual const char* data() const { return data_.start(); } - private: - i::Vector<const char> data_; -}; - - -class UC16VectorResource : public v8::String::ExternalStringResource { - public: - explicit UC16VectorResource(i::Vector<const i::uc16> vector) - : data_(vector) {} - virtual ~UC16VectorResource() {} - virtual size_t length() const { return data_.length(); } - virtual const i::uc16* data() const { return data_.start(); } - private: - i::Vector<const i::uc16> data_; -}; - - -static void MorphAString(i::String* string, - AsciiVectorResource* ascii_resource, - UC16VectorResource* uc16_resource) { - CHECK(i::StringShape(string).IsExternal()); - if (string->IsOneByteRepresentation()) { - // Check old map is not internalized or long. - CHECK(string->map() == HEAP->external_ascii_string_map()); - // Morph external string to be TwoByte string. - string->set_map(HEAP->external_string_map()); - i::ExternalTwoByteString* morphed = - i::ExternalTwoByteString::cast(string); - morphed->set_resource(uc16_resource); - } else { - // Check old map is not internalized or long. - CHECK(string->map() == HEAP->external_string_map()); - // Morph external string to be ASCII string. - string->set_map(HEAP->external_ascii_string_map()); - i::ExternalAsciiString* morphed = - i::ExternalAsciiString::cast(string); - morphed->set_resource(ascii_resource); - } -} - - -// Test that we can still flatten a string if the components it is built up -// from have been turned into 16 bit strings in the mean time. -THREADED_TEST(MorphCompositeStringTest) { - char utf_buffer[129]; - const char* c_string = "Now is the time for all good men" - " to come to the aid of the party"; - uint16_t* two_byte_string = AsciiToTwoByteString(c_string); - { - v8::HandleScope scope; - LocalContext env; - AsciiVectorResource ascii_resource( - i::Vector<const char>(c_string, i::StrLength(c_string))); - UC16VectorResource uc16_resource( - i::Vector<const uint16_t>(two_byte_string, - i::StrLength(c_string))); - - Local<String> lhs(v8::Utils::ToLocal( - FACTORY->NewExternalStringFromAscii(&ascii_resource))); - Local<String> rhs(v8::Utils::ToLocal( - FACTORY->NewExternalStringFromAscii(&ascii_resource))); - - env->Global()->Set(v8_str("lhs"), lhs); - env->Global()->Set(v8_str("rhs"), rhs); - - CompileRun( - "var cons = lhs + rhs;" - "var slice = lhs.substring(1, lhs.length - 1);" - "var slice_on_cons = (lhs + rhs).substring(1, lhs.length *2 - 1);"); - -#ifndef ENABLE_LATIN_1 - CHECK(!lhs->MayContainNonAscii()); - CHECK(!rhs->MayContainNonAscii()); -#endif - - MorphAString(*v8::Utils::OpenHandle(*lhs), &ascii_resource, &uc16_resource); - MorphAString(*v8::Utils::OpenHandle(*rhs), &ascii_resource, &uc16_resource); - - // This should UTF-8 without flattening, since everything is ASCII. - Handle<String> cons = v8_compile("cons")->Run().As<String>(); - CHECK_EQ(128, cons->Utf8Length()); - int nchars = -1; - CHECK_EQ(129, cons->WriteUtf8(utf_buffer, -1, &nchars)); - CHECK_EQ(128, nchars); - CHECK_EQ(0, strcmp( - utf_buffer, - "Now is the time for all good men to come to the aid of the party" - "Now is the time for all good men to come to the aid of the party")); - - // Now do some stuff to make sure the strings are flattened, etc. - CompileRun( - "/[^a-z]/.test(cons);" - "/[^a-z]/.test(slice);" - "/[^a-z]/.test(slice_on_cons);"); - const char* expected_cons = - "Now is the time for all good men to come to the aid of the party" - "Now is the time for all good men to come to the aid of the party"; - const char* expected_slice = - "ow is the time for all good men to come to the aid of the part"; - const char* expected_slice_on_cons = - "ow is the time for all good men to come to the aid of the party" - "Now is the time for all good men to come to the aid of the part"; - CHECK_EQ(String::New(expected_cons), - env->Global()->Get(v8_str("cons"))); - CHECK_EQ(String::New(expected_slice), - env->Global()->Get(v8_str("slice"))); - CHECK_EQ(String::New(expected_slice_on_cons), - env->Global()->Get(v8_str("slice_on_cons"))); - } - i::DeleteArray(two_byte_string); -} - - -TEST(CompileExternalTwoByteSource) { - v8::HandleScope scope; - LocalContext context; - - // This is a very short list of sources, which currently is to check for a - // regression caused by r2703. - const char* ascii_sources[] = { - "0.5", - "-0.5", // This mainly testes PushBack in the Scanner. - "--0.5", // This mainly testes PushBack in the Scanner. - NULL - }; - - // Compile the sources as external two byte strings. - for (int i = 0; ascii_sources[i] != NULL; i++) { - uint16_t* two_byte_string = AsciiToTwoByteString(ascii_sources[i]); - UC16VectorResource uc16_resource( - i::Vector<const uint16_t>(two_byte_string, - i::StrLength(ascii_sources[i]))); - v8::Local<v8::String> source = v8::String::NewExternal(&uc16_resource); - v8::Script::Compile(source); - i::DeleteArray(two_byte_string); - } -} - - -class RegExpStringModificationTest { - public: - RegExpStringModificationTest() - : block_(i::OS::CreateSemaphore(0)), - morphs_(0), - morphs_during_regexp_(0), - ascii_resource_(i::Vector<const char>("aaaaaaaaaaaaaab", 15)), - uc16_resource_(i::Vector<const uint16_t>(two_byte_content_, 15)) {} - ~RegExpStringModificationTest() { delete block_; } - void RunTest() { - regexp_success_ = false; - morph_success_ = false; - - // Initialize the contents of two_byte_content_ to be a uc16 representation - // of "aaaaaaaaaaaaaab". - for (int i = 0; i < 14; i++) { - two_byte_content_[i] = 'a'; - } - two_byte_content_[14] = 'b'; - - // Create the input string for the regexp - the one we are going to change - // properties of. - input_ = FACTORY->NewExternalStringFromAscii(&ascii_resource_); - - // Inject the input as a global variable. - i::Handle<i::String> input_name = - FACTORY->NewStringFromAscii(i::Vector<const char>("input", 5)); - i::Isolate::Current()->native_context()->global_object()->SetProperty( - *input_name, - *input_, - NONE, - i::kNonStrictMode)->ToObjectChecked(); - - MorphThread morph_thread(this); - morph_thread.Start(); - v8::Locker::StartPreemption(1); - LongRunningRegExp(); - { - v8::Unlocker unlock(CcTest::default_isolate()); - morph_thread.Join(); - } - v8::Locker::StopPreemption(); - CHECK(regexp_success_); - CHECK(morph_success_); - } - - private: - // Number of string modifications required. - static const int kRequiredModifications = 5; - static const int kMaxModifications = 100; - - class MorphThread : public i::Thread { - public: - explicit MorphThread(RegExpStringModificationTest* test) - : Thread("MorphThread"), test_(test) {} - virtual void Run() { - test_->MorphString(); - } - private: - RegExpStringModificationTest* test_; - }; - - void MorphString() { - block_->Wait(); - while (morphs_during_regexp_ < kRequiredModifications && - morphs_ < kMaxModifications) { - { - v8::Locker lock(CcTest::default_isolate()); - // Swap string between ascii and two-byte representation. - i::String* string = *input_; - MorphAString(string, &ascii_resource_, &uc16_resource_); - morphs_++; - } - i::OS::Sleep(1); - } - morph_success_ = true; - } - - void LongRunningRegExp() { - block_->Signal(); // Enable morphing thread on next preemption. - while (morphs_during_regexp_ < kRequiredModifications && - morphs_ < kMaxModifications) { - int morphs_before = morphs_; - { - v8::HandleScope scope; - // Match 15-30 "a"'s against 14 and a "b". - const char* c_source = - "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/" - ".exec(input) === null"; - Local<String> source = String::New(c_source); - Local<Script> script = Script::Compile(source); - Local<Value> result = script->Run(); - CHECK(result->IsTrue()); - } - int morphs_after = morphs_; - morphs_during_regexp_ += morphs_after - morphs_before; - } - regexp_success_ = true; - } - - i::uc16 two_byte_content_[15]; - i::Semaphore* block_; - int morphs_; - int morphs_during_regexp_; - bool regexp_success_; - bool morph_success_; - i::Handle<i::String> input_; - AsciiVectorResource ascii_resource_; - UC16VectorResource uc16_resource_; -}; - - -// Test that a regular expression execution can be interrupted and -// the string changed without failing. -TEST(RegExpStringModification) { - v8::Locker lock(CcTest::default_isolate()); - v8::V8::Initialize(); - v8::HandleScope scope; - Local<Context> local_env; - { - LocalContext env; - local_env = env.local(); - } - - // Local context should still be live. - CHECK(!local_env.IsEmpty()); - local_env->Enter(); - - // Should complete without problems. - RegExpStringModificationTest().RunTest(); - - local_env->Exit(); -} - - -// Test that we cannot set a property on the global object if there -// is a read-only property in the prototype chain. -TEST(ReadOnlyPropertyInGlobalProto) { - i::FLAG_es5_readonly = true; - v8::HandleScope scope; - v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(); - LocalContext context(0, templ); - v8::Handle<v8::Object> global = context->Global(); - v8::Handle<v8::Object> global_proto = - v8::Handle<v8::Object>::Cast(global->Get(v8_str("__proto__"))); - global_proto->Set(v8_str("x"), v8::Integer::New(0), v8::ReadOnly); - global_proto->Set(v8_str("y"), v8::Integer::New(0), v8::ReadOnly); - // Check without 'eval' or 'with'. - v8::Handle<v8::Value> res = - CompileRun("function f() { x = 42; return x; }; f()"); - CHECK_EQ(v8::Integer::New(0), res); - // Check with 'eval'. - res = CompileRun("function f() { eval('1'); y = 43; return y; }; f()"); - CHECK_EQ(v8::Integer::New(0), res); - // Check with 'with'. - res = CompileRun("function f() { with (this) { y = 44 }; return y; }; f()"); - CHECK_EQ(v8::Integer::New(0), res); -} - -static int force_set_set_count = 0; -static int force_set_get_count = 0; -bool pass_on_get = false; - -static v8::Handle<v8::Value> ForceSetGetter(v8::Local<v8::String> name, - const v8::AccessorInfo& info) { - force_set_get_count++; - if (pass_on_get) { - return v8::Handle<v8::Value>(); - } else { - return v8::Int32::New(3); - } -} - -static void ForceSetSetter(v8::Local<v8::String> name, - v8::Local<v8::Value> value, - const v8::AccessorInfo& info) { - force_set_set_count++; -} - -static v8::Handle<v8::Value> ForceSetInterceptSetter( - v8::Local<v8::String> name, - v8::Local<v8::Value> value, - const v8::AccessorInfo& info) { - force_set_set_count++; - return v8::Undefined(); -} - -TEST(ForceSet) { - force_set_get_count = 0; - force_set_set_count = 0; - pass_on_get = false; - - v8::HandleScope scope; - v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(); - v8::Handle<v8::String> access_property = v8::String::New("a"); - templ->SetAccessor(access_property, ForceSetGetter, ForceSetSetter); - 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::ReadOnly); - CHECK_EQ(4, global->Get(simple_property)->Int32Value()); - // This should fail because the property is read-only - global->Set(simple_property, v8::Int32::New(5)); - CHECK_EQ(4, global->Get(simple_property)->Int32Value()); - // This should succeed even though the property is read-only - global->ForceSet(simple_property, v8::Int32::New(6)); - CHECK_EQ(6, global->Get(simple_property)->Int32Value()); - - // Accessors - CHECK_EQ(0, force_set_set_count); - CHECK_EQ(0, force_set_get_count); - CHECK_EQ(3, global->Get(access_property)->Int32Value()); - // CHECK_EQ the property shouldn't override it, just call the setter - // which in this case does nothing. - global->Set(access_property, v8::Int32::New(7)); - CHECK_EQ(3, global->Get(access_property)->Int32Value()); - CHECK_EQ(1, force_set_set_count); - CHECK_EQ(2, force_set_get_count); - // Forcing the property to be set should override the accessor without - // calling it - global->ForceSet(access_property, v8::Int32::New(8)); - CHECK_EQ(8, global->Get(access_property)->Int32Value()); - CHECK_EQ(1, force_set_set_count); - CHECK_EQ(2, force_set_get_count); -} - -TEST(ForceSetWithInterceptor) { - force_set_get_count = 0; - force_set_set_count = 0; - pass_on_get = false; - - v8::HandleScope scope; - v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(); - templ->SetNamedPropertyHandler(ForceSetGetter, ForceSetInterceptSetter); - LocalContext context(NULL, templ); - v8::Handle<v8::Object> global = context->Global(); - - v8::Handle<v8::String> some_property = v8::String::New("a"); - CHECK_EQ(0, force_set_set_count); - CHECK_EQ(0, force_set_get_count); - CHECK_EQ(3, global->Get(some_property)->Int32Value()); - // Setting the property shouldn't override it, just call the setter - // which in this case does nothing. - global->Set(some_property, v8::Int32::New(7)); - CHECK_EQ(3, global->Get(some_property)->Int32Value()); - CHECK_EQ(1, force_set_set_count); - CHECK_EQ(2, force_set_get_count); - // Getting the property when the interceptor returns an empty handle - // should yield undefined, since the property isn't present on the - // object itself yet. - pass_on_get = true; - CHECK(global->Get(some_property)->IsUndefined()); - CHECK_EQ(1, force_set_set_count); - CHECK_EQ(3, force_set_get_count); - // Forcing the property to be set should cause the value to be - // set locally without calling the interceptor. - global->ForceSet(some_property, v8::Int32::New(8)); - CHECK_EQ(8, global->Get(some_property)->Int32Value()); - CHECK_EQ(1, force_set_set_count); - CHECK_EQ(4, force_set_get_count); - // Reenabling the interceptor should cause it to take precedence over - // the property - pass_on_get = false; - CHECK_EQ(3, global->Get(some_property)->Int32Value()); - CHECK_EQ(1, force_set_set_count); - CHECK_EQ(5, force_set_get_count); - // The interceptor should also work for other properties - CHECK_EQ(3, global->Get(v8::String::New("b"))->Int32Value()); - 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); -} - - -// Make sure that forcing a delete invalidates any IC stubs, so we -// don't read the hole value. -THREADED_TEST(ForceDeleteIC) { - v8::HandleScope scope; - LocalContext context; - // Create a DontDelete variable on the global object. - CompileRun("this.__proto__ = { foo: 'horse' };" - "var foo = 'fish';" - "function f() { return foo.length; }"); - // Initialize the IC for foo in f. - CompileRun("for (var i = 0; i < 4; i++) f();"); - // Make sure the value of foo is correct before the deletion. - CHECK_EQ(4, CompileRun("f()")->Int32Value()); - // Force the deletion of foo. - CHECK(context->Global()->ForceDelete(v8_str("foo"))); - // Make sure the value for foo is read from the prototype, and that - // we don't get in trouble with reading the deleted cell value - // sentinel. - CHECK_EQ(5, CompileRun("f()")->Int32Value()); -} - - -TEST(InlinedFunctionAcrossContexts) { - i::FLAG_allow_natives_syntax = true; - v8::HandleScope outer_scope; - v8::Persistent<v8::Context> ctx1 = v8::Context::New(); - v8::Persistent<v8::Context> ctx2 = v8::Context::New(); - ctx1->Enter(); - - { - v8::HandleScope inner_scope; - CompileRun("var G = 42; function foo() { return G; }"); - v8::Local<v8::Value> foo = ctx1->Global()->Get(v8_str("foo")); - ctx2->Enter(); - ctx2->Global()->Set(v8_str("o"), foo); - v8::Local<v8::Value> res = CompileRun( - "function f() { return o(); }" - "for (var i = 0; i < 10; ++i) f();" - "%OptimizeFunctionOnNextCall(f);" - "f();"); - CHECK_EQ(42, res->Int32Value()); - ctx2->Exit(); - v8::Handle<v8::String> G_property = v8::String::New("G"); - CHECK(ctx1->Global()->ForceDelete(G_property)); - ctx2->Enter(); - ExpectString( - "(function() {" - " try {" - " return f();" - " } catch(e) {" - " return e.toString();" - " }" - " })()", - "ReferenceError: G is not defined"); - ctx2->Exit(); - ctx1->Exit(); - ctx1.Dispose(ctx1->GetIsolate()); - } - ctx2.Dispose(ctx2->GetIsolate()); -} - - -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 set up 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_context0->GetIsolate()); - calling_context1.Dispose(calling_context1->GetIsolate()); - calling_context2.Dispose(calling_context2->GetIsolate()); - calling_context0.Clear(); - calling_context1.Clear(); - calling_context2.Clear(); -} - - -// Check that a variable declaration with no explicit initialization -// value does shadow an existing property in the prototype chain. -THREADED_TEST(InitGlobalVarInProtoChain) { - i::FLAG_es52_globals = true; - v8::HandleScope scope; - LocalContext context; - // Introduce a variable in the prototype chain. - CompileRun("__proto__.x = 42"); - v8::Handle<v8::Value> result = CompileRun("var x = 43; x"); - CHECK(!result->IsUndefined()); - CHECK_EQ(43, result->Int32Value()); -} - - -// Regression test for issue 398. -// If a function is added to an object, creating a constant function -// field, and the result is cloned, replacing the constant function on the -// original should not affect the clone. -// See http://code.google.com/p/v8/issues/detail?id=398 -THREADED_TEST(ReplaceConstantFunction) { - v8::HandleScope scope; - LocalContext context; - v8::Handle<v8::Object> obj = v8::Object::New(); - v8::Handle<v8::FunctionTemplate> func_templ = v8::FunctionTemplate::New(); - v8::Handle<v8::String> foo_string = v8::String::New("foo"); - obj->Set(foo_string, func_templ->GetFunction()); - v8::Handle<v8::Object> obj_clone = obj->Clone(); - obj_clone->Set(foo_string, v8::String::New("Hello")); - CHECK(!obj->Get(foo_string)->IsUndefined()); -} - - -// Regression test for http://crbug.com/16276. -THREADED_TEST(Regress16276) { - v8::HandleScope scope; - LocalContext context; - // Force the IC in f to be a dictionary load IC. - CompileRun("function f(obj) { return obj.x; }\n" - "var obj = { x: { foo: 42 }, y: 87 };\n" - "var x = obj.x;\n" - "delete obj.y;\n" - "for (var i = 0; i < 5; i++) f(obj);"); - // Detach the global object to make 'this' refer directly to the - // global object (not the proxy), and make sure that the dictionary - // load IC doesn't mess up loading directly from the global object. - context->DetachGlobal(); - CHECK_EQ(42, CompileRun("f(this).foo")->Int32Value()); -} - - -THREADED_TEST(PixelArray) { - v8::HandleScope scope; - LocalContext context; - const int kElementCount = 260; - uint8_t* pixel_data = reinterpret_cast<uint8_t*>(malloc(kElementCount)); - i::Handle<i::ExternalPixelArray> pixels = - i::Handle<i::ExternalPixelArray>::cast( - FACTORY->NewExternalArray(kElementCount, - v8::kExternalPixelArray, - pixel_data)); - // Force GC to trigger verification. - HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); - for (int i = 0; i < kElementCount; i++) { - pixels->set(i, i % 256); - } - // Force GC to trigger verification. - HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); - for (int i = 0; i < kElementCount; i++) { - CHECK_EQ(i % 256, pixels->get_scalar(i)); - CHECK_EQ(i % 256, pixel_data[i]); - } - - v8::Handle<v8::Object> obj = v8::Object::New(); - i::Handle<i::JSObject> jsobj = v8::Utils::OpenHandle(*obj); - // Set the elements to be the pixels. - // jsobj->set_elements(*pixels); - obj->SetIndexedPropertiesToPixelData(pixel_data, kElementCount); - CHECK_EQ(1, i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value()); - obj->Set(v8_str("field"), v8::Int32::New(1503)); - context->Global()->Set(v8_str("pixels"), obj); - v8::Handle<v8::Value> result = CompileRun("pixels.field"); - CHECK_EQ(1503, result->Int32Value()); - result = CompileRun("pixels[1]"); - CHECK_EQ(1, result->Int32Value()); - - result = CompileRun("var sum = 0;" - "for (var i = 0; i < 8; i++) {" - " sum += pixels[i] = pixels[i] = -i;" - "}" - "sum;"); - CHECK_EQ(-28, result->Int32Value()); - - result = CompileRun("var sum = 0;" - "for (var i = 0; i < 8; i++) {" - " sum += pixels[i] = pixels[i] = 0;" - "}" - "sum;"); - CHECK_EQ(0, result->Int32Value()); - - result = CompileRun("var sum = 0;" - "for (var i = 0; i < 8; i++) {" - " sum += pixels[i] = pixels[i] = 255;" - "}" - "sum;"); - CHECK_EQ(8 * 255, result->Int32Value()); - - result = CompileRun("var sum = 0;" - "for (var i = 0; i < 8; i++) {" - " sum += pixels[i] = pixels[i] = 256 + i;" - "}" - "sum;"); - CHECK_EQ(2076, result->Int32Value()); - - result = CompileRun("var sum = 0;" - "for (var i = 0; i < 8; i++) {" - " sum += pixels[i] = pixels[i] = i;" - "}" - "sum;"); - CHECK_EQ(28, result->Int32Value()); - - result = CompileRun("var sum = 0;" - "for (var i = 0; i < 8; i++) {" - " sum += pixels[i];" - "}" - "sum;"); - CHECK_EQ(28, result->Int32Value()); - - i::Handle<i::Smi> value(i::Smi::FromInt(2), - reinterpret_cast<i::Isolate*>(context->GetIsolate())); - i::Handle<i::Object> no_failure; - no_failure = - i::JSObject::SetElement(jsobj, 1, value, NONE, i::kNonStrictMode); - ASSERT(!no_failure.is_null()); - i::USE(no_failure); - CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value()); - *value.location() = i::Smi::FromInt(256); - no_failure = - i::JSObject::SetElement(jsobj, 1, value, NONE, i::kNonStrictMode); - ASSERT(!no_failure.is_null()); - i::USE(no_failure); - CHECK_EQ(255, - i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value()); - *value.location() = i::Smi::FromInt(-1); - no_failure = - i::JSObject::SetElement(jsobj, 1, value, NONE, i::kNonStrictMode); - ASSERT(!no_failure.is_null()); - i::USE(no_failure); - CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value()); - - result = CompileRun("for (var i = 0; i < 8; i++) {" - " pixels[i] = (i * 65) - 109;" - "}" - "pixels[1] + pixels[6];"); - CHECK_EQ(255, result->Int32Value()); - CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(0)->ToObjectChecked())->value()); - CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value()); - CHECK_EQ(21, - i::Smi::cast(jsobj->GetElement(2)->ToObjectChecked())->value()); - CHECK_EQ(86, - i::Smi::cast(jsobj->GetElement(3)->ToObjectChecked())->value()); - CHECK_EQ(151, - i::Smi::cast(jsobj->GetElement(4)->ToObjectChecked())->value()); - CHECK_EQ(216, - i::Smi::cast(jsobj->GetElement(5)->ToObjectChecked())->value()); - CHECK_EQ(255, - i::Smi::cast(jsobj->GetElement(6)->ToObjectChecked())->value()); - CHECK_EQ(255, - i::Smi::cast(jsobj->GetElement(7)->ToObjectChecked())->value()); - result = CompileRun("var sum = 0;" - "for (var i = 0; i < 8; i++) {" - " sum += pixels[i];" - "}" - "sum;"); - CHECK_EQ(984, result->Int32Value()); - - result = CompileRun("for (var i = 0; i < 8; i++) {" - " pixels[i] = (i * 1.1);" - "}" - "pixels[1] + pixels[6];"); - CHECK_EQ(8, result->Int32Value()); - CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(0)->ToObjectChecked())->value()); - CHECK_EQ(1, i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value()); - CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(2)->ToObjectChecked())->value()); - CHECK_EQ(3, i::Smi::cast(jsobj->GetElement(3)->ToObjectChecked())->value()); - CHECK_EQ(4, i::Smi::cast(jsobj->GetElement(4)->ToObjectChecked())->value()); - CHECK_EQ(6, i::Smi::cast(jsobj->GetElement(5)->ToObjectChecked())->value()); - CHECK_EQ(7, i::Smi::cast(jsobj->GetElement(6)->ToObjectChecked())->value()); - CHECK_EQ(8, i::Smi::cast(jsobj->GetElement(7)->ToObjectChecked())->value()); - - result = CompileRun("for (var i = 0; i < 8; i++) {" - " pixels[7] = undefined;" - "}" - "pixels[7];"); - CHECK_EQ(0, result->Int32Value()); - CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(7)->ToObjectChecked())->value()); - - result = CompileRun("for (var i = 0; i < 8; i++) {" - " pixels[6] = '2.3';" - "}" - "pixels[6];"); - CHECK_EQ(2, result->Int32Value()); - CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(6)->ToObjectChecked())->value()); - - result = CompileRun("for (var i = 0; i < 8; i++) {" - " pixels[5] = NaN;" - "}" - "pixels[5];"); - CHECK_EQ(0, result->Int32Value()); - CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5)->ToObjectChecked())->value()); - - result = CompileRun("for (var i = 0; i < 8; i++) {" - " pixels[8] = Infinity;" - "}" - "pixels[8];"); - CHECK_EQ(255, result->Int32Value()); - CHECK_EQ(255, - i::Smi::cast(jsobj->GetElement(8)->ToObjectChecked())->value()); - - result = CompileRun("for (var i = 0; i < 8; i++) {" - " pixels[9] = -Infinity;" - "}" - "pixels[9];"); - CHECK_EQ(0, result->Int32Value()); - CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(9)->ToObjectChecked())->value()); - - result = CompileRun("pixels[3] = 33;" - "delete pixels[3];" - "pixels[3];"); - CHECK_EQ(33, result->Int32Value()); - - result = CompileRun("pixels[0] = 10; pixels[1] = 11;" - "pixels[2] = 12; pixels[3] = 13;" - "pixels.__defineGetter__('2'," - "function() { return 120; });" - "pixels[2];"); - CHECK_EQ(12, result->Int32Value()); - - result = CompileRun("var js_array = new Array(40);" - "js_array[0] = 77;" - "js_array;"); - CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value()); - - result = CompileRun("pixels[1] = 23;" - "pixels.__proto__ = [];" - "js_array.__proto__ = pixels;" - "js_array.concat(pixels);"); - CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value()); - CHECK_EQ(23, v8::Object::Cast(*result)->Get(v8_str("1"))->Int32Value()); - - result = CompileRun("pixels[1] = 23;"); - CHECK_EQ(23, result->Int32Value()); - - // Test for index greater than 255. Regression test for: - // http://code.google.com/p/chromium/issues/detail?id=26337. - result = CompileRun("pixels[256] = 255;"); - CHECK_EQ(255, result->Int32Value()); - result = CompileRun("var i = 0;" - "for (var j = 0; j < 8; j++) { i = pixels[256]; }" - "i"); - CHECK_EQ(255, result->Int32Value()); - - // Make sure that pixel array ICs recognize when a non-pixel array - // is passed to it. - result = CompileRun("function pa_load(p) {" - " var sum = 0;" - " for (var j = 0; j < 256; j++) { sum += p[j]; }" - " return sum;" - "}" - "for (var i = 0; i < 256; ++i) { pixels[i] = i; }" - "for (var i = 0; i < 10; ++i) { pa_load(pixels); }" - "just_ints = new Object();" - "for (var i = 0; i < 256; ++i) { just_ints[i] = i; }" - "for (var i = 0; i < 10; ++i) {" - " result = pa_load(just_ints);" - "}" - "result"); - CHECK_EQ(32640, result->Int32Value()); - - // Make sure that pixel array ICs recognize out-of-bound accesses. - result = CompileRun("function pa_load(p, start) {" - " var sum = 0;" - " for (var j = start; j < 256; j++) { sum += p[j]; }" - " return sum;" - "}" - "for (var i = 0; i < 256; ++i) { pixels[i] = i; }" - "for (var i = 0; i < 10; ++i) { pa_load(pixels,0); }" - "for (var i = 0; i < 10; ++i) {" - " result = pa_load(pixels,-10);" - "}" - "result"); - CHECK_EQ(0, result->Int32Value()); - - // Make sure that generic ICs properly handles a pixel array. - result = CompileRun("function pa_load(p) {" - " var sum = 0;" - " for (var j = 0; j < 256; j++) { sum += p[j]; }" - " return sum;" - "}" - "for (var i = 0; i < 256; ++i) { pixels[i] = i; }" - "just_ints = new Object();" - "for (var i = 0; i < 256; ++i) { just_ints[i] = i; }" - "for (var i = 0; i < 10; ++i) { pa_load(just_ints); }" - "for (var i = 0; i < 10; ++i) {" - " result = pa_load(pixels);" - "}" - "result"); - CHECK_EQ(32640, result->Int32Value()); - - // Make sure that generic load ICs recognize out-of-bound accesses in - // pixel arrays. - result = CompileRun("function pa_load(p, start) {" - " var sum = 0;" - " for (var j = start; j < 256; j++) { sum += p[j]; }" - " return sum;" - "}" - "for (var i = 0; i < 256; ++i) { pixels[i] = i; }" - "just_ints = new Object();" - "for (var i = 0; i < 256; ++i) { just_ints[i] = i; }" - "for (var i = 0; i < 10; ++i) { pa_load(just_ints,0); }" - "for (var i = 0; i < 10; ++i) { pa_load(pixels,0); }" - "for (var i = 0; i < 10; ++i) {" - " result = pa_load(pixels,-10);" - "}" - "result"); - CHECK_EQ(0, result->Int32Value()); - - // Make sure that generic ICs properly handles other types than pixel - // arrays (that the inlined fast pixel array test leaves the right information - // in the right registers). - result = CompileRun("function pa_load(p) {" - " var sum = 0;" - " for (var j = 0; j < 256; j++) { sum += p[j]; }" - " return sum;" - "}" - "for (var i = 0; i < 256; ++i) { pixels[i] = i; }" - "just_ints = new Object();" - "for (var i = 0; i < 256; ++i) { just_ints[i] = i; }" - "for (var i = 0; i < 10; ++i) { pa_load(just_ints); }" - "for (var i = 0; i < 10; ++i) { pa_load(pixels); }" - "sparse_array = new Object();" - "for (var i = 0; i < 256; ++i) { sparse_array[i] = i; }" - "sparse_array[1000000] = 3;" - "for (var i = 0; i < 10; ++i) {" - " result = pa_load(sparse_array);" - "}" - "result"); - CHECK_EQ(32640, result->Int32Value()); - - // Make sure that pixel array store ICs clamp values correctly. - result = CompileRun("function pa_store(p) {" - " for (var j = 0; j < 256; j++) { p[j] = j * 2; }" - "}" - "pa_store(pixels);" - "var sum = 0;" - "for (var j = 0; j < 256; j++) { sum += pixels[j]; }" - "sum"); - CHECK_EQ(48896, result->Int32Value()); - - // Make sure that pixel array stores correctly handle accesses outside - // of the pixel array.. - result = CompileRun("function pa_store(p,start) {" - " for (var j = 0; j < 256; j++) {" - " p[j+start] = j * 2;" - " }" - "}" - "pa_store(pixels,0);" - "pa_store(pixels,-128);" - "var sum = 0;" - "for (var j = 0; j < 256; j++) { sum += pixels[j]; }" - "sum"); - CHECK_EQ(65280, result->Int32Value()); - - // Make sure that the generic store stub correctly handle accesses outside - // of the pixel array.. - result = CompileRun("function pa_store(p,start) {" - " for (var j = 0; j < 256; j++) {" - " p[j+start] = j * 2;" - " }" - "}" - "pa_store(pixels,0);" - "just_ints = new Object();" - "for (var i = 0; i < 256; ++i) { just_ints[i] = i; }" - "pa_store(just_ints, 0);" - "pa_store(pixels,-128);" - "var sum = 0;" - "for (var j = 0; j < 256; j++) { sum += pixels[j]; }" - "sum"); - CHECK_EQ(65280, result->Int32Value()); - - // Make sure that the generic keyed store stub clamps pixel array values - // correctly. - result = CompileRun("function pa_store(p) {" - " for (var j = 0; j < 256; j++) { p[j] = j * 2; }" - "}" - "pa_store(pixels);" - "just_ints = new Object();" - "pa_store(just_ints);" - "pa_store(pixels);" - "var sum = 0;" - "for (var j = 0; j < 256; j++) { sum += pixels[j]; }" - "sum"); - CHECK_EQ(48896, result->Int32Value()); - - // Make sure that pixel array loads are optimized by crankshaft. - result = CompileRun("function pa_load(p) {" - " var sum = 0;" - " for (var i=0; i<256; ++i) {" - " sum += p[i];" - " }" - " return sum; " - "}" - "for (var i = 0; i < 256; ++i) { pixels[i] = i; }" - "for (var i = 0; i < 5000; ++i) {" - " result = pa_load(pixels);" - "}" - "result"); - CHECK_EQ(32640, result->Int32Value()); - - // Make sure that pixel array stores are optimized by crankshaft. - result = CompileRun("function pa_init(p) {" - "for (var i = 0; i < 256; ++i) { p[i] = i; }" - "}" - "function pa_load(p) {" - " var sum = 0;" - " for (var i=0; i<256; ++i) {" - " sum += p[i];" - " }" - " return sum; " - "}" - "for (var i = 0; i < 5000; ++i) {" - " pa_init(pixels);" - "}" - "result = pa_load(pixels);" - "result"); - CHECK_EQ(32640, result->Int32Value()); - - free(pixel_data); -} - - -THREADED_TEST(PixelArrayInfo) { - v8::HandleScope scope; - LocalContext context; - for (int size = 0; size < 100; size += 10) { - uint8_t* pixel_data = reinterpret_cast<uint8_t*>(malloc(size)); - v8::Handle<v8::Object> obj = v8::Object::New(); - obj->SetIndexedPropertiesToPixelData(pixel_data, size); - CHECK(obj->HasIndexedPropertiesInPixelData()); - CHECK_EQ(pixel_data, obj->GetIndexedPropertiesPixelData()); - CHECK_EQ(size, obj->GetIndexedPropertiesPixelDataLength()); - free(pixel_data); - } -} - - -static v8::Handle<Value> NotHandledIndexedPropertyGetter( - uint32_t index, - const AccessorInfo& info) { - ApiTestFuzzer::Fuzz(); - return v8::Handle<Value>(); -} - - -static v8::Handle<Value> NotHandledIndexedPropertySetter( - uint32_t index, - Local<Value> value, - const AccessorInfo& info) { - ApiTestFuzzer::Fuzz(); - return v8::Handle<Value>(); -} - - -THREADED_TEST(PixelArrayWithInterceptor) { - v8::HandleScope scope; - LocalContext context; - const int kElementCount = 260; - uint8_t* pixel_data = reinterpret_cast<uint8_t*>(malloc(kElementCount)); - i::Handle<i::ExternalPixelArray> pixels = - i::Handle<i::ExternalPixelArray>::cast( - FACTORY->NewExternalArray(kElementCount, - v8::kExternalPixelArray, - pixel_data)); - for (int i = 0; i < kElementCount; i++) { - pixels->set(i, i % 256); - } - v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(); - templ->SetIndexedPropertyHandler(NotHandledIndexedPropertyGetter, - NotHandledIndexedPropertySetter); - v8::Handle<v8::Object> obj = templ->NewInstance(); - obj->SetIndexedPropertiesToPixelData(pixel_data, kElementCount); - context->Global()->Set(v8_str("pixels"), obj); - v8::Handle<v8::Value> result = CompileRun("pixels[1]"); - CHECK_EQ(1, result->Int32Value()); - result = CompileRun("var sum = 0;" - "for (var i = 0; i < 8; i++) {" - " sum += pixels[i] = pixels[i] = -i;" - "}" - "sum;"); - CHECK_EQ(-28, result->Int32Value()); - result = CompileRun("pixels.hasOwnProperty('1')"); - CHECK(result->BooleanValue()); - free(pixel_data); -} - - -static int ExternalArrayElementSize(v8::ExternalArrayType array_type) { - switch (array_type) { - case v8::kExternalByteArray: - case v8::kExternalUnsignedByteArray: - case v8::kExternalPixelArray: - return 1; - break; - case v8::kExternalShortArray: - case v8::kExternalUnsignedShortArray: - return 2; - break; - case v8::kExternalIntArray: - case v8::kExternalUnsignedIntArray: - case v8::kExternalFloatArray: - return 4; - break; - case v8::kExternalDoubleArray: - return 8; - break; - default: - UNREACHABLE(); - return -1; - } - UNREACHABLE(); - return -1; -} - - -template <class ExternalArrayClass, class ElementType> -static void ExternalArrayTestHelper(v8::ExternalArrayType array_type, - int64_t low, - int64_t high) { - v8::HandleScope scope; - LocalContext context; - const int kElementCount = 40; - int element_size = ExternalArrayElementSize(array_type); - ElementType* array_data = - static_cast<ElementType*>(malloc(kElementCount * element_size)); - i::Handle<ExternalArrayClass> array = - i::Handle<ExternalArrayClass>::cast( - FACTORY->NewExternalArray(kElementCount, array_type, array_data)); - // Force GC to trigger verification. - HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); - for (int i = 0; i < kElementCount; i++) { - array->set(i, static_cast<ElementType>(i)); - } - // Force GC to trigger verification. - HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); - for (int i = 0; i < kElementCount; i++) { - CHECK_EQ(static_cast<int64_t>(i), - static_cast<int64_t>(array->get_scalar(i))); - CHECK_EQ(static_cast<int64_t>(i), static_cast<int64_t>(array_data[i])); - } - - v8::Handle<v8::Object> obj = v8::Object::New(); - i::Handle<i::JSObject> jsobj = v8::Utils::OpenHandle(*obj); - // Set the elements to be the external array. - obj->SetIndexedPropertiesToExternalArrayData(array_data, - array_type, - kElementCount); - CHECK_EQ( - 1, static_cast<int>(jsobj->GetElement(1)->ToObjectChecked()->Number())); - obj->Set(v8_str("field"), v8::Int32::New(1503)); - context->Global()->Set(v8_str("ext_array"), obj); - v8::Handle<v8::Value> result = CompileRun("ext_array.field"); - CHECK_EQ(1503, result->Int32Value()); - result = CompileRun("ext_array[1]"); - CHECK_EQ(1, result->Int32Value()); - - // Check pass through of assigned smis - result = CompileRun("var sum = 0;" - "for (var i = 0; i < 8; i++) {" - " sum += ext_array[i] = ext_array[i] = -i;" - "}" - "sum;"); - CHECK_EQ(-28, result->Int32Value()); - - // Check assigned smis - result = CompileRun("for (var i = 0; i < 8; i++) {" - " ext_array[i] = i;" - "}" - "var sum = 0;" - "for (var i = 0; i < 8; i++) {" - " sum += ext_array[i];" - "}" - "sum;"); - CHECK_EQ(28, result->Int32Value()); - - // Check assigned smis in reverse order - result = CompileRun("for (var i = 8; --i >= 0; ) {" - " ext_array[i] = i;" - "}" - "var sum = 0;" - "for (var i = 0; i < 8; i++) {" - " sum += ext_array[i];" - "}" - "sum;"); - CHECK_EQ(28, result->Int32Value()); - - // Check pass through of assigned HeapNumbers - result = CompileRun("var sum = 0;" - "for (var i = 0; i < 16; i+=2) {" - " sum += ext_array[i] = ext_array[i] = (-i * 0.5);" - "}" - "sum;"); - CHECK_EQ(-28, result->Int32Value()); - - // Check assigned HeapNumbers - result = CompileRun("for (var i = 0; i < 16; i+=2) {" - " ext_array[i] = (i * 0.5);" - "}" - "var sum = 0;" - "for (var i = 0; i < 16; i+=2) {" - " sum += ext_array[i];" - "}" - "sum;"); - CHECK_EQ(28, result->Int32Value()); - - // Check assigned HeapNumbers in reverse order - result = CompileRun("for (var i = 14; i >= 0; i-=2) {" - " ext_array[i] = (i * 0.5);" - "}" - "var sum = 0;" - "for (var i = 0; i < 16; i+=2) {" - " sum += ext_array[i];" - "}" - "sum;"); - CHECK_EQ(28, result->Int32Value()); - - i::ScopedVector<char> test_buf(1024); - - // Check legal boundary conditions. - // The repeated loads and stores ensure the ICs are exercised. - const char* boundary_program = - "var res = 0;" - "for (var i = 0; i < 16; i++) {" - " ext_array[i] = %lld;" - " if (i > 8) {" - " res = ext_array[i];" - " }" - "}" - "res;"; - i::OS::SNPrintF(test_buf, - boundary_program, - low); - result = CompileRun(test_buf.start()); - CHECK_EQ(low, result->IntegerValue()); - - i::OS::SNPrintF(test_buf, - boundary_program, - high); - result = CompileRun(test_buf.start()); - CHECK_EQ(high, result->IntegerValue()); - - // Check misprediction of type in IC. - result = CompileRun("var tmp_array = ext_array;" - "var sum = 0;" - "for (var i = 0; i < 8; i++) {" - " tmp_array[i] = i;" - " sum += tmp_array[i];" - " if (i == 4) {" - " tmp_array = {};" - " }" - "}" - "sum;"); - // Force GC to trigger verification. - HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); - CHECK_EQ(28, result->Int32Value()); - - // Make sure out-of-range loads do not throw. - i::OS::SNPrintF(test_buf, - "var caught_exception = false;" - "try {" - " ext_array[%d];" - "} catch (e) {" - " caught_exception = true;" - "}" - "caught_exception;", - kElementCount); - result = CompileRun(test_buf.start()); - CHECK_EQ(false, result->BooleanValue()); - - // Make sure out-of-range stores do not throw. - i::OS::SNPrintF(test_buf, - "var caught_exception = false;" - "try {" - " ext_array[%d] = 1;" - "} catch (e) {" - " caught_exception = true;" - "}" - "caught_exception;", - kElementCount); - result = CompileRun(test_buf.start()); - CHECK_EQ(false, result->BooleanValue()); - - // Check other boundary conditions, values and operations. - result = CompileRun("for (var i = 0; i < 8; i++) {" - " ext_array[7] = undefined;" - "}" - "ext_array[7];"); - CHECK_EQ(0, result->Int32Value()); - if (array_type == v8::kExternalDoubleArray || - array_type == v8::kExternalFloatArray) { - CHECK_EQ( - static_cast<int>(i::OS::nan_value()), - static_cast<int>(jsobj->GetElement(7)->ToObjectChecked()->Number())); - } else { - CHECK_EQ(0, static_cast<int>( - jsobj->GetElement(7)->ToObjectChecked()->Number())); - } - - result = CompileRun("for (var i = 0; i < 8; i++) {" - " ext_array[6] = '2.3';" - "}" - "ext_array[6];"); - CHECK_EQ(2, result->Int32Value()); - CHECK_EQ( - 2, static_cast<int>(jsobj->GetElement(6)->ToObjectChecked()->Number())); - - if (array_type != v8::kExternalFloatArray && - array_type != v8::kExternalDoubleArray) { - // Though the specification doesn't state it, be explicit about - // converting NaNs and +/-Infinity to zero. - result = CompileRun("for (var i = 0; i < 8; i++) {" - " ext_array[i] = 5;" - "}" - "for (var i = 0; i < 8; i++) {" - " ext_array[i] = NaN;" - "}" - "ext_array[5];"); - CHECK_EQ(0, result->Int32Value()); - CHECK_EQ(0, - i::Smi::cast(jsobj->GetElement(5)->ToObjectChecked())->value()); - - result = CompileRun("for (var i = 0; i < 8; i++) {" - " ext_array[i] = 5;" - "}" - "for (var i = 0; i < 8; i++) {" - " ext_array[i] = Infinity;" - "}" - "ext_array[5];"); - int expected_value = - (array_type == v8::kExternalPixelArray) ? 255 : 0; - CHECK_EQ(expected_value, result->Int32Value()); - CHECK_EQ(expected_value, - i::Smi::cast(jsobj->GetElement(5)->ToObjectChecked())->value()); - - result = CompileRun("for (var i = 0; i < 8; i++) {" - " ext_array[i] = 5;" - "}" - "for (var i = 0; i < 8; i++) {" - " ext_array[i] = -Infinity;" - "}" - "ext_array[5];"); - CHECK_EQ(0, result->Int32Value()); - CHECK_EQ(0, - i::Smi::cast(jsobj->GetElement(5)->ToObjectChecked())->value()); - - // Check truncation behavior of integral arrays. - const char* unsigned_data = - "var source_data = [0.6, 10.6];" - "var expected_results = [0, 10];"; - const char* signed_data = - "var source_data = [0.6, 10.6, -0.6, -10.6];" - "var expected_results = [0, 10, 0, -10];"; - const char* pixel_data = - "var source_data = [0.6, 10.6];" - "var expected_results = [1, 11];"; - bool is_unsigned = - (array_type == v8::kExternalUnsignedByteArray || - array_type == v8::kExternalUnsignedShortArray || - array_type == v8::kExternalUnsignedIntArray); - bool is_pixel_data = array_type == v8::kExternalPixelArray; - - i::OS::SNPrintF(test_buf, - "%s" - "var all_passed = true;" - "for (var i = 0; i < source_data.length; i++) {" - " for (var j = 0; j < 8; j++) {" - " ext_array[j] = source_data[i];" - " }" - " all_passed = all_passed &&" - " (ext_array[5] == expected_results[i]);" - "}" - "all_passed;", - (is_unsigned ? - unsigned_data : - (is_pixel_data ? pixel_data : signed_data))); - result = CompileRun(test_buf.start()); - CHECK_EQ(true, result->BooleanValue()); - } - - for (int i = 0; i < kElementCount; i++) { - array->set(i, static_cast<ElementType>(i)); - } - // Test complex assignments - result = CompileRun("function ee_op_test_complex_func(sum) {" - " for (var i = 0; i < 40; ++i) {" - " sum += (ext_array[i] += 1);" - " sum += (ext_array[i] -= 1);" - " } " - " return sum;" - "}" - "sum=0;" - "for (var i=0;i<10000;++i) {" - " sum=ee_op_test_complex_func(sum);" - "}" - "sum;"); - CHECK_EQ(16000000, result->Int32Value()); - - // Test count operations - result = CompileRun("function ee_op_test_count_func(sum) {" - " for (var i = 0; i < 40; ++i) {" - " sum += (++ext_array[i]);" - " sum += (--ext_array[i]);" - " } " - " return sum;" - "}" - "sum=0;" - "for (var i=0;i<10000;++i) {" - " sum=ee_op_test_count_func(sum);" - "}" - "sum;"); - CHECK_EQ(16000000, result->Int32Value()); - - result = CompileRun("ext_array[3] = 33;" - "delete ext_array[3];" - "ext_array[3];"); - CHECK_EQ(33, result->Int32Value()); - - result = CompileRun("ext_array[0] = 10; ext_array[1] = 11;" - "ext_array[2] = 12; ext_array[3] = 13;" - "ext_array.__defineGetter__('2'," - "function() { return 120; });" - "ext_array[2];"); - CHECK_EQ(12, result->Int32Value()); - - result = CompileRun("var js_array = new Array(40);" - "js_array[0] = 77;" - "js_array;"); - CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value()); - - result = CompileRun("ext_array[1] = 23;" - "ext_array.__proto__ = [];" - "js_array.__proto__ = ext_array;" - "js_array.concat(ext_array);"); - CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value()); - CHECK_EQ(23, v8::Object::Cast(*result)->Get(v8_str("1"))->Int32Value()); - - result = CompileRun("ext_array[1] = 23;"); - CHECK_EQ(23, result->Int32Value()); - - // Test more complex manipulations which cause eax to contain values - // that won't be completely overwritten by loads from the arrays. - // This catches bugs in the instructions used for the KeyedLoadIC - // for byte and word types. - { - const int kXSize = 300; - const int kYSize = 300; - const int kLargeElementCount = kXSize * kYSize * 4; - ElementType* large_array_data = - static_cast<ElementType*>(malloc(kLargeElementCount * element_size)); - v8::Handle<v8::Object> large_obj = v8::Object::New(); - // Set the elements to be the external array. - large_obj->SetIndexedPropertiesToExternalArrayData(large_array_data, - array_type, - kLargeElementCount); - context->Global()->Set(v8_str("large_array"), large_obj); - // Initialize contents of a few rows. - for (int x = 0; x < 300; x++) { - int row = 0; - int offset = row * 300 * 4; - large_array_data[offset + 4 * x + 0] = (ElementType) 127; - large_array_data[offset + 4 * x + 1] = (ElementType) 0; - large_array_data[offset + 4 * x + 2] = (ElementType) 0; - large_array_data[offset + 4 * x + 3] = (ElementType) 127; - row = 150; - offset = row * 300 * 4; - large_array_data[offset + 4 * x + 0] = (ElementType) 127; - large_array_data[offset + 4 * x + 1] = (ElementType) 0; - large_array_data[offset + 4 * x + 2] = (ElementType) 0; - large_array_data[offset + 4 * x + 3] = (ElementType) 127; - row = 298; - offset = row * 300 * 4; - large_array_data[offset + 4 * x + 0] = (ElementType) 127; - large_array_data[offset + 4 * x + 1] = (ElementType) 0; - large_array_data[offset + 4 * x + 2] = (ElementType) 0; - large_array_data[offset + 4 * x + 3] = (ElementType) 127; - } - // The goal of the code below is to make "offset" large enough - // that the computation of the index (which goes into eax) has - // high bits set which will not be overwritten by a byte or short - // load. - result = CompileRun("var failed = false;" - "var offset = 0;" - "for (var i = 0; i < 300; i++) {" - " if (large_array[4 * i] != 127 ||" - " large_array[4 * i + 1] != 0 ||" - " large_array[4 * i + 2] != 0 ||" - " large_array[4 * i + 3] != 127) {" - " failed = true;" - " }" - "}" - "offset = 150 * 300 * 4;" - "for (var i = 0; i < 300; i++) {" - " if (large_array[offset + 4 * i] != 127 ||" - " large_array[offset + 4 * i + 1] != 0 ||" - " large_array[offset + 4 * i + 2] != 0 ||" - " large_array[offset + 4 * i + 3] != 127) {" - " failed = true;" - " }" - "}" - "offset = 298 * 300 * 4;" - "for (var i = 0; i < 300; i++) {" - " if (large_array[offset + 4 * i] != 127 ||" - " large_array[offset + 4 * i + 1] != 0 ||" - " large_array[offset + 4 * i + 2] != 0 ||" - " large_array[offset + 4 * i + 3] != 127) {" - " failed = true;" - " }" - "}" - "!failed;"); - CHECK_EQ(true, result->BooleanValue()); - free(large_array_data); - } - - // The "" property descriptor is overloaded to store information about - // the external array. Ensure that setting and accessing the "" property - // works (it should overwrite the information cached about the external - // array in the DescriptorArray) in various situations. - result = CompileRun("ext_array[''] = 23; ext_array['']"); - CHECK_EQ(23, result->Int32Value()); - - // Property "" set after the external array is associated with the object. - { - v8::Handle<v8::Object> obj2 = v8::Object::New(); - obj2->Set(v8_str("ee_test_field"), v8::Int32::New(256)); - obj2->Set(v8_str(""), v8::Int32::New(1503)); - // Set the elements to be the external array. - obj2->SetIndexedPropertiesToExternalArrayData(array_data, - array_type, - kElementCount); - context->Global()->Set(v8_str("ext_array"), obj2); - result = CompileRun("ext_array['']"); - CHECK_EQ(1503, result->Int32Value()); - } - - // Property "" set after the external array is associated with the object. - { - v8::Handle<v8::Object> obj2 = v8::Object::New(); - obj2->Set(v8_str("ee_test_field_2"), v8::Int32::New(256)); - // Set the elements to be the external array. - obj2->SetIndexedPropertiesToExternalArrayData(array_data, - array_type, - kElementCount); - obj2->Set(v8_str(""), v8::Int32::New(1503)); - context->Global()->Set(v8_str("ext_array"), obj2); - result = CompileRun("ext_array['']"); - CHECK_EQ(1503, result->Int32Value()); - } - - // Should reuse the map from previous test. - { - v8::Handle<v8::Object> obj2 = v8::Object::New(); - obj2->Set(v8_str("ee_test_field_2"), v8::Int32::New(256)); - // Set the elements to be the external array. Should re-use the map - // from previous test. - obj2->SetIndexedPropertiesToExternalArrayData(array_data, - array_type, - kElementCount); - context->Global()->Set(v8_str("ext_array"), obj2); - result = CompileRun("ext_array['']"); - } - - // Property "" is a constant function that shouldn't not be interfered with - // when an external array is set. - { - v8::Handle<v8::Object> obj2 = v8::Object::New(); - // Start - obj2->Set(v8_str("ee_test_field3"), v8::Int32::New(256)); - - // Add a constant function to an object. - context->Global()->Set(v8_str("ext_array"), obj2); - result = CompileRun("ext_array[''] = function() {return 1503;};" - "ext_array['']();"); - - // Add an external array transition to the same map that - // has the constant transition. - v8::Handle<v8::Object> obj3 = v8::Object::New(); - obj3->Set(v8_str("ee_test_field3"), v8::Int32::New(256)); - obj3->SetIndexedPropertiesToExternalArrayData(array_data, - array_type, - kElementCount); - context->Global()->Set(v8_str("ext_array"), obj3); - } - - // If a external array transition is in the map, it should get clobbered - // by a constant function. - { - // Add an external array transition. - v8::Handle<v8::Object> obj3 = v8::Object::New(); - obj3->Set(v8_str("ee_test_field4"), v8::Int32::New(256)); - obj3->SetIndexedPropertiesToExternalArrayData(array_data, - array_type, - kElementCount); - - // Add a constant function to the same map that just got an external array - // transition. - v8::Handle<v8::Object> obj2 = v8::Object::New(); - obj2->Set(v8_str("ee_test_field4"), v8::Int32::New(256)); - context->Global()->Set(v8_str("ext_array"), obj2); - result = CompileRun("ext_array[''] = function() {return 1503;};" - "ext_array['']();"); - } - - free(array_data); -} - - -THREADED_TEST(ExternalByteArray) { - ExternalArrayTestHelper<i::ExternalByteArray, int8_t>( - v8::kExternalByteArray, - -128, - 127); -} - - -THREADED_TEST(ExternalUnsignedByteArray) { - ExternalArrayTestHelper<i::ExternalUnsignedByteArray, uint8_t>( - v8::kExternalUnsignedByteArray, - 0, - 255); -} - - -THREADED_TEST(ExternalPixelArray) { - ExternalArrayTestHelper<i::ExternalPixelArray, uint8_t>( - v8::kExternalPixelArray, - 0, - 255); -} - - -THREADED_TEST(ExternalShortArray) { - ExternalArrayTestHelper<i::ExternalShortArray, int16_t>( - v8::kExternalShortArray, - -32768, - 32767); -} - - -THREADED_TEST(ExternalUnsignedShortArray) { - ExternalArrayTestHelper<i::ExternalUnsignedShortArray, uint16_t>( - v8::kExternalUnsignedShortArray, - 0, - 65535); -} - - -THREADED_TEST(ExternalIntArray) { - ExternalArrayTestHelper<i::ExternalIntArray, int32_t>( - v8::kExternalIntArray, - INT_MIN, // -2147483648 - INT_MAX); // 2147483647 -} - - -THREADED_TEST(ExternalUnsignedIntArray) { - ExternalArrayTestHelper<i::ExternalUnsignedIntArray, uint32_t>( - v8::kExternalUnsignedIntArray, - 0, - UINT_MAX); // 4294967295 -} - - -THREADED_TEST(ExternalFloatArray) { - ExternalArrayTestHelper<i::ExternalFloatArray, float>( - v8::kExternalFloatArray, - -500, - 500); -} - - -THREADED_TEST(ExternalDoubleArray) { - ExternalArrayTestHelper<i::ExternalDoubleArray, double>( - v8::kExternalDoubleArray, - -500, - 500); -} - - -THREADED_TEST(ExternalArrays) { - TestExternalByteArray(); - TestExternalUnsignedByteArray(); - TestExternalShortArray(); - TestExternalUnsignedShortArray(); - TestExternalIntArray(); - TestExternalUnsignedIntArray(); - TestExternalFloatArray(); -} - - -void ExternalArrayInfoTestHelper(v8::ExternalArrayType array_type) { - v8::HandleScope scope; - LocalContext context; - for (int size = 0; size < 100; size += 10) { - int element_size = ExternalArrayElementSize(array_type); - void* external_data = malloc(size * element_size); - v8::Handle<v8::Object> obj = v8::Object::New(); - obj->SetIndexedPropertiesToExternalArrayData( - external_data, array_type, size); - CHECK(obj->HasIndexedPropertiesInExternalArrayData()); - CHECK_EQ(external_data, obj->GetIndexedPropertiesExternalArrayData()); - CHECK_EQ(array_type, obj->GetIndexedPropertiesExternalArrayDataType()); - CHECK_EQ(size, obj->GetIndexedPropertiesExternalArrayDataLength()); - free(external_data); - } -} - - -THREADED_TEST(ExternalArrayInfo) { - ExternalArrayInfoTestHelper(v8::kExternalByteArray); - ExternalArrayInfoTestHelper(v8::kExternalUnsignedByteArray); - ExternalArrayInfoTestHelper(v8::kExternalShortArray); - ExternalArrayInfoTestHelper(v8::kExternalUnsignedShortArray); - ExternalArrayInfoTestHelper(v8::kExternalIntArray); - ExternalArrayInfoTestHelper(v8::kExternalUnsignedIntArray); - ExternalArrayInfoTestHelper(v8::kExternalFloatArray); - ExternalArrayInfoTestHelper(v8::kExternalDoubleArray); - ExternalArrayInfoTestHelper(v8::kExternalPixelArray); -} - - -void ExternalArrayLimitTestHelper(v8::ExternalArrayType array_type, int size) { - v8::Handle<v8::Object> obj = v8::Object::New(); - v8::V8::SetFatalErrorHandler(StoringErrorCallback); - last_location = last_message = NULL; - obj->SetIndexedPropertiesToExternalArrayData(NULL, array_type, size); - CHECK(!obj->HasIndexedPropertiesInExternalArrayData()); - CHECK_NE(NULL, last_location); - CHECK_NE(NULL, last_message); -} - - -TEST(ExternalArrayLimits) { - v8::HandleScope scope; - LocalContext context; - ExternalArrayLimitTestHelper(v8::kExternalByteArray, 0x40000000); - ExternalArrayLimitTestHelper(v8::kExternalByteArray, 0xffffffff); - ExternalArrayLimitTestHelper(v8::kExternalUnsignedByteArray, 0x40000000); - ExternalArrayLimitTestHelper(v8::kExternalUnsignedByteArray, 0xffffffff); - ExternalArrayLimitTestHelper(v8::kExternalShortArray, 0x40000000); - ExternalArrayLimitTestHelper(v8::kExternalShortArray, 0xffffffff); - ExternalArrayLimitTestHelper(v8::kExternalUnsignedShortArray, 0x40000000); - ExternalArrayLimitTestHelper(v8::kExternalUnsignedShortArray, 0xffffffff); - ExternalArrayLimitTestHelper(v8::kExternalIntArray, 0x40000000); - ExternalArrayLimitTestHelper(v8::kExternalIntArray, 0xffffffff); - ExternalArrayLimitTestHelper(v8::kExternalUnsignedIntArray, 0x40000000); - ExternalArrayLimitTestHelper(v8::kExternalUnsignedIntArray, 0xffffffff); - ExternalArrayLimitTestHelper(v8::kExternalFloatArray, 0x40000000); - ExternalArrayLimitTestHelper(v8::kExternalFloatArray, 0xffffffff); - ExternalArrayLimitTestHelper(v8::kExternalDoubleArray, 0x40000000); - ExternalArrayLimitTestHelper(v8::kExternalDoubleArray, 0xffffffff); - ExternalArrayLimitTestHelper(v8::kExternalPixelArray, 0x40000000); - ExternalArrayLimitTestHelper(v8::kExternalPixelArray, 0xffffffff); -} - - -THREADED_TEST(ScriptContextDependence) { - v8::HandleScope scope; - LocalContext c1; - const char *source = "foo"; - v8::Handle<v8::Script> dep = v8::Script::Compile(v8::String::New(source)); - v8::Handle<v8::Script> indep = v8::Script::New(v8::String::New(source)); - c1->Global()->Set(v8::String::New("foo"), v8::Integer::New(100)); - CHECK_EQ(dep->Run()->Int32Value(), 100); - CHECK_EQ(indep->Run()->Int32Value(), 100); - LocalContext c2; - c2->Global()->Set(v8::String::New("foo"), v8::Integer::New(101)); - CHECK_EQ(dep->Run()->Int32Value(), 100); - CHECK_EQ(indep->Run()->Int32Value(), 101); -} - - -THREADED_TEST(StackTrace) { - v8::HandleScope scope; - LocalContext context; - v8::TryCatch try_catch; - const char *source = "function foo() { FAIL.FAIL; }; foo();"; - v8::Handle<v8::String> src = v8::String::New(source); - v8::Handle<v8::String> origin = v8::String::New("stack-trace-test"); - v8::Script::New(src, origin)->Run(); - CHECK(try_catch.HasCaught()); - v8::String::Utf8Value stack(try_catch.StackTrace()); - CHECK(strstr(*stack, "at foo (stack-trace-test") != NULL); -} - - -// Checks that a StackFrame has certain expected values. -void checkStackFrame(const char* expected_script_name, - const char* expected_func_name, int expected_line_number, - int expected_column, bool is_eval, bool is_constructor, - v8::Handle<v8::StackFrame> frame) { - v8::HandleScope scope; - v8::String::Utf8Value func_name(frame->GetFunctionName()); - v8::String::Utf8Value script_name(frame->GetScriptName()); - if (*script_name == NULL) { - // The situation where there is no associated script, like for evals. - CHECK(expected_script_name == NULL); - } else { - CHECK(strstr(*script_name, expected_script_name) != NULL); - } - CHECK(strstr(*func_name, expected_func_name) != NULL); - CHECK_EQ(expected_line_number, frame->GetLineNumber()); - CHECK_EQ(expected_column, frame->GetColumn()); - CHECK_EQ(is_eval, frame->IsEval()); - CHECK_EQ(is_constructor, frame->IsConstructor()); -} - - -v8::Handle<Value> AnalyzeStackInNativeCode(const v8::Arguments& args) { - v8::HandleScope scope; - const char* origin = "capture-stack-trace-test"; - const int kOverviewTest = 1; - const int kDetailedTest = 2; - - ASSERT(args.Length() == 1); - - int testGroup = args[0]->Int32Value(); - if (testGroup == kOverviewTest) { - v8::Handle<v8::StackTrace> stackTrace = - v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kOverview); - CHECK_EQ(4, stackTrace->GetFrameCount()); - checkStackFrame(origin, "bar", 2, 10, false, false, - stackTrace->GetFrame(0)); - checkStackFrame(origin, "foo", 6, 3, false, false, - stackTrace->GetFrame(1)); - // This is the source string inside the eval which has the call to foo. - checkStackFrame(NULL, "", 1, 5, false, false, - stackTrace->GetFrame(2)); - // The last frame is an anonymous function which has the initial eval call. - checkStackFrame(origin, "", 8, 7, false, false, - stackTrace->GetFrame(3)); - - CHECK(stackTrace->AsArray()->IsArray()); - } else if (testGroup == kDetailedTest) { - v8::Handle<v8::StackTrace> stackTrace = - v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kDetailed); - CHECK_EQ(4, stackTrace->GetFrameCount()); - checkStackFrame(origin, "bat", 4, 22, false, false, - stackTrace->GetFrame(0)); - checkStackFrame(origin, "baz", 8, 3, false, true, - stackTrace->GetFrame(1)); -#ifdef ENABLE_DEBUGGER_SUPPORT - bool is_eval = true; -#else // ENABLE_DEBUGGER_SUPPORT - bool is_eval = false; -#endif // ENABLE_DEBUGGER_SUPPORT - - // This is the source string inside the eval which has the call to baz. - checkStackFrame(NULL, "", 1, 5, is_eval, false, - stackTrace->GetFrame(2)); - // The last frame is an anonymous function which has the initial eval call. - checkStackFrame(origin, "", 10, 1, false, false, - stackTrace->GetFrame(3)); - - CHECK(stackTrace->AsArray()->IsArray()); - } - return v8::Undefined(); -} - - -// Tests the C++ StackTrace API. -// TODO(3074796): Reenable this as a THREADED_TEST once it passes. -// THREADED_TEST(CaptureStackTrace) { -TEST(CaptureStackTrace) { - v8::HandleScope scope; - v8::Handle<v8::String> origin = v8::String::New("capture-stack-trace-test"); - Local<ObjectTemplate> templ = ObjectTemplate::New(); - templ->Set(v8_str("AnalyzeStackInNativeCode"), - v8::FunctionTemplate::New(AnalyzeStackInNativeCode)); - LocalContext context(0, templ); - - // Test getting OVERVIEW information. Should ignore information that is not - // script name, function name, line number, and column offset. - const char *overview_source = - "function bar() {\n" - " var y; AnalyzeStackInNativeCode(1);\n" - "}\n" - "function foo() {\n" - "\n" - " bar();\n" - "}\n" - "var x;eval('new foo();');"; - v8::Handle<v8::String> overview_src = v8::String::New(overview_source); - v8::Handle<Value> overview_result( - v8::Script::New(overview_src, origin)->Run()); - CHECK(!overview_result.IsEmpty()); - CHECK(overview_result->IsObject()); - - // Test getting DETAILED information. - const char *detailed_source = - "function bat() {AnalyzeStackInNativeCode(2);\n" - "}\n" - "\n" - "function baz() {\n" - " bat();\n" - "}\n" - "eval('new baz();');"; - v8::Handle<v8::String> detailed_src = v8::String::New(detailed_source); - // Make the script using a non-zero line and column offset. - v8::Handle<v8::Integer> line_offset = v8::Integer::New(3); - v8::Handle<v8::Integer> column_offset = v8::Integer::New(5); - v8::ScriptOrigin detailed_origin(origin, line_offset, column_offset); - v8::Handle<v8::Script> detailed_script( - v8::Script::New(detailed_src, &detailed_origin)); - v8::Handle<Value> detailed_result(detailed_script->Run()); - CHECK(!detailed_result.IsEmpty()); - CHECK(detailed_result->IsObject()); -} - - -static void StackTraceForUncaughtExceptionListener( - v8::Handle<v8::Message> message, - v8::Handle<Value>) { - v8::Handle<v8::StackTrace> stack_trace = message->GetStackTrace(); - CHECK_EQ(2, stack_trace->GetFrameCount()); - checkStackFrame("origin", "foo", 2, 3, false, false, - stack_trace->GetFrame(0)); - checkStackFrame("origin", "bar", 5, 3, false, false, - stack_trace->GetFrame(1)); -} - -TEST(CaptureStackTraceForUncaughtException) { - report_count = 0; - v8::HandleScope scope; - LocalContext env; - v8::V8::AddMessageListener(StackTraceForUncaughtExceptionListener); - v8::V8::SetCaptureStackTraceForUncaughtExceptions(true); - - Script::Compile(v8_str("function foo() {\n" - " throw 1;\n" - "};\n" - "function bar() {\n" - " foo();\n" - "};"), - v8_str("origin"))->Run(); - v8::Local<v8::Object> global = env->Global(); - Local<Value> trouble = global->Get(v8_str("bar")); - CHECK(trouble->IsFunction()); - Function::Cast(*trouble)->Call(global, 0, NULL); - v8::V8::SetCaptureStackTraceForUncaughtExceptions(false); - v8::V8::RemoveMessageListeners(StackTraceForUncaughtExceptionListener); -} - - -TEST(CaptureStackTraceForUncaughtExceptionAndSetters) { - v8::HandleScope scope; - LocalContext env; - v8::V8::SetCaptureStackTraceForUncaughtExceptions(true, - 1024, - v8::StackTrace::kDetailed); - - CompileRun( - "var setters = ['column', 'lineNumber', 'scriptName',\n" - " 'scriptNameOrSourceURL', 'functionName', 'isEval',\n" - " 'isConstructor'];\n" - "for (var i = 0; i < setters.length; i++) {\n" - " var prop = setters[i];\n" - " Object.prototype.__defineSetter__(prop, function() { throw prop; });\n" - "}\n"); - CompileRun("throw 'exception';"); - v8::V8::SetCaptureStackTraceForUncaughtExceptions(false); -} - - -static void RethrowStackTraceHandler(v8::Handle<v8::Message> message, - v8::Handle<v8::Value> data) { - // Use the frame where JavaScript is called from. - v8::Handle<v8::StackTrace> stack_trace = message->GetStackTrace(); - CHECK(!stack_trace.IsEmpty()); - int frame_count = stack_trace->GetFrameCount(); - CHECK_EQ(3, frame_count); - int line_number[] = {1, 2, 5}; - for (int i = 0; i < frame_count; i++) { - CHECK_EQ(line_number[i], stack_trace->GetFrame(i)->GetLineNumber()); - } -} - - -// Test that we only return the stack trace at the site where the exception -// is first thrown (not where it is rethrown). -TEST(RethrowStackTrace) { - v8::HandleScope scope; - LocalContext env; - // We make sure that - // - the stack trace of the ReferenceError in g() is reported. - // - the stack trace is not overwritten when e1 is rethrown by t(). - // - the stack trace of e2 does not overwrite that of e1. - const char* source = - "function g() { error; } \n" - "function f() { g(); } \n" - "function t(e) { throw e; } \n" - "try { \n" - " f(); \n" - "} catch (e1) { \n" - " try { \n" - " error; \n" - " } catch (e2) { \n" - " t(e1); \n" - " } \n" - "} \n"; - v8::V8::AddMessageListener(RethrowStackTraceHandler); - v8::V8::SetCaptureStackTraceForUncaughtExceptions(true); - CompileRun(source); - v8::V8::SetCaptureStackTraceForUncaughtExceptions(false); - v8::V8::RemoveMessageListeners(RethrowStackTraceHandler); -} - - -static void RethrowPrimitiveStackTraceHandler(v8::Handle<v8::Message> message, - v8::Handle<v8::Value> data) { - v8::Handle<v8::StackTrace> stack_trace = message->GetStackTrace(); - CHECK(!stack_trace.IsEmpty()); - int frame_count = stack_trace->GetFrameCount(); - CHECK_EQ(2, frame_count); - int line_number[] = {3, 7}; - for (int i = 0; i < frame_count; i++) { - CHECK_EQ(line_number[i], stack_trace->GetFrame(i)->GetLineNumber()); - } -} - - -// Test that we do not recognize identity for primitive exceptions. -TEST(RethrowPrimitiveStackTrace) { - v8::HandleScope scope; - LocalContext env; - // We do not capture stack trace for non Error objects on creation time. - // Instead, we capture the stack trace on last throw. - const char* source = - "function g() { throw 404; } \n" - "function f() { g(); } \n" - "function t(e) { throw e; } \n" - "try { \n" - " f(); \n" - "} catch (e1) { \n" - " t(e1) \n" - "} \n"; - v8::V8::AddMessageListener(RethrowPrimitiveStackTraceHandler); - v8::V8::SetCaptureStackTraceForUncaughtExceptions(true); - CompileRun(source); - v8::V8::SetCaptureStackTraceForUncaughtExceptions(false); - v8::V8::RemoveMessageListeners(RethrowPrimitiveStackTraceHandler); -} - - -static void RethrowExistingStackTraceHandler(v8::Handle<v8::Message> message, - v8::Handle<v8::Value> data) { - // Use the frame where JavaScript is called from. - v8::Handle<v8::StackTrace> stack_trace = message->GetStackTrace(); - CHECK(!stack_trace.IsEmpty()); - CHECK_EQ(1, stack_trace->GetFrameCount()); - CHECK_EQ(1, stack_trace->GetFrame(0)->GetLineNumber()); -} - - -// Test that the stack trace is captured when the error object is created and -// not where it is thrown. -TEST(RethrowExistingStackTrace) { - v8::HandleScope scope; - LocalContext env; - const char* source = - "var e = new Error(); \n" - "throw e; \n"; - v8::V8::AddMessageListener(RethrowExistingStackTraceHandler); - v8::V8::SetCaptureStackTraceForUncaughtExceptions(true); - CompileRun(source); - v8::V8::SetCaptureStackTraceForUncaughtExceptions(false); - v8::V8::RemoveMessageListeners(RethrowExistingStackTraceHandler); -} - - -static void RethrowBogusErrorStackTraceHandler(v8::Handle<v8::Message> message, - v8::Handle<v8::Value> data) { - // Use the frame where JavaScript is called from. - v8::Handle<v8::StackTrace> stack_trace = message->GetStackTrace(); - CHECK(!stack_trace.IsEmpty()); - CHECK_EQ(1, stack_trace->GetFrameCount()); - CHECK_EQ(2, stack_trace->GetFrame(0)->GetLineNumber()); -} - - -// Test that the stack trace is captured where the bogus Error object is thrown. -TEST(RethrowBogusErrorStackTrace) { - v8::HandleScope scope; - LocalContext env; - const char* source = - "var e = {__proto__: new Error()} \n" - "throw e; \n"; - v8::V8::AddMessageListener(RethrowBogusErrorStackTraceHandler); - v8::V8::SetCaptureStackTraceForUncaughtExceptions(true); - CompileRun(source); - v8::V8::SetCaptureStackTraceForUncaughtExceptions(false); - v8::V8::RemoveMessageListeners(RethrowBogusErrorStackTraceHandler); -} - - -v8::Handle<Value> AnalyzeStackOfEvalWithSourceURL(const v8::Arguments& args) { - v8::HandleScope scope; - v8::Handle<v8::StackTrace> stackTrace = - v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kDetailed); - CHECK_EQ(5, stackTrace->GetFrameCount()); - v8::Handle<v8::String> url = v8_str("eval_url"); - for (int i = 0; i < 3; i++) { - v8::Handle<v8::String> name = - stackTrace->GetFrame(i)->GetScriptNameOrSourceURL(); - CHECK(!name.IsEmpty()); - CHECK_EQ(url, name); - } - return v8::Undefined(); -} - - -TEST(SourceURLInStackTrace) { - v8::HandleScope scope; - Local<ObjectTemplate> templ = ObjectTemplate::New(); - templ->Set(v8_str("AnalyzeStackOfEvalWithSourceURL"), - v8::FunctionTemplate::New(AnalyzeStackOfEvalWithSourceURL)); - LocalContext context(0, templ); - - const char *source = - "function outer() {\n" - "function bar() {\n" - " AnalyzeStackOfEvalWithSourceURL();\n" - "}\n" - "function foo() {\n" - "\n" - " bar();\n" - "}\n" - "foo();\n" - "}\n" - "eval('(' + outer +')()//@ sourceURL=eval_url');"; - CHECK(CompileRun(source)->IsUndefined()); -} - - -v8::Handle<Value> AnalyzeStackOfInlineScriptWithSourceURL( - const v8::Arguments& args) { - v8::HandleScope scope; - v8::Handle<v8::StackTrace> stackTrace = - v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kDetailed); - CHECK_EQ(4, stackTrace->GetFrameCount()); - v8::Handle<v8::String> url = v8_str("url"); - for (int i = 0; i < 3; i++) { - v8::Handle<v8::String> name = - stackTrace->GetFrame(i)->GetScriptNameOrSourceURL(); - CHECK(!name.IsEmpty()); - CHECK_EQ(url, name); - } - return v8::Undefined(); -} - - -TEST(InlineScriptWithSourceURLInStackTrace) { - v8::HandleScope scope; - Local<ObjectTemplate> templ = ObjectTemplate::New(); - templ->Set(v8_str("AnalyzeStackOfInlineScriptWithSourceURL"), - v8::FunctionTemplate::New( - AnalyzeStackOfInlineScriptWithSourceURL)); - LocalContext context(0, templ); - - const char *source = - "function outer() {\n" - "function bar() {\n" - " AnalyzeStackOfInlineScriptWithSourceURL();\n" - "}\n" - "function foo() {\n" - "\n" - " bar();\n" - "}\n" - "foo();\n" - "}\n" - "outer()\n" - "//@ sourceURL=source_url"; - CHECK(CompileRunWithOrigin(source, "url", 0, 1)->IsUndefined()); -} - - -v8::Handle<Value> AnalyzeStackOfDynamicScriptWithSourceURL( - const v8::Arguments& args) { - v8::HandleScope scope; - v8::Handle<v8::StackTrace> stackTrace = - v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kDetailed); - CHECK_EQ(4, stackTrace->GetFrameCount()); - v8::Handle<v8::String> url = v8_str("source_url"); - for (int i = 0; i < 3; i++) { - v8::Handle<v8::String> name = - stackTrace->GetFrame(i)->GetScriptNameOrSourceURL(); - CHECK(!name.IsEmpty()); - CHECK_EQ(url, name); - } - return v8::Undefined(); -} - - -TEST(DynamicWithSourceURLInStackTrace) { - v8::HandleScope scope; - Local<ObjectTemplate> templ = ObjectTemplate::New(); - templ->Set(v8_str("AnalyzeStackOfDynamicScriptWithSourceURL"), - v8::FunctionTemplate::New( - AnalyzeStackOfDynamicScriptWithSourceURL)); - LocalContext context(0, templ); - - const char *source = - "function outer() {\n" - "function bar() {\n" - " AnalyzeStackOfDynamicScriptWithSourceURL();\n" - "}\n" - "function foo() {\n" - "\n" - " bar();\n" - "}\n" - "foo();\n" - "}\n" - "outer()\n" - "//@ sourceURL=source_url"; - CHECK(CompileRunWithOrigin(source, "url", 0, 0)->IsUndefined()); -} - -static void CreateGarbageInOldSpace() { - v8::HandleScope scope; - i::AlwaysAllocateScope always_allocate; - for (int i = 0; i < 1000; i++) { - FACTORY->NewFixedArray(1000, i::TENURED); - } -} - -// Test that idle notification can be handled and eventually returns true. -TEST(IdleNotification) { - const intptr_t MB = 1024 * 1024; - v8::HandleScope scope; - LocalContext env; - intptr_t initial_size = HEAP->SizeOfObjects(); - CreateGarbageInOldSpace(); - intptr_t size_with_garbage = HEAP->SizeOfObjects(); - CHECK_GT(size_with_garbage, initial_size + MB); - bool finished = false; - for (int i = 0; i < 200 && !finished; i++) { - finished = v8::V8::IdleNotification(); - } - intptr_t final_size = HEAP->SizeOfObjects(); - CHECK(finished); - CHECK_LT(final_size, initial_size + 1); -} - - -// Test that idle notification can be handled and eventually collects garbage. -TEST(IdleNotificationWithSmallHint) { - const intptr_t MB = 1024 * 1024; - const int IdlePauseInMs = 900; - v8::HandleScope scope; - LocalContext env; - intptr_t initial_size = HEAP->SizeOfObjects(); - CreateGarbageInOldSpace(); - intptr_t size_with_garbage = HEAP->SizeOfObjects(); - CHECK_GT(size_with_garbage, initial_size + MB); - bool finished = false; - for (int i = 0; i < 200 && !finished; i++) { - finished = v8::V8::IdleNotification(IdlePauseInMs); - } - intptr_t final_size = HEAP->SizeOfObjects(); - CHECK(finished); - CHECK_LT(final_size, initial_size + 1); -} - - -// Test that idle notification can be handled and eventually collects garbage. -TEST(IdleNotificationWithLargeHint) { - const intptr_t MB = 1024 * 1024; - const int IdlePauseInMs = 900; - v8::HandleScope scope; - LocalContext env; - intptr_t initial_size = HEAP->SizeOfObjects(); - CreateGarbageInOldSpace(); - intptr_t size_with_garbage = HEAP->SizeOfObjects(); - CHECK_GT(size_with_garbage, initial_size + MB); - bool finished = false; - for (int i = 0; i < 200 && !finished; i++) { - finished = v8::V8::IdleNotification(IdlePauseInMs); - } - intptr_t final_size = HEAP->SizeOfObjects(); - CHECK(finished); - CHECK_LT(final_size, initial_size + 1); -} - - -TEST(Regress2107) { - const intptr_t MB = 1024 * 1024; - const int kShortIdlePauseInMs = 100; - const int kLongIdlePauseInMs = 1000; - v8::HandleScope scope; - LocalContext env; - intptr_t initial_size = HEAP->SizeOfObjects(); - // Send idle notification to start a round of incremental GCs. - v8::V8::IdleNotification(kShortIdlePauseInMs); - // Emulate 7 page reloads. - for (int i = 0; i < 7; i++) { - v8::Persistent<v8::Context> ctx = v8::Context::New(); - ctx->Enter(); - CreateGarbageInOldSpace(); - ctx->Exit(); - ctx.Dispose(ctx->GetIsolate()); - v8::V8::ContextDisposedNotification(); - v8::V8::IdleNotification(kLongIdlePauseInMs); - } - // Create garbage and check that idle notification still collects it. - CreateGarbageInOldSpace(); - intptr_t size_with_garbage = HEAP->SizeOfObjects(); - CHECK_GT(size_with_garbage, initial_size + MB); - bool finished = false; - for (int i = 0; i < 200 && !finished; i++) { - finished = v8::V8::IdleNotification(kShortIdlePauseInMs); - } - intptr_t final_size = HEAP->SizeOfObjects(); - CHECK_LT(final_size, initial_size + 1); -} - -static uint32_t* stack_limit; - -static v8::Handle<Value> GetStackLimitCallback(const v8::Arguments& args) { - stack_limit = reinterpret_cast<uint32_t*>( - i::Isolate::Current()->stack_guard()->real_climit()); - return v8::Undefined(); -} - - -// Uses the address of a local variable to determine the stack top now. -// Given a size, returns an address that is that far from the current -// top of stack. -static uint32_t* ComputeStackLimit(uint32_t size) { - uint32_t* answer = &size - (size / sizeof(size)); - // If the size is very large and the stack is very near the bottom of - // memory then the calculation above may wrap around and give an address - // that is above the (downwards-growing) stack. In that case we return - // a very low address. - if (answer > &size) return reinterpret_cast<uint32_t*>(sizeof(size)); - return answer; -} - - -TEST(SetResourceConstraints) { - static const int K = 1024; - uint32_t* set_limit = ComputeStackLimit(128 * K); - - // Set stack limit. - v8::ResourceConstraints constraints; - constraints.set_stack_limit(set_limit); - CHECK(v8::SetResourceConstraints(&constraints)); - - // Execute a script. - v8::HandleScope scope; - LocalContext env; - Local<v8::FunctionTemplate> fun_templ = - v8::FunctionTemplate::New(GetStackLimitCallback); - Local<Function> fun = fun_templ->GetFunction(); - env->Global()->Set(v8_str("get_stack_limit"), fun); - CompileRun("get_stack_limit();"); - - CHECK(stack_limit == set_limit); -} - - -TEST(SetResourceConstraintsInThread) { - uint32_t* set_limit; - { - v8::Locker locker(CcTest::default_isolate()); - static const int K = 1024; - set_limit = ComputeStackLimit(128 * K); - - // Set stack limit. - v8::ResourceConstraints constraints; - constraints.set_stack_limit(set_limit); - CHECK(v8::SetResourceConstraints(&constraints)); - - // Execute a script. - v8::HandleScope scope; - LocalContext env; - Local<v8::FunctionTemplate> fun_templ = - v8::FunctionTemplate::New(GetStackLimitCallback); - Local<Function> fun = fun_templ->GetFunction(); - env->Global()->Set(v8_str("get_stack_limit"), fun); - CompileRun("get_stack_limit();"); - - CHECK(stack_limit == set_limit); - } - { - v8::Locker locker(CcTest::default_isolate()); - CHECK(stack_limit == set_limit); - } -} - - -THREADED_TEST(GetHeapStatistics) { - v8::HandleScope scope; - LocalContext c1; - v8::HeapStatistics heap_statistics; - CHECK_EQ(static_cast<int>(heap_statistics.total_heap_size()), 0); - CHECK_EQ(static_cast<int>(heap_statistics.used_heap_size()), 0); - c1->GetIsolate()->GetHeapStatistics(&heap_statistics); - CHECK_NE(static_cast<int>(heap_statistics.total_heap_size()), 0); - CHECK_NE(static_cast<int>(heap_statistics.used_heap_size()), 0); -} - - -class VisitorImpl : public v8::ExternalResourceVisitor { - public: - explicit VisitorImpl(TestResource** resource) { - for (int i = 0; i < 4; i++) { - resource_[i] = resource[i]; - found_resource_[i] = false; - } - } - virtual ~VisitorImpl() {} - virtual void VisitExternalString(v8::Handle<v8::String> string) { - if (!string->IsExternal()) { - CHECK(string->IsExternalAscii()); - return; - } - v8::String::ExternalStringResource* resource = - string->GetExternalStringResource(); - CHECK(resource); - for (int i = 0; i < 4; i++) { - if (resource_[i] == resource) { - CHECK(!found_resource_[i]); - found_resource_[i] = true; - } - } - } - void CheckVisitedResources() { - for (int i = 0; i < 4; i++) { - CHECK(found_resource_[i]); - } - } - - private: - v8::String::ExternalStringResource* resource_[4]; - bool found_resource_[4]; -}; - -TEST(VisitExternalStrings) { - v8::HandleScope scope; - LocalContext env; - const char* string = "Some string"; - uint16_t* two_byte_string = AsciiToTwoByteString(string); - TestResource* resource[4]; - resource[0] = new TestResource(two_byte_string); - v8::Local<v8::String> string0 = v8::String::NewExternal(resource[0]); - resource[1] = new TestResource(two_byte_string); - v8::Local<v8::String> string1 = v8::String::NewExternal(resource[1]); - - // Externalized symbol. - resource[2] = new TestResource(two_byte_string); - v8::Local<v8::String> string2 = v8::String::NewSymbol(string); - CHECK(string2->MakeExternal(resource[2])); - - // Symbolized External. - resource[3] = new TestResource(AsciiToTwoByteString("Some other string")); - v8::Local<v8::String> string3 = v8::String::NewExternal(resource[3]); - HEAP->CollectAllAvailableGarbage(); // Tenure string. - // Turn into a symbol. - i::Handle<i::String> string3_i = v8::Utils::OpenHandle(*string3); - CHECK(!HEAP->InternalizeString(*string3_i)->IsFailure()); - CHECK(string3_i->IsInternalizedString()); - - // We need to add usages for string* to avoid warnings in GCC 4.7 - CHECK(string0->IsExternal()); - CHECK(string1->IsExternal()); - CHECK(string2->IsExternal()); - CHECK(string3->IsExternal()); - - VisitorImpl visitor(resource); - v8::V8::VisitExternalResources(&visitor); - visitor.CheckVisitedResources(); -} - - -static double DoubleFromBits(uint64_t value) { - double target; - memcpy(&target, &value, sizeof(target)); - return target; -} - - -static uint64_t DoubleToBits(double value) { - uint64_t target; - memcpy(&target, &value, sizeof(target)); - return target; -} - - -static double DoubleToDateTime(double input) { - double date_limit = 864e13; - if (IsNaN(input) || input < -date_limit || input > date_limit) { - return i::OS::nan_value(); - } - return (input < 0) ? -(floor(-input)) : floor(input); -} - -// We don't have a consistent way to write 64-bit constants syntactically, so we -// split them into two 32-bit constants and combine them programmatically. -static double DoubleFromBits(uint32_t high_bits, uint32_t low_bits) { - return DoubleFromBits((static_cast<uint64_t>(high_bits) << 32) | low_bits); -} - - -THREADED_TEST(QuietSignalingNaNs) { - v8::HandleScope scope; - LocalContext context; - v8::TryCatch try_catch; - - // Special double values. - double snan = DoubleFromBits(0x7ff00000, 0x00000001); - double qnan = DoubleFromBits(0x7ff80000, 0x00000000); - double infinity = DoubleFromBits(0x7ff00000, 0x00000000); - double max_normal = DoubleFromBits(0x7fefffff, 0xffffffffu); - double min_normal = DoubleFromBits(0x00100000, 0x00000000); - double max_denormal = DoubleFromBits(0x000fffff, 0xffffffffu); - double min_denormal = DoubleFromBits(0x00000000, 0x00000001); - - // Date values are capped at +/-100000000 days (times 864e5 ms per day) - // on either side of the epoch. - double date_limit = 864e13; - - double test_values[] = { - snan, - qnan, - infinity, - max_normal, - date_limit + 1, - date_limit, - min_normal, - max_denormal, - min_denormal, - 0, - -0, - -min_denormal, - -max_denormal, - -min_normal, - -date_limit, - -date_limit - 1, - -max_normal, - -infinity, - -qnan, - -snan - }; - int num_test_values = 20; - - for (int i = 0; i < num_test_values; i++) { - double test_value = test_values[i]; - - // Check that Number::New preserves non-NaNs and quiets SNaNs. - v8::Handle<v8::Value> number = v8::Number::New(test_value); - double stored_number = number->NumberValue(); - if (!IsNaN(test_value)) { - CHECK_EQ(test_value, stored_number); - } else { - uint64_t stored_bits = DoubleToBits(stored_number); - // Check if quiet nan (bits 51..62 all set). -#if defined(V8_TARGET_ARCH_MIPS) && !defined(USE_SIMULATOR) - // Most significant fraction bit for quiet nan is set to 0 - // on MIPS architecture. Allowed by IEEE-754. - CHECK_EQ(0xffe, static_cast<int>((stored_bits >> 51) & 0xfff)); -#else - CHECK_EQ(0xfff, static_cast<int>((stored_bits >> 51) & 0xfff)); -#endif - } - - // Check that Date::New preserves non-NaNs in the date range and - // quiets SNaNs. - v8::Handle<v8::Value> date = v8::Date::New(test_value); - double expected_stored_date = DoubleToDateTime(test_value); - double stored_date = date->NumberValue(); - if (!IsNaN(expected_stored_date)) { - CHECK_EQ(expected_stored_date, stored_date); - } else { - uint64_t stored_bits = DoubleToBits(stored_date); - // Check if quiet nan (bits 51..62 all set). -#if defined(V8_TARGET_ARCH_MIPS) && !defined(USE_SIMULATOR) - // Most significant fraction bit for quiet nan is set to 0 - // on MIPS architecture. Allowed by IEEE-754. - CHECK_EQ(0xffe, static_cast<int>((stored_bits >> 51) & 0xfff)); -#else - CHECK_EQ(0xfff, static_cast<int>((stored_bits >> 51) & 0xfff)); -#endif - } - } -} - - -static v8::Handle<Value> SpaghettiIncident(const v8::Arguments& args) { - v8::HandleScope scope; - v8::TryCatch tc; - v8::Handle<v8::String> str(args[0]->ToString()); - USE(str); - if (tc.HasCaught()) - return tc.ReThrow(); - return v8::Undefined(); -} - - -// Test that an exception can be propagated down through a spaghetti -// stack using ReThrow. -THREADED_TEST(SpaghettiStackReThrow) { - v8::HandleScope scope; - LocalContext context; - context->Global()->Set( - v8::String::New("s"), - v8::FunctionTemplate::New(SpaghettiIncident)->GetFunction()); - v8::TryCatch try_catch; - CompileRun( - "var i = 0;" - "var o = {" - " toString: function () {" - " if (i == 10) {" - " throw 'Hey!';" - " } else {" - " i++;" - " return s(o);" - " }" - " }" - "};" - "s(o);"); - CHECK(try_catch.HasCaught()); - v8::String::Utf8Value value(try_catch.Exception()); - CHECK_EQ(0, strcmp(*value, "Hey!")); -} - - -TEST(Regress528) { - v8::V8::Initialize(); - - v8::HandleScope scope; - v8::Persistent<Context> context; - v8::Persistent<Context> other_context; - int gc_count; - bool snapshot_enabled = i::Snapshot::IsEnabled(); - - // Create a context used to keep the code from aging in the compilation - // cache. - other_context = Context::New(); - - // Context-dependent context data creates reference from the compilation - // cache to the global object. - const char* source_simple = "1"; - context = Context::New(); - { - v8::HandleScope scope; - - context->Enter(); - Local<v8::String> obj = v8::String::New(""); - context->SetEmbedderData(0, obj); - CompileRun(source_simple); - context->Exit(); - } - context.Dispose(context->GetIsolate()); - v8::V8::ContextDisposedNotification(); - for (gc_count = 1; gc_count < 10; gc_count++) { - other_context->Enter(); - CompileRun(source_simple); - other_context->Exit(); - HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); - if (GetGlobalObjectsCount() == (snapshot_enabled ? 2 : 1)) break; - } - CHECK_GE(2, gc_count); - CHECK_EQ((snapshot_enabled ? 2 : 1), GetGlobalObjectsCount()); - - // Eval in a function creates reference from the compilation cache to the - // global object. - const char* source_eval = "function f(){eval('1')}; f()"; - context = Context::New(); - { - v8::HandleScope scope; - - context->Enter(); - CompileRun(source_eval); - context->Exit(); - } - context.Dispose(context->GetIsolate()); - v8::V8::ContextDisposedNotification(); - for (gc_count = 1; gc_count < 10; gc_count++) { - other_context->Enter(); - CompileRun(source_eval); - other_context->Exit(); - HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); - if (GetGlobalObjectsCount() == (snapshot_enabled ? 2 : 1)) break; - } - CHECK_GE(2, gc_count); - CHECK_EQ((snapshot_enabled ? 2 : 1), GetGlobalObjectsCount()); - - // Looking up the line number for an exception creates reference from the - // compilation cache to the global object. - const char* source_exception = "function f(){throw 1;} f()"; - context = Context::New(); - { - v8::HandleScope scope; - - context->Enter(); - v8::TryCatch try_catch; - CompileRun(source_exception); - CHECK(try_catch.HasCaught()); - v8::Handle<v8::Message> message = try_catch.Message(); - CHECK(!message.IsEmpty()); - CHECK_EQ(1, message->GetLineNumber()); - context->Exit(); - } - context.Dispose(context->GetIsolate()); - v8::V8::ContextDisposedNotification(); - for (gc_count = 1; gc_count < 10; gc_count++) { - other_context->Enter(); - CompileRun(source_exception); - other_context->Exit(); - HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); - if (GetGlobalObjectsCount() == (snapshot_enabled ? 2 : 1)) break; - } - CHECK_GE(2, gc_count); - CHECK_EQ((snapshot_enabled ? 2 : 1), GetGlobalObjectsCount()); - - other_context.Dispose(other_context->GetIsolate()); - v8::V8::ContextDisposedNotification(); -} - - -THREADED_TEST(ScriptOrigin) { - v8::HandleScope scope; - LocalContext env; - v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test")); - v8::Handle<v8::String> script = v8::String::New( - "function f() {}\n\nfunction g() {}"); - v8::Script::Compile(script, &origin)->Run(); - 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"))); - - v8::ScriptOrigin script_origin_f = f->GetScriptOrigin(); - CHECK_EQ("test", *v8::String::AsciiValue(script_origin_f.ResourceName())); - CHECK_EQ(0, script_origin_f.ResourceLineOffset()->Int32Value()); - - v8::ScriptOrigin script_origin_g = g->GetScriptOrigin(); - CHECK_EQ("test", *v8::String::AsciiValue(script_origin_g.ResourceName())); - CHECK_EQ(0, script_origin_g.ResourceLineOffset()->Int32Value()); -} - -THREADED_TEST(FunctionGetInferredName) { - v8::HandleScope scope; - LocalContext env; - v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test")); - v8::Handle<v8::String> script = v8::String::New( - "var foo = { bar : { baz : function() {}}}; var f = foo.bar.baz;"); - v8::Script::Compile(script, &origin)->Run(); - v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast( - env->Global()->Get(v8::String::New("f"))); - CHECK_EQ("foo.bar.baz", *v8::String::AsciiValue(f->GetInferredName())); -} - -THREADED_TEST(ScriptLineNumber) { - v8::HandleScope scope; - LocalContext env; - v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test")); - v8::Handle<v8::String> script = v8::String::New( - "function f() {}\n\nfunction g() {}"); - v8::Script::Compile(script, &origin)->Run(); - 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"))); - CHECK_EQ(0, f->GetScriptLineNumber()); - CHECK_EQ(2, g->GetScriptLineNumber()); -} - - -THREADED_TEST(ScriptColumnNumber) { - v8::HandleScope scope; - LocalContext env; - v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test"), - v8::Integer::New(3), v8::Integer::New(2)); - v8::Handle<v8::String> script = v8::String::New( - "function foo() {}\n\n function bar() {}"); - v8::Script::Compile(script, &origin)->Run(); - v8::Local<v8::Function> foo = v8::Local<v8::Function>::Cast( - env->Global()->Get(v8::String::New("foo"))); - v8::Local<v8::Function> bar = v8::Local<v8::Function>::Cast( - env->Global()->Get(v8::String::New("bar"))); - CHECK_EQ(14, foo->GetScriptColumnNumber()); - CHECK_EQ(17, bar->GetScriptColumnNumber()); -} - - -THREADED_TEST(FunctionGetScriptId) { - v8::HandleScope scope; - LocalContext env; - v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test"), - v8::Integer::New(3), v8::Integer::New(2)); - v8::Handle<v8::String> scriptSource = v8::String::New( - "function foo() {}\n\n function bar() {}"); - v8::Local<v8::Script> script(v8::Script::Compile(scriptSource, &origin)); - script->Run(); - v8::Local<v8::Function> foo = v8::Local<v8::Function>::Cast( - env->Global()->Get(v8::String::New("foo"))); - v8::Local<v8::Function> bar = v8::Local<v8::Function>::Cast( - env->Global()->Get(v8::String::New("bar"))); - CHECK_EQ(script->Id(), foo->GetScriptId()); - CHECK_EQ(script->Id(), bar->GetScriptId()); -} - - -static v8::Handle<Value> GetterWhichReturns42(Local<String> name, - const AccessorInfo& info) { - CHECK(v8::Utils::OpenHandle(*info.This())->IsJSObject()); - CHECK(v8::Utils::OpenHandle(*info.Holder())->IsJSObject()); - return v8_num(42); -} - - -static void SetterWhichSetsYOnThisTo23(Local<String> name, - Local<Value> value, - const AccessorInfo& info) { - CHECK(v8::Utils::OpenHandle(*info.This())->IsJSObject()); - CHECK(v8::Utils::OpenHandle(*info.Holder())->IsJSObject()); - info.This()->Set(v8_str("y"), v8_num(23)); -} - - -Handle<Value> FooGetInterceptor(Local<String> name, - const AccessorInfo& info) { - CHECK(v8::Utils::OpenHandle(*info.This())->IsJSObject()); - CHECK(v8::Utils::OpenHandle(*info.Holder())->IsJSObject()); - if (!name->Equals(v8_str("foo"))) return Handle<Value>(); - return v8_num(42); -} - - -Handle<Value> FooSetInterceptor(Local<String> name, - Local<Value> value, - const AccessorInfo& info) { - CHECK(v8::Utils::OpenHandle(*info.This())->IsJSObject()); - CHECK(v8::Utils::OpenHandle(*info.Holder())->IsJSObject()); - if (!name->Equals(v8_str("foo"))) return Handle<Value>(); - info.This()->Set(v8_str("y"), v8_num(23)); - return v8_num(23); -} - - -TEST(SetterOnConstructorPrototype) { - v8::HandleScope scope; - Local<ObjectTemplate> templ = ObjectTemplate::New(); - templ->SetAccessor(v8_str("x"), - GetterWhichReturns42, - SetterWhichSetsYOnThisTo23); - LocalContext context; - context->Global()->Set(v8_str("P"), templ->NewInstance()); - CompileRun("function C1() {" - " this.x = 23;" - "};" - "C1.prototype = P;" - "function C2() {" - " this.x = 23" - "};" - "C2.prototype = { };" - "C2.prototype.__proto__ = P;"); - - v8::Local<v8::Script> script; - script = v8::Script::Compile(v8_str("new C1();")); - for (int i = 0; i < 10; i++) { - v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run()); - CHECK_EQ(42, c1->Get(v8_str("x"))->Int32Value()); - CHECK_EQ(23, c1->Get(v8_str("y"))->Int32Value()); - } - - script = v8::Script::Compile(v8_str("new C2();")); - for (int i = 0; i < 10; i++) { - v8::Handle<v8::Object> c2 = v8::Handle<v8::Object>::Cast(script->Run()); - CHECK_EQ(42, c2->Get(v8_str("x"))->Int32Value()); - CHECK_EQ(23, c2->Get(v8_str("y"))->Int32Value()); - } -} - - -static v8::Handle<Value> NamedPropertyGetterWhichReturns42( - Local<String> name, const AccessorInfo& info) { - return v8_num(42); -} - - -static v8::Handle<Value> NamedPropertySetterWhichSetsYOnThisTo23( - Local<String> name, Local<Value> value, const AccessorInfo& info) { - if (name->Equals(v8_str("x"))) { - info.This()->Set(v8_str("y"), v8_num(23)); - } - return v8::Handle<Value>(); -} - - -THREADED_TEST(InterceptorOnConstructorPrototype) { - v8::HandleScope scope; - Local<ObjectTemplate> templ = ObjectTemplate::New(); - templ->SetNamedPropertyHandler(NamedPropertyGetterWhichReturns42, - NamedPropertySetterWhichSetsYOnThisTo23); - LocalContext context; - context->Global()->Set(v8_str("P"), templ->NewInstance()); - CompileRun("function C1() {" - " this.x = 23;" - "};" - "C1.prototype = P;" - "function C2() {" - " this.x = 23" - "};" - "C2.prototype = { };" - "C2.prototype.__proto__ = P;"); - - v8::Local<v8::Script> script; - script = v8::Script::Compile(v8_str("new C1();")); - for (int i = 0; i < 10; i++) { - v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run()); - CHECK_EQ(23, c1->Get(v8_str("x"))->Int32Value()); - CHECK_EQ(42, c1->Get(v8_str("y"))->Int32Value()); - } - - script = v8::Script::Compile(v8_str("new C2();")); - for (int i = 0; i < 10; i++) { - v8::Handle<v8::Object> c2 = v8::Handle<v8::Object>::Cast(script->Run()); - CHECK_EQ(23, c2->Get(v8_str("x"))->Int32Value()); - CHECK_EQ(42, c2->Get(v8_str("y"))->Int32Value()); - } -} - - -TEST(Bug618) { - const char* source = "function C1() {" - " this.x = 23;" - "};" - "C1.prototype = P;"; - - v8::HandleScope scope; - LocalContext context; - v8::Local<v8::Script> script; - - // Use a simple object as prototype. - v8::Local<v8::Object> prototype = v8::Object::New(); - prototype->Set(v8_str("y"), v8_num(42)); - context->Global()->Set(v8_str("P"), prototype); - - // This compile will add the code to the compilation cache. - CompileRun(source); - - script = v8::Script::Compile(v8_str("new C1();")); - // Allow enough iterations for the inobject slack tracking logic - // to finalize instance size and install the fast construct stub. - for (int i = 0; i < 256; i++) { - v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run()); - CHECK_EQ(23, c1->Get(v8_str("x"))->Int32Value()); - CHECK_EQ(42, c1->Get(v8_str("y"))->Int32Value()); - } - - // Use an API object with accessors as prototype. - Local<ObjectTemplate> templ = ObjectTemplate::New(); - templ->SetAccessor(v8_str("x"), - GetterWhichReturns42, - SetterWhichSetsYOnThisTo23); - context->Global()->Set(v8_str("P"), templ->NewInstance()); - - // This compile will get the code from the compilation cache. - CompileRun(source); - - script = v8::Script::Compile(v8_str("new C1();")); - for (int i = 0; i < 10; i++) { - v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run()); - CHECK_EQ(42, c1->Get(v8_str("x"))->Int32Value()); - CHECK_EQ(23, c1->Get(v8_str("y"))->Int32Value()); - } -} - -int prologue_call_count = 0; -int epilogue_call_count = 0; -int prologue_call_count_second = 0; -int epilogue_call_count_second = 0; - -void PrologueCallback(v8::GCType, v8::GCCallbackFlags) { - ++prologue_call_count; -} - -void EpilogueCallback(v8::GCType, v8::GCCallbackFlags) { - ++epilogue_call_count; -} - -void PrologueCallbackSecond(v8::GCType, v8::GCCallbackFlags) { - ++prologue_call_count_second; -} - -void EpilogueCallbackSecond(v8::GCType, v8::GCCallbackFlags) { - ++epilogue_call_count_second; -} - -TEST(GCCallbacks) { - LocalContext context; - - v8::V8::AddGCPrologueCallback(PrologueCallback); - v8::V8::AddGCEpilogueCallback(EpilogueCallback); - CHECK_EQ(0, prologue_call_count); - CHECK_EQ(0, epilogue_call_count); - HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); - CHECK_EQ(1, prologue_call_count); - CHECK_EQ(1, epilogue_call_count); - v8::V8::AddGCPrologueCallback(PrologueCallbackSecond); - v8::V8::AddGCEpilogueCallback(EpilogueCallbackSecond); - HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); - CHECK_EQ(2, prologue_call_count); - CHECK_EQ(2, epilogue_call_count); - CHECK_EQ(1, prologue_call_count_second); - CHECK_EQ(1, epilogue_call_count_second); - v8::V8::RemoveGCPrologueCallback(PrologueCallback); - v8::V8::RemoveGCEpilogueCallback(EpilogueCallback); - HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); - CHECK_EQ(2, prologue_call_count); - CHECK_EQ(2, epilogue_call_count); - CHECK_EQ(2, prologue_call_count_second); - CHECK_EQ(2, epilogue_call_count_second); - v8::V8::RemoveGCPrologueCallback(PrologueCallbackSecond); - v8::V8::RemoveGCEpilogueCallback(EpilogueCallbackSecond); - HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); - CHECK_EQ(2, prologue_call_count); - CHECK_EQ(2, epilogue_call_count); - CHECK_EQ(2, prologue_call_count_second); - CHECK_EQ(2, epilogue_call_count_second); -} - - -THREADED_TEST(AddToJSFunctionResultCache) { - i::FLAG_allow_natives_syntax = true; - v8::HandleScope scope; - - LocalContext context; - - const char* code = - "(function() {" - " var key0 = 'a';" - " var key1 = 'b';" - " var r0 = %_GetFromCache(0, key0);" - " var r1 = %_GetFromCache(0, key1);" - " var r0_ = %_GetFromCache(0, key0);" - " if (r0 !== r0_)" - " return 'Different results for ' + key0 + ': ' + r0 + ' vs. ' + r0_;" - " var r1_ = %_GetFromCache(0, key1);" - " if (r1 !== r1_)" - " return 'Different results for ' + key1 + ': ' + r1 + ' vs. ' + r1_;" - " return 'PASSED';" - "})()"; - HEAP->ClearJSFunctionResultCaches(); - ExpectString(code, "PASSED"); -} - - -static const int k0CacheSize = 16; - -THREADED_TEST(FillJSFunctionResultCache) { - i::FLAG_allow_natives_syntax = true; - v8::HandleScope scope; - - LocalContext context; - - const char* code = - "(function() {" - " var k = 'a';" - " var r = %_GetFromCache(0, k);" - " for (var i = 0; i < 16; i++) {" - " %_GetFromCache(0, 'a' + i);" - " };" - " if (r === %_GetFromCache(0, k))" - " return 'FAILED: k0CacheSize is too small';" - " return 'PASSED';" - "})()"; - HEAP->ClearJSFunctionResultCaches(); - ExpectString(code, "PASSED"); -} - - -THREADED_TEST(RoundRobinGetFromCache) { - i::FLAG_allow_natives_syntax = true; - v8::HandleScope scope; - - LocalContext context; - - const char* code = - "(function() {" - " var keys = [];" - " for (var i = 0; i < 16; i++) keys.push(i);" - " var values = [];" - " for (var i = 0; i < 16; i++) values[i] = %_GetFromCache(0, keys[i]);" - " for (var i = 0; i < 16; i++) {" - " var v = %_GetFromCache(0, keys[i]);" - " if (v.toString() !== values[i].toString())" - " return 'Wrong value for ' + " - " keys[i] + ': ' + v + ' vs. ' + values[i];" - " };" - " return 'PASSED';" - "})()"; - HEAP->ClearJSFunctionResultCaches(); - ExpectString(code, "PASSED"); -} - - -THREADED_TEST(ReverseGetFromCache) { - i::FLAG_allow_natives_syntax = true; - v8::HandleScope scope; - - LocalContext context; - - const char* code = - "(function() {" - " var keys = [];" - " for (var i = 0; i < 16; i++) keys.push(i);" - " var values = [];" - " for (var i = 0; i < 16; i++) values[i] = %_GetFromCache(0, keys[i]);" - " for (var i = 15; i >= 16; i--) {" - " var v = %_GetFromCache(0, keys[i]);" - " if (v !== values[i])" - " return 'Wrong value for ' + " - " keys[i] + ': ' + v + ' vs. ' + values[i];" - " };" - " return 'PASSED';" - "})()"; - HEAP->ClearJSFunctionResultCaches(); - ExpectString(code, "PASSED"); -} - - -THREADED_TEST(TestEviction) { - i::FLAG_allow_natives_syntax = true; - v8::HandleScope scope; - - LocalContext context; - - const char* code = - "(function() {" - " for (var i = 0; i < 2*16; i++) {" - " %_GetFromCache(0, 'a' + i);" - " };" - " return 'PASSED';" - "})()"; - HEAP->ClearJSFunctionResultCaches(); - ExpectString(code, "PASSED"); -} - - -THREADED_TEST(TwoByteStringInAsciiCons) { - // See Chromium issue 47824. - v8::HandleScope scope; - - LocalContext context; - const char* init_code = - "var str1 = 'abelspendabel';" - "var str2 = str1 + str1 + str1;" - "str2;"; - Local<Value> result = CompileRun(init_code); - - Local<Value> indexof = CompileRun("str2.indexOf('els')"); - Local<Value> lastindexof = CompileRun("str2.lastIndexOf('dab')"); - - CHECK(result->IsString()); - i::Handle<i::String> string = v8::Utils::OpenHandle(String::Cast(*result)); - int length = string->length(); - CHECK(string->IsOneByteRepresentation()); - - FlattenString(string); - i::Handle<i::String> flat_string = FlattenGetString(string); - - CHECK(string->IsOneByteRepresentation()); - CHECK(flat_string->IsOneByteRepresentation()); - - // Create external resource. - uint16_t* uc16_buffer = new uint16_t[length + 1]; - - i::String::WriteToFlat(*flat_string, uc16_buffer, 0, length); - uc16_buffer[length] = 0; - - TestResource resource(uc16_buffer); - - flat_string->MakeExternal(&resource); - - CHECK(flat_string->IsTwoByteRepresentation()); - - // At this point, we should have a Cons string which is flat and ASCII, - // with a first half that is a two-byte string (although it only contains - // ASCII characters). This is a valid sequence of steps, and it can happen - // in real pages. - - CHECK(string->IsOneByteRepresentation()); - i::ConsString* cons = i::ConsString::cast(*string); - CHECK_EQ(0, cons->second()->length()); - CHECK(cons->first()->IsTwoByteRepresentation()); - - // Check that some string operations work. - - // Atom RegExp. - Local<Value> reresult = CompileRun("str2.match(/abel/g).length;"); - CHECK_EQ(6, reresult->Int32Value()); - - // Nonatom RegExp. - reresult = CompileRun("str2.match(/abe./g).length;"); - CHECK_EQ(6, reresult->Int32Value()); - - reresult = CompileRun("str2.search(/bel/g);"); - CHECK_EQ(1, reresult->Int32Value()); - - reresult = CompileRun("str2.search(/be./g);"); - CHECK_EQ(1, reresult->Int32Value()); - - ExpectTrue("/bel/g.test(str2);"); - - ExpectTrue("/be./g.test(str2);"); - - reresult = CompileRun("/bel/g.exec(str2);"); - CHECK(!reresult->IsNull()); - - reresult = CompileRun("/be./g.exec(str2);"); - CHECK(!reresult->IsNull()); - - ExpectString("str2.substring(2, 10);", "elspenda"); - - ExpectString("str2.substring(2, 20);", "elspendabelabelspe"); - - ExpectString("str2.charAt(2);", "e"); - - ExpectObject("str2.indexOf('els');", indexof); - - ExpectObject("str2.lastIndexOf('dab');", lastindexof); - - reresult = CompileRun("str2.charCodeAt(2);"); - CHECK_EQ(static_cast<int32_t>('e'), reresult->Int32Value()); -} - - -// Failed access check callback that performs a GC on each invocation. -void FailedAccessCheckCallbackGC(Local<v8::Object> target, - v8::AccessType type, - Local<v8::Value> data) { - HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); -} - - -TEST(GCInFailedAccessCheckCallback) { - // Install a failed access check callback that performs a GC on each - // invocation. Then force the callback to be called from va - - v8::V8::Initialize(); - v8::V8::SetFailedAccessCheckCallbackFunction(&FailedAccessCheckCallbackGC); - - v8::HandleScope scope; - - // Create an ObjectTemplate for global objects and install access - // check callbacks that will block access. - v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); - global_template->SetAccessCheckCallbacks(NamedGetAccessBlocker, - IndexedGetAccessBlocker, - v8::Handle<v8::Value>(), - false); - - // Create a context and set an x property on it's global object. - LocalContext context0(NULL, global_template); - context0->Global()->Set(v8_str("x"), v8_num(42)); - v8::Handle<v8::Object> global0 = context0->Global(); - - // Create a context with a different security token so that the - // failed access check callback will be called on each access. - LocalContext context1(NULL, global_template); - context1->Global()->Set(v8_str("other"), global0); - - // Get property with failed access check. - ExpectUndefined("other.x"); - - // Get element with failed access check. - ExpectUndefined("other[0]"); - - // Set property with failed access check. - v8::Handle<v8::Value> result = CompileRun("other.x = new Object()"); - CHECK(result->IsObject()); - - // Set element with failed access check. - result = CompileRun("other[0] = new Object()"); - CHECK(result->IsObject()); - - // Get property attribute with failed access check. - ExpectFalse("\'x\' in other"); - - // Get property attribute for element with failed access check. - ExpectFalse("0 in other"); - - // Delete property. - ExpectFalse("delete other.x"); - - // Delete element. - CHECK_EQ(false, global0->Delete(0)); - - // DefineAccessor. - CHECK_EQ(false, - global0->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("x"))); - - // Define JavaScript accessor. - ExpectUndefined("Object.prototype.__defineGetter__.call(" - " other, \'x\', function() { return 42; })"); - - // LookupAccessor. - ExpectUndefined("Object.prototype.__lookupGetter__.call(" - " other, \'x\')"); - - // HasLocalElement. - ExpectFalse("Object.prototype.hasOwnProperty.call(other, \'0\')"); - - CHECK_EQ(false, global0->HasRealIndexedProperty(0)); - CHECK_EQ(false, global0->HasRealNamedProperty(v8_str("x"))); - CHECK_EQ(false, global0->HasRealNamedCallbackProperty(v8_str("x"))); - - // Reset the failed access check callback so it does not influence - // the other tests. - v8::V8::SetFailedAccessCheckCallbackFunction(NULL); -} - -TEST(DefaultIsolateGetCurrent) { - CHECK(v8::Isolate::GetCurrent() != NULL); - v8::Isolate* isolate = v8::Isolate::GetCurrent(); - CHECK(reinterpret_cast<i::Isolate*>(isolate)->IsDefaultIsolate()); - printf("*** %s\n", "DefaultIsolateGetCurrent success"); -} - -TEST(IsolateNewDispose) { - v8::Isolate* current_isolate = v8::Isolate::GetCurrent(); - v8::Isolate* isolate = v8::Isolate::New(); - CHECK(isolate != NULL); - CHECK(!reinterpret_cast<i::Isolate*>(isolate)->IsDefaultIsolate()); - CHECK(current_isolate != isolate); - CHECK(current_isolate == v8::Isolate::GetCurrent()); - - v8::V8::SetFatalErrorHandler(StoringErrorCallback); - last_location = last_message = NULL; - isolate->Dispose(); - CHECK_EQ(last_location, NULL); - CHECK_EQ(last_message, NULL); -} - -TEST(IsolateEnterExitDefault) { - v8::HandleScope scope; - LocalContext context; - v8::Isolate* current_isolate = v8::Isolate::GetCurrent(); - CHECK(current_isolate != NULL); // Default isolate. - ExpectString("'hello'", "hello"); - current_isolate->Enter(); - ExpectString("'still working'", "still working"); - current_isolate->Exit(); - ExpectString("'still working 2'", "still working 2"); - current_isolate->Exit(); - // Default isolate is always, well, 'default current'. - CHECK_EQ(v8::Isolate::GetCurrent(), current_isolate); - // Still working since default isolate is auto-entering any thread - // that has no isolate and attempts to execute V8 APIs. - ExpectString("'still working 3'", "still working 3"); -} - -TEST(DisposeDefaultIsolate) { - v8::V8::SetFatalErrorHandler(StoringErrorCallback); - - // Run some V8 code to trigger default isolate to become 'current'. - v8::HandleScope scope; - LocalContext context; - ExpectString("'run some V8'", "run some V8"); - - v8::Isolate* isolate = v8::Isolate::GetCurrent(); - CHECK(reinterpret_cast<i::Isolate*>(isolate)->IsDefaultIsolate()); - last_location = last_message = NULL; - isolate->Dispose(); - // It is not possible to dispose default isolate via Isolate API. - CHECK_NE(last_location, NULL); - CHECK_NE(last_message, NULL); -} - -TEST(RunDefaultAndAnotherIsolate) { - v8::HandleScope scope; - LocalContext context; - - // Enter new isolate. - v8::Isolate* isolate = v8::Isolate::New(); - CHECK(isolate); - isolate->Enter(); - { // Need this block because subsequent Exit() will deallocate Heap, - // so we need all scope objects to be deconstructed when it happens. - v8::HandleScope scope_new; - LocalContext context_new; - - // Run something in new isolate. - CompileRun("var foo = 153;"); - ExpectTrue("function f() { return foo == 153; }; f()"); - } - isolate->Exit(); - - // This runs automatically in default isolate. - // Variables in another isolate should be not available. - ExpectTrue("function f() {" - " try {" - " foo;" - " return false;" - " } catch(e) {" - " return true;" - " }" - "};" - "var bar = 371;" - "f()"); - - v8::V8::SetFatalErrorHandler(StoringErrorCallback); - last_location = last_message = NULL; - isolate->Dispose(); - CHECK_EQ(last_location, NULL); - CHECK_EQ(last_message, NULL); - - // Check that default isolate still runs. - ExpectTrue("function f() { return bar == 371; }; f()"); -} - -TEST(DisposeIsolateWhenInUse) { - v8::Isolate* isolate = v8::Isolate::New(); - CHECK(isolate); - isolate->Enter(); - v8::HandleScope scope; - LocalContext context; - // Run something in this isolate. - ExpectTrue("true"); - v8::V8::SetFatalErrorHandler(StoringErrorCallback); - last_location = last_message = NULL; - // Still entered, should fail. - isolate->Dispose(); - CHECK_NE(last_location, NULL); - CHECK_NE(last_message, NULL); -} - -TEST(RunTwoIsolatesOnSingleThread) { - // Run isolate 1. - v8::Isolate* isolate1 = v8::Isolate::New(); - isolate1->Enter(); - v8::Persistent<v8::Context> context1 = v8::Context::New(); - - { - v8::Context::Scope cscope(context1); - v8::HandleScope scope; - // Run something in new isolate. - CompileRun("var foo = 'isolate 1';"); - ExpectString("function f() { return foo; }; f()", "isolate 1"); - } - - // Run isolate 2. - v8::Isolate* isolate2 = v8::Isolate::New(); - v8::Persistent<v8::Context> context2; - - { - v8::Isolate::Scope iscope(isolate2); - context2 = v8::Context::New(); - v8::Context::Scope cscope(context2); - v8::HandleScope scope; - - // Run something in new isolate. - CompileRun("var foo = 'isolate 2';"); - ExpectString("function f() { return foo; }; f()", "isolate 2"); - } - - { - v8::Context::Scope cscope(context1); - v8::HandleScope scope; - // Now again in isolate 1 - ExpectString("function f() { return foo; }; f()", "isolate 1"); - } - - isolate1->Exit(); - - // Run some stuff in default isolate. - v8::Persistent<v8::Context> context_default = v8::Context::New(); - - { - v8::Context::Scope cscope(context_default); - v8::HandleScope scope; - // Variables in other isolates should be not available, verify there - // is an exception. - ExpectTrue("function f() {" - " try {" - " foo;" - " return false;" - " } catch(e) {" - " return true;" - " }" - "};" - "var isDefaultIsolate = true;" - "f()"); - } - - isolate1->Enter(); - - { - v8::Isolate::Scope iscope(isolate2); - v8::Context::Scope cscope(context2); - v8::HandleScope scope; - ExpectString("function f() { return foo; }; f()", "isolate 2"); - } - - { - v8::Context::Scope cscope(context1); - v8::HandleScope scope; - ExpectString("function f() { return foo; }; f()", "isolate 1"); - } - - { - v8::Isolate::Scope iscope(isolate2); - context2.Dispose(context2->GetIsolate()); - } - - context1.Dispose(context1->GetIsolate()); - isolate1->Exit(); - - v8::V8::SetFatalErrorHandler(StoringErrorCallback); - last_location = last_message = NULL; - - isolate1->Dispose(); - CHECK_EQ(last_location, NULL); - CHECK_EQ(last_message, NULL); - - isolate2->Dispose(); - CHECK_EQ(last_location, NULL); - CHECK_EQ(last_message, NULL); - - // Check that default isolate still runs. - { - v8::Context::Scope cscope(context_default); - v8::HandleScope scope; - ExpectTrue("function f() { return isDefaultIsolate; }; f()"); - } -} - -static int CalcFibonacci(v8::Isolate* isolate, int limit) { - v8::Isolate::Scope isolate_scope(isolate); - v8::HandleScope scope; - LocalContext context; - i::ScopedVector<char> code(1024); - i::OS::SNPrintF(code, "function fib(n) {" - " if (n <= 2) return 1;" - " return fib(n-1) + fib(n-2);" - "}" - "fib(%d)", limit); - Local<Value> value = CompileRun(code.start()); - CHECK(value->IsNumber()); - return static_cast<int>(value->NumberValue()); -} - -class IsolateThread : public v8::internal::Thread { - public: - IsolateThread(v8::Isolate* isolate, int fib_limit) - : Thread("IsolateThread"), - isolate_(isolate), - fib_limit_(fib_limit), - result_(0) { } - - void Run() { - result_ = CalcFibonacci(isolate_, fib_limit_); - } - - int result() { return result_; } - - private: - v8::Isolate* isolate_; - int fib_limit_; - int result_; -}; - -TEST(MultipleIsolatesOnIndividualThreads) { - v8::Isolate* isolate1 = v8::Isolate::New(); - v8::Isolate* isolate2 = v8::Isolate::New(); - - IsolateThread thread1(isolate1, 21); - IsolateThread thread2(isolate2, 12); - - // Compute some fibonacci numbers on 3 threads in 3 isolates. - thread1.Start(); - thread2.Start(); - - int result1 = CalcFibonacci(v8::Isolate::GetCurrent(), 21); - int result2 = CalcFibonacci(v8::Isolate::GetCurrent(), 12); - - thread1.Join(); - thread2.Join(); - - // Compare results. The actual fibonacci numbers for 12 and 21 are taken - // (I'm lazy!) from http://en.wikipedia.org/wiki/Fibonacci_number - CHECK_EQ(result1, 10946); - CHECK_EQ(result2, 144); - CHECK_EQ(result1, thread1.result()); - CHECK_EQ(result2, thread2.result()); - - isolate1->Dispose(); - isolate2->Dispose(); -} - -TEST(IsolateDifferentContexts) { - v8::Isolate* isolate = v8::Isolate::New(); - Persistent<v8::Context> context; - { - v8::Isolate::Scope isolate_scope(isolate); - v8::HandleScope handle_scope; - context = v8::Context::New(); - v8::Context::Scope context_scope(context); - Local<Value> v = CompileRun("2"); - CHECK(v->IsNumber()); - CHECK_EQ(2, static_cast<int>(v->NumberValue())); - } - { - v8::Isolate::Scope isolate_scope(isolate); - v8::HandleScope handle_scope; - context = v8::Context::New(); - v8::Context::Scope context_scope(context); - Local<Value> v = CompileRun("22"); - CHECK(v->IsNumber()); - CHECK_EQ(22, static_cast<int>(v->NumberValue())); - } - isolate->Dispose(); -} - -class InitDefaultIsolateThread : public v8::internal::Thread { - public: - enum TestCase { - IgnoreOOM, - SetResourceConstraints, - SetFatalHandler, - SetCounterFunction, - SetCreateHistogramFunction, - SetAddHistogramSampleFunction - }; - - explicit InitDefaultIsolateThread(TestCase testCase) - : Thread("InitDefaultIsolateThread"), - testCase_(testCase), - result_(false) { } - - void Run() { - switch (testCase_) { - case IgnoreOOM: - v8::V8::IgnoreOutOfMemoryException(); - break; - - case SetResourceConstraints: { - static const int K = 1024; - v8::ResourceConstraints constraints; - constraints.set_max_young_space_size(256 * K); - constraints.set_max_old_space_size(4 * K * K); - v8::SetResourceConstraints(&constraints); - break; - } - - case SetFatalHandler: - v8::V8::SetFatalErrorHandler(NULL); - break; - - case SetCounterFunction: - v8::V8::SetCounterFunction(NULL); - break; - - case SetCreateHistogramFunction: - v8::V8::SetCreateHistogramFunction(NULL); - break; - - case SetAddHistogramSampleFunction: - v8::V8::SetAddHistogramSampleFunction(NULL); - break; - } - result_ = true; - } - - bool result() { return result_; } - - private: - TestCase testCase_; - bool result_; -}; - - -static void InitializeTestHelper(InitDefaultIsolateThread::TestCase testCase) { - InitDefaultIsolateThread thread(testCase); - thread.Start(); - thread.Join(); - CHECK_EQ(thread.result(), true); -} - -TEST(InitializeDefaultIsolateOnSecondaryThread1) { - InitializeTestHelper(InitDefaultIsolateThread::IgnoreOOM); -} - -TEST(InitializeDefaultIsolateOnSecondaryThread2) { - InitializeTestHelper(InitDefaultIsolateThread::SetResourceConstraints); -} - -TEST(InitializeDefaultIsolateOnSecondaryThread3) { - InitializeTestHelper(InitDefaultIsolateThread::SetFatalHandler); -} - -TEST(InitializeDefaultIsolateOnSecondaryThread4) { - InitializeTestHelper(InitDefaultIsolateThread::SetCounterFunction); -} - -TEST(InitializeDefaultIsolateOnSecondaryThread5) { - InitializeTestHelper(InitDefaultIsolateThread::SetCreateHistogramFunction); -} - -TEST(InitializeDefaultIsolateOnSecondaryThread6) { - InitializeTestHelper(InitDefaultIsolateThread::SetAddHistogramSampleFunction); -} - - -TEST(StringCheckMultipleContexts) { - const char* code = - "(function() { return \"a\".charAt(0); })()"; - - { - // Run the code twice in the first context to initialize the call IC. - v8::HandleScope scope; - LocalContext context1; - ExpectString(code, "a"); - ExpectString(code, "a"); - } - - { - // Change the String.prototype in the second context and check - // that the right function gets called. - v8::HandleScope scope; - LocalContext context2; - CompileRun("String.prototype.charAt = function() { return \"not a\"; }"); - ExpectString(code, "not a"); - } -} - - -TEST(NumberCheckMultipleContexts) { - const char* code = - "(function() { return (42).toString(); })()"; - - { - // Run the code twice in the first context to initialize the call IC. - v8::HandleScope scope; - LocalContext context1; - ExpectString(code, "42"); - ExpectString(code, "42"); - } - - { - // Change the Number.prototype in the second context and check - // that the right function gets called. - v8::HandleScope scope; - LocalContext context2; - CompileRun("Number.prototype.toString = function() { return \"not 42\"; }"); - ExpectString(code, "not 42"); - } -} - - -TEST(BooleanCheckMultipleContexts) { - const char* code = - "(function() { return true.toString(); })()"; - - { - // Run the code twice in the first context to initialize the call IC. - v8::HandleScope scope; - LocalContext context1; - ExpectString(code, "true"); - ExpectString(code, "true"); - } - - { - // Change the Boolean.prototype in the second context and check - // that the right function gets called. - v8::HandleScope scope; - LocalContext context2; - CompileRun("Boolean.prototype.toString = function() { return \"\"; }"); - ExpectString(code, ""); - } -} - - -TEST(DontDeleteCellLoadIC) { - const char* function_code = - "function readCell() { while (true) { return cell; } }"; - - { - // Run the code twice in the first context to initialize the load - // IC for a don't delete cell. - v8::HandleScope scope; - LocalContext context1; - CompileRun("var cell = \"first\";"); - ExpectBoolean("delete cell", false); - CompileRun(function_code); - ExpectString("readCell()", "first"); - ExpectString("readCell()", "first"); - } - - { - // Use a deletable cell in the second context. - v8::HandleScope scope; - LocalContext context2; - CompileRun("cell = \"second\";"); - CompileRun(function_code); - ExpectString("readCell()", "second"); - ExpectBoolean("delete cell", true); - ExpectString("(function() {" - " try {" - " return readCell();" - " } catch(e) {" - " return e.toString();" - " }" - "})()", - "ReferenceError: cell is not defined"); - CompileRun("cell = \"new_second\";"); - HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); - ExpectString("readCell()", "new_second"); - ExpectString("readCell()", "new_second"); - } -} - - -TEST(DontDeleteCellLoadICForceDelete) { - const char* function_code = - "function readCell() { while (true) { return cell; } }"; - - // Run the code twice to initialize the load IC for a don't delete - // cell. - v8::HandleScope scope; - LocalContext context; - CompileRun("var cell = \"value\";"); - ExpectBoolean("delete cell", false); - CompileRun(function_code); - ExpectString("readCell()", "value"); - ExpectString("readCell()", "value"); - - // Delete the cell using the API and check the inlined code works - // correctly. - CHECK(context->Global()->ForceDelete(v8_str("cell"))); - ExpectString("(function() {" - " try {" - " return readCell();" - " } catch(e) {" - " return e.toString();" - " }" - "})()", - "ReferenceError: cell is not defined"); -} - - -TEST(DontDeleteCellLoadICAPI) { - const char* function_code = - "function readCell() { while (true) { return cell; } }"; - - // Run the code twice to initialize the load IC for a don't delete - // cell created using the API. - v8::HandleScope scope; - LocalContext context; - context->Global()->Set(v8_str("cell"), v8_str("value"), v8::DontDelete); - ExpectBoolean("delete cell", false); - CompileRun(function_code); - ExpectString("readCell()", "value"); - ExpectString("readCell()", "value"); - - // Delete the cell using the API and check the inlined code works - // correctly. - CHECK(context->Global()->ForceDelete(v8_str("cell"))); - ExpectString("(function() {" - " try {" - " return readCell();" - " } catch(e) {" - " return e.toString();" - " }" - "})()", - "ReferenceError: cell is not defined"); -} - - -class Visitor42 : public v8::PersistentHandleVisitor { - public: - explicit Visitor42(v8::Persistent<v8::Object> object) - : counter_(0), object_(object) { } - - virtual void VisitPersistentHandle(Persistent<Value> value, - uint16_t class_id) { - if (class_id == 42) { - CHECK(value->IsObject()); - v8::Persistent<v8::Object> visited = - v8::Persistent<v8::Object>::Cast(value); - CHECK_EQ(42, visited.WrapperClassId(v8::Isolate::GetCurrent())); - CHECK_EQ(object_, visited); - ++counter_; - } - } - - int counter_; - v8::Persistent<v8::Object> object_; -}; - - -TEST(PersistentHandleVisitor) { - v8::HandleScope scope; - LocalContext context; - v8::Isolate* isolate = context->GetIsolate(); - v8::Persistent<v8::Object> object = - v8::Persistent<v8::Object>::New(isolate, v8::Object::New()); - CHECK_EQ(0, object.WrapperClassId(isolate)); - object.SetWrapperClassId(isolate, 42); - CHECK_EQ(42, object.WrapperClassId(isolate)); - - Visitor42 visitor(object); - v8::V8::VisitHandlesWithClassIds(&visitor); - CHECK_EQ(1, visitor.counter_); - - object.Dispose(isolate); -} - - -TEST(WrapperClassId) { - v8::HandleScope scope; - LocalContext context; - v8::Isolate* isolate = context->GetIsolate(); - v8::Persistent<v8::Object> object = - v8::Persistent<v8::Object>::New(isolate, v8::Object::New()); - CHECK_EQ(0, object.WrapperClassId(isolate)); - object.SetWrapperClassId(isolate, 65535); - CHECK_EQ(65535, object.WrapperClassId(isolate)); - object.Dispose(isolate); -} - - -TEST(PersistentHandleInNewSpaceVisitor) { - v8::HandleScope scope; - LocalContext context; - v8::Isolate* isolate = context->GetIsolate(); - v8::Persistent<v8::Object> object1 = - v8::Persistent<v8::Object>::New(isolate, v8::Object::New()); - CHECK_EQ(0, object1.WrapperClassId(isolate)); - object1.SetWrapperClassId(isolate, 42); - CHECK_EQ(42, object1.WrapperClassId(isolate)); - - HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); - - v8::Persistent<v8::Object> object2 = - v8::Persistent<v8::Object>::New(isolate, v8::Object::New()); - CHECK_EQ(0, object2.WrapperClassId(isolate)); - object2.SetWrapperClassId(isolate, 42); - CHECK_EQ(42, object2.WrapperClassId(isolate)); - - Visitor42 visitor(object2); - v8::V8::VisitHandlesForPartialDependence(isolate, &visitor); - CHECK_EQ(1, visitor.counter_); - - object1.Dispose(isolate); - object2.Dispose(isolate); -} - - -TEST(RegExp) { - v8::HandleScope scope; - LocalContext context; - - v8::Handle<v8::RegExp> re = v8::RegExp::New(v8_str("foo"), v8::RegExp::kNone); - CHECK(re->IsRegExp()); - CHECK(re->GetSource()->Equals(v8_str("foo"))); - CHECK_EQ(v8::RegExp::kNone, re->GetFlags()); - - re = v8::RegExp::New(v8_str("bar"), - static_cast<v8::RegExp::Flags>(v8::RegExp::kIgnoreCase | - v8::RegExp::kGlobal)); - CHECK(re->IsRegExp()); - CHECK(re->GetSource()->Equals(v8_str("bar"))); - CHECK_EQ(v8::RegExp::kIgnoreCase | v8::RegExp::kGlobal, - static_cast<int>(re->GetFlags())); - - re = v8::RegExp::New(v8_str("baz"), - static_cast<v8::RegExp::Flags>(v8::RegExp::kIgnoreCase | - v8::RegExp::kMultiline)); - CHECK(re->IsRegExp()); - CHECK(re->GetSource()->Equals(v8_str("baz"))); - CHECK_EQ(v8::RegExp::kIgnoreCase | v8::RegExp::kMultiline, - static_cast<int>(re->GetFlags())); - - re = CompileRun("/quux/").As<v8::RegExp>(); - CHECK(re->IsRegExp()); - CHECK(re->GetSource()->Equals(v8_str("quux"))); - CHECK_EQ(v8::RegExp::kNone, re->GetFlags()); - - re = CompileRun("/quux/gm").As<v8::RegExp>(); - CHECK(re->IsRegExp()); - CHECK(re->GetSource()->Equals(v8_str("quux"))); - CHECK_EQ(v8::RegExp::kGlobal | v8::RegExp::kMultiline, - static_cast<int>(re->GetFlags())); - - // Override the RegExp constructor and check the API constructor - // still works. - CompileRun("RegExp = function() {}"); - - re = v8::RegExp::New(v8_str("foobar"), v8::RegExp::kNone); - CHECK(re->IsRegExp()); - CHECK(re->GetSource()->Equals(v8_str("foobar"))); - CHECK_EQ(v8::RegExp::kNone, re->GetFlags()); - - re = v8::RegExp::New(v8_str("foobarbaz"), - static_cast<v8::RegExp::Flags>(v8::RegExp::kIgnoreCase | - v8::RegExp::kMultiline)); - CHECK(re->IsRegExp()); - CHECK(re->GetSource()->Equals(v8_str("foobarbaz"))); - CHECK_EQ(v8::RegExp::kIgnoreCase | v8::RegExp::kMultiline, - static_cast<int>(re->GetFlags())); - - context->Global()->Set(v8_str("re"), re); - ExpectTrue("re.test('FoobarbaZ')"); - - // RegExps are objects on which you can set properties. - re->Set(v8_str("property"), v8::Integer::New(32)); - v8::Handle<v8::Value> value(CompileRun("re.property")); - CHECK_EQ(32, value->Int32Value()); - - v8::TryCatch try_catch; - re = v8::RegExp::New(v8_str("foo["), v8::RegExp::kNone); - CHECK(re.IsEmpty()); - CHECK(try_catch.HasCaught()); - context->Global()->Set(v8_str("ex"), try_catch.Exception()); - ExpectTrue("ex instanceof SyntaxError"); -} - - -THREADED_TEST(Equals) { - v8::HandleScope handleScope; - LocalContext localContext; - - v8::Handle<v8::Object> globalProxy = localContext->Global(); - v8::Handle<Value> global = globalProxy->GetPrototype(); - - CHECK(global->StrictEquals(global)); - CHECK(!global->StrictEquals(globalProxy)); - CHECK(!globalProxy->StrictEquals(global)); - CHECK(globalProxy->StrictEquals(globalProxy)); - - CHECK(global->Equals(global)); - CHECK(!global->Equals(globalProxy)); - CHECK(!globalProxy->Equals(global)); - CHECK(globalProxy->Equals(globalProxy)); -} - - -static v8::Handle<v8::Value> Getter(v8::Local<v8::String> property, - const v8::AccessorInfo& info ) { - return v8_str("42!"); -} - - -static v8::Handle<v8::Array> Enumerator(const v8::AccessorInfo& info) { - v8::Handle<v8::Array> result = v8::Array::New(); - result->Set(0, v8_str("universalAnswer")); - return result; -} - - -TEST(NamedEnumeratorAndForIn) { - v8::HandleScope handle_scope; - LocalContext context; - v8::Context::Scope context_scope(context.local()); - - v8::Handle<v8::ObjectTemplate> tmpl = v8::ObjectTemplate::New(); - tmpl->SetNamedPropertyHandler(Getter, NULL, NULL, NULL, Enumerator); - context->Global()->Set(v8_str("o"), tmpl->NewInstance()); - v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun( - "var result = []; for (var k in o) result.push(k); result")); - CHECK_EQ(1, result->Length()); - CHECK_EQ(v8_str("universalAnswer"), result->Get(0)); -} - - -TEST(DefinePropertyPostDetach) { - v8::HandleScope scope; - LocalContext context; - v8::Handle<v8::Object> proxy = context->Global(); - v8::Handle<v8::Function> define_property = - CompileRun("(function() {" - " Object.defineProperty(" - " this," - " 1," - " { configurable: true, enumerable: true, value: 3 });" - "})").As<Function>(); - context->DetachGlobal(); - define_property->Call(proxy, 0, NULL); -} - - -static void InstallContextId(v8::Handle<Context> context, int id) { - Context::Scope scope(context); - CompileRun("Object.prototype").As<Object>()-> - Set(v8_str("context_id"), v8::Integer::New(id)); -} - - -static void CheckContextId(v8::Handle<Object> object, int expected) { - CHECK_EQ(expected, object->Get(v8_str("context_id"))->Int32Value()); -} - - -THREADED_TEST(CreationContext) { - HandleScope handle_scope; - Persistent<Context> context1 = Context::New(); - InstallContextId(context1, 1); - Persistent<Context> context2 = Context::New(); - InstallContextId(context2, 2); - Persistent<Context> context3 = Context::New(); - InstallContextId(context3, 3); - - Local<v8::FunctionTemplate> tmpl = v8::FunctionTemplate::New(); - - Local<Object> object1; - Local<Function> func1; - { - Context::Scope scope(context1); - object1 = Object::New(); - func1 = tmpl->GetFunction(); - } - - Local<Object> object2; - Local<Function> func2; - { - Context::Scope scope(context2); - object2 = Object::New(); - func2 = tmpl->GetFunction(); - } - - Local<Object> instance1; - Local<Object> instance2; - - { - Context::Scope scope(context3); - instance1 = func1->NewInstance(); - instance2 = func2->NewInstance(); - } - - CHECK(object1->CreationContext() == context1); - CheckContextId(object1, 1); - CHECK(func1->CreationContext() == context1); - CheckContextId(func1, 1); - CHECK(instance1->CreationContext() == context1); - CheckContextId(instance1, 1); - CHECK(object2->CreationContext() == context2); - CheckContextId(object2, 2); - CHECK(func2->CreationContext() == context2); - CheckContextId(func2, 2); - CHECK(instance2->CreationContext() == context2); - CheckContextId(instance2, 2); - - { - Context::Scope scope(context1); - CHECK(object1->CreationContext() == context1); - CheckContextId(object1, 1); - CHECK(func1->CreationContext() == context1); - CheckContextId(func1, 1); - CHECK(instance1->CreationContext() == context1); - CheckContextId(instance1, 1); - CHECK(object2->CreationContext() == context2); - CheckContextId(object2, 2); - CHECK(func2->CreationContext() == context2); - CheckContextId(func2, 2); - CHECK(instance2->CreationContext() == context2); - CheckContextId(instance2, 2); - } - - { - Context::Scope scope(context2); - CHECK(object1->CreationContext() == context1); - CheckContextId(object1, 1); - CHECK(func1->CreationContext() == context1); - CheckContextId(func1, 1); - CHECK(instance1->CreationContext() == context1); - CheckContextId(instance1, 1); - CHECK(object2->CreationContext() == context2); - CheckContextId(object2, 2); - CHECK(func2->CreationContext() == context2); - CheckContextId(func2, 2); - CHECK(instance2->CreationContext() == context2); - CheckContextId(instance2, 2); - } - - context1.Dispose(context1->GetIsolate()); - context2.Dispose(context2->GetIsolate()); - context3.Dispose(context3->GetIsolate()); -} - - -THREADED_TEST(CreationContextOfJsFunction) { - HandleScope handle_scope; - Persistent<Context> context = Context::New(); - InstallContextId(context, 1); - - Local<Object> function; - { - Context::Scope scope(context); - function = CompileRun("function foo() {}; foo").As<Object>(); - } - - CHECK(function->CreationContext() == context); - CheckContextId(function, 1); - - context.Dispose(context->GetIsolate()); -} - - -Handle<Value> HasOwnPropertyIndexedPropertyGetter(uint32_t index, - const AccessorInfo& info) { - if (index == 42) return v8_str("yes"); - return Handle<v8::Integer>(); -} - - -Handle<Value> HasOwnPropertyNamedPropertyGetter(Local<String> property, - const AccessorInfo& info) { - if (property->Equals(v8_str("foo"))) return v8_str("yes"); - return Handle<Value>(); -} - - -Handle<v8::Integer> HasOwnPropertyIndexedPropertyQuery( - uint32_t index, const AccessorInfo& info) { - if (index == 42) return v8_num(1).As<v8::Integer>(); - return Handle<v8::Integer>(); -} - - -Handle<v8::Integer> HasOwnPropertyNamedPropertyQuery( - Local<String> property, const AccessorInfo& info) { - if (property->Equals(v8_str("foo"))) return v8_num(1).As<v8::Integer>(); - return Handle<v8::Integer>(); -} - - -Handle<v8::Integer> HasOwnPropertyNamedPropertyQuery2( - Local<String> property, const AccessorInfo& info) { - if (property->Equals(v8_str("bar"))) return v8_num(1).As<v8::Integer>(); - return Handle<v8::Integer>(); -} - - -Handle<Value> HasOwnPropertyAccessorGetter(Local<String> property, - const AccessorInfo& info) { - return v8_str("yes"); -} - - -TEST(HasOwnProperty) { - v8::HandleScope scope; - LocalContext env; - { // Check normal properties and defined getters. - Handle<Value> value = CompileRun( - "function Foo() {" - " this.foo = 11;" - " this.__defineGetter__('baz', function() { return 1; });" - "};" - "function Bar() { " - " this.bar = 13;" - " this.__defineGetter__('bla', function() { return 2; });" - "};" - "Bar.prototype = new Foo();" - "new Bar();"); - CHECK(value->IsObject()); - Handle<Object> object = value->ToObject(); - CHECK(object->Has(v8_str("foo"))); - CHECK(!object->HasOwnProperty(v8_str("foo"))); - CHECK(object->HasOwnProperty(v8_str("bar"))); - CHECK(object->Has(v8_str("baz"))); - CHECK(!object->HasOwnProperty(v8_str("baz"))); - CHECK(object->HasOwnProperty(v8_str("bla"))); - } - { // Check named getter interceptors. - Handle<ObjectTemplate> templ = ObjectTemplate::New(); - templ->SetNamedPropertyHandler(HasOwnPropertyNamedPropertyGetter); - Handle<Object> instance = templ->NewInstance(); - CHECK(!instance->HasOwnProperty(v8_str("42"))); - CHECK(instance->HasOwnProperty(v8_str("foo"))); - CHECK(!instance->HasOwnProperty(v8_str("bar"))); - } - { // Check indexed getter interceptors. - Handle<ObjectTemplate> templ = ObjectTemplate::New(); - templ->SetIndexedPropertyHandler(HasOwnPropertyIndexedPropertyGetter); - Handle<Object> instance = templ->NewInstance(); - CHECK(instance->HasOwnProperty(v8_str("42"))); - CHECK(!instance->HasOwnProperty(v8_str("43"))); - CHECK(!instance->HasOwnProperty(v8_str("foo"))); - } - { // Check named query interceptors. - Handle<ObjectTemplate> templ = ObjectTemplate::New(); - templ->SetNamedPropertyHandler(0, 0, HasOwnPropertyNamedPropertyQuery); - Handle<Object> instance = templ->NewInstance(); - CHECK(instance->HasOwnProperty(v8_str("foo"))); - CHECK(!instance->HasOwnProperty(v8_str("bar"))); - } - { // Check indexed query interceptors. - Handle<ObjectTemplate> templ = ObjectTemplate::New(); - templ->SetIndexedPropertyHandler(0, 0, HasOwnPropertyIndexedPropertyQuery); - Handle<Object> instance = templ->NewInstance(); - CHECK(instance->HasOwnProperty(v8_str("42"))); - CHECK(!instance->HasOwnProperty(v8_str("41"))); - } - { // Check callbacks. - Handle<ObjectTemplate> templ = ObjectTemplate::New(); - templ->SetAccessor(v8_str("foo"), HasOwnPropertyAccessorGetter); - Handle<Object> instance = templ->NewInstance(); - CHECK(instance->HasOwnProperty(v8_str("foo"))); - CHECK(!instance->HasOwnProperty(v8_str("bar"))); - } - { // Check that query wins on disagreement. - Handle<ObjectTemplate> templ = ObjectTemplate::New(); - templ->SetNamedPropertyHandler(HasOwnPropertyNamedPropertyGetter, - 0, - HasOwnPropertyNamedPropertyQuery2); - Handle<Object> instance = templ->NewInstance(); - CHECK(!instance->HasOwnProperty(v8_str("foo"))); - CHECK(instance->HasOwnProperty(v8_str("bar"))); - } -} - - -TEST(IndexedInterceptorWithStringProto) { - v8::HandleScope scope; - Handle<ObjectTemplate> templ = ObjectTemplate::New(); - templ->SetIndexedPropertyHandler(NULL, - NULL, - HasOwnPropertyIndexedPropertyQuery); - LocalContext context; - context->Global()->Set(v8_str("obj"), templ->NewInstance()); - CompileRun("var s = new String('foobar'); obj.__proto__ = s;"); - // These should be intercepted. - CHECK(CompileRun("42 in obj")->BooleanValue()); - CHECK(CompileRun("'42' in obj")->BooleanValue()); - // These should fall through to the String prototype. - CHECK(CompileRun("0 in obj")->BooleanValue()); - CHECK(CompileRun("'0' in obj")->BooleanValue()); - // And these should both fail. - CHECK(!CompileRun("32 in obj")->BooleanValue()); - CHECK(!CompileRun("'32' in obj")->BooleanValue()); -} - - -void CheckCodeGenerationAllowed() { - Handle<Value> result = CompileRun("eval('42')"); - CHECK_EQ(42, result->Int32Value()); - result = CompileRun("(function(e) { return e('42'); })(eval)"); - CHECK_EQ(42, result->Int32Value()); - result = CompileRun("var f = new Function('return 42'); f()"); - CHECK_EQ(42, result->Int32Value()); -} - - -void CheckCodeGenerationDisallowed() { - TryCatch try_catch; - - Handle<Value> result = CompileRun("eval('42')"); - CHECK(result.IsEmpty()); - CHECK(try_catch.HasCaught()); - try_catch.Reset(); - - result = CompileRun("(function(e) { return e('42'); })(eval)"); - CHECK(result.IsEmpty()); - CHECK(try_catch.HasCaught()); - try_catch.Reset(); - - result = CompileRun("var f = new Function('return 42'); f()"); - CHECK(result.IsEmpty()); - CHECK(try_catch.HasCaught()); -} - - -bool CodeGenerationAllowed(Local<Context> context) { - ApiTestFuzzer::Fuzz(); - return true; -} - - -bool CodeGenerationDisallowed(Local<Context> context) { - ApiTestFuzzer::Fuzz(); - return false; -} - - -THREADED_TEST(AllowCodeGenFromStrings) { - v8::HandleScope scope; - LocalContext context; - - // eval and the Function constructor allowed by default. - CHECK(context->IsCodeGenerationFromStringsAllowed()); - CheckCodeGenerationAllowed(); - - // Disallow eval and the Function constructor. - context->AllowCodeGenerationFromStrings(false); - CHECK(!context->IsCodeGenerationFromStringsAllowed()); - CheckCodeGenerationDisallowed(); - - // Allow again. - context->AllowCodeGenerationFromStrings(true); - CheckCodeGenerationAllowed(); - - // Disallow but setting a global callback that will allow the calls. - context->AllowCodeGenerationFromStrings(false); - V8::SetAllowCodeGenerationFromStringsCallback(&CodeGenerationAllowed); - CHECK(!context->IsCodeGenerationFromStringsAllowed()); - CheckCodeGenerationAllowed(); - - // Set a callback that disallows the code generation. - V8::SetAllowCodeGenerationFromStringsCallback(&CodeGenerationDisallowed); - CHECK(!context->IsCodeGenerationFromStringsAllowed()); - CheckCodeGenerationDisallowed(); -} - - -TEST(SetErrorMessageForCodeGenFromStrings) { - v8::HandleScope scope; - LocalContext context; - TryCatch try_catch; - - Handle<String> message = v8_str("Message") ; - Handle<String> expected_message = v8_str("Uncaught EvalError: Message"); - V8::SetAllowCodeGenerationFromStringsCallback(&CodeGenerationDisallowed); - context->AllowCodeGenerationFromStrings(false); - context->SetErrorMessageForCodeGenerationFromStrings(message); - Handle<Value> result = CompileRun("eval('42')"); - CHECK(result.IsEmpty()); - CHECK(try_catch.HasCaught()); - Handle<String> actual_message = try_catch.Message()->Get(); - CHECK(expected_message->Equals(actual_message)); -} - - -static v8::Handle<Value> NonObjectThis(const v8::Arguments& args) { - return v8::Undefined(); -} - - -THREADED_TEST(CallAPIFunctionOnNonObject) { - v8::HandleScope scope; - LocalContext context; - Handle<FunctionTemplate> templ = v8::FunctionTemplate::New(NonObjectThis); - Handle<Function> function = templ->GetFunction(); - context->Global()->Set(v8_str("f"), function); - TryCatch try_catch; - CompileRun("f.call(2)"); -} - - -// Regression test for issue 1470. -THREADED_TEST(ReadOnlyIndexedProperties) { - v8::HandleScope scope; - Local<ObjectTemplate> templ = ObjectTemplate::New(); - - LocalContext context; - Local<v8::Object> obj = templ->NewInstance(); - context->Global()->Set(v8_str("obj"), obj); - obj->Set(v8_str("1"), v8_str("DONT_CHANGE"), v8::ReadOnly); - obj->Set(v8_str("1"), v8_str("foobar")); - CHECK_EQ(v8_str("DONT_CHANGE"), obj->Get(v8_str("1"))); - obj->Set(v8_num(2), v8_str("DONT_CHANGE"), v8::ReadOnly); - obj->Set(v8_num(2), v8_str("foobar")); - CHECK_EQ(v8_str("DONT_CHANGE"), obj->Get(v8_num(2))); - - // Test non-smi case. - obj->Set(v8_str("2000000000"), v8_str("DONT_CHANGE"), v8::ReadOnly); - obj->Set(v8_str("2000000000"), v8_str("foobar")); - CHECK_EQ(v8_str("DONT_CHANGE"), obj->Get(v8_str("2000000000"))); -} - - -THREADED_TEST(Regress1516) { - v8::HandleScope scope; - - LocalContext context; - { v8::HandleScope temp_scope; - CompileRun("({'a': 0})"); - } - - int elements; - { i::MapCache* map_cache = - i::MapCache::cast(i::Isolate::Current()->context()->map_cache()); - elements = map_cache->NumberOfElements(); - CHECK_LE(1, elements); - } - - i::Isolate::Current()->heap()->CollectAllGarbage( - i::Heap::kAbortIncrementalMarkingMask); - { i::Object* raw_map_cache = i::Isolate::Current()->context()->map_cache(); - if (raw_map_cache != i::Isolate::Current()->heap()->undefined_value()) { - i::MapCache* map_cache = i::MapCache::cast(raw_map_cache); - CHECK_GT(elements, map_cache->NumberOfElements()); - } - } -} - - -static bool BlockProtoNamedSecurityTestCallback(Local<v8::Object> global, - Local<Value> name, - v8::AccessType type, - Local<Value> data) { - // Only block read access to __proto__. - if (type == v8::ACCESS_GET && - name->IsString() && - name->ToString()->Length() == 9 && - name->ToString()->Utf8Length() == 9) { - char buffer[10]; - CHECK_EQ(10, name->ToString()->WriteUtf8(buffer)); - return strncmp(buffer, "__proto__", 9) != 0; - } - - return true; -} - - -THREADED_TEST(Regress93759) { - HandleScope scope; - - // Template for object with security check. - Local<ObjectTemplate> no_proto_template = v8::ObjectTemplate::New(); - // We don't do indexing, so any callback can be used for that. - no_proto_template->SetAccessCheckCallbacks( - BlockProtoNamedSecurityTestCallback, - IndexedSecurityTestCallback); - - // Templates for objects with hidden prototypes and possibly security check. - Local<FunctionTemplate> hidden_proto_template = v8::FunctionTemplate::New(); - hidden_proto_template->SetHiddenPrototype(true); - - Local<FunctionTemplate> protected_hidden_proto_template = - v8::FunctionTemplate::New(); - protected_hidden_proto_template->InstanceTemplate()->SetAccessCheckCallbacks( - BlockProtoNamedSecurityTestCallback, - IndexedSecurityTestCallback); - protected_hidden_proto_template->SetHiddenPrototype(true); - - // Context for "foreign" objects used in test. - Persistent<Context> context = v8::Context::New(); - context->Enter(); - - // Plain object, no security check. - Local<Object> simple_object = Object::New(); - - // Object with explicit security check. - Local<Object> protected_object = - no_proto_template->NewInstance(); - - // JSGlobalProxy object, always have security check. - Local<Object> proxy_object = - context->Global(); - - // Global object, the prototype of proxy_object. No security checks. - Local<Object> global_object = - proxy_object->GetPrototype()->ToObject(); - - // Hidden prototype without security check. - Local<Object> hidden_prototype = - hidden_proto_template->GetFunction()->NewInstance(); - Local<Object> object_with_hidden = - Object::New(); - object_with_hidden->SetPrototype(hidden_prototype); - - // Hidden prototype with security check on the hidden prototype. - Local<Object> protected_hidden_prototype = - protected_hidden_proto_template->GetFunction()->NewInstance(); - Local<Object> object_with_protected_hidden = - Object::New(); - object_with_protected_hidden->SetPrototype(protected_hidden_prototype); - - context->Exit(); - - // Template for object for second context. Values to test are put on it as - // properties. - Local<ObjectTemplate> global_template = ObjectTemplate::New(); - global_template->Set(v8_str("simple"), simple_object); - global_template->Set(v8_str("protected"), protected_object); - global_template->Set(v8_str("global"), global_object); - global_template->Set(v8_str("proxy"), proxy_object); - global_template->Set(v8_str("hidden"), object_with_hidden); - global_template->Set(v8_str("phidden"), object_with_protected_hidden); - - LocalContext context2(NULL, global_template); - - Local<Value> result1 = CompileRun("Object.getPrototypeOf(simple)"); - CHECK(result1->Equals(simple_object->GetPrototype())); - - Local<Value> result2 = CompileRun("Object.getPrototypeOf(protected)"); - CHECK(result2->Equals(Undefined())); - - Local<Value> result3 = CompileRun("Object.getPrototypeOf(global)"); - CHECK(result3->Equals(global_object->GetPrototype())); - - Local<Value> result4 = CompileRun("Object.getPrototypeOf(proxy)"); - CHECK(result4->Equals(Undefined())); - - Local<Value> result5 = CompileRun("Object.getPrototypeOf(hidden)"); - CHECK(result5->Equals( - object_with_hidden->GetPrototype()->ToObject()->GetPrototype())); - - Local<Value> result6 = CompileRun("Object.getPrototypeOf(phidden)"); - CHECK(result6->Equals(Undefined())); - - context.Dispose(context->GetIsolate()); -} - - -THREADED_TEST(Regress125988) { - v8::HandleScope scope; - Handle<FunctionTemplate> intercept = FunctionTemplate::New(); - AddInterceptor(intercept, EmptyInterceptorGetter, EmptyInterceptorSetter); - LocalContext env; - env->Global()->Set(v8_str("Intercept"), intercept->GetFunction()); - CompileRun("var a = new Object();" - "var b = new Intercept();" - "var c = new Object();" - "c.__proto__ = b;" - "b.__proto__ = a;" - "a.x = 23;" - "for (var i = 0; i < 3; i++) c.x;"); - ExpectBoolean("c.hasOwnProperty('x')", false); - ExpectInt32("c.x", 23); - CompileRun("a.y = 42;" - "for (var i = 0; i < 3; i++) c.x;"); - ExpectBoolean("c.hasOwnProperty('x')", false); - ExpectInt32("c.x", 23); - ExpectBoolean("c.hasOwnProperty('y')", false); - ExpectInt32("c.y", 42); -} - - -static void TestReceiver(Local<Value> expected_result, - Local<Value> expected_receiver, - const char* code) { - Local<Value> result = CompileRun(code); - CHECK(result->IsObject()); - CHECK(expected_receiver->Equals(result->ToObject()->Get(1))); - CHECK(expected_result->Equals(result->ToObject()->Get(0))); -} - - -THREADED_TEST(ForeignFunctionReceiver) { - HandleScope scope; - - // Create two contexts with different "id" properties ('i' and 'o'). - // Call a function both from its own context and from a the foreign - // context, and see what "this" is bound to (returning both "this" - // and "this.id" for comparison). - - Persistent<Context> foreign_context = v8::Context::New(); - foreign_context->Enter(); - Local<Value> foreign_function = - CompileRun("function func() { return { 0: this.id, " - " 1: this, " - " toString: function() { " - " return this[0];" - " }" - " };" - "}" - "var id = 'i';" - "func;"); - CHECK(foreign_function->IsFunction()); - foreign_context->Exit(); - - LocalContext context; - - Local<String> password = v8_str("Password"); - // Don't get hit by security checks when accessing foreign_context's - // global receiver (aka. global proxy). - context->SetSecurityToken(password); - foreign_context->SetSecurityToken(password); - - Local<String> i = v8_str("i"); - Local<String> o = v8_str("o"); - Local<String> id = v8_str("id"); - - CompileRun("function ownfunc() { return { 0: this.id, " - " 1: this, " - " toString: function() { " - " return this[0];" - " }" - " };" - "}" - "var id = 'o';" - "ownfunc"); - context->Global()->Set(v8_str("func"), foreign_function); - - // Sanity check the contexts. - CHECK(i->Equals(foreign_context->Global()->Get(id))); - CHECK(o->Equals(context->Global()->Get(id))); - - // Checking local function's receiver. - // Calling function using its call/apply methods. - TestReceiver(o, context->Global(), "ownfunc.call()"); - TestReceiver(o, context->Global(), "ownfunc.apply()"); - // Making calls through built-in functions. - TestReceiver(o, context->Global(), "[1].map(ownfunc)[0]"); - CHECK(o->Equals(CompileRun("'abcbd'.replace(/b/,ownfunc)[1]"))); - CHECK(o->Equals(CompileRun("'abcbd'.replace(/b/g,ownfunc)[1]"))); - CHECK(o->Equals(CompileRun("'abcbd'.replace(/b/g,ownfunc)[3]"))); - // Calling with environment record as base. - TestReceiver(o, context->Global(), "ownfunc()"); - // Calling with no base. - TestReceiver(o, context->Global(), "(1,ownfunc)()"); - - // Checking foreign function return value. - // Calling function using its call/apply methods. - TestReceiver(i, foreign_context->Global(), "func.call()"); - TestReceiver(i, foreign_context->Global(), "func.apply()"); - // Calling function using another context's call/apply methods. - TestReceiver(i, foreign_context->Global(), - "Function.prototype.call.call(func)"); - TestReceiver(i, foreign_context->Global(), - "Function.prototype.call.apply(func)"); - TestReceiver(i, foreign_context->Global(), - "Function.prototype.apply.call(func)"); - TestReceiver(i, foreign_context->Global(), - "Function.prototype.apply.apply(func)"); - // Making calls through built-in functions. - TestReceiver(i, foreign_context->Global(), "[1].map(func)[0]"); - // ToString(func()) is func()[0], i.e., the returned this.id. - CHECK(i->Equals(CompileRun("'abcbd'.replace(/b/,func)[1]"))); - CHECK(i->Equals(CompileRun("'abcbd'.replace(/b/g,func)[1]"))); - CHECK(i->Equals(CompileRun("'abcbd'.replace(/b/g,func)[3]"))); - - // TODO(1547): Make the following also return "i". - // Calling with environment record as base. - TestReceiver(o, context->Global(), "func()"); - // Calling with no base. - TestReceiver(o, context->Global(), "(1,func)()"); - - foreign_context.Dispose(foreign_context->GetIsolate()); -} - - -uint8_t callback_fired = 0; - - -void CallCompletedCallback1() { - i::OS::Print("Firing callback 1.\n"); - callback_fired ^= 1; // Toggle first bit. -} - - -void CallCompletedCallback2() { - i::OS::Print("Firing callback 2.\n"); - callback_fired ^= 2; // Toggle second bit. -} - - -Handle<Value> RecursiveCall(const Arguments& args) { - int32_t level = args[0]->Int32Value(); - if (level < 3) { - level++; - i::OS::Print("Entering recursion level %d.\n", level); - char script[64]; - i::Vector<char> script_vector(script, sizeof(script)); - i::OS::SNPrintF(script_vector, "recursion(%d)", level); - CompileRun(script_vector.start()); - i::OS::Print("Leaving recursion level %d.\n", level); - CHECK_EQ(0, callback_fired); - } else { - i::OS::Print("Recursion ends.\n"); - CHECK_EQ(0, callback_fired); - } - return Undefined(); -} - - -TEST(CallCompletedCallback) { - v8::HandleScope scope; - LocalContext env; - v8::Handle<v8::FunctionTemplate> recursive_runtime = - v8::FunctionTemplate::New(RecursiveCall); - env->Global()->Set(v8_str("recursion"), - recursive_runtime->GetFunction()); - // Adding the same callback a second time has no effect. - v8::V8::AddCallCompletedCallback(CallCompletedCallback1); - v8::V8::AddCallCompletedCallback(CallCompletedCallback1); - v8::V8::AddCallCompletedCallback(CallCompletedCallback2); - i::OS::Print("--- Script (1) ---\n"); - Local<Script> script = - v8::Script::Compile(v8::String::New("recursion(0)")); - script->Run(); - CHECK_EQ(3, callback_fired); - - i::OS::Print("\n--- Script (2) ---\n"); - callback_fired = 0; - v8::V8::RemoveCallCompletedCallback(CallCompletedCallback1); - script->Run(); - CHECK_EQ(2, callback_fired); - - i::OS::Print("\n--- Function ---\n"); - callback_fired = 0; - Local<Function> recursive_function = - Local<Function>::Cast(env->Global()->Get(v8_str("recursion"))); - v8::Handle<Value> args[] = { v8_num(0) }; - recursive_function->Call(env->Global(), 1, args); - CHECK_EQ(2, callback_fired); -} - - -void CallCompletedCallbackNoException() { - v8::HandleScope scope; - CompileRun("1+1;"); -} - - -void CallCompletedCallbackException() { - v8::HandleScope scope; - CompileRun("throw 'second exception';"); -} - - -TEST(CallCompletedCallbackOneException) { - v8::HandleScope scope; - LocalContext env; - v8::V8::AddCallCompletedCallback(CallCompletedCallbackNoException); - CompileRun("throw 'exception';"); -} - - -TEST(CallCompletedCallbackTwoExceptions) { - v8::HandleScope scope; - LocalContext env; - v8::V8::AddCallCompletedCallback(CallCompletedCallbackException); - CompileRun("throw 'first exception';"); -} - - -static int probes_counter = 0; -static int misses_counter = 0; -static int updates_counter = 0; - - -static int* LookupCounter(const char* name) { - if (strcmp(name, "c:V8.MegamorphicStubCacheProbes") == 0) { - return &probes_counter; - } else if (strcmp(name, "c:V8.MegamorphicStubCacheMisses") == 0) { - return &misses_counter; - } else if (strcmp(name, "c:V8.MegamorphicStubCacheUpdates") == 0) { - return &updates_counter; - } - return NULL; -} - - -static const char* kMegamorphicTestProgram = - "function ClassA() { };" - "function ClassB() { };" - "ClassA.prototype.foo = function() { };" - "ClassB.prototype.foo = function() { };" - "function fooify(obj) { obj.foo(); };" - "var a = new ClassA();" - "var b = new ClassB();" - "for (var i = 0; i < 10000; i++) {" - " fooify(a);" - " fooify(b);" - "}"; - - -static void StubCacheHelper(bool primary) { - V8::SetCounterFunction(LookupCounter); - USE(kMegamorphicTestProgram); -#ifdef DEBUG - i::FLAG_native_code_counters = true; - if (primary) { - i::FLAG_test_primary_stub_cache = true; - } else { - i::FLAG_test_secondary_stub_cache = true; - } - i::FLAG_crankshaft = false; - v8::HandleScope scope; - LocalContext env; - int initial_probes = probes_counter; - int initial_misses = misses_counter; - int initial_updates = updates_counter; - CompileRun(kMegamorphicTestProgram); - int probes = probes_counter - initial_probes; - int misses = misses_counter - initial_misses; - int updates = updates_counter - initial_updates; - CHECK_LT(updates, 10); - CHECK_LT(misses, 10); - CHECK_GE(probes, 10000); -#endif -} - - -TEST(SecondaryStubCache) { - StubCacheHelper(true); -} - - -TEST(PrimaryStubCache) { - StubCacheHelper(false); -} - - -static int fatal_error_callback_counter = 0; -static void CountingErrorCallback(const char* location, const char* message) { - printf("CountingErrorCallback(\"%s\", \"%s\")\n", location, message); - fatal_error_callback_counter++; -} - - -TEST(StaticGetters) { - v8::HandleScope scope; - LocalContext context; - v8::Isolate* isolate = v8::Isolate::GetCurrent(); - i::Handle<i::Object> undefined_value = FACTORY->undefined_value(); - CHECK(*v8::Utils::OpenHandle(*v8::Undefined()) == *undefined_value); - CHECK(*v8::Utils::OpenHandle(*v8::Undefined(isolate)) == *undefined_value); - i::Handle<i::Object> null_value = FACTORY->null_value(); - CHECK(*v8::Utils::OpenHandle(*v8::Null()) == *null_value); - CHECK(*v8::Utils::OpenHandle(*v8::Null(isolate)) == *null_value); - i::Handle<i::Object> true_value = FACTORY->true_value(); - CHECK(*v8::Utils::OpenHandle(*v8::True()) == *true_value); - CHECK(*v8::Utils::OpenHandle(*v8::True(isolate)) == *true_value); - i::Handle<i::Object> false_value = FACTORY->false_value(); - CHECK(*v8::Utils::OpenHandle(*v8::False()) == *false_value); - CHECK(*v8::Utils::OpenHandle(*v8::False(isolate)) == *false_value); - - // Test after-death behavior. - CHECK(i::Internals::IsInitialized(isolate)); - CHECK_EQ(0, fatal_error_callback_counter); - v8::V8::SetFatalErrorHandler(CountingErrorCallback); - v8::Utils::ReportApiFailure("StaticGetters()", "Kill V8"); - i::Isolate::Current()->TearDown(); - CHECK(!i::Internals::IsInitialized(isolate)); - CHECK_EQ(1, fatal_error_callback_counter); - CHECK(v8::Undefined().IsEmpty()); - CHECK_EQ(2, fatal_error_callback_counter); - CHECK(v8::Undefined(isolate).IsEmpty()); - CHECK_EQ(3, fatal_error_callback_counter); - CHECK(v8::Null().IsEmpty()); - CHECK_EQ(4, fatal_error_callback_counter); - CHECK(v8::Null(isolate).IsEmpty()); - CHECK_EQ(5, fatal_error_callback_counter); - CHECK(v8::True().IsEmpty()); - CHECK_EQ(6, fatal_error_callback_counter); - CHECK(v8::True(isolate).IsEmpty()); - CHECK_EQ(7, fatal_error_callback_counter); - CHECK(v8::False().IsEmpty()); - CHECK_EQ(8, fatal_error_callback_counter); - CHECK(v8::False(isolate).IsEmpty()); - CHECK_EQ(9, fatal_error_callback_counter); -} - - -TEST(IsolateEmbedderData) { - v8::Isolate* isolate = v8::Isolate::GetCurrent(); - CHECK_EQ(NULL, isolate->GetData()); - CHECK_EQ(NULL, ISOLATE->GetData()); - static void* data1 = reinterpret_cast<void*>(0xacce55ed); - isolate->SetData(data1); - CHECK_EQ(data1, isolate->GetData()); - CHECK_EQ(data1, ISOLATE->GetData()); - static void* data2 = reinterpret_cast<void*>(0xdecea5ed); - ISOLATE->SetData(data2); - CHECK_EQ(data2, isolate->GetData()); - CHECK_EQ(data2, ISOLATE->GetData()); - ISOLATE->TearDown(); - CHECK_EQ(data2, isolate->GetData()); - CHECK_EQ(data2, ISOLATE->GetData()); -} - - -TEST(StringEmpty) { - v8::HandleScope scope; - LocalContext context; - v8::Isolate* isolate = v8::Isolate::GetCurrent(); - i::Handle<i::Object> empty_string = FACTORY->empty_string(); - CHECK(*v8::Utils::OpenHandle(*v8::String::Empty()) == *empty_string); - CHECK(*v8::Utils::OpenHandle(*v8::String::Empty(isolate)) == *empty_string); - - // Test after-death behavior. - CHECK(i::Internals::IsInitialized(isolate)); - CHECK_EQ(0, fatal_error_callback_counter); - v8::V8::SetFatalErrorHandler(CountingErrorCallback); - v8::Utils::ReportApiFailure("StringEmpty()", "Kill V8"); - i::Isolate::Current()->TearDown(); - CHECK(!i::Internals::IsInitialized(isolate)); - CHECK_EQ(1, fatal_error_callback_counter); - CHECK(v8::String::Empty().IsEmpty()); - CHECK_EQ(2, fatal_error_callback_counter); - CHECK(v8::String::Empty(isolate).IsEmpty()); - CHECK_EQ(3, fatal_error_callback_counter); -} - - -static int instance_checked_getter_count = 0; -static Handle<Value> InstanceCheckedGetter(Local<String> name, - const AccessorInfo& info) { - CHECK_EQ(name, v8_str("foo")); - instance_checked_getter_count++; - return v8_num(11); -} - - -static int instance_checked_setter_count = 0; -static void InstanceCheckedSetter(Local<String> name, - Local<Value> value, - const AccessorInfo& info) { - CHECK_EQ(name, v8_str("foo")); - CHECK_EQ(value, v8_num(23)); - instance_checked_setter_count++; -} - - -static void CheckInstanceCheckedResult(int getters, - int setters, - bool expects_callbacks, - TryCatch* try_catch) { - if (expects_callbacks) { - CHECK(!try_catch->HasCaught()); - CHECK_EQ(getters, instance_checked_getter_count); - CHECK_EQ(setters, instance_checked_setter_count); - } else { - CHECK(try_catch->HasCaught()); - CHECK_EQ(0, instance_checked_getter_count); - CHECK_EQ(0, instance_checked_setter_count); - } - try_catch->Reset(); -} - - -static void CheckInstanceCheckedAccessors(bool expects_callbacks) { - instance_checked_getter_count = 0; - instance_checked_setter_count = 0; - TryCatch try_catch; - - // Test path through generic runtime code. - CompileRun("obj.foo"); - CheckInstanceCheckedResult(1, 0, expects_callbacks, &try_catch); - CompileRun("obj.foo = 23"); - CheckInstanceCheckedResult(1, 1, expects_callbacks, &try_catch); - - // Test path through generated LoadIC and StoredIC. - CompileRun("function test_get(o) { o.foo; }" - "test_get(obj);"); - CheckInstanceCheckedResult(2, 1, expects_callbacks, &try_catch); - CompileRun("test_get(obj);"); - CheckInstanceCheckedResult(3, 1, expects_callbacks, &try_catch); - CompileRun("test_get(obj);"); - CheckInstanceCheckedResult(4, 1, expects_callbacks, &try_catch); - CompileRun("function test_set(o) { o.foo = 23; }" - "test_set(obj);"); - CheckInstanceCheckedResult(4, 2, expects_callbacks, &try_catch); - CompileRun("test_set(obj);"); - CheckInstanceCheckedResult(4, 3, expects_callbacks, &try_catch); - CompileRun("test_set(obj);"); - CheckInstanceCheckedResult(4, 4, expects_callbacks, &try_catch); - - // Test path through optimized code. - CompileRun("%OptimizeFunctionOnNextCall(test_get);" - "test_get(obj);"); - CheckInstanceCheckedResult(5, 4, expects_callbacks, &try_catch); - CompileRun("%OptimizeFunctionOnNextCall(test_set);" - "test_set(obj);"); - CheckInstanceCheckedResult(5, 5, expects_callbacks, &try_catch); - - // Cleanup so that closures start out fresh in next check. - CompileRun("%DeoptimizeFunction(test_get);" - "%ClearFunctionTypeFeedback(test_get);" - "%DeoptimizeFunction(test_set);" - "%ClearFunctionTypeFeedback(test_set);"); -} - - -THREADED_TEST(InstanceCheckOnInstanceAccessor) { - v8::internal::FLAG_allow_natives_syntax = true; - v8::HandleScope scope; - LocalContext context; - - Local<FunctionTemplate> templ = FunctionTemplate::New(); - Local<ObjectTemplate> inst = templ->InstanceTemplate(); - inst->SetAccessor(v8_str("foo"), - InstanceCheckedGetter, InstanceCheckedSetter, - Handle<Value>(), - v8::DEFAULT, - v8::None, - v8::AccessorSignature::New(templ)); - context->Global()->Set(v8_str("f"), templ->GetFunction()); - - printf("Testing positive ...\n"); - CompileRun("var obj = new f();"); - CHECK(templ->HasInstance(context->Global()->Get(v8_str("obj")))); - CheckInstanceCheckedAccessors(true); - - printf("Testing negative ...\n"); - CompileRun("var obj = {};" - "obj.__proto__ = new f();"); - CHECK(!templ->HasInstance(context->Global()->Get(v8_str("obj")))); - CheckInstanceCheckedAccessors(false); -} - - -THREADED_TEST(InstanceCheckOnInstanceAccessorWithInterceptor) { - v8::internal::FLAG_allow_natives_syntax = true; - v8::HandleScope scope; - LocalContext context; - - Local<FunctionTemplate> templ = FunctionTemplate::New(); - Local<ObjectTemplate> inst = templ->InstanceTemplate(); - AddInterceptor(templ, EmptyInterceptorGetter, EmptyInterceptorSetter); - inst->SetAccessor(v8_str("foo"), - InstanceCheckedGetter, InstanceCheckedSetter, - Handle<Value>(), - v8::DEFAULT, - v8::None, - v8::AccessorSignature::New(templ)); - context->Global()->Set(v8_str("f"), templ->GetFunction()); - - printf("Testing positive ...\n"); - CompileRun("var obj = new f();"); - CHECK(templ->HasInstance(context->Global()->Get(v8_str("obj")))); - CheckInstanceCheckedAccessors(true); - - printf("Testing negative ...\n"); - CompileRun("var obj = {};" - "obj.__proto__ = new f();"); - CHECK(!templ->HasInstance(context->Global()->Get(v8_str("obj")))); - CheckInstanceCheckedAccessors(false); -} - - -THREADED_TEST(InstanceCheckOnPrototypeAccessor) { - v8::internal::FLAG_allow_natives_syntax = true; - v8::HandleScope scope; - LocalContext context; - - Local<FunctionTemplate> templ = FunctionTemplate::New(); - Local<ObjectTemplate> proto = templ->PrototypeTemplate(); - proto->SetAccessor(v8_str("foo"), - InstanceCheckedGetter, InstanceCheckedSetter, - Handle<Value>(), - v8::DEFAULT, - v8::None, - v8::AccessorSignature::New(templ)); - context->Global()->Set(v8_str("f"), templ->GetFunction()); - - printf("Testing positive ...\n"); - CompileRun("var obj = new f();"); - CHECK(templ->HasInstance(context->Global()->Get(v8_str("obj")))); - CheckInstanceCheckedAccessors(true); - - printf("Testing negative ...\n"); - CompileRun("var obj = {};" - "obj.__proto__ = new f();"); - CHECK(!templ->HasInstance(context->Global()->Get(v8_str("obj")))); - CheckInstanceCheckedAccessors(false); - - printf("Testing positive with modified prototype chain ...\n"); - CompileRun("var obj = new f();" - "var pro = {};" - "pro.__proto__ = obj.__proto__;" - "obj.__proto__ = pro;"); - CHECK(templ->HasInstance(context->Global()->Get(v8_str("obj")))); - CheckInstanceCheckedAccessors(true); -} - - -TEST(TryFinallyMessage) { - v8::HandleScope scope; - LocalContext context; - { - // Test that the original error message is not lost if there is a - // recursive call into Javascript is done in the finally block, e.g. to - // initialize an IC. (crbug.com/129171) - TryCatch try_catch; - const char* trigger_ic = - "try { \n" - " throw new Error('test'); \n" - "} finally { \n" - " var x = 0; \n" - " x++; \n" // Trigger an IC initialization here. - "} \n"; - CompileRun(trigger_ic); - CHECK(try_catch.HasCaught()); - Local<Message> message = try_catch.Message(); - CHECK(!message.IsEmpty()); - CHECK_EQ(2, message->GetLineNumber()); - } - - { - // Test that the original exception message is indeed overwritten if - // a new error is thrown in the finally block. - TryCatch try_catch; - const char* throw_again = - "try { \n" - " throw new Error('test'); \n" - "} finally { \n" - " var x = 0; \n" - " x++; \n" - " throw new Error('again'); \n" // This is the new uncaught error. - "} \n"; - CompileRun(throw_again); - CHECK(try_catch.HasCaught()); - Local<Message> message = try_catch.Message(); - CHECK(!message.IsEmpty()); - CHECK_EQ(6, message->GetLineNumber()); - } -} - - -static void Helper137002(bool do_store, - bool polymorphic, - bool remove_accessor, - bool interceptor) { - LocalContext context; - Local<ObjectTemplate> templ = ObjectTemplate::New(); - if (interceptor) { - templ->SetNamedPropertyHandler(FooGetInterceptor, FooSetInterceptor); - } else { - templ->SetAccessor(v8_str("foo"), - GetterWhichReturns42, - SetterWhichSetsYOnThisTo23); - } - context->Global()->Set(v8_str("obj"), templ->NewInstance()); - - // Turn monomorphic on slow object with native accessor, then turn - // polymorphic, finally optimize to create negative lookup and fail. - CompileRun(do_store ? - "function f(x) { x.foo = void 0; }" : - "function f(x) { return x.foo; }"); - CompileRun("obj.y = void 0;"); - if (!interceptor) { - CompileRun("%OptimizeObjectForAddingMultipleProperties(obj, 1);"); - } - CompileRun("obj.__proto__ = null;" - "f(obj); f(obj); f(obj);"); - if (polymorphic) { - CompileRun("f({});"); - } - CompileRun("obj.y = void 0;" - "%OptimizeFunctionOnNextCall(f);"); - if (remove_accessor) { - CompileRun("delete obj.foo;"); - } - CompileRun("var result = f(obj);"); - if (do_store) { - CompileRun("result = obj.y;"); - } - if (remove_accessor && !interceptor) { - CHECK(context->Global()->Get(v8_str("result"))->IsUndefined()); - } else { - CHECK_EQ(do_store ? 23 : 42, - context->Global()->Get(v8_str("result"))->Int32Value()); - } -} - - -THREADED_TEST(Regress137002a) { - i::FLAG_allow_natives_syntax = true; - i::FLAG_compilation_cache = false; - v8::HandleScope scope; - for (int i = 0; i < 16; i++) { - Helper137002(i & 8, i & 4, i & 2, i & 1); - } -} - - -THREADED_TEST(Regress137002b) { - i::FLAG_allow_natives_syntax = true; - v8::HandleScope scope; - LocalContext context; - Local<ObjectTemplate> templ = ObjectTemplate::New(); - templ->SetAccessor(v8_str("foo"), - GetterWhichReturns42, - SetterWhichSetsYOnThisTo23); - context->Global()->Set(v8_str("obj"), templ->NewInstance()); - - // Turn monomorphic on slow object with native accessor, then just - // delete the property and fail. - CompileRun("function load(x) { return x.foo; }" - "function store(x) { x.foo = void 0; }" - "function keyed_load(x, key) { return x[key]; }" - // Second version of function has a different source (add void 0) - // so that it does not share code with the first version. This - // ensures that the ICs are monomorphic. - "function load2(x) { void 0; return x.foo; }" - "function store2(x) { void 0; x.foo = void 0; }" - "function keyed_load2(x, key) { void 0; return x[key]; }" - - "obj.y = void 0;" - "obj.__proto__ = null;" - "var subobj = {};" - "subobj.y = void 0;" - "subobj.__proto__ = obj;" - "%OptimizeObjectForAddingMultipleProperties(obj, 1);" - - // Make the ICs monomorphic. - "load(obj); load(obj);" - "load2(subobj); load2(subobj);" - "store(obj); store(obj);" - "store2(subobj); store2(subobj);" - "keyed_load(obj, 'foo'); keyed_load(obj, 'foo');" - "keyed_load2(subobj, 'foo'); keyed_load2(subobj, 'foo');" - - // Actually test the shiny new ICs and better not crash. This - // serves as a regression test for issue 142088 as well. - "load(obj);" - "load2(subobj);" - "store(obj);" - "store2(subobj);" - "keyed_load(obj, 'foo');" - "keyed_load2(subobj, 'foo');" - - // Delete the accessor. It better not be called any more now. - "delete obj.foo;" - "obj.y = void 0;" - "subobj.y = void 0;" - - "var load_result = load(obj);" - "var load_result2 = load2(subobj);" - "var keyed_load_result = keyed_load(obj, 'foo');" - "var keyed_load_result2 = keyed_load2(subobj, 'foo');" - "store(obj);" - "store2(subobj);" - "var y_from_obj = obj.y;" - "var y_from_subobj = subobj.y;"); - CHECK(context->Global()->Get(v8_str("load_result"))->IsUndefined()); - CHECK(context->Global()->Get(v8_str("load_result2"))->IsUndefined()); - CHECK(context->Global()->Get(v8_str("keyed_load_result"))->IsUndefined()); - CHECK(context->Global()->Get(v8_str("keyed_load_result2"))->IsUndefined()); - CHECK(context->Global()->Get(v8_str("y_from_obj"))->IsUndefined()); - CHECK(context->Global()->Get(v8_str("y_from_subobj"))->IsUndefined()); -} - - -THREADED_TEST(Regress142088) { - i::FLAG_allow_natives_syntax = true; - v8::HandleScope scope; - LocalContext context; - Local<ObjectTemplate> templ = ObjectTemplate::New(); - templ->SetAccessor(v8_str("foo"), - GetterWhichReturns42, - SetterWhichSetsYOnThisTo23); - context->Global()->Set(v8_str("obj"), templ->NewInstance()); - - CompileRun("function load(x) { return x.foo; }" - "var o = Object.create(obj);" - "%OptimizeObjectForAddingMultipleProperties(obj, 1);" - "load(o); load(o); load(o); load(o);"); -} - - -THREADED_TEST(Regress137496) { - i::FLAG_expose_gc = true; - v8::HandleScope scope; - LocalContext context; - - // Compile a try-finally clause where the finally block causes a GC - // while there still is a message pending for external reporting. - TryCatch try_catch; - try_catch.SetVerbose(true); - CompileRun("try { throw new Error(); } finally { gc(); }"); - CHECK(try_catch.HasCaught()); -} - - -THREADED_TEST(Regress149912) { - v8::HandleScope scope; - LocalContext context; - Handle<FunctionTemplate> templ = FunctionTemplate::New(); - AddInterceptor(templ, EmptyInterceptorGetter, EmptyInterceptorSetter); - context->Global()->Set(v8_str("Bug"), templ->GetFunction()); - CompileRun("Number.prototype.__proto__ = new Bug; var x = 0; x.foo();"); -} - - -THREADED_TEST(Regress157124) { - v8::HandleScope scope; - LocalContext context; - Local<ObjectTemplate> templ = ObjectTemplate::New(); - Local<Object> obj = templ->NewInstance(); - obj->GetIdentityHash(); - obj->DeleteHiddenValue(v8_str("Bug")); -} - - -#ifndef WIN32 -class ThreadInterruptTest { - public: - ThreadInterruptTest() : sem_(NULL), sem_value_(0) { } - ~ThreadInterruptTest() { delete sem_; } - - void RunTest() { - sem_ = i::OS::CreateSemaphore(0); - - InterruptThread i_thread(this); - i_thread.Start(); - - sem_->Wait(); - CHECK_EQ(kExpectedValue, sem_value_); - } - - private: - static const int kExpectedValue = 1; - - class InterruptThread : public i::Thread { - public: - explicit InterruptThread(ThreadInterruptTest* test) - : Thread("InterruptThread"), test_(test) {} - - virtual void Run() { - struct sigaction action; - - // Ensure that we'll enter waiting condition - i::OS::Sleep(100); - - // Setup signal handler - memset(&action, 0, sizeof(action)); - action.sa_handler = SignalHandler; - sigaction(SIGCHLD, &action, NULL); - - // Send signal - kill(getpid(), SIGCHLD); - - // Ensure that if wait has returned because of error - i::OS::Sleep(100); - - // Set value and signal semaphore - test_->sem_value_ = 1; - test_->sem_->Signal(); - } - - static void SignalHandler(int signal) { - } - - private: - ThreadInterruptTest* test_; - }; - - i::Semaphore* sem_; - volatile int sem_value_; -}; - - -THREADED_TEST(SemaphoreInterruption) { - ThreadInterruptTest().RunTest(); -} - -#endif // WIN32 |