diff options
Diffstat (limited to 'deps/v8/test/cctest/test-api.cc')
-rw-r--r-- | deps/v8/test/cctest/test-api.cc | 2444 |
1 files changed, 2106 insertions, 338 deletions
diff --git a/deps/v8/test/cctest/test-api.cc b/deps/v8/test/cctest/test-api.cc index f450a3459..097743dd1 100644 --- a/deps/v8/test/cctest/test-api.cc +++ b/deps/v8/test/cctest/test-api.cc @@ -1,4 +1,4 @@ -// Copyright 2007-2009 the V8 project authors. All rights reserved. +// Copyright 2011 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: @@ -30,17 +30,17 @@ #include "v8.h" #include "api.h" +#include "isolate.h" #include "compilation-cache.h" #include "execution.h" #include "snapshot.h" #include "platform.h" -#include "top.h" #include "utils.h" #include "cctest.h" #include "parser.h" #include "unicode-inl.h" -static const bool kLogThreading = true; +static const bool kLogThreading = false; static bool IsNaN(double x) { #ifdef WIN32 @@ -50,15 +50,27 @@ static bool IsNaN(double x) { #endif } -using ::v8::ObjectTemplate; -using ::v8::Value; +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::String; +using ::v8::Message; +using ::v8::MessageCallback; +using ::v8::Object; +using ::v8::ObjectTemplate; +using ::v8::Persistent; using ::v8::Script; -using ::v8::Function; -using ::v8::AccessorInfo; -using ::v8::Extension; +using ::v8::StackTrace; +using ::v8::String; +using ::v8::TryCatch; +using ::v8::Undefined; +using ::v8::V8; +using ::v8::Value; namespace i = ::i; @@ -191,8 +203,6 @@ THREADED_TEST(ReceiverSignature) { } - - THREADED_TEST(ArgumentSignature) { v8::HandleScope scope; LocalContext env; @@ -394,11 +404,11 @@ THREADED_TEST(ScriptUsingStringResource) { CHECK(source->IsExternal()); CHECK_EQ(resource, static_cast<TestResource*>(source->GetExternalStringResource())); - i::Heap::CollectAllGarbage(false); + HEAP->CollectAllGarbage(false); CHECK_EQ(0, TestResource::dispose_count); } - i::CompilationCache::Clear(); - i::Heap::CollectAllGarbage(false); + v8::internal::Isolate::Current()->compilation_cache()->Clear(); + HEAP->CollectAllGarbage(false); CHECK_EQ(1, TestResource::dispose_count); } @@ -415,11 +425,11 @@ THREADED_TEST(ScriptUsingAsciiStringResource) { Local<Value> value = script->Run(); CHECK(value->IsNumber()); CHECK_EQ(7, value->Int32Value()); - i::Heap::CollectAllGarbage(false); + HEAP->CollectAllGarbage(false); CHECK_EQ(0, TestAsciiResource::dispose_count); } - i::CompilationCache::Clear(); - i::Heap::CollectAllGarbage(false); + i::Isolate::Current()->compilation_cache()->Clear(); + HEAP->CollectAllGarbage(false); CHECK_EQ(1, TestAsciiResource::dispose_count); } @@ -432,19 +442,19 @@ THREADED_TEST(ScriptMakingExternalString) { LocalContext env; Local<String> source = String::New(two_byte_source); // Trigger GCs so that the newly allocated string moves to old gen. - i::Heap::CollectGarbage(i::NEW_SPACE); // in survivor space now - i::Heap::CollectGarbage(i::NEW_SPACE); // in old gen now + HEAP->CollectGarbage(i::NEW_SPACE); // in survivor space now + HEAP->CollectGarbage(i::NEW_SPACE); // in old gen now bool success = source->MakeExternal(new TestResource(two_byte_source)); CHECK(success); Local<Script> script = Script::Compile(source); Local<Value> value = script->Run(); CHECK(value->IsNumber()); CHECK_EQ(7, value->Int32Value()); - i::Heap::CollectAllGarbage(false); + HEAP->CollectAllGarbage(false); CHECK_EQ(0, TestResource::dispose_count); } - i::CompilationCache::Clear(); - i::Heap::CollectAllGarbage(false); + i::Isolate::Current()->compilation_cache()->Clear(); + HEAP->CollectAllGarbage(false); CHECK_EQ(1, TestResource::dispose_count); } @@ -457,8 +467,8 @@ THREADED_TEST(ScriptMakingExternalAsciiString) { LocalContext env; Local<String> source = v8_str(c_source); // Trigger GCs so that the newly allocated string moves to old gen. - i::Heap::CollectGarbage(i::NEW_SPACE); // in survivor space now - i::Heap::CollectGarbage(i::NEW_SPACE); // in old gen now + 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))); CHECK(success); @@ -466,11 +476,11 @@ THREADED_TEST(ScriptMakingExternalAsciiString) { Local<Value> value = script->Run(); CHECK(value->IsNumber()); CHECK_EQ(7, value->Int32Value()); - i::Heap::CollectAllGarbage(false); + HEAP->CollectAllGarbage(false); CHECK_EQ(0, TestAsciiResource::dispose_count); } - i::CompilationCache::Clear(); - i::Heap::CollectAllGarbage(false); + i::Isolate::Current()->compilation_cache()->Clear(); + HEAP->CollectAllGarbage(false); CHECK_EQ(1, TestAsciiResource::dispose_count); } @@ -480,8 +490,8 @@ TEST(MakingExternalStringConditions) { LocalContext env; // Free some space in the new space so that we can check freshness. - i::Heap::CollectGarbage(i::NEW_SPACE); - i::Heap::CollectGarbage(i::NEW_SPACE); + HEAP->CollectGarbage(i::NEW_SPACE); + HEAP->CollectGarbage(i::NEW_SPACE); uint16_t* two_byte_string = AsciiToTwoByteString("small"); Local<String> small_string = String::New(two_byte_string); @@ -490,8 +500,8 @@ TEST(MakingExternalStringConditions) { // 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. - i::Heap::CollectGarbage(i::NEW_SPACE); // in survivor space now - i::Heap::CollectGarbage(i::NEW_SPACE); // in old gen now + 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()); @@ -526,15 +536,15 @@ TEST(MakingExternalAsciiStringConditions) { LocalContext env; // Free some space in the new space so that we can check freshness. - i::Heap::CollectGarbage(i::NEW_SPACE); - i::Heap::CollectGarbage(i::NEW_SPACE); + HEAP->CollectGarbage(i::NEW_SPACE); + HEAP->CollectGarbage(i::NEW_SPACE); Local<String> small_string = String::New("small"); // 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. - i::Heap::CollectGarbage(i::NEW_SPACE); // in survivor space now - i::Heap::CollectGarbage(i::NEW_SPACE); // in old gen now + 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()); @@ -566,13 +576,13 @@ THREADED_TEST(UsingExternalString) { String::NewExternal(new TestResource(two_byte_string)); i::Handle<i::String> istring = v8::Utils::OpenHandle(*string); // Trigger GCs so that the newly allocated string moves to old gen. - i::Heap::CollectGarbage(i::NEW_SPACE); // in survivor space now - i::Heap::CollectGarbage(i::NEW_SPACE); // in old gen now - i::Handle<i::String> isymbol = i::Factory::SymbolFromString(istring); + HEAP->CollectGarbage(i::NEW_SPACE); // in survivor space now + HEAP->CollectGarbage(i::NEW_SPACE); // in old gen now + i::Handle<i::String> isymbol = FACTORY->SymbolFromString(istring); CHECK(isymbol->IsSymbol()); } - i::Heap::CollectAllGarbage(false); - i::Heap::CollectAllGarbage(false); + HEAP->CollectAllGarbage(false); + HEAP->CollectAllGarbage(false); } @@ -584,13 +594,13 @@ THREADED_TEST(UsingExternalAsciiString) { new TestAsciiResource(i::StrDup(one_byte_string))); i::Handle<i::String> istring = v8::Utils::OpenHandle(*string); // Trigger GCs so that the newly allocated string moves to old gen. - i::Heap::CollectGarbage(i::NEW_SPACE); // in survivor space now - i::Heap::CollectGarbage(i::NEW_SPACE); // in old gen now - i::Handle<i::String> isymbol = i::Factory::SymbolFromString(istring); + HEAP->CollectGarbage(i::NEW_SPACE); // in survivor space now + HEAP->CollectGarbage(i::NEW_SPACE); // in old gen now + i::Handle<i::String> isymbol = FACTORY->SymbolFromString(istring); CHECK(isymbol->IsSymbol()); } - i::Heap::CollectAllGarbage(false); - i::Heap::CollectAllGarbage(false); + HEAP->CollectAllGarbage(false); + HEAP->CollectAllGarbage(false); } @@ -603,12 +613,12 @@ THREADED_TEST(ScavengeExternalString) { Local<String> string = String::NewExternal(new TestResource(two_byte_string)); i::Handle<i::String> istring = v8::Utils::OpenHandle(*string); - i::Heap::CollectGarbage(i::NEW_SPACE); - in_new_space = i::Heap::InNewSpace(*istring); - CHECK(in_new_space || i::Heap::old_data_space()->Contains(*istring)); + HEAP->CollectGarbage(i::NEW_SPACE); + in_new_space = HEAP->InNewSpace(*istring); + CHECK(in_new_space || HEAP->old_data_space()->Contains(*istring)); CHECK_EQ(0, TestResource::dispose_count); } - i::Heap::CollectGarbage(in_new_space ? i::NEW_SPACE : i::OLD_DATA_SPACE); + HEAP->CollectGarbage(in_new_space ? i::NEW_SPACE : i::OLD_DATA_SPACE); CHECK_EQ(1, TestResource::dispose_count); } @@ -622,12 +632,12 @@ THREADED_TEST(ScavengeExternalAsciiString) { Local<String> string = String::NewExternal( new TestAsciiResource(i::StrDup(one_byte_string))); i::Handle<i::String> istring = v8::Utils::OpenHandle(*string); - i::Heap::CollectGarbage(i::NEW_SPACE); - in_new_space = i::Heap::InNewSpace(*istring); - CHECK(in_new_space || i::Heap::old_data_space()->Contains(*istring)); + HEAP->CollectGarbage(i::NEW_SPACE); + in_new_space = HEAP->InNewSpace(*istring); + CHECK(in_new_space || HEAP->old_data_space()->Contains(*istring)); CHECK_EQ(0, TestAsciiResource::dispose_count); } - i::Heap::CollectGarbage(in_new_space ? i::NEW_SPACE : i::OLD_DATA_SPACE); + HEAP->CollectGarbage(in_new_space ? i::NEW_SPACE : i::OLD_DATA_SPACE); CHECK_EQ(1, TestAsciiResource::dispose_count); } @@ -667,11 +677,11 @@ TEST(ExternalStringWithDisposeHandling) { Local<Value> value = script->Run(); CHECK(value->IsNumber()); CHECK_EQ(7, value->Int32Value()); - i::Heap::CollectAllGarbage(false); + HEAP->CollectAllGarbage(false); CHECK_EQ(0, TestAsciiResource::dispose_count); } - i::CompilationCache::Clear(); - i::Heap::CollectAllGarbage(false); + i::Isolate::Current()->compilation_cache()->Clear(); + HEAP->CollectAllGarbage(false); CHECK_EQ(1, TestAsciiResourceWithDisposeControl::dispose_calls); CHECK_EQ(0, TestAsciiResource::dispose_count); @@ -688,11 +698,11 @@ TEST(ExternalStringWithDisposeHandling) { Local<Value> value = script->Run(); CHECK(value->IsNumber()); CHECK_EQ(7, value->Int32Value()); - i::Heap::CollectAllGarbage(false); + HEAP->CollectAllGarbage(false); CHECK_EQ(0, TestAsciiResource::dispose_count); } - i::CompilationCache::Clear(); - i::Heap::CollectAllGarbage(false); + i::Isolate::Current()->compilation_cache()->Clear(); + HEAP->CollectAllGarbage(false); CHECK_EQ(1, TestAsciiResourceWithDisposeControl::dispose_calls); CHECK_EQ(1, TestAsciiResource::dispose_count); } @@ -738,9 +748,9 @@ THREADED_TEST(StringConcat) { CHECK(value->IsNumber()); CHECK_EQ(68, value->Int32Value()); } - i::CompilationCache::Clear(); - i::Heap::CollectAllGarbage(false); - i::Heap::CollectAllGarbage(false); + i::Isolate::Current()->compilation_cache()->Clear(); + HEAP->CollectAllGarbage(false); + HEAP->CollectAllGarbage(false); } @@ -1041,8 +1051,10 @@ THREADED_TEST(Date) { v8::HandleScope scope; LocalContext env; double PI = 3.1415926; - Local<Value> date_obj = v8::Date::New(PI); - CHECK_EQ(3.0, date_obj->NumberValue()); + 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()); } @@ -1575,12 +1587,12 @@ THREADED_TEST(InternalFieldsNativePointers) { // Check reading and writing aligned pointers. obj->SetPointerInInternalField(0, aligned); - i::Heap::CollectAllGarbage(false); + HEAP->CollectAllGarbage(false); CHECK_EQ(aligned, obj->GetPointerFromInternalField(0)); // Check reading and writing unaligned pointers. obj->SetPointerInInternalField(0, unaligned); - i::Heap::CollectAllGarbage(false); + HEAP->CollectAllGarbage(false); CHECK_EQ(unaligned, obj->GetPointerFromInternalField(0)); delete[] data; @@ -1606,19 +1618,19 @@ THREADED_TEST(InternalFieldsNativePointersAndExternal) { CHECK_EQ(1, static_cast<int>(reinterpret_cast<uintptr_t>(unaligned) & 0x1)); obj->SetPointerInInternalField(0, aligned); - i::Heap::CollectAllGarbage(false); + HEAP->CollectAllGarbage(false); CHECK_EQ(aligned, v8::External::Unwrap(obj->GetInternalField(0))); obj->SetPointerInInternalField(0, unaligned); - i::Heap::CollectAllGarbage(false); + HEAP->CollectAllGarbage(false); CHECK_EQ(unaligned, v8::External::Unwrap(obj->GetInternalField(0))); obj->SetInternalField(0, v8::External::Wrap(aligned)); - i::Heap::CollectAllGarbage(false); + HEAP->CollectAllGarbage(false); CHECK_EQ(aligned, obj->GetPointerFromInternalField(0)); obj->SetInternalField(0, v8::External::Wrap(unaligned)); - i::Heap::CollectAllGarbage(false); + HEAP->CollectAllGarbage(false); CHECK_EQ(unaligned, obj->GetPointerFromInternalField(0)); delete[] data; @@ -1631,7 +1643,7 @@ THREADED_TEST(IdentityHash) { // Ensure that the test starts with an fresh heap to test whether the hash // code is based on the address. - i::Heap::CollectAllGarbage(false); + HEAP->CollectAllGarbage(false); Local<v8::Object> obj = v8::Object::New(); int hash = obj->GetIdentityHash(); int hash1 = obj->GetIdentityHash(); @@ -1641,7 +1653,7 @@ THREADED_TEST(IdentityHash) { // 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); - i::Heap::CollectAllGarbage(false); + HEAP->CollectAllGarbage(false); 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 @@ -1678,7 +1690,7 @@ THREADED_TEST(HiddenProperties) { v8::Local<v8::String> empty = v8_str(""); v8::Local<v8::String> prop_name = v8_str("prop_name"); - i::Heap::CollectAllGarbage(false); + HEAP->CollectAllGarbage(false); // Make sure delete of a non-existent hidden value works CHECK(obj->DeleteHiddenValue(key)); @@ -1688,7 +1700,7 @@ THREADED_TEST(HiddenProperties) { CHECK(obj->SetHiddenValue(key, v8::Integer::New(2002))); CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); - i::Heap::CollectAllGarbage(false); + HEAP->CollectAllGarbage(false); // Make sure we do not find the hidden property. CHECK(!obj->Has(empty)); @@ -1699,7 +1711,7 @@ THREADED_TEST(HiddenProperties) { CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); CHECK_EQ(2003, obj->Get(empty)->Int32Value()); - i::Heap::CollectAllGarbage(false); + HEAP->CollectAllGarbage(false); // Add another property and delete it afterwards to force the object in // slow case. @@ -1710,7 +1722,7 @@ THREADED_TEST(HiddenProperties) { CHECK(obj->Delete(prop_name)); CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); - i::Heap::CollectAllGarbage(false); + HEAP->CollectAllGarbage(false); CHECK(obj->DeleteHiddenValue(key)); CHECK(obj->GetHiddenValue(key).IsEmpty()); @@ -1789,6 +1801,180 @@ THREADED_TEST(GlobalHandle) { } +static int NumberOfWeakCalls = 0; +static void WeakPointerCallback(Persistent<Value> handle, void* id) { + CHECK_EQ(reinterpret_cast<void*>(1234), id); + NumberOfWeakCalls++; + handle.Dispose(); +} + +THREADED_TEST(ApiObjectGroups) { + HandleScope scope; + LocalContext env; + + NumberOfWeakCalls = 0; + + Persistent<Object> g1s1; + Persistent<Object> g1s2; + Persistent<Object> g1c1; + Persistent<Object> g2s1; + Persistent<Object> g2s2; + Persistent<Object> g2c1; + + { + HandleScope scope; + g1s1 = Persistent<Object>::New(Object::New()); + g1s2 = Persistent<Object>::New(Object::New()); + g1c1 = Persistent<Object>::New(Object::New()); + g1s1.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback); + g1s2.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback); + g1c1.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback); + + g2s1 = Persistent<Object>::New(Object::New()); + g2s2 = Persistent<Object>::New(Object::New()); + g2c1 = Persistent<Object>::New(Object::New()); + g2s1.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback); + g2s2.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback); + g2c1.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback); + } + + Persistent<Object> root = Persistent<Object>::New(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 full GC + HEAP->CollectGarbage(i::OLD_POINTER_SPACE); + + // All object should be alive. + CHECK_EQ(0, NumberOfWeakCalls); + + // Weaken the root. + root.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback); + // But make children strong roots---all the objects (except for children) + // should be collectable now. + g1c1.ClearWeak(); + g2c1.ClearWeak(); + + // 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->CollectGarbage(i::OLD_POINTER_SPACE); + + // All objects should be gone. 5 global handles in total. + CHECK_EQ(5, NumberOfWeakCalls); + + // And now make children weak again and collect them. + g1c1.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback); + g2c1.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback); + + HEAP->CollectGarbage(i::OLD_POINTER_SPACE); + CHECK_EQ(7, NumberOfWeakCalls); +} + + +THREADED_TEST(ApiObjectGroupsCycle) { + HandleScope scope; + LocalContext env; + + NumberOfWeakCalls = 0; + + Persistent<Object> g1s1; + Persistent<Object> g1s2; + Persistent<Object> g2s1; + Persistent<Object> g2s2; + Persistent<Object> g3s1; + Persistent<Object> g3s2; + + { + HandleScope scope; + g1s1 = Persistent<Object>::New(Object::New()); + g1s2 = Persistent<Object>::New(Object::New()); + g1s1.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback); + g1s2.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback); + + g2s1 = Persistent<Object>::New(Object::New()); + g2s2 = Persistent<Object>::New(Object::New()); + g2s1.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback); + g2s2.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback); + + g3s1 = Persistent<Object>::New(Object::New()); + g3s2 = Persistent<Object>::New(Object::New()); + g3s1.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback); + g3s2.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback); + } + + Persistent<Object> root = Persistent<Object>::New(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[] = { 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); + } + // Do a full GC + HEAP->CollectGarbage(i::OLD_POINTER_SPACE); + + // All object should be alive. + CHECK_EQ(0, NumberOfWeakCalls); + + // Weaken the root. + root.MakeWeak(reinterpret_cast<void*>(1234), &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[] = { 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); + } + + HEAP->CollectGarbage(i::OLD_POINTER_SPACE); + + // All objects should be gone. 7 global handles in total. + CHECK_EQ(7, NumberOfWeakCalls); +} + + THREADED_TEST(ScriptException) { v8::HandleScope scope; LocalContext env; @@ -1900,6 +2086,10 @@ THREADED_TEST(Array) { 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()); } @@ -2082,8 +2272,6 @@ TEST(OutOfMemoryNested) { TEST(HugeConsStringOutOfMemory) { // It's not possible to read a snapshot into a heap with different dimensions. if (i::Snapshot::IsEnabled()) return; - v8::HandleScope scope; - LocalContext context; // Set heap limits. static const int K = 1024; v8::ResourceConstraints constraints; @@ -2094,6 +2282,9 @@ TEST(HugeConsStringOutOfMemory) { // 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();" @@ -2473,6 +2664,21 @@ TEST(APIThrowMessageAndVerboseTryCatch) { } +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(); @@ -2513,7 +2719,7 @@ v8::Handle<Value> CThrowCountDown(const v8::Arguments& args) { if (try_catch.HasCaught()) { CHECK_EQ(expected, count); CHECK(result.IsEmpty()); - CHECK(!i::Top::has_scheduled_exception()); + CHECK(!i::Isolate::Current()->has_scheduled_exception()); } else { CHECK_NE(expected, count); } @@ -2833,8 +3039,7 @@ THREADED_TEST(DefinePropertyOnAPIAccessor) { result = script_define->Run(); CHECK(try_catch.HasCaught()); String::AsciiValue exception_value(try_catch.Exception()); - CHECK_EQ(*exception_value, - "TypeError: Cannot redefine property: defineProperty"); + CHECK_EQ(*exception_value, "TypeError: Cannot redefine property: x"); } THREADED_TEST(DefinePropertyOnDefineGetterSetter) { @@ -2879,8 +3084,7 @@ THREADED_TEST(DefinePropertyOnDefineGetterSetter) { result = script_define->Run(); CHECK(try_catch.HasCaught()); String::AsciiValue exception_value(try_catch.Exception()); - CHECK_EQ(*exception_value, - "TypeError: Cannot redefine property: defineProperty"); + CHECK_EQ(*exception_value, "TypeError: Cannot redefine property: x"); } @@ -2998,8 +3202,7 @@ THREADED_TEST(DontDeleteAPIAccessorsCannotBeOverriden) { "{get: function() { return 'func'; }})"); CHECK(try_catch.HasCaught()); String::AsciiValue exception_value(try_catch.Exception()); - CHECK_EQ(*exception_value, - "TypeError: Cannot redefine property: defineProperty"); + CHECK_EQ(*exception_value, "TypeError: Cannot redefine property: x"); } { v8::TryCatch try_catch; @@ -3007,8 +3210,7 @@ THREADED_TEST(DontDeleteAPIAccessorsCannotBeOverriden) { "{get: function() { return 'func'; }})"); CHECK(try_catch.HasCaught()); String::AsciiValue exception_value(try_catch.Exception()); - CHECK_EQ(*exception_value, - "TypeError: Cannot redefine property: defineProperty"); + CHECK_EQ(*exception_value, "TypeError: Cannot redefine property: x"); } } @@ -3660,6 +3862,49 @@ THREADED_TEST(UndetectableObject) { } +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; @@ -3688,10 +3933,8 @@ THREADED_TEST(ExtensibleOnUndetectable) { source = v8_str("undetectable.y = 2000;"); script = Script::Compile(source); - v8::TryCatch try_catch; Local<Value> result = script->Run(); - CHECK(result.IsEmpty()); - CHECK(try_catch.HasCaught()); + ExpectBoolean("undetectable.y == undefined", true); } @@ -3736,6 +3979,38 @@ THREADED_TEST(UndetectableString) { } +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) { } @@ -3925,6 +4200,69 @@ THREADED_TEST(NativeCallInExtensions) { } +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); + ASSERT(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); + ASSERT(context.IsEmpty()); +} + + static void CheckDependencies(const char* name, const char* expected) { v8::HandleScope handle_scope; v8::ExtensionConfiguration config(1, &name); @@ -4196,55 +4534,116 @@ THREADED_TEST(WeakReference) { } -static bool in_scavenge = false; -static int last = -1; - -static void ForceScavenge(v8::Persistent<v8::Value> obj, void* data) { - CHECK_EQ(-1, last); - last = 0; +static void DisposeAndSetFlag(v8::Persistent<v8::Value> obj, void* data) { obj.Dispose(); obj.Clear(); - in_scavenge = true; - i::Heap::PerformScavenge(); - in_scavenge = false; *(reinterpret_cast<bool*>(data)) = true; } -static void CheckIsNotInvokedInScavenge(v8::Persistent<v8::Value> obj, - void* data) { - CHECK_EQ(0, last); - last = 1; - *(reinterpret_cast<bool*>(data)) = in_scavenge; - obj.Dispose(); - obj.Clear(); -} -THREADED_TEST(NoWeakRefCallbacksInScavenge) { - // Test verifies that scavenge cannot invoke WeakReferenceCallbacks. - // Calling callbacks from scavenges is unsafe as objects held by those - // handlers might have become strongly reachable, but scavenge doesn't - // check that. +THREADED_TEST(IndependentWeakHandle) { v8::Persistent<Context> context = Context::New(); Context::Scope context_scope(context); v8::Persistent<v8::Object> object_a; - v8::Persistent<v8::Object> object_b; { v8::HandleScope handle_scope; - object_b = v8::Persistent<v8::Object>::New(v8::Object::New()); object_a = v8::Persistent<v8::Object>::New(v8::Object::New()); } bool object_a_disposed = false; - object_a.MakeWeak(&object_a_disposed, &ForceScavenge); - bool released_in_scavenge = false; - object_b.MakeWeak(&released_in_scavenge, &CheckIsNotInvokedInScavenge); + object_a.MakeWeak(&object_a_disposed, &DisposeAndSetFlag); + object_a.MarkIndependent(); + HEAP->PerformScavenge(); + CHECK(object_a_disposed); +} + + +static void InvokeScavenge() { + HEAP->PerformScavenge(); +} + + +static void InvokeMarkSweep() { + HEAP->CollectAllGarbage(false); +} + + +static void ForceScavenge(v8::Persistent<v8::Value> obj, void* data) { + obj.Dispose(); + obj.Clear(); + *(reinterpret_cast<bool*>(data)) = true; + InvokeScavenge(); +} + + +static void ForceMarkSweep(v8::Persistent<v8::Value> obj, void* data) { + obj.Dispose(); + obj.Clear(); + *(reinterpret_cast<bool*>(data)) = true; + InvokeMarkSweep(); +} + + +THREADED_TEST(GCFromWeakCallbacks) { + v8::Persistent<Context> context = Context::New(); + Context::Scope context_scope(context); + + static const int kNumberOfGCTypes = 2; + v8::WeakReferenceCallback gc_forcing_callback[kNumberOfGCTypes] = + {&ForceScavenge, &ForceMarkSweep}; - while (!object_a_disposed) { - i::Heap::CollectAllGarbage(false); + 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(v8::Object::New()); + } + bool disposed = false; + object.MakeWeak(&disposed, gc_forcing_callback[inner_gc]); + object.MarkIndependent(); + invoke_gc[outer_gc](); + CHECK(disposed); + } + } +} + + +static void RevivingCallback(v8::Persistent<v8::Value> obj, void* data) { + obj.ClearWeak(); + *(reinterpret_cast<bool*>(data)) = true; +} + + +THREADED_TEST(IndependentHandleRevival) { + v8::Persistent<Context> context = Context::New(); + Context::Scope context_scope(context); + + v8::Persistent<v8::Object> object; + { + v8::HandleScope handle_scope; + object = v8::Persistent<v8::Object>::New(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(&revived, &RevivingCallback); + object.MarkIndependent(); + HEAP->PerformScavenge(); + CHECK(revived); + HEAP->CollectAllGarbage(true); + { + 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)); } - CHECK(!released_in_scavenge); } @@ -4260,7 +4659,7 @@ static v8::Handle<Value> ArgumentsTestCallback(const v8::Arguments& args) { CHECK_EQ(v8::Integer::New(3), args[2]); CHECK_EQ(v8::Undefined(), args[3]); v8::HandleScope scope; - i::Heap::CollectAllGarbage(false); + HEAP->CollectAllGarbage(false); return v8::Undefined(); } @@ -4562,130 +4961,130 @@ THREADED_TEST(StringWrite) { memset(utf8buf, 0x1, sizeof(utf8buf)); len = str2->WriteUtf8(utf8buf, sizeof(utf8buf), &charlen); - CHECK_EQ(len, 9); - CHECK_EQ(charlen, 5); - CHECK_EQ(strcmp(utf8buf, "abc\303\260\342\230\203"), 0); + CHECK_EQ(9, len); + CHECK_EQ(5, charlen); + CHECK_EQ(0, strcmp(utf8buf, "abc\303\260\342\230\203")); memset(utf8buf, 0x1, sizeof(utf8buf)); len = str2->WriteUtf8(utf8buf, 8, &charlen); - CHECK_EQ(len, 8); - CHECK_EQ(charlen, 5); - CHECK_EQ(strncmp(utf8buf, "abc\303\260\342\230\203\1", 9), 0); + CHECK_EQ(8, len); + CHECK_EQ(5, charlen); + CHECK_EQ(0, strncmp(utf8buf, "abc\303\260\342\230\203\1", 9)); memset(utf8buf, 0x1, sizeof(utf8buf)); len = str2->WriteUtf8(utf8buf, 7, &charlen); - CHECK_EQ(len, 5); - CHECK_EQ(charlen, 4); - CHECK_EQ(strncmp(utf8buf, "abc\303\260\1", 5), 0); + CHECK_EQ(5, len); + CHECK_EQ(4, charlen); + CHECK_EQ(0, strncmp(utf8buf, "abc\303\260\1", 5)); memset(utf8buf, 0x1, sizeof(utf8buf)); len = str2->WriteUtf8(utf8buf, 6, &charlen); - CHECK_EQ(len, 5); - CHECK_EQ(charlen, 4); - CHECK_EQ(strncmp(utf8buf, "abc\303\260\1", 5), 0); + CHECK_EQ(5, len); + CHECK_EQ(4, charlen); + CHECK_EQ(0, strncmp(utf8buf, "abc\303\260\1", 5)); memset(utf8buf, 0x1, sizeof(utf8buf)); len = str2->WriteUtf8(utf8buf, 5, &charlen); - CHECK_EQ(len, 5); - CHECK_EQ(charlen, 4); - CHECK_EQ(strncmp(utf8buf, "abc\303\260\1", 5), 0); + CHECK_EQ(5, len); + CHECK_EQ(4, charlen); + CHECK_EQ(0, strncmp(utf8buf, "abc\303\260\1", 5)); memset(utf8buf, 0x1, sizeof(utf8buf)); len = str2->WriteUtf8(utf8buf, 4, &charlen); - CHECK_EQ(len, 3); - CHECK_EQ(charlen, 3); - CHECK_EQ(strncmp(utf8buf, "abc\1", 4), 0); + CHECK_EQ(3, len); + CHECK_EQ(3, charlen); + CHECK_EQ(0, strncmp(utf8buf, "abc\1", 4)); memset(utf8buf, 0x1, sizeof(utf8buf)); len = str2->WriteUtf8(utf8buf, 3, &charlen); - CHECK_EQ(len, 3); - CHECK_EQ(charlen, 3); - CHECK_EQ(strncmp(utf8buf, "abc\1", 4), 0); + CHECK_EQ(3, len); + CHECK_EQ(3, charlen); + CHECK_EQ(0, strncmp(utf8buf, "abc\1", 4)); memset(utf8buf, 0x1, sizeof(utf8buf)); len = str2->WriteUtf8(utf8buf, 2, &charlen); - CHECK_EQ(len, 2); - CHECK_EQ(charlen, 2); - CHECK_EQ(strncmp(utf8buf, "ab\1", 3), 0); + CHECK_EQ(2, len); + CHECK_EQ(2, charlen); + CHECK_EQ(0, strncmp(utf8buf, "ab\1", 3)); memset(buf, 0x1, sizeof(buf)); memset(wbuf, 0x1, sizeof(wbuf)); len = str->WriteAscii(buf); - CHECK_EQ(len, 5); + CHECK_EQ(5, len); len = str->Write(wbuf); - CHECK_EQ(len, 5); - CHECK_EQ(strcmp("abcde", buf), 0); + CHECK_EQ(5, len); + CHECK_EQ(0, strcmp("abcde", buf)); uint16_t answer1[] = {'a', 'b', 'c', 'd', 'e', '\0'}; - CHECK_EQ(StrCmp16(answer1, wbuf), 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(len, 4); + CHECK_EQ(4, len); len = str->Write(wbuf, 0, 4); - CHECK_EQ(len, 4); - CHECK_EQ(strncmp("abcd\1", buf, 5), 0); + CHECK_EQ(4, len); + CHECK_EQ(0, strncmp("abcd\1", buf, 5)); uint16_t answer2[] = {'a', 'b', 'c', 'd', 0x101}; - CHECK_EQ(StrNCmp16(answer2, wbuf, 5), 0); + 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(len, 5); + CHECK_EQ(5, len); len = str->Write(wbuf, 0, 5); - CHECK_EQ(len, 5); - CHECK_EQ(strncmp("abcde\1", buf, 6), 0); + CHECK_EQ(5, len); + CHECK_EQ(0, strncmp("abcde\1", buf, 6)); uint16_t answer3[] = {'a', 'b', 'c', 'd', 'e', 0x101}; - CHECK_EQ(StrNCmp16(answer3, wbuf, 6), 0); + 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(len, 5); + CHECK_EQ(5, len); len = str->Write(wbuf, 0, 6); - CHECK_EQ(len, 5); - CHECK_EQ(strcmp("abcde", buf), 0); + CHECK_EQ(5, len); + CHECK_EQ(0, strcmp("abcde", buf)); uint16_t answer4[] = {'a', 'b', 'c', 'd', 'e', '\0'}; - CHECK_EQ(StrCmp16(answer4, wbuf), 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(len, 1); + CHECK_EQ(1, len); len = str->Write(wbuf, 4, -1); - CHECK_EQ(len, 1); - CHECK_EQ(strcmp("e", buf), 0); + CHECK_EQ(1, len); + CHECK_EQ(0, strcmp("e", buf)); uint16_t answer5[] = {'e', '\0'}; - CHECK_EQ(StrCmp16(answer5, wbuf), 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(len, 1); + CHECK_EQ(1, len); len = str->Write(wbuf, 4, 6); - CHECK_EQ(len, 1); - CHECK_EQ(strcmp("e", buf), 0); - CHECK_EQ(StrCmp16(answer5, wbuf), 0); + 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(len, 1); + CHECK_EQ(1, len); len = str->Write(wbuf, 4, 1); - CHECK_EQ(len, 1); - CHECK_EQ(strncmp("e\1", buf, 2), 0); + CHECK_EQ(1, len); + CHECK_EQ(0, strncmp("e\1", buf, 2)); uint16_t answer6[] = {'e', 0x101}; - CHECK_EQ(StrNCmp16(answer6, wbuf, 2), 0); + 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(len, 1); + CHECK_EQ(1, len); len = str->Write(wbuf, 3, 1); - CHECK_EQ(len, 1); - CHECK_EQ(strncmp("d\1", buf, 2), 0); + CHECK_EQ(1, len); + CHECK_EQ(0, strncmp("d\1", buf, 2)); uint16_t answer7[] = {'d', 0x101}; - CHECK_EQ(StrNCmp16(answer7, wbuf, 2), 0); + CHECK_EQ(0, StrNCmp16(answer7, wbuf, 2)); } @@ -5659,6 +6058,14 @@ TEST(AccessControlES5) { 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, @@ -5699,6 +6106,18 @@ TEST(AccessControlES5) { 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()); } @@ -6347,7 +6766,7 @@ THREADED_TEST(ShadowObject) { context->Global()->Set(v8_str("__proto__"), o); Local<Value> value = - Script::Compile(v8_str("propertyIsEnumerable(0)"))->Run(); + Script::Compile(v8_str("this.propertyIsEnumerable(0)"))->Run(); CHECK(value->IsBoolean()); CHECK(!value->BooleanValue()); @@ -6466,6 +6885,56 @@ THREADED_TEST(SetPrototype) { } +THREADED_TEST(SetPrototypeProperties) { + v8::HandleScope handle_scope; + LocalContext context; + + Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New(); + t1->SetPrototypeAttributes(v8::DontDelete); + context->Global()->Set(v8_str("func1"), t1->GetFunction()); + CHECK(CompileRun( + "(function() {" + " descriptor = Object.getOwnPropertyDescriptor(func1, 'prototype');" + " return (descriptor['writable'] == true) &&" + " (descriptor['enumerable'] == true) &&" + " (descriptor['configurable'] == false);" + "})()")->BooleanValue()); + + Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New(); + t2->SetPrototypeAttributes(v8::DontEnum); + context->Global()->Set(v8_str("func2"), t2->GetFunction()); + CHECK(CompileRun( + "(function() {" + " descriptor = Object.getOwnPropertyDescriptor(func2, 'prototype');" + " return (descriptor['writable'] == true) &&" + " (descriptor['enumerable'] == false) &&" + " (descriptor['configurable'] == true);" + "})()")->BooleanValue()); + + Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New(); + t3->SetPrototypeAttributes(v8::ReadOnly); + context->Global()->Set(v8_str("func3"), t3->GetFunction()); + CHECK(CompileRun( + "(function() {" + " descriptor = Object.getOwnPropertyDescriptor(func3, 'prototype');" + " return (descriptor['writable'] == false) &&" + " (descriptor['enumerable'] == true) &&" + " (descriptor['configurable'] == true);" + "})()")->BooleanValue()); + + Local<v8::FunctionTemplate> t4 = v8::FunctionTemplate::New(); + t4->SetPrototypeAttributes(v8::ReadOnly | v8::DontEnum | v8::DontDelete); + context->Global()->Set(v8_str("func4"), t4->GetFunction()); + CHECK(CompileRun( + "(function() {" + " descriptor = Object.getOwnPropertyDescriptor(func4, 'prototype');" + " return (descriptor['writable'] == false) &&" + " (descriptor['enumerable'] == false) &&" + " (descriptor['configurable'] == false);" + "})()")->BooleanValue()); +} + + THREADED_TEST(SetPrototypeThrows) { v8::HandleScope handle_scope; LocalContext context; @@ -6481,7 +6950,7 @@ THREADED_TEST(SetPrototypeThrows) { v8::TryCatch try_catch; CHECK(!o1->SetPrototype(o0)); CHECK(!try_catch.HasCaught()); - ASSERT(!i::Top::has_pending_exception()); + ASSERT(!i::Isolate::Current()->has_pending_exception()); CHECK_EQ(42, CompileRun("function f() { return 42; }; f()")->Int32Value()); } @@ -6523,6 +6992,200 @@ THREADED_TEST(Constructor) { 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::Boolean::New(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; @@ -6739,50 +7402,153 @@ 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()); + { 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("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()); + 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()); + // 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()); + 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()); + // 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()); + 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 + // 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(); + 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()); + } } @@ -6863,7 +7629,7 @@ static v8::Handle<Value> InterceptorHasOwnPropertyGetterGC( Local<String> name, const AccessorInfo& info) { ApiTestFuzzer::Fuzz(); - i::Heap::CollectAllGarbage(false); + HEAP->CollectAllGarbage(false); return v8::Handle<Value>(); } @@ -7593,7 +8359,7 @@ static v8::Handle<Value> InterceptorCallICFastApi(Local<String> name, int* call_count = reinterpret_cast<int*>(v8::External::Unwrap(info.Data())); ++(*call_count); if ((*call_count) % 20 == 0) { - i::Heap::CollectAllGarbage(true); + HEAP->CollectAllGarbage(true); } return v8::Handle<Value>(); } @@ -7631,7 +8397,7 @@ static void GenerateSomeGarbage() { v8::Handle<v8::Value> DirectApiCallback(const v8::Arguments& args) { static int count = 0; if (count++ % 3 == 0) { - i::Heap::CollectAllGarbage(true); // This should move the stub + HEAP-> CollectAllGarbage(true); // This should move the stub GenerateSomeGarbage(); // This should ensure the old stub memory is flushed } return v8::Handle<v8::Value>(); @@ -7686,7 +8452,7 @@ THREADED_TEST(CallICFastApi_DirectCall_Throw) { v8::Handle<v8::Value> DirectGetterCallback(Local<String> name, const v8::AccessorInfo& info) { if (++p_getter_count % 3 == 0) { - i::Heap::CollectAllGarbage(true); + HEAP->CollectAllGarbage(true); GenerateSomeGarbage(); } return v8::Handle<v8::Value>(); @@ -8374,6 +9140,134 @@ THREADED_TEST(NamedPropertyHandlerGetterAttributes) { } +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(); @@ -8595,11 +9489,10 @@ static unsigned linear_congruential_generator; void ApiTestFuzzer::Setup(PartOfTest part) { linear_congruential_generator = i::FLAG_testing_prng_seed; fuzzing_ = true; - int start = (part == FIRST_PART) ? 0 : (RegisterThreadedTest::count() >> 1); - int end = (part == FIRST_PART) - ? (RegisterThreadedTest::count() >> 1) - : RegisterThreadedTest::count(); - active_tests_ = tests_being_run_ = end - start; + 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); } @@ -8668,6 +9561,17 @@ TEST(Threading2) { 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) @@ -8823,11 +9727,12 @@ static void CheckSurvivingGlobalObjectsCount(int expected) { // 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. - i::Heap::CollectAllGarbage(false); - i::Heap::CollectAllGarbage(false); + HEAP->global_context_map(); + HEAP->CollectAllGarbage(false); + HEAP->CollectAllGarbage(false); int count = GetGlobalObjectsCount(); #ifdef DEBUG - if (count != expected) i::Heap::TracePathToGlobal(); + if (count != expected) HEAP->TracePathToGlobal(); #endif CHECK_EQ(expected, count); } @@ -8893,7 +9798,7 @@ THREADED_TEST(NewPersistentHandleFromWeakCallback) { // weak callback of the first handle would be able to 'reallocate' it. handle1.MakeWeak(NULL, NewPersistentHandleCallback); handle2.Dispose(); - i::Heap::CollectAllGarbage(false); + HEAP->CollectAllGarbage(false); } @@ -8901,7 +9806,7 @@ v8::Persistent<v8::Object> to_be_disposed; void DisposeAndForceGcCallback(v8::Persistent<v8::Value> handle, void*) { to_be_disposed.Dispose(); - i::Heap::CollectAllGarbage(false); + HEAP->CollectAllGarbage(false); handle.Dispose(); } @@ -8917,7 +9822,7 @@ THREADED_TEST(DoNotUseDeletedNodesInSecondLevelGc) { } handle1.MakeWeak(NULL, DisposeAndForceGcCallback); to_be_disposed = handle2; - i::Heap::CollectAllGarbage(false); + HEAP->CollectAllGarbage(false); } void DisposingCallback(v8::Persistent<v8::Value> handle, void*) { @@ -8943,7 +9848,7 @@ THREADED_TEST(NoGlobalHandlesOrphaningDueToWeakCallback) { } handle2.MakeWeak(NULL, DisposingCallback); handle3.MakeWeak(NULL, HandleCreatingCallback); - i::Heap::CollectAllGarbage(false); + HEAP->CollectAllGarbage(false); } @@ -9161,6 +10066,19 @@ void CheckProperties(v8::Handle<v8::Value> val, int elmc, const char* elmv[]) { } +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; @@ -9178,17 +10096,48 @@ THREADED_TEST(PropertyEnumeration) { 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, @@ -9694,6 +10643,7 @@ class RegExpInterruptTest { CHECK(regexp_success_); CHECK(gc_success_); } + private: // Number of garbage collections required. static const int kRequiredGCs = 5; @@ -9701,7 +10651,7 @@ class RegExpInterruptTest { class GCThread : public i::Thread { public: explicit GCThread(RegExpInterruptTest* test) - : test_(test) {} + : Thread("GCThread"), test_(test) {} virtual void Run() { test_->CollectGarbage(); } @@ -9715,7 +10665,7 @@ class RegExpInterruptTest { { v8::Locker lock; // TODO(lrn): Perhaps create some garbage before collecting. - i::Heap::CollectAllGarbage(false); + HEAP->CollectAllGarbage(false); gc_count_++; } i::OS::Sleep(1); @@ -9816,6 +10766,7 @@ class ApplyInterruptTest { CHECK(apply_success_); CHECK(gc_success_); } + private: // Number of garbage collections required. static const int kRequiredGCs = 2; @@ -9823,7 +10774,7 @@ class ApplyInterruptTest { class GCThread : public i::Thread { public: explicit GCThread(ApplyInterruptTest* test) - : test_(test) {} + : Thread("GCThread"), test_(test) {} virtual void Run() { test_->CollectGarbage(); } @@ -9836,7 +10787,7 @@ class ApplyInterruptTest { while (gc_during_apply_ < kRequiredGCs) { { v8::Locker lock; - i::Heap::CollectAllGarbage(false); + HEAP->CollectAllGarbage(false); gc_count_++; } i::OS::Sleep(1); @@ -9965,17 +10916,17 @@ static void MorphAString(i::String* string, CHECK(i::StringShape(string).IsExternal()); if (string->IsAsciiRepresentation()) { // Check old map is not symbol or long. - CHECK(string->map() == i::Heap::external_ascii_string_map()); + CHECK(string->map() == HEAP->external_ascii_string_map()); // Morph external string to be TwoByte string. - string->set_map(i::Heap::external_string_map()); + 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 symbol or long. - CHECK(string->map() == i::Heap::external_string_map()); + CHECK(string->map() == HEAP->external_string_map()); // Morph external string to be ASCII string. - string->set_map(i::Heap::external_ascii_string_map()); + string->set_map(HEAP->external_ascii_string_map()); i::ExternalAsciiString* morphed = i::ExternalAsciiString::cast(string); morphed->set_resource(ascii_resource); @@ -9999,9 +10950,9 @@ THREADED_TEST(MorphCompositeStringTest) { i::StrLength(c_string))); Local<String> lhs(v8::Utils::ToLocal( - i::Factory::NewExternalStringFromAscii(&ascii_resource))); + FACTORY->NewExternalStringFromAscii(&ascii_resource))); Local<String> rhs(v8::Utils::ToLocal( - i::Factory::NewExternalStringFromAscii(&ascii_resource))); + FACTORY->NewExternalStringFromAscii(&ascii_resource))); env->Global()->Set(v8_str("lhs"), lhs); env->Global()->Set(v8_str("rhs"), rhs); @@ -10086,12 +11037,12 @@ class RegExpStringModificationTest { // Create the input string for the regexp - the one we are going to change // properties of. - input_ = i::Factory::NewExternalStringFromAscii(&ascii_resource_); + input_ = FACTORY->NewExternalStringFromAscii(&ascii_resource_); // Inject the input as a global variable. i::Handle<i::String> input_name = - i::Factory::NewStringFromAscii(i::Vector<const char>("input", 5)); - i::Top::global_context()->global()->SetProperty( + FACTORY->NewStringFromAscii(i::Vector<const char>("input", 5)); + i::Isolate::Current()->global_context()->global()->SetProperty( *input_name, *input_, NONE, @@ -10109,8 +11060,8 @@ class RegExpStringModificationTest { CHECK(regexp_success_); CHECK(morph_success_); } - private: + private: // Number of string modifications required. static const int kRequiredModifications = 5; static const int kMaxModifications = 100; @@ -10118,7 +11069,7 @@ class RegExpStringModificationTest { class MorphThread : public i::Thread { public: explicit MorphThread(RegExpStringModificationTest* test) - : test_(test) {} + : Thread("MorphThread"), test_(test) {} virtual void Run() { test_->MorphString(); } @@ -10549,13 +11500,16 @@ THREADED_TEST(PixelArray) { LocalContext context; const int kElementCount = 260; uint8_t* pixel_data = reinterpret_cast<uint8_t*>(malloc(kElementCount)); - i::Handle<i::PixelArray> pixels = i::Factory::NewPixelArray(kElementCount, - pixel_data); - i::Heap::CollectAllGarbage(false); // Force GC to trigger verification. + i::Handle<i::ExternalPixelArray> pixels = + i::Handle<i::ExternalPixelArray>::cast( + FACTORY->NewExternalArray(kElementCount, + v8::kExternalPixelArray, + pixel_data)); + HEAP->CollectAllGarbage(false); // Force GC to trigger verification. for (int i = 0; i < kElementCount; i++) { pixels->set(i, i % 256); } - i::Heap::CollectAllGarbage(false); // Force GC to trigger verification. + HEAP->CollectAllGarbage(false); // Force GC to trigger verification. for (int i = 0; i < kElementCount; i++) { CHECK_EQ(i % 256, pixels->get(i)); CHECK_EQ(i % 256, pixel_data[i]); @@ -10617,14 +11571,14 @@ THREADED_TEST(PixelArray) { CHECK_EQ(28, result->Int32Value()); i::Handle<i::Smi> value(i::Smi::FromInt(2)); - i::SetElement(jsobj, 1, value); + i::SetElement(jsobj, 1, value, i::kNonStrictMode); CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value()); *value.location() = i::Smi::FromInt(256); - i::SetElement(jsobj, 1, value); + i::SetElement(jsobj, 1, value, i::kNonStrictMode); CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value()); *value.location() = i::Smi::FromInt(-1); - i::SetElement(jsobj, 1, value); + i::SetElement(jsobj, 1, value, i::kNonStrictMode); CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value()); result = CompileRun("for (var i = 0; i < 8; i++) {" @@ -10890,7 +11844,7 @@ THREADED_TEST(PixelArray) { " return sum; " "}" "for (var i = 0; i < 256; ++i) { pixels[i] = i; }" - "for (var i = 0; i < 10000; ++i) {" + "for (var i = 0; i < 5000; ++i) {" " result = pa_load(pixels);" "}" "result"); @@ -10907,7 +11861,7 @@ THREADED_TEST(PixelArray) { " }" " return sum; " "}" - "for (var i = 0; i < 100000; ++i) {" + "for (var i = 0; i < 5000; ++i) {" " pa_init(pixels);" "}" "result = pa_load(pixels);" @@ -10955,8 +11909,11 @@ THREADED_TEST(PixelArrayWithInterceptor) { LocalContext context; const int kElementCount = 260; uint8_t* pixel_data = reinterpret_cast<uint8_t*>(malloc(kElementCount)); - i::Handle<i::PixelArray> pixels = - i::Factory::NewPixelArray(kElementCount, pixel_data); + 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); } @@ -10984,6 +11941,7 @@ 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: @@ -10995,6 +11953,9 @@ static int ExternalArrayElementSize(v8::ExternalArrayType array_type) { case v8::kExternalFloatArray: return 4; break; + case v8::kExternalDoubleArray: + return 8; + break; default: UNREACHABLE(); return -1; @@ -11016,12 +11977,12 @@ static void ExternalArrayTestHelper(v8::ExternalArrayType array_type, static_cast<ElementType*>(malloc(kElementCount * element_size)); i::Handle<ExternalArrayClass> array = i::Handle<ExternalArrayClass>::cast( - i::Factory::NewExternalArray(kElementCount, array_type, array_data)); - i::Heap::CollectAllGarbage(false); // Force GC to trigger verification. + FACTORY->NewExternalArray(kElementCount, array_type, array_data)); + HEAP->CollectAllGarbage(false); // Force GC to trigger verification. for (int i = 0; i < kElementCount; i++) { array->set(i, static_cast<ElementType>(i)); } - i::Heap::CollectAllGarbage(false); // Force GC to trigger verification. + HEAP->CollectAllGarbage(false); // Force GC to trigger verification. for (int i = 0; i < kElementCount; i++) { CHECK_EQ(static_cast<int64_t>(i), static_cast<int64_t>(array->get(i))); CHECK_EQ(static_cast<int64_t>(i), static_cast<int64_t>(array_data[i])); @@ -11138,7 +12099,7 @@ static void ExternalArrayTestHelper(v8::ExternalArrayType array_type, " }" "}" "sum;"); - i::Heap::CollectAllGarbage(false); // Force GC to trigger verification. + HEAP->CollectAllGarbage(false); // Force GC to trigger verification. CHECK_EQ(28, result->Int32Value()); // Make sure out-of-range loads do not throw. @@ -11184,7 +12145,8 @@ static void ExternalArrayTestHelper(v8::ExternalArrayType array_type, CHECK_EQ( 2, static_cast<int>(jsobj->GetElement(6)->ToObjectChecked()->Number())); - if (array_type != v8::kExternalFloatArray) { + 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++) {" @@ -11205,8 +12167,10 @@ static void ExternalArrayTestHelper(v8::ExternalArrayType array_type, " ext_array[i] = Infinity;" "}" "ext_array[5];"); - CHECK_EQ(0, result->Int32Value()); - CHECK_EQ(0, + 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++) {" @@ -11227,10 +12191,14 @@ static void ExternalArrayTestHelper(v8::ExternalArrayType array_type, 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" @@ -11243,11 +12211,46 @@ static void ExternalArrayTestHelper(v8::ExternalArrayType array_type, " (ext_array[5] == expected_results[i]);" "}" "all_passed;", - (is_unsigned ? unsigned_data : signed_data)); + (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];"); @@ -11287,7 +12290,7 @@ static void ExternalArrayTestHelper(v8::ExternalArrayType array_type, static_cast<ElementType*>(malloc(kLargeElementCount * element_size)); i::Handle<ExternalArrayClass> large_array = i::Handle<ExternalArrayClass>::cast( - i::Factory::NewExternalArray(kLargeElementCount, + FACTORY->NewExternalArray(kLargeElementCount, array_type, array_data)); v8::Handle<v8::Object> large_obj = v8::Object::New(); @@ -11354,6 +12357,95 @@ static void ExternalArrayTestHelper(v8::ExternalArrayType array_type, 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); } @@ -11374,6 +12466,14 @@ THREADED_TEST(ExternalUnsignedByteArray) { } +THREADED_TEST(ExternalPixelArray) { + ExternalArrayTestHelper<i::ExternalPixelArray, uint8_t>( + v8::kExternalPixelArray, + 0, + 255); +} + + THREADED_TEST(ExternalShortArray) { ExternalArrayTestHelper<i::ExternalShortArray, int16_t>( v8::kExternalShortArray, @@ -11414,6 +12514,14 @@ THREADED_TEST(ExternalFloatArray) { } +THREADED_TEST(ExternalDoubleArray) { + ExternalArrayTestHelper<i::ExternalDoubleArray, double>( + v8::kExternalDoubleArray, + -500, + 500); +} + + THREADED_TEST(ExternalArrays) { TestExternalByteArray(); TestExternalUnsignedByteArray(); @@ -11451,6 +12559,8 @@ THREADED_TEST(ExternalArrayInfo) { ExternalArrayInfoTestHelper(v8::kExternalIntArray); ExternalArrayInfoTestHelper(v8::kExternalUnsignedIntArray); ExternalArrayInfoTestHelper(v8::kExternalFloatArray); + ExternalArrayInfoTestHelper(v8::kExternalDoubleArray); + ExternalArrayInfoTestHelper(v8::kExternalPixelArray); } @@ -11714,7 +12824,8 @@ THREADED_TEST(IdleNotification) { static uint32_t* stack_limit; static v8::Handle<Value> GetStackLimitCallback(const v8::Arguments& args) { - stack_limit = reinterpret_cast<uint32_t*>(i::StackGuard::real_climit()); + stack_limit = reinterpret_cast<uint32_t*>( + i::Isolate::Current()->stack_guard()->real_climit()); return v8::Undefined(); } @@ -11799,28 +12910,14 @@ THREADED_TEST(GetHeapStatistics) { static double DoubleFromBits(uint64_t value) { double target; -#ifdef BIG_ENDIAN_FLOATING_POINT - const int kIntSize = 4; - // Somebody swapped the lower and higher half of doubles. - memcpy(&target, reinterpret_cast<char*>(&value) + kIntSize, kIntSize); - memcpy(reinterpret_cast<char*>(&target) + kIntSize, &value, kIntSize); -#else memcpy(&target, &value, sizeof(target)); -#endif return target; } static uint64_t DoubleToBits(double value) { uint64_t target; -#ifdef BIG_ENDIAN_FLOATING_POINT - const int kIntSize = 4; - // Somebody swapped the lower and higher half of doubles. - memcpy(&target, reinterpret_cast<char*>(&value) + kIntSize, kIntSize); - memcpy(reinterpret_cast<char*>(&target) + kIntSize, &value, kIntSize); -#else memcpy(&target, &value, sizeof(target)); -#endif return target; } @@ -11980,7 +13077,7 @@ TEST(Regress528) { other_context->Enter(); CompileRun(source_simple); other_context->Exit(); - i::Heap::CollectAllGarbage(false); + HEAP->CollectAllGarbage(false); if (GetGlobalObjectsCount() == 1) break; } CHECK_GE(2, gc_count); @@ -12002,7 +13099,7 @@ TEST(Regress528) { other_context->Enter(); CompileRun(source_eval); other_context->Exit(); - i::Heap::CollectAllGarbage(false); + HEAP->CollectAllGarbage(false); if (GetGlobalObjectsCount() == 1) break; } CHECK_GE(2, gc_count); @@ -12029,7 +13126,7 @@ TEST(Regress528) { other_context->Enter(); CompileRun(source_exception); other_context->Exit(); - i::Heap::CollectAllGarbage(false); + HEAP->CollectAllGarbage(false); if (GetGlobalObjectsCount() == 1) break; } CHECK_GE(2, gc_count); @@ -12247,26 +13344,26 @@ TEST(GCCallbacks) { v8::V8::AddGCEpilogueCallback(EpilogueCallback); CHECK_EQ(0, prologue_call_count); CHECK_EQ(0, epilogue_call_count); - i::Heap::CollectAllGarbage(false); + HEAP->CollectAllGarbage(false); CHECK_EQ(1, prologue_call_count); CHECK_EQ(1, epilogue_call_count); v8::V8::AddGCPrologueCallback(PrologueCallbackSecond); v8::V8::AddGCEpilogueCallback(EpilogueCallbackSecond); - i::Heap::CollectAllGarbage(false); + HEAP->CollectAllGarbage(false); 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); - i::Heap::CollectAllGarbage(false); + HEAP->CollectAllGarbage(false); 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); - i::Heap::CollectAllGarbage(false); + HEAP->CollectAllGarbage(false); CHECK_EQ(2, prologue_call_count); CHECK_EQ(2, epilogue_call_count); CHECK_EQ(2, prologue_call_count_second); @@ -12294,7 +13391,7 @@ THREADED_TEST(AddToJSFunctionResultCache) { " return 'Different results for ' + key1 + ': ' + r1 + ' vs. ' + r1_;" " return 'PASSED';" "})()"; - i::Heap::ClearJSFunctionResultCaches(); + HEAP->ClearJSFunctionResultCaches(); ExpectString(code, "PASSED"); } @@ -12318,7 +13415,7 @@ THREADED_TEST(FillJSFunctionResultCache) { " return 'FAILED: k0CacheSize is too small';" " return 'PASSED';" "})()"; - i::Heap::ClearJSFunctionResultCaches(); + HEAP->ClearJSFunctionResultCaches(); ExpectString(code, "PASSED"); } @@ -12343,7 +13440,7 @@ THREADED_TEST(RoundRobinGetFromCache) { " };" " return 'PASSED';" "})()"; - i::Heap::ClearJSFunctionResultCaches(); + HEAP->ClearJSFunctionResultCaches(); ExpectString(code, "PASSED"); } @@ -12368,7 +13465,7 @@ THREADED_TEST(ReverseGetFromCache) { " };" " return 'PASSED';" "})()"; - i::Heap::ClearJSFunctionResultCaches(); + HEAP->ClearJSFunctionResultCaches(); ExpectString(code, "PASSED"); } @@ -12386,7 +13483,7 @@ THREADED_TEST(TestEviction) { " };" " return 'PASSED';" "})()"; - i::Heap::ClearJSFunctionResultCaches(); + HEAP->ClearJSFunctionResultCaches(); ExpectString(code, "PASSED"); } @@ -12476,7 +13573,7 @@ THREADED_TEST(TwoByteStringInAsciiCons) { void FailedAccessCheckCallbackGC(Local<v8::Object> target, v8::AccessType type, Local<v8::Value> data) { - i::Heap::CollectAllGarbage(true); + HEAP->CollectAllGarbage(true); } @@ -12557,6 +13654,396 @@ TEST(GCInFailedAccessCheckCallback) { 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(); + } + + context1.Dispose(); + 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())); + } +} + +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 = @@ -12660,7 +14147,7 @@ TEST(DontDeleteCellLoadIC) { "})()", "ReferenceError: cell is not defined"); CompileRun("cell = \"new_second\";"); - i::Heap::CollectAllGarbage(true); + HEAP->CollectAllGarbage(true); ExpectString("readCell()", "new_second"); ExpectString("readCell()", "new_second"); } @@ -12723,48 +14210,6 @@ TEST(DontDeleteCellLoadICAPI) { } -TEST(GlobalLoadICGC) { - const char* function_code = - "function readCell() { while (true) { return cell; } }"; - - // Check inline load code for a don't delete cell is cleared during - // GC. - { - v8::HandleScope scope; - LocalContext context; - CompileRun("var cell = \"value\";"); - ExpectBoolean("delete cell", false); - CompileRun(function_code); - ExpectString("readCell()", "value"); - ExpectString("readCell()", "value"); - } - { - v8::HandleScope scope; - LocalContext context2; - // Hold the code object in the second context. - CompileRun(function_code); - CheckSurvivingGlobalObjectsCount(1); - } - - // Check inline load code for a deletable cell is cleared during GC. - { - v8::HandleScope scope; - LocalContext context; - CompileRun("cell = \"value\";"); - CompileRun(function_code); - ExpectString("readCell()", "value"); - ExpectString("readCell()", "value"); - } - { - v8::HandleScope scope; - LocalContext context2; - // Hold the code object in the second context. - CompileRun(function_code); - CheckSurvivingGlobalObjectsCount(1); - } -} - - TEST(RegExp) { v8::HandleScope scope; LocalContext context; @@ -12821,6 +14266,11 @@ TEST(RegExp) { 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"); + ASSERT_EQ(32, value->Int32Value()); + v8::TryCatch try_catch; re = v8::RegExp::New(v8_str("foo["), v8::RegExp::kNone); CHECK(re.IsEmpty()); @@ -12891,3 +14341,321 @@ TEST(DefinePropertyPostDetach) { 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(); + context2.Dispose(); + context3.Dispose(); +} + + +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"))); + } +} + + +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. + CheckCodeGenerationAllowed(); + + // Disallow eval and the Function constructor. + context->AllowCodeGenerationFromStrings(false); + CheckCodeGenerationDisallowed(); + + // Allow again. + context->AllowCodeGenerationFromStrings(true); + CheckCodeGenerationAllowed(); + + // Disallow but setting a global callback that will allow the calls. + context->AllowCodeGenerationFromStrings(false); + V8::SetAllowCodeGenerationFromStringsCallback(&CodeGenerationAllowed); + CheckCodeGenerationAllowed(); + + // Set a callback that disallows the code generation. + V8::SetAllowCodeGenerationFromStringsCallback(&CodeGenerationDisallowed); + CheckCodeGenerationDisallowed(); +} + + +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"))); +} |