summaryrefslogtreecommitdiff
path: root/deps/v8/test/cctest/test-api.cc
diff options
context:
space:
mode:
Diffstat (limited to 'deps/v8/test/cctest/test-api.cc')
-rw-r--r--deps/v8/test/cctest/test-api.cc2444
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")));
+}