blob: 93878390c85a7e7fa28bbaeeeb1086d40f7cf4ae [file] [log] [blame]
// Copyright 2012 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <climits>
#include <csignal>
#include <map>
#include <string>
#include "test/cctest/test-api.h"
#if V8_OS_POSIX
#include <unistd.h> // NOLINT
#endif
#include "include/v8-util.h"
#include "src/api.h"
#include "src/arguments.h"
#include "src/base/platform/platform.h"
#include "src/compilation-cache.h"
#include "src/debug.h"
#include "src/execution.h"
#include "src/objects.h"
#include "src/parser.h"
#include "src/smart-pointers.h"
#include "src/unicode-inl.h"
#include "src/utils.h"
#include "src/vm-state.h"
static const bool kLogThreading = false;
using ::v8::Boolean;
using ::v8::BooleanObject;
using ::v8::Context;
using ::v8::Extension;
using ::v8::Function;
using ::v8::FunctionTemplate;
using ::v8::Handle;
using ::v8::HandleScope;
using ::v8::Local;
using ::v8::Maybe;
using ::v8::Message;
using ::v8::MessageCallback;
using ::v8::Name;
using ::v8::None;
using ::v8::Object;
using ::v8::ObjectTemplate;
using ::v8::Persistent;
using ::v8::PropertyAttribute;
using ::v8::Script;
using ::v8::StackTrace;
using ::v8::String;
using ::v8::Symbol;
using ::v8::TryCatch;
using ::v8::Undefined;
using ::v8::UniqueId;
using ::v8::V8;
using ::v8::Value;
#define THREADED_PROFILED_TEST(Name) \
static void Test##Name(); \
TEST(Name##WithProfiler) { \
RunWithProfiler(&Test##Name); \
} \
THREADED_TEST(Name)
void RunWithProfiler(void (*test)()) {
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
v8::Local<v8::String> profile_name =
v8::String::NewFromUtf8(env->GetIsolate(), "my_profile1");
v8::CpuProfiler* cpu_profiler = env->GetIsolate()->GetCpuProfiler();
cpu_profiler->StartProfiling(profile_name);
(*test)();
reinterpret_cast<i::CpuProfiler*>(cpu_profiler)->DeleteAllProfiles();
}
static int signature_callback_count;
static Local<Value> signature_expected_receiver;
static void IncrementingSignatureCallback(
const v8::FunctionCallbackInfo<v8::Value>& args) {
ApiTestFuzzer::Fuzz();
signature_callback_count++;
CHECK(signature_expected_receiver->Equals(args.Holder()));
CHECK(signature_expected_receiver->Equals(args.This()));
v8::Handle<v8::Array> result =
v8::Array::New(args.GetIsolate(), args.Length());
for (int i = 0; i < args.Length(); i++)
result->Set(v8::Integer::New(args.GetIsolate(), i), args[i]);
args.GetReturnValue().Set(result);
}
static void Returns42(const v8::FunctionCallbackInfo<v8::Value>& info) {
info.GetReturnValue().Set(42);
}
// Tests that call v8::V8::Dispose() cannot be threaded.
UNINITIALIZED_TEST(InitializeAndDisposeOnce) {
CHECK(v8::V8::Initialize());
CHECK(v8::V8::Dispose());
}
// Tests that call v8::V8::Dispose() cannot be threaded.
UNINITIALIZED_TEST(InitializeAndDisposeMultiple) {
for (int i = 0; i < 3; ++i) CHECK(v8::V8::Dispose());
for (int i = 0; i < 3; ++i) CHECK(v8::V8::Initialize());
for (int i = 0; i < 3; ++i) CHECK(v8::V8::Dispose());
for (int i = 0; i < 3; ++i) CHECK(v8::V8::Initialize());
for (int i = 0; i < 3; ++i) CHECK(v8::V8::Dispose());
}
THREADED_TEST(Handles) {
v8::HandleScope scope(CcTest::isolate());
Local<Context> local_env;
{
LocalContext env;
local_env = env.local();
}
// Local context should still be live.
CHECK(!local_env.IsEmpty());
local_env->Enter();
v8::Handle<v8::Primitive> undef = v8::Undefined(CcTest::isolate());
CHECK(!undef.IsEmpty());
CHECK(undef->IsUndefined());
const char* source = "1 + 2 + 3";
Local<Script> script = v8_compile(source);
CHECK_EQ(6, script->Run()->Int32Value());
local_env->Exit();
}
THREADED_TEST(IsolateOfContext) {
v8::HandleScope scope(CcTest::isolate());
v8::Handle<Context> env = Context::New(CcTest::isolate());
CHECK(!env->GetIsolate()->InContext());
CHECK(env->GetIsolate() == CcTest::isolate());
env->Enter();
CHECK(env->GetIsolate()->InContext());
CHECK(env->GetIsolate() == CcTest::isolate());
env->Exit();
CHECK(!env->GetIsolate()->InContext());
CHECK(env->GetIsolate() == CcTest::isolate());
}
static void TestSignature(const char* loop_js, Local<Value> receiver,
v8::Isolate* isolate) {
i::ScopedVector<char> source(200);
i::SNPrintF(source,
"for (var i = 0; i < 10; i++) {"
" %s"
"}",
loop_js);
signature_callback_count = 0;
signature_expected_receiver = receiver;
bool expected_to_throw = receiver.IsEmpty();
v8::TryCatch try_catch(isolate);
CompileRun(source.start());
CHECK_EQ(expected_to_throw, try_catch.HasCaught());
if (!expected_to_throw) {
CHECK_EQ(10, signature_callback_count);
} else {
CHECK(v8_str("TypeError: Illegal invocation")
->Equals(try_catch.Exception()->ToString(isolate)));
}
}
THREADED_TEST(ReceiverSignature) {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
// Setup templates.
v8::Handle<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(isolate);
v8::Handle<v8::Signature> sig = v8::Signature::New(isolate, fun);
v8::Handle<v8::FunctionTemplate> callback_sig =
v8::FunctionTemplate::New(
isolate, IncrementingSignatureCallback, Local<Value>(), sig);
v8::Handle<v8::FunctionTemplate> callback =
v8::FunctionTemplate::New(isolate, IncrementingSignatureCallback);
v8::Handle<v8::FunctionTemplate> sub_fun = v8::FunctionTemplate::New(isolate);
sub_fun->Inherit(fun);
v8::Handle<v8::FunctionTemplate> unrel_fun =
v8::FunctionTemplate::New(isolate);
// Install properties.
v8::Handle<v8::ObjectTemplate> fun_proto = fun->PrototypeTemplate();
fun_proto->Set(v8_str("prop_sig"), callback_sig);
fun_proto->Set(v8_str("prop"), callback);
fun_proto->SetAccessorProperty(
v8_str("accessor_sig"), callback_sig, callback_sig);
fun_proto->SetAccessorProperty(v8_str("accessor"), callback, callback);
// Instantiate templates.
Local<Value> fun_instance = fun->InstanceTemplate()->NewInstance();
Local<Value> sub_fun_instance = sub_fun->InstanceTemplate()->NewInstance();
// Setup global variables.
env->Global()->Set(v8_str("Fun"), fun->GetFunction());
env->Global()->Set(v8_str("UnrelFun"), unrel_fun->GetFunction());
env->Global()->Set(v8_str("fun_instance"), fun_instance);
env->Global()->Set(v8_str("sub_fun_instance"), sub_fun_instance);
CompileRun(
"var accessor_sig_key = 'accessor_sig';"
"var accessor_key = 'accessor';"
"var prop_sig_key = 'prop_sig';"
"var prop_key = 'prop';"
""
"function copy_props(obj) {"
" var keys = [accessor_sig_key, accessor_key, prop_sig_key, prop_key];"
" var source = Fun.prototype;"
" for (var i in keys) {"
" var key = keys[i];"
" var desc = Object.getOwnPropertyDescriptor(source, key);"
" Object.defineProperty(obj, key, desc);"
" }"
"}"
""
"var obj = {};"
"copy_props(obj);"
"var unrel = new UnrelFun();"
"copy_props(unrel);");
// Test with and without ICs
const char* test_objects[] = {
"fun_instance", "sub_fun_instance", "obj", "unrel" };
unsigned bad_signature_start_offset = 2;
for (unsigned i = 0; i < arraysize(test_objects); i++) {
i::ScopedVector<char> source(200);
i::SNPrintF(
source, "var test_object = %s; test_object", test_objects[i]);
Local<Value> test_object = CompileRun(source.start());
TestSignature("test_object.prop();", test_object, isolate);
TestSignature("test_object.accessor;", test_object, isolate);
TestSignature("test_object[accessor_key];", test_object, isolate);
TestSignature("test_object.accessor = 1;", test_object, isolate);
TestSignature("test_object[accessor_key] = 1;", test_object, isolate);
if (i >= bad_signature_start_offset) test_object = Local<Value>();
TestSignature("test_object.prop_sig();", test_object, isolate);
TestSignature("test_object.accessor_sig;", test_object, isolate);
TestSignature("test_object[accessor_sig_key];", test_object, isolate);
TestSignature("test_object.accessor_sig = 1;", test_object, isolate);
TestSignature("test_object[accessor_sig_key] = 1;", test_object, isolate);
}
}
THREADED_TEST(HulIgennem) {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::Primitive> undef = v8::Undefined(isolate);
Local<String> undef_str = undef->ToString(isolate);
char* value = i::NewArray<char>(undef_str->Utf8Length() + 1);
undef_str->WriteUtf8(value);
CHECK_EQ(0, strcmp(value, "undefined"));
i::DeleteArray(value);
}
THREADED_TEST(Access) {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
Local<v8::Object> obj = v8::Object::New(isolate);
Local<Value> foo_before = obj->Get(v8_str("foo"));
CHECK(foo_before->IsUndefined());
Local<String> bar_str = v8_str("bar");
obj->Set(v8_str("foo"), bar_str);
Local<Value> foo_after = obj->Get(v8_str("foo"));
CHECK(!foo_after->IsUndefined());
CHECK(foo_after->IsString());
CHECK(bar_str->Equals(foo_after));
}
THREADED_TEST(AccessElement) {
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
Local<v8::Object> obj = v8::Object::New(env->GetIsolate());
Local<Value> before = obj->Get(1);
CHECK(before->IsUndefined());
Local<String> bar_str = v8_str("bar");
obj->Set(1, bar_str);
Local<Value> after = obj->Get(1);
CHECK(!after->IsUndefined());
CHECK(after->IsString());
CHECK(bar_str->Equals(after));
Local<v8::Array> value = CompileRun("[\"a\", \"b\"]").As<v8::Array>();
CHECK(v8_str("a")->Equals(value->Get(0)));
CHECK(v8_str("b")->Equals(value->Get(1)));
}
THREADED_TEST(Script) {
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
const char* source = "1 + 2 + 3";
Local<Script> script = v8_compile(source);
CHECK_EQ(6, script->Run()->Int32Value());
}
class TestResource: public String::ExternalStringResource {
public:
explicit TestResource(uint16_t* data, int* counter = NULL,
bool owning_data = true)
: data_(data), length_(0), counter_(counter), owning_data_(owning_data) {
while (data[length_]) ++length_;
}
~TestResource() {
if (owning_data_) i::DeleteArray(data_);
if (counter_ != NULL) ++*counter_;
}
const uint16_t* data() const {
return data_;
}
size_t length() const {
return length_;
}
private:
uint16_t* data_;
size_t length_;
int* counter_;
bool owning_data_;
};
class TestOneByteResource : public String::ExternalOneByteStringResource {
public:
explicit TestOneByteResource(const char* data, int* counter = NULL,
size_t offset = 0)
: orig_data_(data),
data_(data + offset),
length_(strlen(data) - offset),
counter_(counter) {}
~TestOneByteResource() {
i::DeleteArray(orig_data_);
if (counter_ != NULL) ++*counter_;
}
const char* data() const {
return data_;
}
size_t length() const {
return length_;
}
private:
const char* orig_data_;
const char* data_;
size_t length_;
int* counter_;
};
THREADED_TEST(ScriptUsingStringResource) {
int dispose_count = 0;
const char* c_source = "1 + 2 * 3";
uint16_t* two_byte_source = AsciiToTwoByteString(c_source);
{
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
TestResource* resource = new TestResource(two_byte_source, &dispose_count);
Local<String> source = String::NewExternal(env->GetIsolate(), resource);
Local<Script> script = v8_compile(source);
Local<Value> value = script->Run();
CHECK(value->IsNumber());
CHECK_EQ(7, value->Int32Value());
CHECK(source->IsExternal());
CHECK_EQ(resource,
static_cast<TestResource*>(source->GetExternalStringResource()));
String::Encoding encoding = String::UNKNOWN_ENCODING;
CHECK_EQ(static_cast<const String::ExternalStringResourceBase*>(resource),
source->GetExternalStringResourceBase(&encoding));
CHECK_EQ(String::TWO_BYTE_ENCODING, encoding);
CcTest::heap()->CollectAllGarbage();
CHECK_EQ(0, dispose_count);
}
CcTest::i_isolate()->compilation_cache()->Clear();
CcTest::heap()->CollectAllAvailableGarbage();
CHECK_EQ(1, dispose_count);
}
THREADED_TEST(ScriptUsingOneByteStringResource) {
int dispose_count = 0;
const char* c_source = "1 + 2 * 3";
{
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
TestOneByteResource* resource =
new TestOneByteResource(i::StrDup(c_source), &dispose_count);
Local<String> source = String::NewExternal(env->GetIsolate(), resource);
CHECK(source->IsExternalOneByte());
CHECK_EQ(static_cast<const String::ExternalStringResourceBase*>(resource),
source->GetExternalOneByteStringResource());
String::Encoding encoding = String::UNKNOWN_ENCODING;
CHECK_EQ(static_cast<const String::ExternalStringResourceBase*>(resource),
source->GetExternalStringResourceBase(&encoding));
CHECK_EQ(String::ONE_BYTE_ENCODING, encoding);
Local<Script> script = v8_compile(source);
Local<Value> value = script->Run();
CHECK(value->IsNumber());
CHECK_EQ(7, value->Int32Value());
CcTest::heap()->CollectAllGarbage();
CHECK_EQ(0, dispose_count);
}
CcTest::i_isolate()->compilation_cache()->Clear();
CcTest::heap()->CollectAllAvailableGarbage();
CHECK_EQ(1, dispose_count);
}
THREADED_TEST(ScriptMakingExternalString) {
int dispose_count = 0;
uint16_t* two_byte_source = AsciiToTwoByteString("1 + 2 * 3");
{
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
Local<String> source =
String::NewFromTwoByte(env->GetIsolate(), two_byte_source);
// Trigger GCs so that the newly allocated string moves to old gen.
CcTest::heap()->CollectGarbage(i::NEW_SPACE); // in survivor space now
CcTest::heap()->CollectGarbage(i::NEW_SPACE); // in old gen now
CHECK_EQ(source->IsExternal(), false);
CHECK_EQ(source->IsExternalOneByte(), false);
String::Encoding encoding = String::UNKNOWN_ENCODING;
CHECK(!source->GetExternalStringResourceBase(&encoding));
CHECK_EQ(String::ONE_BYTE_ENCODING, encoding);
bool success = source->MakeExternal(new TestResource(two_byte_source,
&dispose_count));
CHECK(success);
Local<Script> script = v8_compile(source);
Local<Value> value = script->Run();
CHECK(value->IsNumber());
CHECK_EQ(7, value->Int32Value());
CcTest::heap()->CollectAllGarbage();
CHECK_EQ(0, dispose_count);
}
CcTest::i_isolate()->compilation_cache()->Clear();
CcTest::heap()->CollectAllGarbage();
CHECK_EQ(1, dispose_count);
}
THREADED_TEST(ScriptMakingExternalOneByteString) {
int dispose_count = 0;
const char* c_source = "1 + 2 * 3";
{
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
Local<String> source = v8_str(c_source);
// Trigger GCs so that the newly allocated string moves to old gen.
CcTest::heap()->CollectGarbage(i::NEW_SPACE); // in survivor space now
CcTest::heap()->CollectGarbage(i::NEW_SPACE); // in old gen now
bool success = source->MakeExternal(
new TestOneByteResource(i::StrDup(c_source), &dispose_count));
CHECK(success);
Local<Script> script = v8_compile(source);
Local<Value> value = script->Run();
CHECK(value->IsNumber());
CHECK_EQ(7, value->Int32Value());
CcTest::heap()->CollectAllGarbage();
CHECK_EQ(0, dispose_count);
}
CcTest::i_isolate()->compilation_cache()->Clear();
CcTest::heap()->CollectAllGarbage();
CHECK_EQ(1, dispose_count);
}
TEST(MakingExternalStringConditions) {
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
// Free some space in the new space so that we can check freshness.
CcTest::heap()->CollectGarbage(i::NEW_SPACE);
CcTest::heap()->CollectGarbage(i::NEW_SPACE);
uint16_t* two_byte_string = AsciiToTwoByteString("s1");
Local<String> small_string =
String::NewFromTwoByte(env->GetIsolate(), two_byte_string);
i::DeleteArray(two_byte_string);
// We should refuse to externalize newly created small string.
CHECK(!small_string->CanMakeExternal());
// Trigger GCs so that the newly allocated string moves to old gen.
CcTest::heap()->CollectGarbage(i::NEW_SPACE); // in survivor space now
CcTest::heap()->CollectGarbage(i::NEW_SPACE); // in old gen now
// Old space strings should be accepted.
CHECK(small_string->CanMakeExternal());
two_byte_string = AsciiToTwoByteString("small string 2");
small_string = String::NewFromTwoByte(env->GetIsolate(), two_byte_string);
i::DeleteArray(two_byte_string);
// We should refuse externalizing newly created small string.
CHECK(!small_string->CanMakeExternal());
for (int i = 0; i < 100; i++) {
String::Value value(small_string);
}
// Frequently used strings should be accepted.
CHECK(small_string->CanMakeExternal());
const int buf_size = 10 * 1024;
char* buf = i::NewArray<char>(buf_size);
memset(buf, 'a', buf_size);
buf[buf_size - 1] = '\0';
two_byte_string = AsciiToTwoByteString(buf);
Local<String> large_string =
String::NewFromTwoByte(env->GetIsolate(), two_byte_string);
i::DeleteArray(buf);
i::DeleteArray(two_byte_string);
// Large strings should be immediately accepted.
CHECK(large_string->CanMakeExternal());
}
TEST(MakingExternalOneByteStringConditions) {
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
// Free some space in the new space so that we can check freshness.
CcTest::heap()->CollectGarbage(i::NEW_SPACE);
CcTest::heap()->CollectGarbage(i::NEW_SPACE);
Local<String> small_string = String::NewFromUtf8(env->GetIsolate(), "s1");
// We should refuse to externalize newly created small string.
CHECK(!small_string->CanMakeExternal());
// Trigger GCs so that the newly allocated string moves to old gen.
CcTest::heap()->CollectGarbage(i::NEW_SPACE); // in survivor space now
CcTest::heap()->CollectGarbage(i::NEW_SPACE); // in old gen now
// Old space strings should be accepted.
CHECK(small_string->CanMakeExternal());
small_string = String::NewFromUtf8(env->GetIsolate(), "small string 2");
// We should refuse externalizing newly created small string.
CHECK(!small_string->CanMakeExternal());
for (int i = 0; i < 100; i++) {
String::Value value(small_string);
}
// Frequently used strings should be accepted.
CHECK(small_string->CanMakeExternal());
const int buf_size = 10 * 1024;
char* buf = i::NewArray<char>(buf_size);
memset(buf, 'a', buf_size);
buf[buf_size - 1] = '\0';
Local<String> large_string = String::NewFromUtf8(env->GetIsolate(), buf);
i::DeleteArray(buf);
// Large strings should be immediately accepted.
CHECK(large_string->CanMakeExternal());
}
TEST(MakingExternalUnalignedOneByteString) {
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
CompileRun("function cons(a, b) { return a + b; }"
"function slice(a) { return a.substring(1); }");
// Create a cons string that will land in old pointer space.
Local<String> cons = Local<String>::Cast(CompileRun(
"cons('abcdefghijklm', 'nopqrstuvwxyz');"));
// Create a sliced string that will land in old pointer space.
Local<String> slice = Local<String>::Cast(CompileRun(
"slice('abcdefghijklmnopqrstuvwxyz');"));
// Trigger GCs so that the newly allocated string moves to old gen.
SimulateFullSpace(CcTest::heap()->old_space());
CcTest::heap()->CollectGarbage(i::NEW_SPACE); // in survivor space now
CcTest::heap()->CollectGarbage(i::NEW_SPACE); // in old gen now
// Turn into external string with unaligned resource data.
const char* c_cons = "_abcdefghijklmnopqrstuvwxyz";
bool success =
cons->MakeExternal(new TestOneByteResource(i::StrDup(c_cons), NULL, 1));
CHECK(success);
const char* c_slice = "_bcdefghijklmnopqrstuvwxyz";
success =
slice->MakeExternal(new TestOneByteResource(i::StrDup(c_slice), NULL, 1));
CHECK(success);
// Trigger GCs and force evacuation.
CcTest::heap()->CollectAllGarbage();
CcTest::heap()->CollectAllGarbage(i::Heap::kReduceMemoryFootprintMask);
}
THREADED_TEST(UsingExternalString) {
i::Factory* factory = CcTest::i_isolate()->factory();
{
v8::HandleScope scope(CcTest::isolate());
uint16_t* two_byte_string = AsciiToTwoByteString("test string");
Local<String> string = String::NewExternal(
CcTest::isolate(), 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.
CcTest::heap()->CollectGarbage(i::NEW_SPACE); // in survivor space now
CcTest::heap()->CollectGarbage(i::NEW_SPACE); // in old gen now
i::Handle<i::String> isymbol =
factory->InternalizeString(istring);
CHECK(isymbol->IsInternalizedString());
}
CcTest::heap()->CollectAllGarbage();
CcTest::heap()->CollectAllGarbage();
}
THREADED_TEST(UsingExternalOneByteString) {
i::Factory* factory = CcTest::i_isolate()->factory();
{
v8::HandleScope scope(CcTest::isolate());
const char* one_byte_string = "test string";
Local<String> string = String::NewExternal(
CcTest::isolate(), new TestOneByteResource(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.
CcTest::heap()->CollectGarbage(i::NEW_SPACE); // in survivor space now
CcTest::heap()->CollectGarbage(i::NEW_SPACE); // in old gen now
i::Handle<i::String> isymbol =
factory->InternalizeString(istring);
CHECK(isymbol->IsInternalizedString());
}
CcTest::heap()->CollectAllGarbage();
CcTest::heap()->CollectAllGarbage();
}
class RandomLengthResource : public v8::String::ExternalStringResource {
public:
explicit RandomLengthResource(int length) : length_(length) {}
virtual const uint16_t* data() const { return string_; }
virtual size_t length() const { return length_; }
private:
uint16_t string_[10];
int length_;
};
class RandomLengthOneByteResource
: public v8::String::ExternalOneByteStringResource {
public:
explicit RandomLengthOneByteResource(int length) : length_(length) {}
virtual const char* data() const { return string_; }
virtual size_t length() const { return length_; }
private:
char string_[10];
int length_;
};
THREADED_TEST(NewExternalForVeryLongString) {
auto isolate = CcTest::isolate();
{
v8::HandleScope scope(isolate);
v8::TryCatch try_catch(isolate);
RandomLengthOneByteResource r(1 << 30);
v8::Local<v8::String> str = v8::String::NewExternal(isolate, &r);
CHECK(str.IsEmpty());
CHECK(!try_catch.HasCaught());
}
{
v8::HandleScope scope(isolate);
v8::TryCatch try_catch(isolate);
RandomLengthResource r(1 << 30);
v8::Local<v8::String> str = v8::String::NewExternal(isolate, &r);
CHECK(str.IsEmpty());
CHECK(!try_catch.HasCaught());
}
}
THREADED_TEST(ScavengeExternalString) {
i::FLAG_stress_compaction = false;
i::FLAG_gc_global = false;
int dispose_count = 0;
bool in_new_space = false;
{
v8::HandleScope scope(CcTest::isolate());
uint16_t* two_byte_string = AsciiToTwoByteString("test string");
Local<String> string = String::NewExternal(
CcTest::isolate(), new TestResource(two_byte_string, &dispose_count));
i::Handle<i::String> istring = v8::Utils::OpenHandle(*string);
CcTest::heap()->CollectGarbage(i::NEW_SPACE);
in_new_space = CcTest::heap()->InNewSpace(*istring);
CHECK(in_new_space || CcTest::heap()->old_space()->Contains(*istring));
CHECK_EQ(0, dispose_count);
}
CcTest::heap()->CollectGarbage(in_new_space ? i::NEW_SPACE : i::OLD_SPACE);
CHECK_EQ(1, dispose_count);
}
THREADED_TEST(ScavengeExternalOneByteString) {
i::FLAG_stress_compaction = false;
i::FLAG_gc_global = false;
int dispose_count = 0;
bool in_new_space = false;
{
v8::HandleScope scope(CcTest::isolate());
const char* one_byte_string = "test string";
Local<String> string = String::NewExternal(
CcTest::isolate(),
new TestOneByteResource(i::StrDup(one_byte_string), &dispose_count));
i::Handle<i::String> istring = v8::Utils::OpenHandle(*string);
CcTest::heap()->CollectGarbage(i::NEW_SPACE);
in_new_space = CcTest::heap()->InNewSpace(*istring);
CHECK(in_new_space || CcTest::heap()->old_space()->Contains(*istring));
CHECK_EQ(0, dispose_count);
}
CcTest::heap()->CollectGarbage(in_new_space ? i::NEW_SPACE : i::OLD_SPACE);
CHECK_EQ(1, dispose_count);
}
class TestOneByteResourceWithDisposeControl : public TestOneByteResource {
public:
// Only used by non-threaded tests, so it can use static fields.
static int dispose_calls;
static int dispose_count;
TestOneByteResourceWithDisposeControl(const char* data, bool dispose)
: TestOneByteResource(data, &dispose_count), dispose_(dispose) {}
void Dispose() {
++dispose_calls;
if (dispose_) delete this;
}
private:
bool dispose_;
};
int TestOneByteResourceWithDisposeControl::dispose_count = 0;
int TestOneByteResourceWithDisposeControl::dispose_calls = 0;
TEST(ExternalStringWithDisposeHandling) {
const char* c_source = "1 + 2 * 3";
// Use a stack allocated external string resource allocated object.
TestOneByteResourceWithDisposeControl::dispose_count = 0;
TestOneByteResourceWithDisposeControl::dispose_calls = 0;
TestOneByteResourceWithDisposeControl res_stack(i::StrDup(c_source), false);
{
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
Local<String> source = String::NewExternal(env->GetIsolate(), &res_stack);
Local<Script> script = v8_compile(source);
Local<Value> value = script->Run();
CHECK(value->IsNumber());
CHECK_EQ(7, value->Int32Value());
CcTest::heap()->CollectAllAvailableGarbage();
CHECK_EQ(0, TestOneByteResourceWithDisposeControl::dispose_count);
}
CcTest::i_isolate()->compilation_cache()->Clear();
CcTest::heap()->CollectAllAvailableGarbage();
CHECK_EQ(1, TestOneByteResourceWithDisposeControl::dispose_calls);
CHECK_EQ(0, TestOneByteResourceWithDisposeControl::dispose_count);
// Use a heap allocated external string resource allocated object.
TestOneByteResourceWithDisposeControl::dispose_count = 0;
TestOneByteResourceWithDisposeControl::dispose_calls = 0;
TestOneByteResource* res_heap =
new TestOneByteResourceWithDisposeControl(i::StrDup(c_source), true);
{
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
Local<String> source = String::NewExternal(env->GetIsolate(), res_heap);
Local<Script> script = v8_compile(source);
Local<Value> value = script->Run();
CHECK(value->IsNumber());
CHECK_EQ(7, value->Int32Value());
CcTest::heap()->CollectAllAvailableGarbage();
CHECK_EQ(0, TestOneByteResourceWithDisposeControl::dispose_count);
}
CcTest::i_isolate()->compilation_cache()->Clear();
CcTest::heap()->CollectAllAvailableGarbage();
CHECK_EQ(1, TestOneByteResourceWithDisposeControl::dispose_calls);
CHECK_EQ(1, TestOneByteResourceWithDisposeControl::dispose_count);
}
THREADED_TEST(StringConcat) {
{
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
const char* one_byte_string_1 = "function a_times_t";
const char* two_byte_string_1 = "wo_plus_b(a, b) {return ";
const char* one_byte_extern_1 = "a * 2 + b;} a_times_two_plus_b(4, 8) + ";
const char* two_byte_extern_1 = "a_times_two_plus_b(4, 8) + ";
const char* one_byte_string_2 = "a_times_two_plus_b(4, 8) + ";
const char* two_byte_string_2 = "a_times_two_plus_b(4, 8) + ";
const char* two_byte_extern_2 = "a_times_two_plus_b(1, 2);";
Local<String> left = v8_str(one_byte_string_1);
uint16_t* two_byte_source = AsciiToTwoByteString(two_byte_string_1);
Local<String> right =
String::NewFromTwoByte(env->GetIsolate(), two_byte_source);
i::DeleteArray(two_byte_source);
Local<String> source = String::Concat(left, right);
right = String::NewExternal(
env->GetIsolate(),
new TestOneByteResource(i::StrDup(one_byte_extern_1)));
source = String::Concat(source, right);
right = String::NewExternal(
env->GetIsolate(),
new TestResource(AsciiToTwoByteString(two_byte_extern_1)));
source = String::Concat(source, right);
right = v8_str(one_byte_string_2);
source = String::Concat(source, right);
two_byte_source = AsciiToTwoByteString(two_byte_string_2);
right = String::NewFromTwoByte(env->GetIsolate(), two_byte_source);
i::DeleteArray(two_byte_source);
source = String::Concat(source, right);
right = String::NewExternal(
env->GetIsolate(),
new TestResource(AsciiToTwoByteString(two_byte_extern_2)));
source = String::Concat(source, right);
Local<Script> script = v8_compile(source);
Local<Value> value = script->Run();
CHECK(value->IsNumber());
CHECK_EQ(68, value->Int32Value());
}
CcTest::i_isolate()->compilation_cache()->Clear();
CcTest::heap()->CollectAllGarbage();
CcTest::heap()->CollectAllGarbage();
}
THREADED_TEST(GlobalProperties) {
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
v8::Handle<v8::Object> global = env->Global();
global->Set(v8_str("pi"), v8_num(3.1415926));
Local<Value> pi = global->Get(v8_str("pi"));
CHECK_EQ(3.1415926, pi->NumberValue());
}
static void handle_callback_impl(const v8::FunctionCallbackInfo<Value>& info,
i::Address callback) {
ApiTestFuzzer::Fuzz();
CheckReturnValue(info, callback);
info.GetReturnValue().Set(v8_str("bad value"));
info.GetReturnValue().Set(v8_num(102));
}
static void handle_callback(const v8::FunctionCallbackInfo<Value>& info) {
return handle_callback_impl(info, FUNCTION_ADDR(handle_callback));
}
static void handle_callback_2(const v8::FunctionCallbackInfo<Value>& info) {
return handle_callback_impl(info, FUNCTION_ADDR(handle_callback_2));
}
static void construct_callback(
const v8::FunctionCallbackInfo<Value>& info) {
ApiTestFuzzer::Fuzz();
CheckReturnValue(info, FUNCTION_ADDR(construct_callback));
info.This()->Set(v8_str("x"), v8_num(1));
info.This()->Set(v8_str("y"), v8_num(2));
info.GetReturnValue().Set(v8_str("bad value"));
info.GetReturnValue().Set(info.This());
}
static void Return239Callback(
Local<String> name, const v8::PropertyCallbackInfo<Value>& info) {
ApiTestFuzzer::Fuzz();
CheckReturnValue(info, FUNCTION_ADDR(Return239Callback));
info.GetReturnValue().Set(v8_str("bad value"));
info.GetReturnValue().Set(v8_num(239));
}
template<typename Handler>
static void TestFunctionTemplateInitializer(Handler handler,
Handler handler_2) {
// Test constructor calls.
{
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
Local<v8::FunctionTemplate> fun_templ =
v8::FunctionTemplate::New(isolate, handler);
Local<Function> fun = fun_templ->GetFunction();
env->Global()->Set(v8_str("obj"), fun);
Local<Script> script = v8_compile("obj()");
for (int i = 0; i < 30; i++) {
CHECK_EQ(102, script->Run()->Int32Value());
}
}
// Use SetCallHandler to initialize a function template, should work like
// the previous one.
{
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(isolate);
fun_templ->SetCallHandler(handler_2);
Local<Function> fun = fun_templ->GetFunction();
env->Global()->Set(v8_str("obj"), fun);
Local<Script> script = v8_compile("obj()");
for (int i = 0; i < 30; i++) {
CHECK_EQ(102, script->Run()->Int32Value());
}
}
}
template<typename Constructor, typename Accessor>
static void TestFunctionTemplateAccessor(Constructor constructor,
Accessor accessor) {
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
Local<v8::FunctionTemplate> fun_templ =
v8::FunctionTemplate::New(env->GetIsolate(), constructor);
fun_templ->SetClassName(v8_str("funky"));
fun_templ->InstanceTemplate()->SetAccessor(v8_str("m"), accessor);
Local<Function> fun = fun_templ->GetFunction();
env->Global()->Set(v8_str("obj"), fun);
Local<Value> result = v8_compile("(new obj()).toString()")->Run();
CHECK(v8_str("[object funky]")->Equals(result));
CompileRun("var obj_instance = new obj();");
Local<Script> script;
script = v8_compile("obj_instance.x");
for (int i = 0; i < 30; i++) {
CHECK_EQ(1, script->Run()->Int32Value());
}
script = v8_compile("obj_instance.m");
for (int i = 0; i < 30; i++) {
CHECK_EQ(239, script->Run()->Int32Value());
}
}
THREADED_PROFILED_TEST(FunctionTemplate) {
TestFunctionTemplateInitializer(handle_callback, handle_callback_2);
TestFunctionTemplateAccessor(construct_callback, Return239Callback);
}
static void SimpleCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
ApiTestFuzzer::Fuzz();
CheckReturnValue(info, FUNCTION_ADDR(SimpleCallback));
info.GetReturnValue().Set(v8_num(51423 + info.Length()));
}
template<typename Callback>
static void TestSimpleCallback(Callback callback) {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::ObjectTemplate> object_template =
v8::ObjectTemplate::New(isolate);
object_template->Set(isolate, "callback",
v8::FunctionTemplate::New(isolate, callback));
v8::Local<v8::Object> object = object_template->NewInstance();
(*env)->Global()->Set(v8_str("callback_object"), object);
v8::Handle<v8::Script> script;
script = v8_compile("callback_object.callback(17)");
for (int i = 0; i < 30; i++) {
CHECK_EQ(51424, script->Run()->Int32Value());
}
script = v8_compile("callback_object.callback(17, 24)");
for (int i = 0; i < 30; i++) {
CHECK_EQ(51425, script->Run()->Int32Value());
}
}
THREADED_PROFILED_TEST(SimpleCallback) {
TestSimpleCallback(SimpleCallback);
}
template<typename T>
void FastReturnValueCallback(const v8::FunctionCallbackInfo<v8::Value>& info);
// constant return values
static int32_t fast_return_value_int32 = 471;
static uint32_t fast_return_value_uint32 = 571;
static const double kFastReturnValueDouble = 2.7;
// variable return values
static bool fast_return_value_bool = false;
enum ReturnValueOddball {
kNullReturnValue,
kUndefinedReturnValue,
kEmptyStringReturnValue
};
static ReturnValueOddball fast_return_value_void;
static bool fast_return_value_object_is_empty = false;
// Helper function to avoid compiler error: insufficient contextual information
// to determine type when applying FUNCTION_ADDR to a template function.
static i::Address address_of(v8::FunctionCallback callback) {
return FUNCTION_ADDR(callback);
}
template<>
void FastReturnValueCallback<int32_t>(
const v8::FunctionCallbackInfo<v8::Value>& info) {
CheckReturnValue(info, address_of(FastReturnValueCallback<int32_t>));
info.GetReturnValue().Set(fast_return_value_int32);
}
template<>
void FastReturnValueCallback<uint32_t>(
const v8::FunctionCallbackInfo<v8::Value>& info) {
CheckReturnValue(info, address_of(FastReturnValueCallback<uint32_t>));
info.GetReturnValue().Set(fast_return_value_uint32);
}
template<>
void FastReturnValueCallback<double>(
const v8::FunctionCallbackInfo<v8::Value>& info) {
CheckReturnValue(info, address_of(FastReturnValueCallback<double>));
info.GetReturnValue().Set(kFastReturnValueDouble);
}
template<>
void FastReturnValueCallback<bool>(
const v8::FunctionCallbackInfo<v8::Value>& info) {
CheckReturnValue(info, address_of(FastReturnValueCallback<bool>));
info.GetReturnValue().Set(fast_return_value_bool);
}
template<>
void FastReturnValueCallback<void>(
const v8::FunctionCallbackInfo<v8::Value>& info) {
CheckReturnValue(info, address_of(FastReturnValueCallback<void>));
switch (fast_return_value_void) {
case kNullReturnValue:
info.GetReturnValue().SetNull();
break;
case kUndefinedReturnValue:
info.GetReturnValue().SetUndefined();
break;
case kEmptyStringReturnValue:
info.GetReturnValue().SetEmptyString();
break;
}
}
template<>
void FastReturnValueCallback<Object>(
const v8::FunctionCallbackInfo<v8::Value>& info) {
v8::Handle<v8::Object> object;
if (!fast_return_value_object_is_empty) {
object = Object::New(info.GetIsolate());
}
info.GetReturnValue().Set(object);
}
template<typename T>
Handle<Value> TestFastReturnValues() {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::EscapableHandleScope scope(isolate);
v8::Handle<v8::ObjectTemplate> object_template =
v8::ObjectTemplate::New(isolate);
v8::FunctionCallback callback = &FastReturnValueCallback<T>;
object_template->Set(isolate, "callback",
v8::FunctionTemplate::New(isolate, callback));
v8::Local<v8::Object> object = object_template->NewInstance();
(*env)->Global()->Set(v8_str("callback_object"), object);
return scope.Escape(CompileRun("callback_object.callback()"));
}
THREADED_PROFILED_TEST(FastReturnValues) {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::Value> value;
// check int32_t and uint32_t
int32_t int_values[] = {
0, 234, -723,
i::Smi::kMinValue, i::Smi::kMaxValue
};
for (size_t i = 0; i < arraysize(int_values); i++) {
for (int modifier = -1; modifier <= 1; modifier++) {
int int_value = int_values[i] + modifier;
// check int32_t
fast_return_value_int32 = int_value;
value = TestFastReturnValues<int32_t>();
CHECK(value->IsInt32());
CHECK(fast_return_value_int32 == value->Int32Value());
// check uint32_t
fast_return_value_uint32 = static_cast<uint32_t>(int_value);
value = TestFastReturnValues<uint32_t>();
CHECK(value->IsUint32());
CHECK(fast_return_value_uint32 == value->Uint32Value());
}
}
// check double
value = TestFastReturnValues<double>();
CHECK(value->IsNumber());
CHECK_EQ(kFastReturnValueDouble, value->ToNumber(isolate)->Value());
// check bool values
for (int i = 0; i < 2; i++) {
fast_return_value_bool = i == 0;
value = TestFastReturnValues<bool>();
CHECK(value->IsBoolean());
CHECK_EQ(fast_return_value_bool, value->ToBoolean(isolate)->Value());
}
// check oddballs
ReturnValueOddball oddballs[] = {
kNullReturnValue,
kUndefinedReturnValue,
kEmptyStringReturnValue
};
for (size_t i = 0; i < arraysize(oddballs); i++) {
fast_return_value_void = oddballs[i];
value = TestFastReturnValues<void>();
switch (fast_return_value_void) {
case kNullReturnValue:
CHECK(value->IsNull());
break;
case kUndefinedReturnValue:
CHECK(value->IsUndefined());
break;
case kEmptyStringReturnValue:
CHECK(value->IsString());
CHECK_EQ(0, v8::String::Cast(*value)->Length());
break;
}
}
// check handles
fast_return_value_object_is_empty = false;
value = TestFastReturnValues<Object>();
CHECK(value->IsObject());
fast_return_value_object_is_empty = true;
value = TestFastReturnValues<Object>();
CHECK(value->IsUndefined());
}
THREADED_TEST(FunctionTemplateSetLength) {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
{
Local<v8::FunctionTemplate> fun_templ =
v8::FunctionTemplate::New(isolate,
handle_callback,
Handle<v8::Value>(),
Handle<v8::Signature>(),
23);
Local<Function> fun = fun_templ->GetFunction();
env->Global()->Set(v8_str("obj"), fun);
Local<Script> script = v8_compile("obj.length");
CHECK_EQ(23, script->Run()->Int32Value());
}
{
Local<v8::FunctionTemplate> fun_templ =
v8::FunctionTemplate::New(isolate, handle_callback);
fun_templ->SetLength(22);
Local<Function> fun = fun_templ->GetFunction();
env->Global()->Set(v8_str("obj"), fun);
Local<Script> script = v8_compile("obj.length");
CHECK_EQ(22, script->Run()->Int32Value());
}
{
// Without setting length it defaults to 0.
Local<v8::FunctionTemplate> fun_templ =
v8::FunctionTemplate::New(isolate, handle_callback);
Local<Function> fun = fun_templ->GetFunction();
env->Global()->Set(v8_str("obj"), fun);
Local<Script> script = v8_compile("obj.length");
CHECK_EQ(0, script->Run()->Int32Value());
}
}
static void* expected_ptr;
static void callback(const v8::FunctionCallbackInfo<v8::Value>& args) {
void* ptr = v8::External::Cast(*args.Data())->Value();
CHECK_EQ(expected_ptr, ptr);
args.GetReturnValue().Set(true);
}
static void TestExternalPointerWrapping() {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::Value> data =
v8::External::New(isolate, expected_ptr);
v8::Handle<v8::Object> obj = v8::Object::New(isolate);
obj->Set(v8_str("func"),
v8::FunctionTemplate::New(isolate, callback, data)->GetFunction());
env->Global()->Set(v8_str("obj"), obj);
CHECK(CompileRun(
"function foo() {\n"
" for (var i = 0; i < 13; i++) obj.func();\n"
"}\n"
"foo(), true")->BooleanValue());
}
THREADED_TEST(ExternalWrap) {
// Check heap allocated object.
int* ptr = new int;
expected_ptr = ptr;
TestExternalPointerWrapping();
delete ptr;
// Check stack allocated object.
int foo;
expected_ptr = &foo;
TestExternalPointerWrapping();
// Check not aligned addresses.
const int n = 100;
char* s = new char[n];
for (int i = 0; i < n; i++) {
expected_ptr = s + i;
TestExternalPointerWrapping();
}
delete[] s;
// Check several invalid addresses.
expected_ptr = reinterpret_cast<void*>(1);
TestExternalPointerWrapping();
expected_ptr = reinterpret_cast<void*>(0xdeadbeef);
TestExternalPointerWrapping();
expected_ptr = reinterpret_cast<void*>(0xdeadbeef + 1);
TestExternalPointerWrapping();
#if defined(V8_HOST_ARCH_X64)
// Check a value with a leading 1 bit in x64 Smi encoding.
expected_ptr = reinterpret_cast<void*>(0x400000000);
TestExternalPointerWrapping();
expected_ptr = reinterpret_cast<void*>(0xdeadbeefdeadbeef);
TestExternalPointerWrapping();
expected_ptr = reinterpret_cast<void*>(0xdeadbeefdeadbeef + 1);
TestExternalPointerWrapping();
#endif
}
THREADED_TEST(FindInstanceInPrototypeChain) {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
Local<v8::FunctionTemplate> base = v8::FunctionTemplate::New(isolate);
Local<v8::FunctionTemplate> derived = v8::FunctionTemplate::New(isolate);
Local<v8::FunctionTemplate> other = v8::FunctionTemplate::New(isolate);
derived->Inherit(base);
Local<v8::Function> base_function = base->GetFunction();
Local<v8::Function> derived_function = derived->GetFunction();
Local<v8::Function> other_function = other->GetFunction();
Local<v8::Object> base_instance = base_function->NewInstance();
Local<v8::Object> derived_instance = derived_function->NewInstance();
Local<v8::Object> derived_instance2 = derived_function->NewInstance();
Local<v8::Object> other_instance = other_function->NewInstance();
derived_instance2->Set(v8_str("__proto__"), derived_instance);
other_instance->Set(v8_str("__proto__"), derived_instance2);
// base_instance is only an instance of base.
CHECK(
base_instance->Equals(base_instance->FindInstanceInPrototypeChain(base)));
CHECK(base_instance->FindInstanceInPrototypeChain(derived).IsEmpty());
CHECK(base_instance->FindInstanceInPrototypeChain(other).IsEmpty());
// derived_instance is an instance of base and derived.
CHECK(derived_instance->Equals(
derived_instance->FindInstanceInPrototypeChain(base)));
CHECK(derived_instance->Equals(
derived_instance->FindInstanceInPrototypeChain(derived)));
CHECK(derived_instance->FindInstanceInPrototypeChain(other).IsEmpty());
// other_instance is an instance of other and its immediate
// prototype derived_instance2 is an instance of base and derived.
// Note, derived_instance is an instance of base and derived too,
// but it comes after derived_instance2 in the prototype chain of
// other_instance.
CHECK(derived_instance2->Equals(
other_instance->FindInstanceInPrototypeChain(base)));
CHECK(derived_instance2->Equals(
other_instance->FindInstanceInPrototypeChain(derived)));
CHECK(other_instance->Equals(
other_instance->FindInstanceInPrototypeChain(other)));
}
THREADED_TEST(TinyInteger) {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
int32_t value = 239;
Local<v8::Integer> value_obj = v8::Integer::New(isolate, value);
CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
value_obj = v8::Integer::New(isolate, value);
CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
}
THREADED_TEST(BigSmiInteger) {
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
v8::Isolate* isolate = CcTest::isolate();
int32_t value = i::Smi::kMaxValue;
// We cannot add one to a Smi::kMaxValue without wrapping.
if (i::SmiValuesAre31Bits()) {
CHECK(i::Smi::IsValid(value));
CHECK(!i::Smi::IsValid(value + 1));
Local<v8::Integer> value_obj = v8::Integer::New(isolate, value);
CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
value_obj = v8::Integer::New(isolate, value);
CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
}
}
THREADED_TEST(BigInteger) {
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
v8::Isolate* isolate = CcTest::isolate();
// We cannot add one to a Smi::kMaxValue without wrapping.
if (i::SmiValuesAre31Bits()) {
// The casts allow this to compile, even if Smi::kMaxValue is 2^31-1.
// The code will not be run in that case, due to the "if" guard.
int32_t value =
static_cast<int32_t>(static_cast<uint32_t>(i::Smi::kMaxValue) + 1);
CHECK(value > i::Smi::kMaxValue);
CHECK(!i::Smi::IsValid(value));
Local<v8::Integer> value_obj = v8::Integer::New(isolate, value);
CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
value_obj = v8::Integer::New(isolate, value);
CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
}
}
THREADED_TEST(TinyUnsignedInteger) {
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
v8::Isolate* isolate = CcTest::isolate();
uint32_t value = 239;
Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(isolate, value);
CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
value_obj = v8::Integer::NewFromUnsigned(isolate, value);
CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
}
THREADED_TEST(BigUnsignedSmiInteger) {
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
v8::Isolate* isolate = CcTest::isolate();
uint32_t value = static_cast<uint32_t>(i::Smi::kMaxValue);
CHECK(i::Smi::IsValid(value));
CHECK(!i::Smi::IsValid(value + 1));
Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(isolate, value);
CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
value_obj = v8::Integer::NewFromUnsigned(isolate, value);
CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
}
THREADED_TEST(BigUnsignedInteger) {
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
v8::Isolate* isolate = CcTest::isolate();
uint32_t value = static_cast<uint32_t>(i::Smi::kMaxValue) + 1;
CHECK(value > static_cast<uint32_t>(i::Smi::kMaxValue));
CHECK(!i::Smi::IsValid(value));
Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(isolate, value);
CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
value_obj = v8::Integer::NewFromUnsigned(isolate, value);
CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
}
THREADED_TEST(OutOfSignedRangeUnsignedInteger) {
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
v8::Isolate* isolate = CcTest::isolate();
uint32_t INT32_MAX_AS_UINT = (1U << 31) - 1;
uint32_t value = INT32_MAX_AS_UINT + 1;
CHECK(value > INT32_MAX_AS_UINT); // No overflow.
Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(isolate, value);
CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
value_obj = v8::Integer::NewFromUnsigned(isolate, value);
CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
}
THREADED_TEST(IsNativeError) {
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
v8::Handle<Value> syntax_error = CompileRun(
"var out = 0; try { eval(\"#\"); } catch(x) { out = x; } out; ");
CHECK(syntax_error->IsNativeError());
v8::Handle<Value> not_error = CompileRun("{a:42}");
CHECK(!not_error->IsNativeError());
v8::Handle<Value> not_object = CompileRun("42");
CHECK(!not_object->IsNativeError());
}
THREADED_TEST(IsGeneratorFunctionOrObject) {
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
CompileRun("function *gen() { yield 1; }\nfunction func() {}");
v8::Handle<Value> gen = CompileRun("gen");
v8::Handle<Value> genObj = CompileRun("gen()");
v8::Handle<Value> object = CompileRun("{a:42}");
v8::Handle<Value> func = CompileRun("func");
CHECK(gen->IsGeneratorFunction());
CHECK(gen->IsFunction());
CHECK(!gen->IsGeneratorObject());
CHECK(!genObj->IsGeneratorFunction());
CHECK(!genObj->IsFunction());
CHECK(genObj->IsGeneratorObject());
CHECK(!object->IsGeneratorFunction());
CHECK(!object->IsFunction());
CHECK(!object->IsGeneratorObject());
CHECK(!func->IsGeneratorFunction());
CHECK(func->IsFunction());
CHECK(!func->IsGeneratorObject());
}
THREADED_TEST(ArgumentsObject) {
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
v8::Handle<Value> arguments_object =
CompileRun("var out = 0; (function(){ out = arguments; })(1,2,3); out;");
CHECK(arguments_object->IsArgumentsObject());
v8::Handle<Value> array = CompileRun("[1,2,3]");
CHECK(!array->IsArgumentsObject());
v8::Handle<Value> object = CompileRun("{a:42}");
CHECK(!object->IsArgumentsObject());
}
THREADED_TEST(IsMapOrSet) {
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
v8::Handle<Value> map = CompileRun("new Map()");
v8::Handle<Value> set = CompileRun("new Set()");
v8::Handle<Value> weak_map = CompileRun("new WeakMap()");
v8::Handle<Value> weak_set = CompileRun("new WeakSet()");
CHECK(map->IsMap());
CHECK(set->IsSet());
CHECK(weak_map->IsWeakMap());
CHECK(weak_set->IsWeakSet());
CHECK(!map->IsSet());
CHECK(!map->IsWeakMap());
CHECK(!map->IsWeakSet());
CHECK(!set->IsMap());
CHECK(!set->IsWeakMap());
CHECK(!set->IsWeakSet());
CHECK(!weak_map->IsMap());
CHECK(!weak_map->IsSet());
CHECK(!weak_map->IsWeakSet());
CHECK(!weak_set->IsMap());
CHECK(!weak_set->IsSet());
CHECK(!weak_set->IsWeakMap());
v8::Handle<Value> object = CompileRun("{a:42}");
CHECK(!object->IsMap());
CHECK(!object->IsSet());
CHECK(!object->IsWeakMap());
CHECK(!object->IsWeakSet());
}
THREADED_TEST(StringObject) {
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
v8::Handle<Value> boxed_string = CompileRun("new String(\"test\")");
CHECK(boxed_string->IsStringObject());
v8::Handle<Value> unboxed_string = CompileRun("\"test\"");
CHECK(!unboxed_string->IsStringObject());
v8::Handle<Value> boxed_not_string = CompileRun("new Number(42)");
CHECK(!boxed_not_string->IsStringObject());
v8::Handle<Value> not_object = CompileRun("0");
CHECK(!not_object->IsStringObject());
v8::Handle<v8::StringObject> as_boxed = boxed_string.As<v8::StringObject>();
CHECK(!as_boxed.IsEmpty());
Local<v8::String> the_string = as_boxed->ValueOf();
CHECK(!the_string.IsEmpty());
ExpectObject("\"test\"", the_string);
v8::Handle<v8::Value> new_boxed_string = v8::StringObject::New(the_string);
CHECK(new_boxed_string->IsStringObject());
as_boxed = new_boxed_string.As<v8::StringObject>();
the_string = as_boxed->ValueOf();
CHECK(!the_string.IsEmpty());
ExpectObject("\"test\"", the_string);
}
TEST(StringObjectDelete) {
LocalContext context;
v8::HandleScope scope(context->GetIsolate());
v8::Handle<Value> boxed_string = CompileRun("new String(\"test\")");
CHECK(boxed_string->IsStringObject());
v8::Handle<v8::Object> str_obj = boxed_string.As<v8::Object>();
CHECK(!str_obj->Delete(2));
CHECK(!str_obj->Delete(v8_num(2)));
}
THREADED_TEST(NumberObject) {
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
v8::Handle<Value> boxed_number = CompileRun("new Number(42)");
CHECK(boxed_number->IsNumberObject());
v8::Handle<Value> unboxed_number = CompileRun("42");
CHECK(!unboxed_number->IsNumberObject());
v8::Handle<Value> boxed_not_number = CompileRun("new Boolean(false)");
CHECK(!boxed_not_number->IsNumberObject());
v8::Handle<v8::NumberObject> as_boxed = boxed_number.As<v8::NumberObject>();
CHECK(!as_boxed.IsEmpty());
double the_number = as_boxed->ValueOf();
CHECK_EQ(42.0, the_number);
v8::Handle<v8::Value> new_boxed_number =
v8::NumberObject::New(env->GetIsolate(), 43);
CHECK(new_boxed_number->IsNumberObject());
as_boxed = new_boxed_number.As<v8::NumberObject>();
the_number = as_boxed->ValueOf();
CHECK_EQ(43.0, the_number);
}
THREADED_TEST(BooleanObject) {
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
v8::Handle<Value> boxed_boolean = CompileRun("new Boolean(true)");
CHECK(boxed_boolean->IsBooleanObject());
v8::Handle<Value> unboxed_boolean = CompileRun("true");
CHECK(!unboxed_boolean->IsBooleanObject());
v8::Handle<Value> boxed_not_boolean = CompileRun("new Number(42)");
CHECK(!boxed_not_boolean->IsBooleanObject());
v8::Handle<v8::BooleanObject> as_boxed =
boxed_boolean.As<v8::BooleanObject>();
CHECK(!as_boxed.IsEmpty());
bool the_boolean = as_boxed->ValueOf();
CHECK_EQ(true, the_boolean);
v8::Handle<v8::Value> boxed_true = v8::BooleanObject::New(true);
v8::Handle<v8::Value> boxed_false = v8::BooleanObject::New(false);
CHECK(boxed_true->IsBooleanObject());
CHECK(boxed_false->IsBooleanObject());
as_boxed = boxed_true.As<v8::BooleanObject>();
CHECK_EQ(true, as_boxed->ValueOf());
as_boxed = boxed_false.As<v8::BooleanObject>();
CHECK_EQ(false, as_boxed->ValueOf());
}
THREADED_TEST(PrimitiveAndWrappedBooleans) {
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
Local<Value> primitive_false = Boolean::New(env->GetIsolate(), false);
CHECK(primitive_false->IsBoolean());
CHECK(!primitive_false->IsBooleanObject());
CHECK(!primitive_false->BooleanValue());
CHECK(!primitive_false->IsTrue());
CHECK(primitive_false->IsFalse());
Local<Value> false_value = BooleanObject::New(false);
CHECK(!false_value->IsBoolean());
CHECK(false_value->IsBooleanObject());
CHECK(false_value->BooleanValue());
CHECK(!false_value->IsTrue());
CHECK(!false_value->IsFalse());
Local<BooleanObject> false_boolean_object = false_value.As<BooleanObject>();
CHECK(!false_boolean_object->IsBoolean());
CHECK(false_boolean_object->IsBooleanObject());
// TODO(svenpanne) Uncomment when BooleanObject::BooleanValue() is deleted.
// CHECK(false_boolean_object->BooleanValue());
CHECK(!false_boolean_object->ValueOf());
CHECK(!false_boolean_object->IsTrue());
CHECK(!false_boolean_object->IsFalse());
Local<Value> primitive_true = Boolean::New(env->GetIsolate(), true);
CHECK(primitive_true->IsBoolean());
CHECK(!primitive_true->IsBooleanObject());
CHECK(primitive_true->BooleanValue());
CHECK(primitive_true->IsTrue());
CHECK(!primitive_true->IsFalse());
Local<Value> true_value = BooleanObject::New(true);
CHECK(!true_value->IsBoolean());
CHECK(true_value->IsBooleanObject());
CHECK(true_value->BooleanValue());
CHECK(!true_value->IsTrue());
CHECK(!true_value->IsFalse());
Local<BooleanObject> true_boolean_object = true_value.As<BooleanObject>();
CHECK(!true_boolean_object->IsBoolean());
CHECK(true_boolean_object->IsBooleanObject());
// TODO(svenpanne) Uncomment when BooleanObject::BooleanValue() is deleted.
// CHECK(true_boolean_object->BooleanValue());
CHECK(true_boolean_object->ValueOf());
CHECK(!true_boolean_object->IsTrue());
CHECK(!true_boolean_object->IsFalse());
}
THREADED_TEST(Number) {
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
double PI = 3.1415926;
Local<v8::Number> pi_obj = v8::Number::New(env->GetIsolate(), PI);
CHECK_EQ(PI, pi_obj->NumberValue());
}
THREADED_TEST(ToNumber) {
LocalContext env;
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
Local<String> str = v8_str("3.1415926");
CHECK_EQ(3.1415926, str->NumberValue());
v8::Handle<v8::Boolean> t = v8::True(isolate);
CHECK_EQ(1.0, t->NumberValue());
v8::Handle<v8::Boolean> f = v8::False(isolate);
CHECK_EQ(0.0, f->NumberValue());
}
THREADED_TEST(Date) {
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
double PI = 3.1415926;
Local<Value> date = v8::Date::New(env->GetIsolate(), PI);
CHECK_EQ(3.0, date->NumberValue());
date.As<v8::Date>()->Set(v8_str("property"),
v8::Integer::New(env->GetIsolate(), 42));
CHECK_EQ(42, date.As<v8::Date>()->Get(v8_str("property"))->Int32Value());
}
THREADED_TEST(Boolean) {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::Boolean> t = v8::True(isolate);
CHECK(t->Value());
v8::Handle<v8::Boolean> f = v8::False(isolate);
CHECK(!f->Value());
v8::Handle<v8::Primitive> u = v8::Undefined(isolate);
CHECK(!u->BooleanValue());
v8::Handle<v8::Primitive> n = v8::Null(isolate);
CHECK(!n->BooleanValue());
v8::Handle<String> str1 = v8_str("");
CHECK(!str1->BooleanValue());
v8::Handle<String> str2 = v8_str("x");
CHECK(str2->BooleanValue());
CHECK(!v8::Number::New(isolate, 0)->BooleanValue());
CHECK(v8::Number::New(isolate, -1)->BooleanValue());
CHECK(v8::Number::New(isolate, 1)->BooleanValue());
CHECK(v8::Number::New(isolate, 42)->BooleanValue());
CHECK(!v8_compile("NaN")->Run()->BooleanValue());
}
static void DummyCallHandler(const v8::FunctionCallbackInfo<v8::Value>& args) {
ApiTestFuzzer::Fuzz();
args.GetReturnValue().Set(v8_num(13.4));
}
static void GetM(Local<String> name,
const v8::PropertyCallbackInfo<v8::Value>& info) {
ApiTestFuzzer::Fuzz();
info.GetReturnValue().Set(v8_num(876));
}
THREADED_TEST(GlobalPrototype) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::FunctionTemplate> func_templ =
v8::FunctionTemplate::New(isolate);
func_templ->PrototypeTemplate()->Set(
isolate, "dummy", v8::FunctionTemplate::New(isolate, DummyCallHandler));
v8::Handle<ObjectTemplate> templ = func_templ->InstanceTemplate();
templ->Set(isolate, "x", v8_num(200));
templ->SetAccessor(v8_str("m"), GetM);
LocalContext env(0, templ);
v8::Handle<Script> script(v8_compile("dummy()"));
v8::Handle<Value> result(script->Run());
CHECK_EQ(13.4, result->NumberValue());
CHECK_EQ(200, v8_compile("x")->Run()->Int32Value());
CHECK_EQ(876, v8_compile("m")->Run()->Int32Value());
}
THREADED_TEST(ObjectTemplate) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(isolate);
v8::Local<v8::String> class_name =
v8::String::NewFromUtf8(isolate, "the_class_name");
fun->SetClassName(class_name);
Local<ObjectTemplate> templ1 = ObjectTemplate::New(isolate, fun);
templ1->Set(isolate, "x", v8_num(10));
templ1->Set(isolate, "y", v8_num(13));
LocalContext env;
Local<v8::Object> instance1 = templ1->NewInstance();
CHECK(class_name->StrictEquals(instance1->GetConstructorName()));
env->Global()->Set(v8_str("p"), instance1);
CHECK(v8_compile("(p.x == 10)")->Run()->BooleanValue());
CHECK(v8_compile("(p.y == 13)")->Run()->BooleanValue());
Local<v8::FunctionTemplate> fun2 = v8::FunctionTemplate::New(isolate);
fun2->PrototypeTemplate()->Set(isolate, "nirk", v8_num(123));
Local<ObjectTemplate> templ2 = fun2->InstanceTemplate();
templ2->Set(isolate, "a", v8_num(12));
templ2->Set(isolate, "b", templ1);
Local<v8::Object> instance2 = templ2->NewInstance();
env->Global()->Set(v8_str("q"), instance2);
CHECK(v8_compile("(q.nirk == 123)")->Run()->BooleanValue());
CHECK(v8_compile("(q.a == 12)")->Run()->BooleanValue());
CHECK(v8_compile("(q.b.x == 10)")->Run()->BooleanValue());
CHECK(v8_compile("(q.b.y == 13)")->Run()->BooleanValue());
}
static void GetFlabby(const v8::FunctionCallbackInfo<v8::Value>& args) {
ApiTestFuzzer::Fuzz();
args.GetReturnValue().Set(v8_num(17.2));
}
static void GetKnurd(Local<String> property,
const v8::PropertyCallbackInfo<v8::Value>& info) {
ApiTestFuzzer::Fuzz();
info.GetReturnValue().Set(v8_num(15.2));
}
THREADED_TEST(DescriptorInheritance) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::FunctionTemplate> super = v8::FunctionTemplate::New(isolate);
super->PrototypeTemplate()->Set(isolate, "flabby",
v8::FunctionTemplate::New(isolate,
GetFlabby));
super->PrototypeTemplate()->Set(isolate, "PI", v8_num(3.14));
super->InstanceTemplate()->SetAccessor(v8_str("knurd"), GetKnurd);
v8::Handle<v8::FunctionTemplate> base1 = v8::FunctionTemplate::New(isolate);
base1->Inherit(super);
base1->PrototypeTemplate()->Set(isolate, "v1", v8_num(20.1));
v8::Handle<v8::FunctionTemplate> base2 = v8::FunctionTemplate::New(isolate);
base2->Inherit(super);
base2->PrototypeTemplate()->Set(isolate, "v2", v8_num(10.1));
LocalContext env;
env->Global()->Set(v8_str("s"), super->GetFunction());
env->Global()->Set(v8_str("base1"), base1->GetFunction());
env->Global()->Set(v8_str("base2"), base2->GetFunction());
// Checks right __proto__ chain.
CHECK(CompileRun("base1.prototype.__proto__ == s.prototype")->BooleanValue());
CHECK(CompileRun("base2.prototype.__proto__ == s.prototype")->BooleanValue());
CHECK(v8_compile("s.prototype.PI == 3.14")->Run()->BooleanValue());
// Instance accessor should not be visible on function object or its prototype
CHECK(CompileRun("s.knurd == undefined")->BooleanValue());
CHECK(CompileRun("s.prototype.knurd == undefined")->BooleanValue());
CHECK(CompileRun("base1.prototype.knurd == undefined")->BooleanValue());
env->Global()->Set(v8_str("obj"),
base1->GetFunction()->NewInstance());
CHECK_EQ(17.2, v8_compile("obj.flabby()")->Run()->NumberValue());
CHECK(v8_compile("'flabby' in obj")->Run()->BooleanValue());
CHECK_EQ(15.2, v8_compile("obj.knurd")->Run()->NumberValue());
CHECK(v8_compile("'knurd' in obj")->Run()->BooleanValue());
CHECK_EQ(20.1, v8_compile("obj.v1")->Run()->NumberValue());
env->Global()->Set(v8_str("obj2"),
base2->GetFunction()->NewInstance());
CHECK_EQ(17.2, v8_compile("obj2.flabby()")->Run()->NumberValue());
CHECK(v8_compile("'flabby' in obj2")->Run()->BooleanValue());
CHECK_EQ(15.2, v8_compile("obj2.knurd")->Run()->NumberValue());
CHECK(v8_compile("'knurd' in obj2")->Run()->BooleanValue());
CHECK_EQ(10.1, v8_compile("obj2.v2")->Run()->NumberValue());
// base1 and base2 cannot cross reference to each's prototype
CHECK(v8_compile("obj.v2")->Run()->IsUndefined());
CHECK(v8_compile("obj2.v1")->Run()->IsUndefined());
}
// Helper functions for Interceptor/Accessor interaction tests
void SimpleAccessorGetter(Local<String> name,
const v8::PropertyCallbackInfo<v8::Value>& info) {
Handle<Object> self = Handle<Object>::Cast(info.This());
info.GetReturnValue().Set(
self->Get(String::Concat(v8_str("accessor_"), name)));
}
void SimpleAccessorSetter(Local<String> name, Local<Value> value,
const v8::PropertyCallbackInfo<void>& info) {
Handle<Object> self = Handle<Object>::Cast(info.This());
self->Set(String::Concat(v8_str("accessor_"), name), value);
}
void SymbolAccessorGetter(Local<Name> name,
const v8::PropertyCallbackInfo<v8::Value>& info) {
CHECK(name->IsSymbol());
Local<Symbol> sym = Local<Symbol>::Cast(name);
if (sym->Name()->IsUndefined())
return;
SimpleAccessorGetter(Local<String>::Cast(sym->Name()), info);
}
void SymbolAccessorSetter(Local<Name> name, Local<Value> value,
const v8::PropertyCallbackInfo<void>& info) {
CHECK(name->IsSymbol());
Local<Symbol> sym = Local<Symbol>::Cast(name);
if (sym->Name()->IsUndefined())
return;
SimpleAccessorSetter(Local<String>::Cast(sym->Name()), value, info);
}
void SymbolAccessorGetterReturnsDefault(
Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
CHECK(name->IsSymbol());
Local<Symbol> sym = Local<Symbol>::Cast(name);
if (sym->Name()->IsUndefined()) return;
info.GetReturnValue().Set(info.Data());
}
static void ThrowingSymbolAccessorGetter(
Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
info.GetReturnValue().Set(info.GetIsolate()->ThrowException(name));
}
THREADED_TEST(ExecutableAccessorIsPreservedOnAttributeChange) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
LocalContext env;
v8::Local<v8::Value> res = CompileRun("var a = []; a;");
i::Handle<i::JSObject> a(v8::Utils::OpenHandle(v8::Object::Cast(*res)));
CHECK(a->map()->instance_descriptors()->IsFixedArray());
CHECK_GT(i::FixedArray::cast(a->map()->instance_descriptors())->length(), 0);
CompileRun("Object.defineProperty(a, 'length', { writable: false });");
CHECK_EQ(i::FixedArray::cast(a->map()->instance_descriptors())->length(), 0);
// But we should still have an ExecutableAccessorInfo.
i::Handle<i::String> name(v8::Utils::OpenHandle(*v8_str("length")));
i::LookupIterator it(a, name, i::LookupIterator::OWN_SKIP_INTERCEPTOR);
CHECK_EQ(i::LookupIterator::ACCESSOR, it.state());
CHECK(it.GetAccessors()->IsExecutableAccessorInfo());
}
THREADED_TEST(UndefinedIsNotEnumerable) {
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
v8::Handle<Value> result = CompileRun("this.propertyIsEnumerable(undefined)");
CHECK(result->IsFalse());
}
v8::Handle<Script> call_recursively_script;
static const int kTargetRecursionDepth = 200; // near maximum
static void CallScriptRecursivelyCall(
const v8::FunctionCallbackInfo<v8::Value>& args) {
ApiTestFuzzer::Fuzz();
int depth = args.This()->Get(v8_str("depth"))->Int32Value();
if (depth == kTargetRecursionDepth) return;
args.This()->Set(v8_str("depth"),
v8::Integer::New(args.GetIsolate(), depth + 1));
args.GetReturnValue().Set(call_recursively_script->Run());
}
static void CallFunctionRecursivelyCall(
const v8::FunctionCallbackInfo<v8::Value>& args) {
ApiTestFuzzer::Fuzz();
int depth = args.This()->Get(v8_str("depth"))->Int32Value();
if (depth == kTargetRecursionDepth) {
printf("[depth = %d]\n", depth);
return;
}
args.This()->Set(v8_str("depth"),
v8::Integer::New(args.GetIsolate(), depth + 1));
v8::Handle<Value> function =
args.This()->Get(v8_str("callFunctionRecursively"));
args.GetReturnValue().Set(
function.As<Function>()->Call(args.This(), 0, NULL));
}
THREADED_TEST(DeepCrossLanguageRecursion) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::ObjectTemplate> global = ObjectTemplate::New(isolate);
global->Set(v8_str("callScriptRecursively"),
v8::FunctionTemplate::New(isolate, CallScriptRecursivelyCall));
global->Set(v8_str("callFunctionRecursively"),
v8::FunctionTemplate::New(isolate, CallFunctionRecursivelyCall));
LocalContext env(NULL, global);
env->Global()->Set(v8_str("depth"), v8::Integer::New(isolate, 0));
call_recursively_script = v8_compile("callScriptRecursively()");
call_recursively_script->Run();
call_recursively_script = v8::Handle<Script>();
env->Global()->Set(v8_str("depth"), v8::Integer::New(isolate, 0));
CompileRun("callFunctionRecursively()");
}
static void ThrowingPropertyHandlerGet(
Local<Name> key, const v8::PropertyCallbackInfo<v8::Value>& info) {
// Since this interceptor is used on "with" objects, the runtime will look up
// @@unscopables. Punt.
if (key->IsSymbol()) return;
ApiTestFuzzer::Fuzz();
info.GetReturnValue().Set(info.GetIsolate()->ThrowException(key));
}
static void ThrowingPropertyHandlerSet(
Local<Name> key, Local<Value>,
const v8::PropertyCallbackInfo<v8::Value>& info) {
info.GetIsolate()->ThrowException(key);
info.GetReturnValue().SetUndefined(); // not the same as empty handle
}
THREADED_TEST(CallbackExceptionRegression) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(isolate);
obj->SetHandler(v8::NamedPropertyHandlerConfiguration(
ThrowingPropertyHandlerGet, ThrowingPropertyHandlerSet));
LocalContext env;
env->Global()->Set(v8_str("obj"), obj->NewInstance());
v8::Handle<Value> otto =
CompileRun("try { with (obj) { otto; } } catch (e) { e; }");
CHECK(v8_str("otto")->Equals(otto));
v8::Handle<Value> netto =
CompileRun("try { with (obj) { netto = 4; } } catch (e) { e; }");
CHECK(v8_str("netto")->Equals(netto));
}
THREADED_TEST(FunctionPrototype) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
Local<v8::FunctionTemplate> Foo = v8::FunctionTemplate::New(isolate);
Foo->PrototypeTemplate()->Set(v8_str("plak"), v8_num(321));
LocalContext env;
env->Global()->Set(v8_str("Foo"), Foo->GetFunction());
Local<Script> script = v8_compile("Foo.prototype.plak");
CHECK_EQ(script->Run()->Int32Value(), 321);
}
THREADED_TEST(InternalFields) {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate);
Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate();
instance_templ->SetInternalFieldCount(1);
Local<v8::Object> obj = templ->GetFunction()->NewInstance();
CHECK_EQ(1, obj->InternalFieldCount());
CHECK(obj->GetInternalField(0)->IsUndefined());
obj->SetInternalField(0, v8_num(17));
CHECK_EQ(17, obj->GetInternalField(0)->Int32Value());
}
THREADED_TEST(GlobalObjectInternalFields) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
Local<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(isolate);
global_template->SetInternalFieldCount(1);
LocalContext env(NULL, global_template);
v8::Handle<v8::Object> global_proxy = env->Global();
v8::Handle<v8::Object> global = global_proxy->GetPrototype().As<v8::Object>();
CHECK_EQ(1, global->InternalFieldCount());
CHECK(global->GetInternalField(0)->IsUndefined());
global->SetInternalField(0, v8_num(17));
CHECK_EQ(17, global->GetInternalField(0)->Int32Value());
}
THREADED_TEST(GlobalObjectHasRealIndexedProperty) {
LocalContext env;
v8::HandleScope scope(CcTest::isolate());
v8::Local<v8::Object> global = env->Global();
global->Set(0, v8::String::NewFromUtf8(CcTest::isolate(), "value"));
CHECK(global->HasRealIndexedProperty(0));
}
static void CheckAlignedPointerInInternalField(Handle<v8::Object> obj,
void* value) {
CHECK_EQ(0, static_cast<int>(reinterpret_cast<uintptr_t>(value) & 0x1));
obj->SetAlignedPointerInInternalField(0, value);
CcTest::heap()->CollectAllGarbage();
CHECK_EQ(value, obj->GetAlignedPointerFromInternalField(0));
}
THREADED_TEST(InternalFieldsAlignedPointers) {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate);
Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate();
instance_templ->SetInternalFieldCount(1);
Local<v8::Object> obj = templ->GetFunction()->NewInstance();
CHECK_EQ(1, obj->InternalFieldCount());
CheckAlignedPointerInInternalField(obj, NULL);
int* heap_allocated = new int[100];
CheckAlignedPointerInInternalField(obj, heap_allocated);
delete[] heap_allocated;
int stack_allocated[100];
CheckAlignedPointerInInternalField(obj, stack_allocated);
void* huge = reinterpret_cast<void*>(~static_cast<uintptr_t>(1));
CheckAlignedPointerInInternalField(obj, huge);
v8::Global<v8::Object> persistent(isolate, obj);
CHECK_EQ(1, Object::InternalFieldCount(persistent));
CHECK_EQ(huge, Object::GetAlignedPointerFromInternalField(persistent, 0));
}
static void CheckAlignedPointerInEmbedderData(LocalContext* env, int index,
void* value) {
CHECK_EQ(0, static_cast<int>(reinterpret_cast<uintptr_t>(value) & 0x1));
(*env)->SetAlignedPointerInEmbedderData(index, value);
CcTest::heap()->CollectAllGarbage();
CHECK_EQ(value, (*env)->GetAlignedPointerFromEmbedderData(index));
}
static void* AlignedTestPointer(int i) {
return reinterpret_cast<void*>(i * 1234);
}
THREADED_TEST(EmbedderDataAlignedPointers) {
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
CheckAlignedPointerInEmbedderData(&env, 0, NULL);
int* heap_allocated = new int[100];
CheckAlignedPointerInEmbedderData(&env, 1, heap_allocated);
delete[] heap_allocated;
int stack_allocated[100];
CheckAlignedPointerInEmbedderData(&env, 2, stack_allocated);
void* huge = reinterpret_cast<void*>(~static_cast<uintptr_t>(1));
CheckAlignedPointerInEmbedderData(&env, 3, huge);
// Test growing of the embedder data's backing store.
for (int i = 0; i < 100; i++) {
env->SetAlignedPointerInEmbedderData(i, AlignedTestPointer(i));
}
CcTest::heap()->CollectAllGarbage();
for (int i = 0; i < 100; i++) {
CHECK_EQ(AlignedTestPointer(i), env->GetAlignedPointerFromEmbedderData(i));
}
}
static void CheckEmbedderData(LocalContext* env, int index,
v8::Handle<Value> data) {
(*env)->SetEmbedderData(index, data);
CHECK((*env)->GetEmbedderData(index)->StrictEquals(data));
}
THREADED_TEST(EmbedderData) {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
CheckEmbedderData(
&env, 3, v8::String::NewFromUtf8(isolate, "The quick brown fox jumps"));
CheckEmbedderData(&env, 2,
v8::String::NewFromUtf8(isolate, "over the lazy dog."));
CheckEmbedderData(&env, 1, v8::Number::New(isolate, 1.2345));
CheckEmbedderData(&env, 0, v8::Boolean::New(isolate, true));
}
THREADED_TEST(GetIsolate) {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
Local<v8::Object> obj = v8::Object::New(isolate);
CHECK_EQ(isolate, obj->GetIsolate());
CHECK_EQ(isolate, CcTest::global()->GetIsolate());
}
THREADED_TEST(IdentityHash) {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
// Ensure that the test starts with an fresh heap to test whether the hash
// code is based on the address.
CcTest::heap()->CollectAllGarbage();
Local<v8::Object> obj = v8::Object::New(isolate);
int hash = obj->GetIdentityHash();
int hash1 = obj->GetIdentityHash();
CHECK_EQ(hash, hash1);
int hash2 = v8::Object::New(isolate)->GetIdentityHash();
// Since the identity hash is essentially a random number two consecutive
// objects should not be assigned the same hash code. If the test below fails
// the random number generator should be evaluated.
CHECK_NE(hash, hash2);
CcTest::heap()->CollectAllGarbage();
int hash3 = v8::Object::New(isolate)->GetIdentityHash();
// Make sure that the identity hash is not based on the initial address of
// the object alone. If the test below fails the random number generator
// should be evaluated.
CHECK_NE(hash, hash3);
int hash4 = obj->GetIdentityHash();
CHECK_EQ(hash, hash4);
// Check identity hashes behaviour in the presence of JS accessors.
// Put a getter for 'v8::IdentityHash' on the Object's prototype:
{
CompileRun("Object.prototype['v8::IdentityHash'] = 42;\n");
Local<v8::Object> o1 = v8::Object::New(isolate);
Local<v8::Object> o2 = v8::Object::New(isolate);
CHECK_NE(o1->GetIdentityHash(), o2->GetIdentityHash());
}
{
CompileRun(
"function cnst() { return 42; };\n"
"Object.prototype.__defineGetter__('v8::IdentityHash', cnst);\n");
Local<v8::Object> o1 = v8::Object::New(isolate);
Local<v8::Object> o2 = v8::Object::New(isolate);
CHECK_NE(o1->GetIdentityHash(), o2->GetIdentityHash());
}
}
void GlobalProxyIdentityHash(bool set_in_js) {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
v8::HandleScope scope(isolate);
Handle<Object> global_proxy = env->Global();
i::Handle<i::Object> i_global_proxy = v8::Utils::OpenHandle(*global_proxy);
env->Global()->Set(v8_str("global"), global_proxy);
i::Handle<i::Object> original_hash;
if (set_in_js) {
CompileRun("var m = new Set(); m.add(global);");
original_hash = i::Handle<i::Object>(i_global_proxy->GetHash(), i_isolate);
} else {
original_hash = i::Handle<i::Object>(
i::Object::GetOrCreateHash(i_isolate, i_global_proxy));
}
CHECK(original_hash->IsSmi());
int32_t hash1 = i::Handle<i::Smi>::cast(original_hash)->value();
// Hash should be retained after being detached.
env->DetachGlobal();
int hash2 = global_proxy->GetIdentityHash();
CHECK_EQ(hash1, hash2);
{
// Re-attach global proxy to a new context, hash should stay the same.
LocalContext env2(NULL, Handle<ObjectTemplate>(), global_proxy);
int hash3 = global_proxy->GetIdentityHash();
CHECK_EQ(hash1, hash3);
}
}
THREADED_TEST(GlobalProxyIdentityHash) {
GlobalProxyIdentityHash(true);
GlobalProxyIdentityHash(false);
}
TEST(SymbolIdentityHash) {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
{
Local<v8::Symbol> symbol = v8::Symbol::New(isolate);
int hash = symbol->GetIdentityHash();
int hash1 = symbol->GetIdentityHash();
CHECK_EQ(hash, hash1);
CcTest::heap()->CollectAllGarbage();
int hash3 = symbol->GetIdentityHash();
CHECK_EQ(hash, hash3);
}
{
v8::Handle<v8::Symbol> js_symbol =
CompileRun("Symbol('foo')").As<v8::Symbol>();
int hash = js_symbol->GetIdentityHash();
int hash1 = js_symbol->GetIdentityHash();
CHECK_EQ(hash, hash1);
CcTest::heap()->CollectAllGarbage();
int hash3 = js_symbol->GetIdentityHash();
CHECK_EQ(hash, hash3);
}
}
TEST(StringIdentityHash) {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
Local<v8::String> str = v8::String::NewFromUtf8(isolate, "str1");
int hash = str->GetIdentityHash();
int hash1 = str->GetIdentityHash();
CHECK_EQ(hash, hash1);
CcTest::heap()->CollectAllGarbage();
int hash3 = str->GetIdentityHash();
CHECK_EQ(hash, hash3);
Local<v8::String> str2 = v8::String::NewFromUtf8(isolate, "str1");
int hash4 = str2->GetIdentityHash();
CHECK_EQ(hash, hash4);
}
THREADED_TEST(SymbolProperties) {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
v8::Local<v8::Object> obj = v8::Object::New(isolate);
v8::Local<v8::Symbol> sym1 = v8::Symbol::New(isolate);
v8::Local<v8::Symbol> sym2 = v8::Symbol::New(isolate, v8_str("my-symbol"));
v8::Local<v8::Symbol> sym3 = v8::Symbol::New(isolate, v8_str("sym3"));
CcTest::heap()->CollectAllGarbage();
// Check basic symbol functionality.
CHECK(sym1->IsSymbol());
CHECK(sym2->IsSymbol());
CHECK(!obj->IsSymbol());
CHECK(sym1->Equals(sym1));
CHECK(sym2->Equals(sym2));
CHECK(!sym1->Equals(sym2));
CHECK(!sym2->Equals(sym1));
CHECK(sym1->StrictEquals(sym1));
CHECK(sym2->StrictEquals(sym2));
CHECK(!sym1->StrictEquals(sym2));
CHECK(!sym2->StrictEquals(sym1));
CHECK(sym2->Name()->Equals(v8_str("my-symbol")));
v8::Local<v8::Value> sym_val = sym2;
CHECK(sym_val->IsSymbol());
CHECK(sym_val->Equals(sym2));
CHECK(sym_val->StrictEquals(sym2));
CHECK(v8::Symbol::Cast(*sym_val)->Equals(sym2));
v8::Local<v8::Value> sym_obj = v8::SymbolObject::New(isolate, sym2);
CHECK(sym_obj->IsSymbolObject());
CHECK(!sym2->IsSymbolObject());
CHECK(!obj->IsSymbolObject());
CHECK(!sym_obj->Equals(sym2));
CHECK(!sym_obj->StrictEquals(sym2));
CHECK(v8::SymbolObject::Cast(*sym_obj)->Equals(sym_obj));
CHECK(v8::SymbolObject::Cast(*sym_obj)->ValueOf()->Equals(sym2));
// Make sure delete of a non-existent symbol property works.
CHECK(obj->Delete(sym1));
CHECK(!obj->Has(sym1));
CHECK(obj->Set(sym1, v8::Integer::New(isolate, 1503)));
CHECK(obj->Has(sym1));
CHECK_EQ(1503, obj->Get(sym1)->Int32Value());
CHECK(obj->Set(sym1, v8::Integer::New(isolate, 2002)));
CHECK(obj->Has(sym1));
CHECK_EQ(2002, obj->Get(sym1)->Int32Value());
CHECK_EQ(v8::None, obj->GetPropertyAttributes(sym1));
CHECK_EQ(0u, obj->GetOwnPropertyNames()->Length());
unsigned num_props = obj->GetPropertyNames()->Length();
CHECK(obj->Set(v8::String::NewFromUtf8(isolate, "bla"),
v8::Integer::New(isolate, 20)));
CHECK_EQ(1u, obj->GetOwnPropertyNames()->Length());
CHECK_EQ(num_props + 1, obj->GetPropertyNames()->Length());
CcTest::heap()->CollectAllGarbage();
CHECK(obj->SetAccessor(sym3, SymbolAccessorGetter, SymbolAccessorSetter));
CHECK(obj->Get(sym3)->IsUndefined());
CHECK(obj->Set(sym3, v8::Integer::New(isolate, 42)));
CHECK(obj->Get(sym3)->Equals(v8::Integer::New(isolate, 42)));
CHECK(obj->Get(v8::String::NewFromUtf8(isolate, "accessor_sym3"))
->Equals(v8::Integer::New(isolate, 42)));
// Add another property and delete it afterwards to force the object in
// slow case.
CHECK(obj->Set(sym2, v8::Integer::New(isolate, 2008)));
CHECK_EQ(2002, obj->Get(sym1)->Int32Value());
CHECK_EQ(2008, obj->Get(sym2)->Int32Value());
CHECK_EQ(2002, obj->Get(sym1)->Int32Value());
CHECK_EQ(2u, obj->GetOwnPropertyNames()->Length());
CHECK(obj->Has(sym1));
CHECK(obj->Has(sym2));
CHECK(obj->Has(sym3));
CHECK(obj->Has(v8::String::NewFromUtf8(isolate, "accessor_sym3")));
CHECK(obj->Delete(sym2));
CHECK(obj->Has(sym1));
CHECK(!obj->Has(sym2));
CHECK(obj->Has(sym3));
CHECK(obj->Has(v8::String::NewFromUtf8(isolate, "accessor_sym3")));
CHECK_EQ(2002, obj->Get(sym1)->Int32Value());
CHECK(obj->Get(sym3)->Equals(v8::Integer::New(isolate, 42)));
CHECK(obj->Get(v8::String::NewFromUtf8(isolate, "accessor_sym3"))
->Equals(v8::Integer::New(isolate, 42)));
CHECK_EQ(2u, obj->GetOwnPropertyNames()->Length());
// Symbol properties are inherited.
v8::Local<v8::Object> child = v8::Object::New(isolate);
child->SetPrototype(obj);
CHECK(child->Has(sym1));
CHECK_EQ(2002, child->Get(sym1)->Int32Value());
CHECK(obj->Get(sym3)->Equals(v8::Integer::New(isolate, 42)));
CHECK(obj->Get(v8::String::NewFromUtf8(isolate, "accessor_sym3"))
->Equals(v8::Integer::New(isolate, 42)));
CHECK_EQ(0u, child->GetOwnPropertyNames()->Length());
}
THREADED_TEST(SymbolTemplateProperties) {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
v8::Local<v8::FunctionTemplate> foo = v8::FunctionTemplate::New(isolate);
v8::Local<v8::Name> name = v8::Symbol::New(isolate);
CHECK(!name.IsEmpty());
foo->PrototypeTemplate()->Set(name, v8::FunctionTemplate::New(isolate));
v8::Local<v8::Object> new_instance = foo->InstanceTemplate()->NewInstance();
CHECK(!new_instance.IsEmpty());
CHECK(new_instance->Has(name));
}
THREADED_TEST(GlobalSymbols) {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
v8::Local<String> name = v8_str("my-symbol");
v8::Local<v8::Symbol> glob = v8::Symbol::For(isolate, name);
v8::Local<v8::Symbol> glob2 = v8::Symbol::For(isolate, name);
CHECK(glob2->SameValue(glob));
v8::Local<v8::Symbol> glob_api = v8::Symbol::ForApi(isolate, name);
v8::Local<v8::Symbol> glob_api2 = v8::Symbol::ForApi(isolate, name);
CHECK(glob_api2->SameValue(glob_api));
CHECK(!glob_api->SameValue(glob));
v8::Local<v8::Symbol> sym = v8::Symbol::New(isolate, name);
CHECK(!sym->SameValue(glob));
CompileRun("var sym2 = Symbol.for('my-symbol')");
v8::Local<Value> sym2 = env->Global()->Get(v8_str("sym2"));
CHECK(sym2->SameValue(glob));
CHECK(!sym2->SameValue(glob_api));
}
static void CheckWellKnownSymbol(v8::Local<v8::Symbol>(*getter)(v8::Isolate*),
const char* name) {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
v8::Local<v8::Symbol> symbol = getter(isolate);
std::string script = std::string("var sym = ") + name;
CompileRun(script.c_str());
v8::Local<Value> value = env->Global()->Get(v8_str("sym"));
CHECK(!value.IsEmpty());
CHECK(!symbol.IsEmpty());
CHECK(value->SameValue(symbol));
}
THREADED_TEST(WellKnownSymbols) {
CheckWellKnownSymbol(v8::Symbol::GetIterator, "Symbol.iterator");
CheckWellKnownSymbol(v8::Symbol::GetUnscopables, "Symbol.unscopables");
}
class ScopedArrayBufferContents {
public:
explicit ScopedArrayBufferContents(const v8::ArrayBuffer::Contents& contents)
: contents_(contents) {}
~ScopedArrayBufferContents() { free(contents_.Data()); }
void* Data() const { return contents_.Data(); }
size_t ByteLength() const { return contents_.ByteLength(); }
private:
const v8::ArrayBuffer::Contents contents_;
};
template <typename T>
static void CheckInternalFieldsAreZero(v8::Handle<T> value) {
CHECK_EQ(T::kInternalFieldCount, value->InternalFieldCount());
for (int i = 0; i < value->InternalFieldCount(); i++) {
CHECK_EQ(0, value->GetInternalField(i)->Int32Value());
}
}
THREADED_TEST(ArrayBuffer_ApiInternalToExternal) {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope handle_scope(isolate);
Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(isolate, 1024);
CheckInternalFieldsAreZero(ab);
CHECK_EQ(1024, static_cast<int>(ab->ByteLength()));
CHECK(!ab->IsExternal());
CcTest::heap()->CollectAllGarbage();
ScopedArrayBufferContents ab_contents(ab->Externalize());
CHECK(ab->IsExternal());
CHECK_EQ(1024, static_cast<int>(ab_contents.ByteLength()));
uint8_t* data = static_cast<uint8_t*>(ab_contents.Data());
DCHECK(data != NULL);
env->Global()->Set(v8_str("ab"), ab);
v8::Handle<v8::Value> result = CompileRun("ab.byteLength");
CHECK_EQ(1024, result->Int32Value());
result = CompileRun(
"var u8 = new Uint8Array(ab);"
"u8[0] = 0xFF;"
"u8[1] = 0xAA;"
"u8.length");
CHECK_EQ(1024, result->Int32Value());
CHECK_EQ(0xFF, data[0]);
CHECK_EQ(0xAA, data[1]);
data[0] = 0xCC;
data[1] = 0x11;
result = CompileRun("u8[0] + u8[1]");
CHECK_EQ(0xDD, result->Int32Value());
}
THREADED_TEST(ArrayBuffer_JSInternalToExternal) {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope handle_scope(isolate);
v8::Local<v8::Value> result = CompileRun(
"var ab1 = new ArrayBuffer(2);"
"var u8_a = new Uint8Array(ab1);"
"u8_a[0] = 0xAA;"
"u8_a[1] = 0xFF; u8_a.buffer");
Local<v8::ArrayBuffer> ab1 = Local<v8::ArrayBuffer>::Cast(result);
CheckInternalFieldsAreZero(ab1);
CHECK_EQ(2, static_cast<int>(ab1->ByteLength()));
CHECK(!ab1->IsExternal());
ScopedArrayBufferContents ab1_contents(ab1->Externalize());
CHECK(ab1->IsExternal());
result = CompileRun("ab1.byteLength");
CHECK_EQ(2, result->Int32Value());
result = CompileRun("u8_a[0]");
CHECK_EQ(0xAA, result->Int32Value());
result = CompileRun("u8_a[1]");
CHECK_EQ(0xFF, result->Int32Value());
result = CompileRun(
"var u8_b = new Uint8Array(ab1);"
"u8_b[0] = 0xBB;"
"u8_a[0]");
CHECK_EQ(0xBB, result->Int32Value());
result = CompileRun("u8_b[1]");
CHECK_EQ(0xFF, result->Int32Value());
CHECK_EQ(2, static_cast<int>(ab1_contents.ByteLength()));
uint8_t* ab1_data = static_cast<uint8_t*>(ab1_contents.Data());
CHECK_EQ(0xBB, ab1_data[0]);
CHECK_EQ(0xFF, ab1_data[1]);
ab1_data[0] = 0xCC;
ab1_data[1] = 0x11;
result = CompileRun("u8_a[0] + u8_a[1]");
CHECK_EQ(0xDD, result->Int32Value());
}
THREADED_TEST(ArrayBuffer_External) {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope handle_scope(isolate);
i::ScopedVector<uint8_t> my_data(100);
memset(my_data.start(), 0, 100);
Local<v8::ArrayBuffer> ab3 =
v8::ArrayBuffer::New(isolate, my_data.start(), 100);
CheckInternalFieldsAreZero(ab3);
CHECK_EQ(100, static_cast<int>(ab3->ByteLength()));
CHECK(ab3->IsExternal());
env->Global()->Set(v8_str("ab3"), ab3);
v8::Handle<v8::Value> result = CompileRun("ab3.byteLength");
CHECK_EQ(100, result->Int32Value());
result = CompileRun(
"var u8_b = new Uint8Array(ab3);"
"u8_b[0] = 0xBB;"
"u8_b[1] = 0xCC;"
"u8_b.length");
CHECK_EQ(100, result->Int32Value());
CHECK_EQ(0xBB, my_data[0]);
CHECK_EQ(0xCC, my_data[1]);
my_data[0] = 0xCC;
my_data[1] = 0x11;
result = CompileRun("u8_b[0] + u8_b[1]");
CHECK_EQ(0xDD, result->Int32Value());
}
THREADED_TEST(ArrayBuffer_DisableNeuter) {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope handle_scope(isolate);
i::ScopedVector<uint8_t> my_data(100);
memset(my_data.start(), 0, 100);
Local<v8::ArrayBuffer> ab =
v8::ArrayBuffer::New(isolate, my_data.start(), 100);
CHECK(ab->IsNeuterable());
i::Handle<i::JSArrayBuffer> buf = v8::Utils::OpenHandle(*ab);
buf->set_is_neuterable(false);
CHECK(!ab->IsNeuterable());
}
static void CheckDataViewIsNeutered(v8::Handle<v8::DataView> dv) {
CHECK_EQ(0, static_cast<int>(dv->ByteLength()));
CHECK_EQ(0, static_cast<int>(dv->ByteOffset()));
}
static void CheckIsNeutered(v8::Handle<v8::TypedArray> ta) {
CHECK_EQ(0, static_cast<int>(ta->ByteLength()));
CHECK_EQ(0, static_cast<int>(ta->Length()));
CHECK_EQ(0, static_cast<int>(ta->ByteOffset()));
}
static void CheckIsTypedArrayVarNeutered(const char* name) {
i::ScopedVector<char> source(1024);
i::SNPrintF(source,
"%s.byteLength == 0 && %s.byteOffset == 0 && %s.length == 0",
name, name, name);
CHECK(CompileRun(source.start())->IsTrue());
v8::Handle<v8::TypedArray> ta =
v8::Handle<v8::TypedArray>::Cast(CompileRun(name));
CheckIsNeutered(ta);
}
template <typename TypedArray, int kElementSize>
static Handle<TypedArray> CreateAndCheck(Handle<v8::ArrayBuffer> ab,
int byteOffset, int length) {
v8::Handle<TypedArray> ta = TypedArray::New(ab, byteOffset, length);
CheckInternalFieldsAreZero<v8::ArrayBufferView>(ta);
CHECK_EQ(byteOffset, static_cast<int>(ta->ByteOffset()));
CHECK_EQ(length, static_cast<int>(ta->Length()));
CHECK_EQ(length * kElementSize, static_cast<int>(ta->ByteLength()));
return ta;
}
THREADED_TEST(ArrayBuffer_NeuteringApi) {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope handle_scope(isolate);
v8::Handle<v8::ArrayBuffer> buffer = v8::ArrayBuffer::New(isolate, 1024);
v8::Handle<v8::Uint8Array> u8a =
CreateAndCheck<v8::Uint8Array, 1>(buffer, 1, 1023);
v8::Handle<v8::Uint8ClampedArray> u8c =
CreateAndCheck<v8::Uint8ClampedArray, 1>(buffer, 1, 1023);
v8::Handle<v8::Int8Array> i8a =
CreateAndCheck<v8::Int8Array, 1>(buffer, 1, 1023);
v8::Handle<v8::Uint16Array> u16a =
CreateAndCheck<v8::Uint16Array, 2>(buffer, 2, 511);
v8::Handle<v8::Int16Array> i16a =
CreateAndCheck<v8::Int16Array, 2>(buffer, 2, 511);
v8::Handle<v8::Uint32Array> u32a =
CreateAndCheck<v8::Uint32Array, 4>(buffer, 4, 255);
v8::Handle<v8::Int32Array> i32a =
CreateAndCheck<v8::Int32Array, 4>(buffer, 4, 255);
v8::Handle<v8::Float32Array> f32a =
CreateAndCheck<v8::Float32Array, 4>(buffer, 4, 255);
v8::Handle<v8::Float64Array> f64a =
CreateAndCheck<v8::Float64Array, 8>(buffer, 8, 127);
v8::Handle<v8::DataView> dv = v8::DataView::New(buffer, 1, 1023);
CheckInternalFieldsAreZero<v8::ArrayBufferView>(dv);
CHECK_EQ(1, static_cast<int>(dv->ByteOffset()));
CHECK_EQ(1023, static_cast<int>(dv->ByteLength()));
ScopedArrayBufferContents contents(buffer->Externalize());
buffer->Neuter();
CHECK_EQ(0, static_cast<int>(buffer->ByteLength()));
CheckIsNeutered(u8a);
CheckIsNeutered(u8c);
CheckIsNeutered(i8a);
CheckIsNeutered(u16a);
CheckIsNeutered(i16a);
CheckIsNeutered(u32a);
CheckIsNeutered(i32a);
CheckIsNeutered(f32a);
CheckIsNeutered(f64a);
CheckDataViewIsNeutered(dv);
}
THREADED_TEST(ArrayBuffer_NeuteringScript) {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope handle_scope(isolate);
CompileRun(
"var ab = new ArrayBuffer(1024);"
"var u8a = new Uint8Array(ab, 1, 1023);"
"var u8c = new Uint8ClampedArray(ab, 1, 1023);"
"var i8a = new Int8Array(ab, 1, 1023);"
"var u16a = new Uint16Array(ab, 2, 511);"
"var i16a = new Int16Array(ab, 2, 511);"
"var u32a = new Uint32Array(ab, 4, 255);"
"var i32a = new Int32Array(ab, 4, 255);"
"var f32a = new Float32Array(ab, 4, 255);"
"var f64a = new Float64Array(ab, 8, 127);"
"var dv = new DataView(ab, 1, 1023);");
v8::Handle<v8::ArrayBuffer> ab =
Local<v8::ArrayBuffer>::Cast(CompileRun("ab"));
v8::Handle<v8::DataView> dv =
v8::Handle<v8::DataView>::Cast(CompileRun("dv"));
ScopedArrayBufferContents contents(ab->Externalize());
ab->Neuter();
CHECK_EQ(0, static_cast<int>(ab->ByteLength()));
CHECK_EQ(0, CompileRun("ab.byteLength")->Int32Value());
CheckIsTypedArrayVarNeutered("u8a");
CheckIsTypedArrayVarNeutered("u8c");
CheckIsTypedArrayVarNeutered("i8a");
CheckIsTypedArrayVarNeutered("u16a");
CheckIsTypedArrayVarNeutered("i16a");
CheckIsTypedArrayVarNeutered("u32a");
CheckIsTypedArrayVarNeutered("i32a");
CheckIsTypedArrayVarNeutered("f32a");
CheckIsTypedArrayVarNeutered("f64a");
CHECK(CompileRun("dv.byteLength == 0 && dv.byteOffset == 0")->IsTrue());
CheckDataViewIsNeutered(dv);
}
class ScopedSharedArrayBufferContents {
public:
explicit ScopedSharedArrayBufferContents(
const v8::SharedArrayBuffer::Contents& contents)
: contents_(contents) {}
~ScopedSharedArrayBufferContents() { free(contents_.Data()); }
void* Data() const { return contents_.Data(); }
size_t ByteLength() const { return contents_.ByteLength(); }
private:
const v8::SharedArrayBuffer::Contents contents_;
};
THREADED_TEST(SharedArrayBuffer_ApiInternalToExternal) {
i::FLAG_harmony_sharedarraybuffer = true;
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope handle_scope(isolate);
Local<v8::SharedArrayBuffer> ab = v8::SharedArrayBuffer::New(isolate, 1024);
CheckInternalFieldsAreZero(ab);
CHECK_EQ(1024, static_cast<int>(ab->ByteLength()));
CHECK(!ab->IsExternal());
CcTest::heap()->CollectAllGarbage();
ScopedSharedArrayBufferContents ab_contents(ab->Externalize());
CHECK(ab->IsExternal());
CHECK_EQ(1024, static_cast<int>(ab_contents.ByteLength()));
uint8_t* data = static_cast<uint8_t*>(ab_contents.Data());
DCHECK(data != NULL);
env->Global()->Set(v8_str("ab"), ab);
v8::Handle<v8::Value> result = CompileRun("ab.byteLength");
CHECK_EQ(1024, result->Int32Value());
result = CompileRun(
"var u8 = new Uint8Array(ab);"
"u8[0] = 0xFF;"
"u8[1] = 0xAA;"
"u8.length");
CHECK_EQ(1024, result->Int32Value());
CHECK_EQ(0xFF, data[0]);
CHECK_EQ(0xAA, data[1]);
data[0] = 0xCC;
data[1] = 0x11;
result = CompileRun("u8[0] + u8[1]");
CHECK_EQ(0xDD, result->Int32Value());
}
THREADED_TEST(SharedArrayBuffer_JSInternalToExternal) {
i::FLAG_harmony_sharedarraybuffer = true;
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope handle_scope(isolate);
v8::Local<v8::Value> result = CompileRun(
"var ab1 = new SharedArrayBuffer(2);"
"var u8_a = new Uint8Array(ab1);"
"u8_a[0] = 0xAA;"
"u8_a[1] = 0xFF; u8_a.buffer");
Local<v8::SharedArrayBuffer> ab1 = Local<v8::SharedArrayBuffer>::Cast(result);
CheckInternalFieldsAreZero(ab1);
CHECK_EQ(2, static_cast<int>(ab1->ByteLength()));
CHECK(!ab1->IsExternal());
ScopedSharedArrayBufferContents ab1_contents(ab1->Externalize());
CHECK(ab1->IsExternal());
result = CompileRun("ab1.byteLength");
CHECK_EQ(2, result->Int32Value());
result = CompileRun("u8_a[0]");
CHECK_EQ(0xAA, result->Int32Value());
result = CompileRun("u8_a[1]");
CHECK_EQ(0xFF, result->Int32Value());
result = CompileRun(
"var u8_b = new Uint8Array(ab1);"
"u8_b[0] = 0xBB;"
"u8_a[0]");
CHECK_EQ(0xBB, result->Int32Value());
result = CompileRun("u8_b[1]");
CHECK_EQ(0xFF, result->Int32Value());
CHECK_EQ(2, static_cast<int>(ab1_contents.ByteLength()));
uint8_t* ab1_data = static_cast<uint8_t*>(ab1_contents.Data());
CHECK_EQ(0xBB, ab1_data[0]);
CHECK_EQ(0xFF, ab1_data[1]);
ab1_data[0] = 0xCC;
ab1_data[1] = 0x11;
result = CompileRun("u8_a[0] + u8_a[1]");
CHECK_EQ(0xDD, result->Int32Value());
}
THREADED_TEST(SharedArrayBuffer_External) {
i::FLAG_harmony_sharedarraybuffer = true;
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope handle_scope(isolate);
i::ScopedVector<uint8_t> my_data(100);
memset(my_data.start(), 0, 100);
Local<v8::SharedArrayBuffer> ab3 =
v8::SharedArrayBuffer::New(isolate, my_data.start(), 100);
CheckInternalFieldsAreZero(ab3);
CHECK_EQ(100, static_cast<int>(ab3->ByteLength()));
CHECK(ab3->IsExternal());
env->Global()->Set(v8_str("ab3"), ab3);
v8::Handle<v8::Value> result = CompileRun("ab3.byteLength");
CHECK_EQ(100, result->Int32Value());
result = CompileRun(
"var u8_b = new Uint8Array(ab3);"
"u8_b[0] = 0xBB;"
"u8_b[1] = 0xCC;"
"u8_b.length");
CHECK_EQ(100, result->Int32Value());
CHECK_EQ(0xBB, my_data[0]);
CHECK_EQ(0xCC, my_data[1]);
my_data[0] = 0xCC;
my_data[1] = 0x11;
result = CompileRun("u8_b[0] + u8_b[1]");
CHECK_EQ(0xDD, result->Int32Value());
}
THREADED_TEST(HiddenProperties) {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
v8::Local<v8::Object> obj = v8::Object::New(env->GetIsolate());
v8::Local<v8::String> key = v8_str("api-test::hidden-key");
v8::Local<v8::String> empty = v8_str("");
v8::Local<v8::String> prop_name = v8_str("prop_name");
CcTest::heap()->CollectAllGarbage();
// Make sure delete of a non-existent hidden value works
CHECK(obj->DeleteHiddenValue(key));
CHECK(obj->SetHiddenValue(key, v8::Integer::New(isolate, 1503)));
CHECK_EQ(1503, obj->GetHiddenValue(key)->Int32Value());
CHECK(obj->SetHiddenValue(key, v8::Integer::New(isolate, 2002)));
CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
CcTest::heap()->CollectAllGarbage();
// Make sure we do not find the hidden property.
CHECK(!obj->Has(empty));
CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
CHECK(obj->Get(empty)->IsUndefined());
CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
CHECK(obj->Set(empty, v8::Integer::New(isolate, 2003)));
CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
CHECK_EQ(2003, obj->Get(empty)->Int32Value());
CcTest::heap()->CollectAllGarbage();
// Add another property and delete it afterwards to force the object in
// slow case.
CHECK(obj->Set(prop_name, v8::Integer::New(isolate, 2008)));
CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
CHECK_EQ(2008, obj->Get(prop_name)->Int32Value());
CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
CHECK(obj->Delete(prop_name));
CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
CcTest::heap()->CollectAllGarbage();
CHECK(obj->SetHiddenValue(key, Handle<Value>()));
CHECK(obj->GetHiddenValue(key).IsEmpty());
CHECK(obj->SetHiddenValue(key, v8::Integer::New(isolate, 2002)));
CHECK(obj->DeleteHiddenValue(key));
CHECK(obj->GetHiddenValue(key).IsEmpty());
}
THREADED_TEST(Regress97784) {
// Regression test for crbug.com/97784
// Messing with the Object.prototype should not have effect on
// hidden properties.
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
v8::Local<v8::Object> obj = v8::Object::New(env->GetIsolate());
v8::Local<v8::String> key = v8_str("hidden");
CompileRun(
"set_called = false;"
"Object.defineProperty("
" Object.prototype,"
" 'hidden',"
" {get: function() { return 45; },"
" set: function() { set_called = true; }})");
CHECK(obj->GetHiddenValue(key).IsEmpty());
// Make sure that the getter and setter from Object.prototype is not invoked.
// If it did we would have full access to the hidden properties in
// the accessor.
CHECK(obj->SetHiddenValue(key, v8::Integer::New(env->GetIsolate(), 42)));
ExpectFalse("set_called");
CHECK_EQ(42, obj->GetHiddenValue(key)->Int32Value());
}
THREADED_TEST(External) {
v8::HandleScope scope(CcTest::isolate());
int x = 3;
Local<v8::External> ext = v8::External::New(CcTest::isolate(), &x);
LocalContext env;
env->Global()->Set(v8_str("ext"), ext);
Local<Value> reext_obj = CompileRun("this.ext");
v8::Handle<v8::External> reext = reext_obj.As<v8::External>();
int* ptr = static_cast<int*>(reext->Value());
CHECK_EQ(x, 3);
*ptr = 10;
CHECK_EQ(x, 10);
// Make sure unaligned pointers are wrapped properly.
char* data = i::StrDup("0123456789");
Local<v8::Value> zero = v8::External::New(CcTest::isolate(), &data[0]);
Local<v8::Value> one = v8::External::New(CcTest::isolate(), &data[1]);
Local<v8::Value> two = v8::External::New(CcTest::isolate(), &data[2]);
Local<v8::Value> three = v8::External::New(CcTest::isolate(), &data[3]);
char* char_ptr = reinterpret_cast<char*>(v8::External::Cast(*zero)->Value());
CHECK_EQ('0', *char_ptr);
char_ptr = reinterpret_cast<char*>(v8::External::Cast(*one)->Value());
CHECK_EQ('1', *char_ptr);
char_ptr = reinterpret_cast<char*>(v8::External::Cast(*two)->Value());
CHECK_EQ('2', *char_ptr);
char_ptr = reinterpret_cast<char*>(v8::External::Cast(*three)->Value());
CHECK_EQ('3', *char_ptr);
i::DeleteArray(data);
}
THREADED_TEST(GlobalHandle) {
v8::Isolate* isolate = CcTest::isolate();
v8::Persistent<String> global;
{
v8::HandleScope scope(isolate);
global.Reset(isolate, v8_str("str"));
}
{
v8::HandleScope scope(isolate);
CHECK_EQ(v8::Local<String>::New(isolate, global)->Length(), 3);
}
global.Reset();
{
v8::HandleScope scope(isolate);
global.Reset(isolate, v8_str("str"));
}
{
v8::HandleScope scope(isolate);
CHECK_EQ(v8::Local<String>::New(isolate, global)->Length(), 3);
}
global.Reset();
}
THREADED_TEST(ResettingGlobalHandle) {
v8::Isolate* isolate = CcTest::isolate();
v8::Persistent<String> global;
{
v8::HandleScope scope(isolate);
global.Reset(isolate, v8_str("str"));
}
v8::internal::GlobalHandles* global_handles =
reinterpret_cast<v8::internal::Isolate*>(isolate)->global_handles();
int initial_handle_count = global_handles->global_handles_count();
{
v8::HandleScope scope(isolate);
CHECK_EQ(v8::Local<String>::New(isolate, global)->Length(), 3);
}
{
v8::HandleScope scope(isolate);
global.Reset(isolate, v8_str("longer"));
}
CHECK_EQ(global_handles->global_handles_count(), initial_handle_count);
{
v8::HandleScope scope(isolate);
CHECK_EQ(v8::Local<String>::New(isolate, global)->Length(), 6);
}
global.Reset();
CHECK_EQ(global_handles->global_handles_count(), initial_handle_count - 1);
}
THREADED_TEST(ResettingGlobalHandleToEmpty) {
v8::Isolate* isolate = CcTest::isolate();
v8::Persistent<String> global;
{
v8::HandleScope scope(isolate);
global.Reset(isolate, v8_str("str"));
}
v8::internal::GlobalHandles* global_handles =
reinterpret_cast<v8::internal::Isolate*>(isolate)->global_handles();
int initial_handle_count = global_handles->global_handles_count();
{
v8::HandleScope scope(isolate);
CHECK_EQ(v8::Local<String>::New(isolate, global)->Length(), 3);
}
{
v8::HandleScope scope(isolate);
Local<String> empty;
global.Reset(isolate, empty);
}
CHECK(global.IsEmpty());
CHECK_EQ(global_handles->global_handles_count(), initial_handle_count - 1);
}
template <class T>
static v8::Global<T> PassUnique(v8::Global<T> unique) {
return unique.Pass();
}
template <class T>
static v8::Global<T> ReturnUnique(v8::Isolate* isolate,
const v8::Persistent<T>& global) {
v8::Global<String> unique(isolate, global);
return unique.Pass();
}
THREADED_TEST(Global) {
v8::Isolate* isolate = CcTest::isolate();
v8::Persistent<String> global;
{
v8::HandleScope scope(isolate);
global.Reset(isolate, v8_str("str"));
}
v8::internal::GlobalHandles* global_handles =
reinterpret_cast<v8::internal::Isolate*>(isolate)->global_handles();
int initial_handle_count = global_handles->global_handles_count();
{
v8::Global<String> unique(isolate, global);
CHECK_EQ(initial_handle_count + 1, global_handles->global_handles_count());
// Test assignment via Pass
{
v8::Global<String> copy = unique.Pass();
CHECK(unique.IsEmpty());
CHECK(copy == global);
CHECK_EQ(initial_handle_count + 1,
global_handles->global_handles_count());
unique = copy.Pass();
}
// Test ctor via Pass
{
v8::Global<String> copy(unique.Pass());
CHECK(unique.IsEmpty());
CHECK(copy == global);
CHECK_EQ(initial_handle_count + 1,
global_handles->global_handles_count());
unique = copy.Pass();
}
// Test pass through function call
{
v8::Global<String> copy = PassUnique(unique.Pass());
CHECK(unique.IsEmpty());
CHECK(copy == global);
CHECK_EQ(initial_handle_count + 1,
global_handles->global_handles_count());
unique = copy.Pass();
}
CHECK_EQ(initial_handle_count + 1, global_handles->global_handles_count());
}
// Test pass from function call
{
v8::Global<String> unique = ReturnUnique(isolate, global);
CHECK(unique == global);
CHECK_EQ(initial_handle_count + 1, global_handles->global_handles_count());
}
CHECK_EQ(initial_handle_count, global_handles->global_handles_count());
global.Reset();
}
namespace {
class TwoPassCallbackData;
void FirstPassCallback(const v8::WeakCallbackInfo<TwoPassCallbackData>& data);
void SecondPassCallback(const v8::WeakCallbackInfo<TwoPassCallbackData>& data);
class TwoPassCallbackData {
public:
TwoPassCallbackData(v8::Isolate* isolate, int* instance_counter)
: first_pass_called_(false),
second_pass_called_(false),
trigger_gc_(false),
instance_counter_(instance_counter) {
HandleScope scope(isolate);
i::ScopedVector<char> buffer(40);
i::SNPrintF(buffer, "%p", static_cast<void*>(this));
auto string =
v8::String::NewFromUtf8(isolate, buffer.start(),
v8::NewStringType::kNormal).ToLocalChecked();
cell_.Reset(isolate, string);
(*instance_counter_)++;
}
~TwoPassCallbackData() {
CHECK(first_pass_called_);
CHECK(second_pass_called_);
CHECK(cell_.IsEmpty());
(*instance_counter_)--;
}
void FirstPass() {
CHECK(!first_pass_called_);
CHECK(!second_pass_called_);
CHECK(!cell_.IsEmpty());
cell_.Reset();
first_pass_called_ = true;
}
void SecondPass() {
CHECK(first_pass_called_);
CHECK(!second_pass_called_);
CHECK(cell_.IsEmpty());
second_pass_called_ = true;
delete this;
}
void SetWeak() {
cell_.SetWeak(this, FirstPassCallback, v8::WeakCallbackType::kParameter);
}
void MarkTriggerGc() { trigger_gc_ = true; }
bool trigger_gc() { return trigger_gc_; }
int* instance_counter() { return instance_counter_; }
private:
bool first_pass_called_;
bool second_pass_called_;
bool trigger_gc_;
v8::Global<v8::String> cell_;
int* instance_counter_;
};
void SecondPassCallback(const v8::WeakCallbackInfo<TwoPassCallbackData>& data) {
ApiTestFuzzer::Fuzz();
bool trigger_gc = data.GetParameter()->trigger_gc();
int* instance_counter = data.GetParameter()->instance_counter();
data.GetParameter()->SecondPass();
if (!trigger_gc) return;
auto data_2 = new TwoPassCallbackData(data.GetIsolate(), instance_counter);
data_2->SetWeak();
CcTest::heap()->CollectAllGarbage();
}
void FirstPassCallback(const v8::WeakCallbackInfo<TwoPassCallbackData>& data) {
data.GetParameter()->FirstPass();
data.SetSecondPassCallback(SecondPassCallback);
}
} // namespace
TEST(TwoPassPhantomCallbacks) {
auto isolate = CcTest::isolate();
const size_t kLength = 20;
int instance_counter = 0;
for (size_t i = 0; i < kLength; ++i) {
auto data = new TwoPassCallbackData(isolate, &instance_counter);
data->SetWeak();
}
CHECK_EQ(static_cast<int>(kLength), instance_counter);
CcTest::heap()->CollectAllGarbage();
CHECK_EQ(0, instance_counter);
}
TEST(TwoPassPhantomCallbacksNestedGc) {
auto isolate = CcTest::isolate();
const size_t kLength = 20;
TwoPassCallbackData* array[kLength];
int instance_counter = 0;
for (size_t i = 0; i < kLength; ++i) {
array[i] = new TwoPassCallbackData(isolate, &instance_counter);
array[i]->SetWeak();
}
array[5]->MarkTriggerGc();
array[10]->MarkTriggerGc();
array[15]->MarkTriggerGc();
CHECK_EQ(static_cast<int>(kLength), instance_counter);
CcTest::heap()->CollectAllGarbage();
CHECK_EQ(0, instance_counter);
}
namespace {
void* IntKeyToVoidPointer(int key) { return reinterpret_cast<void*>(key << 1); }
Local<v8::Object> NewObjectForIntKey(
v8::Isolate* isolate, const v8::Global<v8::ObjectTemplate>& templ,
int key) {
auto local = Local<v8::ObjectTemplate>::New(isolate, templ);
auto obj = local->NewInstance();
obj->SetAlignedPointerInInternalField(0, IntKeyToVoidPointer(key));
return obj;
}
template <typename K, typename V>
class PhantomStdMapTraits : public v8::StdMapTraits<K, V> {
public:
typedef typename v8::GlobalValueMap<K, V, PhantomStdMapTraits<K, V>> MapType;
static const v8::PersistentContainerCallbackType kCallbackType =
v8::kWeakWithInternalFields;
struct WeakCallbackDataType {
MapType* map;
K key;
};
static WeakCallbackDataType* WeakCallbackParameter(MapType* map, const K& key,
Local<V> value) {
WeakCallbackDataType* data = new WeakCallbackDataType;
data->map = map;
data->key = key;
return data;
}
static MapType* MapFromWeakCallbackInfo(
const v8::WeakCallbackInfo<WeakCallbackDataType>& data) {
return data.GetParameter()->map;
}
static K KeyFromWeakCallbackInfo(
const v8::WeakCallbackInfo<WeakCallbackDataType>& data) {
return data.GetParameter()->key;
}
static void DisposeCallbackData(WeakCallbackDataType* data) { delete data; }
static void Dispose(v8::Isolate* isolate, v8::Global<V> value, K key) {
CHECK_EQ(IntKeyToVoidPointer(key),
v8::Object::GetAlignedPointerFromInternalField(value, 0));
}
static void DisposeWeak(
const v8::WeakCallbackInfo<WeakCallbackDataType>& info) {
K key = KeyFromWeakCallbackInfo(info);
CHECK_EQ(IntKeyToVoidPointer(key), info.GetInternalField(0));
DisposeCallbackData(info.GetParameter());
}
};
template <typename Map>
void TestGlobalValueMap() {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::Global<ObjectTemplate> templ;
{
HandleScope scope(isolate);
auto t = ObjectTemplate::New(isolate);
t->SetInternalFieldCount(1);
templ.Reset(isolate, t);
}
Map map(isolate);
v8::internal::GlobalHandles* global_handles =
reinterpret_cast<v8::internal::Isolate*>(isolate)->global_handles();
int initial_handle_count = global_handles->global_handles_count();
CHECK_EQ(0, static_cast<int>(map.Size()));
{
HandleScope scope(isolate);
Local<v8::Object> obj = map.Get(7);
CHECK(obj.IsEmpty());
Local<v8::Object> expected = v8::Object::New(isolate);
map.Set(7, expected);
CHECK_EQ(1, static_cast<int>(map.Size()));
obj = map.Get(7);
CHECK(expected->Equals(obj));
{
typename Map::PersistentValueReference ref = map.GetReference(7);
CHECK(expected->Equals(ref.NewLocal(isolate)));
}
v8::Global<v8::Object> removed = map.Remove(7);
CHECK_EQ(0, static_cast<int>(map.Size()));
CHECK(expected == removed);
removed = map.Remove(7);
CHECK(removed.IsEmpty());
map.Set(8, expected);
CHECK_EQ(1, static_cast<int>(map.Size()));
map.Set(8, expected);
CHECK_EQ(1, static_cast<int>(map.Size()));
{
typename Map::PersistentValueReference ref;
Local<v8::Object> expected2 = NewObjectForIntKey(isolate, templ, 8);
removed = map.Set(8, v8::Global<v8::Object>(isolate, expected2), &ref);
CHECK_EQ(1, static_cast<int>(map.Size()));
CHECK(expected == removed);
CHECK(expected2->Equals(ref.NewLocal(isolate)));
}
}
CHECK_EQ(initial_handle_count + 1, global_handles->global_handles_count());
if (map.IsWeak()) {
CcTest::i_isolate()->heap()->CollectAllGarbage(
i::Heap::kAbortIncrementalMarkingMask);
} else {
map.Clear();
}
CHECK_EQ(0, static_cast<int>(map.Size()));
CHECK_EQ(initial_handle_count, global_handles->global_handles_count());
{
HandleScope scope(isolate);
Local<v8::Object> value = NewObjectForIntKey(isolate, templ, 9);
map.Set(9, value);
map.Clear();
}
CHECK_EQ(0, static_cast<int>(map.Size()));
CHECK_EQ(initial_handle_count, global_handles->global_handles_count());
}
} // namespace
TEST(GlobalValueMap) {
// Default case, w/o weak callbacks:
TestGlobalValueMap<v8::StdGlobalValueMap<int, v8::Object>>();
// Custom traits with weak callbacks:
typedef v8::GlobalValueMap<int, v8::Object,
PhantomStdMapTraits<int, v8::Object>> WeakMap;
TestGlobalValueMap<WeakMap>();
}
TEST(PersistentValueVector) {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::internal::GlobalHandles* global_handles =
reinterpret_cast<v8::internal::Isolate*>(isolate)->global_handles();
int handle_count = global_handles->global_handles_count();
HandleScope scope(isolate);
v8::PersistentValueVector<v8::Object> vector(isolate);
Local<v8::Object> obj1 = v8::Object::New(isolate);
Local<v8::Object> obj2 = v8::Object::New(isolate);
v8::Global<v8::Object> obj3(isolate, v8::Object::New(isolate));
CHECK(vector.IsEmpty());
CHECK_EQ(0, static_cast<int>(vector.Size()));
vector.ReserveCapacity(3);
CHECK(vector.IsEmpty());
vector.Append(obj1);
vector.Append(obj2);
vector.Append(obj1);
vector.Append(obj3.Pass());
vector.Append(obj1);
CHECK(!vector.IsEmpty());
CHECK_EQ(5, static_cast<int>(vector.Size()));
CHECK(obj3.IsEmpty());
CHECK(obj1->Equals(vector.Get(0)));
CHECK(obj1->Equals(vector.Get(2)));
CHECK(obj1->Equals(vector.Get(4)));
CHECK(obj2->Equals(vector.Get(1)));
CHECK_EQ(5 + handle_count, global_handles->global_handles_count());
vector.Clear();
CHECK(vector.IsEmpty());
CHECK_EQ(0, static_cast<int>(vector.Size()));
CHECK_EQ(handle_count, global_handles->global_handles_count());
}
THREADED_TEST(GlobalHandleUpcast) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Local<String> local = v8::Local<String>::New(isolate, v8_str("str"));
v8::Persistent<String> global_string(isolate, local);
v8::Persistent<Value>& global_value =
v8::Persistent<Value>::Cast(global_string);
CHECK(v8::Local<v8::Value>::New(isolate, global_value)->IsString());
CHECK(global_string == v8::Persistent<String>::Cast(global_value));
global_string.Reset();
}
THREADED_TEST(HandleEquality) {
v8::Isolate* isolate = CcTest::isolate();
v8::Persistent<String> global1;
v8::Persistent<String> global2;
{
v8::HandleScope scope(isolate);
global1.Reset(isolate, v8_str("str"));
global2.Reset(isolate, v8_str("str2"));
}
CHECK_EQ(global1 == global1, true);
CHECK_EQ(global1 != global1, false);
{
v8::HandleScope scope(isolate);
Local<String> local1 = Local<String>::New(isolate, global1);
Local<String> local2 = Local<String>::New(isolate, global2);
CHECK_EQ(global1 == local1, true);
CHECK_EQ(global1 != local1, false);
CHECK_EQ(local1 == global1, true);
CHECK_EQ(local1 != global1, false);
CHECK_EQ(global1 == local2, false);
CHECK_EQ(global1 != local2, true);
CHECK_EQ(local2 == global1, false);
CHECK_EQ(local2 != global1, true);
CHECK_EQ(local1 == local2, false);
CHECK_EQ(local1 != local2, true);
Local<String> anotherLocal1 = Local<String>::New(isolate, global1);
CHECK_EQ(local1 == anotherLocal1, true);
CHECK_EQ(local1 != anotherLocal1, false);
}
global1.Reset();
global2.Reset();
}
THREADED_TEST(LocalHandle) {
v8::HandleScope scope(CcTest::isolate());
v8::Local<String> local =
v8::Local<String>::New(CcTest::isolate(), v8_str("str"));
CHECK_EQ(local->Length(), 3);
}
class WeakCallCounter {
public:
explicit WeakCallCounter(int id) : id_(id), number_of_weak_calls_(0) {}
int id() { return id_; }
void increment() { number_of_weak_calls_++; }
int NumberOfWeakCalls() { return number_of_weak_calls_; }
private:
int id_;
int number_of_weak_calls_;
};
template <typename T>
struct WeakCallCounterAndPersistent {
explicit WeakCallCounterAndPersistent(WeakCallCounter* counter)
: counter(counter) {}
WeakCallCounter* counter;
v8::Persistent<T> handle;
};
template <typename T>
static void WeakPointerCallback(
const v8::WeakCallbackInfo<WeakCallCounterAndPersistent<T>>& data) {
CHECK_EQ(1234, data.GetParameter()->counter->id());
data.GetParameter()->counter->increment();
data.GetParameter()->handle.Reset();
}
template <typename T>
static UniqueId MakeUniqueId(const Persistent<T>& p) {
return UniqueId(reinterpret_cast<uintptr_t>(*v8::Utils::OpenPersistent(p)));
}
THREADED_TEST(ApiObjectGroups) {
LocalContext env;
v8::Isolate* iso = env->GetIsolate();
HandleScope scope(iso);
WeakCallCounter counter(1234);
WeakCallCounterAndPersistent<Value> g1s1(&counter);
WeakCallCounterAndPersistent<Value> g1s2(&counter);
WeakCallCounterAndPersistent<Value> g1c1(&counter);
WeakCallCounterAndPersistent<Value> g2s1(&counter);
WeakCallCounterAndPersistent<Value> g2s2(&counter);
WeakCallCounterAndPersistent<Value> g2c1(&counter);
{
HandleScope scope(iso);
g1s1.handle.Reset(iso, Object::New(iso));
g1s2.handle.Reset(iso, Object::New(iso));
g1c1.handle.Reset(iso, Object::New(iso));
g1s1.handle.SetWeak(&g1s1, &WeakPointerCallback,
v8::WeakCallbackType::kParameter);
g1s2.handle.SetWeak(&g1s2, &WeakPointerCallback,
v8::WeakCallbackType::kParameter);
g1c1.handle.SetWeak(&g1c1, &WeakPointerCallback,
v8::WeakCallbackType::kParameter);
g2s1.handle.Reset(iso, Object::New(iso));
g2s2.handle.Reset(iso, Object::New(iso));
g2c1.handle.Reset(iso, Object::New(iso));
g2s1.handle.SetWeak(&g2s1, &WeakPointerCallback,
v8::WeakCallbackType::kParameter);
g2s2.handle.SetWeak(&g2s2, &WeakPointerCallback,
v8::WeakCallbackType::kParameter);
g2c1.handle.SetWeak(&g2c1, &WeakPointerCallback,
v8::WeakCallbackType::kParameter);
}
WeakCallCounterAndPersistent<Value> root(&counter);
root.handle.Reset(iso, g1s1.handle); // make a root.
// Connect group 1 and 2, make a cycle.
{
HandleScope scope(iso);
CHECK(Local<Object>::New(iso, g1s2.handle.As<Object>())
->Set(0, Local<Value>::New(iso, g2s2.handle)));
CHECK(Local<Object>::New(iso, g2s1.handle.As<Object>())
->Set(0, Local<Value>::New(iso, g1s1.handle)));
}
{
UniqueId id1 = MakeUniqueId(g1s1.handle);
UniqueId id2 = MakeUniqueId(g2s2.handle);
iso->SetObjectGroupId(g1s1.handle, id1);
iso->SetObjectGroupId(g1s2.handle, id1);
iso->SetReferenceFromGroup(id1, g1c1.handle);
iso->SetObjectGroupId(g2s1.handle, id2);
iso->SetObjectGroupId(g2s2.handle, id2);
iso->SetReferenceFromGroup(id2, g2c1.handle);
}
// Do a single full GC, ensure incremental marking is stopped.
v8::internal::Heap* heap =
reinterpret_cast<v8::internal::Isolate*>(iso)->heap();
heap->CollectAllGarbage();
// All object should be alive.
CHECK_EQ(0, counter.NumberOfWeakCalls());
// Weaken the root.
root.handle.SetWeak(&root, &WeakPointerCallback,
v8::WeakCallbackType::kParameter);
// But make children strong roots---all the objects (except for children)
// should be collectable now.
g1c1.handle.ClearWeak();
g2c1.handle.ClearWeak();
// Groups are deleted, rebuild groups.
{
UniqueId id1 = MakeUniqueId(g1s1.handle);
UniqueId id2 = MakeUniqueId(g2s2.handle);
iso->SetObjectGroupId(g1s1.handle, id1);
iso->SetObjectGroupId(g1s2.handle, id1);
iso->SetReferenceFromGroup(id1, g1c1.handle);
iso->SetObjectGroupId(g2s1.handle, id2);
iso->SetObjectGroupId(g2s2.handle, id2);
iso->SetReferenceFromGroup(id2, g2c1.handle);
}
heap->CollectAllGarbage();
// All objects should be gone. 5 global handles in total.
CHECK_EQ(5, counter.NumberOfWeakCalls());
// And now make children weak again and collect them.
g1c1.handle.SetWeak(&g1c1, &WeakPointerCallback,
v8::WeakCallbackType::kParameter);
g2c1.handle.SetWeak(&g2c1, &WeakPointerCallback,
v8::WeakCallbackType::kParameter);
heap->CollectAllGarbage();
CHECK_EQ(7, counter.NumberOfWeakCalls());
}
THREADED_TEST(ApiObjectGroupsForSubtypes) {
LocalContext env;
v8::Isolate* iso = env->GetIsolate();
HandleScope scope(iso);
WeakCallCounter counter(1234);
WeakCallCounterAndPersistent<Object> g1s1(&counter);
WeakCallCounterAndPersistent<String> g1s2(&counter);
WeakCallCounterAndPersistent<String> g1c1(&counter);
WeakCallCounterAndPersistent<Object> g2s1(&counter);
WeakCallCounterAndPersistent<String> g2s2(&counter);
WeakCallCounterAndPersistent<String> g2c1(&counter);
{
HandleScope scope(iso);
g1s1.handle.Reset(iso, Object::New(iso));
g1s2.handle.Reset(iso, String::NewFromUtf8(iso, "foo1"));
g1c1.handle.Reset(iso, String::NewFromUtf8(iso, "foo2"));
g1s1.handle.SetWeak(&g1s1, &WeakPointerCallback,
v8::WeakCallbackType::kParameter);
g1s2.handle.SetWeak(&g1s2, &WeakPointerCallback,
v8::WeakCallbackType::kParameter);
g1c1.handle.SetWeak(&g1c1, &WeakPointerCallback,
v8::WeakCallbackType::kParameter);
g2s1.handle.Reset(iso, Object::New(iso));
g2s2.handle.Reset(iso, String::NewFromUtf8(iso, "foo3"));
g2c1.handle.Reset(iso, String::NewFromUtf8(iso, "foo4"));
g2s1.handle.SetWeak(&g2s1, &WeakPointerCallback,
v8::WeakCallbackType::kParameter);
g2s2.handle.SetWeak(&g2s2, &WeakPointerCallback,
v8::WeakCallbackType::kParameter);
g2c1.handle.SetWeak(&g2c1, &WeakPointerCallback,
v8::WeakCallbackType::kParameter);
}
WeakCallCounterAndPersistent<Value> root(&counter);
root.handle.Reset(iso, g1s1.handle); // make a root.
// Connect group 1 and 2, make a cycle.
{
HandleScope scope(iso);
CHECK(Local<Object>::New(iso, g1s1.handle)
->Set(0, Local<Object>::New(iso, g2s1.handle)));
CHECK(Local<Object>::New(iso, g2s1.handle)
->Set(0, Local<Object>::New(iso, g1s1.handle)));
}
{
UniqueId id1 = MakeUniqueId(g1s1.handle);
UniqueId id2 = MakeUniqueId(g2s2.handle);
iso->SetObjectGroupId(g1s1.handle, id1);
iso->SetObjectGroupId(g1s2.handle, id1);
iso->SetReference(g1s1.handle, g1c1.handle);
iso->SetObjectGroupId(g2s1.handle, id2);
iso->SetObjectGroupId(g2s2.handle, id2);
iso->SetReferenceFromGroup(id2, g2c1.handle);
}
// Do a single full GC, ensure incremental marking is stopped.
v8::internal::Heap* heap =
reinterpret_cast<v8::internal::Isolate*>(iso)->heap();
heap->CollectAllGarbage();
// All object should be alive.
CHECK_EQ(0, counter.NumberOfWeakCalls());
// Weaken the root.
root.handle.SetWeak(&root, &WeakPointerCallback,
v8::WeakCallbackType::kParameter);
// But make children strong roots---all the objects (except for children)
// should be collectable now.
g1c1.handle.ClearWeak();
g2c1.handle.ClearWeak();
// Groups are deleted, rebuild groups.
{
UniqueId id1 = MakeUniqueId(g1s1.handle);
UniqueId id2 = MakeUniqueId(g2s2.handle);
iso->SetObjectGroupId(g1s1.handle, id1);
iso->SetObjectGroupId(g1s2.handle, id1);
iso->SetReference(g1s1.handle, g1c1.handle);
iso->SetObjectGroupId(g2s1.handle, id2);
iso->SetObjectGroupId(g2s2.handle, id2);
iso->SetReferenceFromGroup(id2, g2c1.handle);
}
heap->CollectAllGarbage();
// All objects should be gone. 5 global handles in total.
CHECK_EQ(5, counter.NumberOfWeakCalls());
// And now make children weak again and collect them.
g1c1.handle.SetWeak(&g1c1, &WeakPointerCallback,
v8::WeakCallbackType::kParameter);
g2c1.handle.SetWeak(&g2c1, &WeakPointerCallback,
v8::WeakCallbackType::kParameter);
heap->CollectAllGarbage();
CHECK_EQ(7, counter.NumberOfWeakCalls());
}
THREADED_TEST(ApiObjectGroupsCycle) {
LocalContext env;
v8::Isolate* iso = env->GetIsolate();
HandleScope scope(iso);
WeakCallCounter counter(1234);
WeakCallCounterAndPersistent<Value> g1s1(&counter);
WeakCallCounterAndPersistent<Value> g1s2(&counter);
WeakCallCounterAndPersistent<Value> g2s1(&counter);
WeakCallCounterAndPersistent<Value> g2s2(&counter);
WeakCallCounterAndPersistent<Value> g3s1(&counter);
WeakCallCounterAndPersistent<Value> g3s2(&counter);
WeakCallCounterAndPersistent<Value> g4s1(&counter);
WeakCallCounterAndPersistent<Value> g4s2(&counter);
{
HandleScope scope(iso);
g1s1.handle.Reset(iso, Object::New(iso));
g1s2.handle.Reset(iso, Object::New(iso));
g1s1.handle.SetWeak(&g1s1, &WeakPointerCallback,
v8::WeakCallbackType::kParameter);
g1s2.handle.SetWeak(&g1s2, &WeakPointerCallback,
v8::WeakCallbackType::kParameter);
CHECK(g1s1.handle.IsWeak());
CHECK(g1s2.handle.IsWeak());
g2s1.handle.Reset(iso, Object::New(iso));
g2s2.handle.Reset(iso, Object::New(iso));
g2s1.handle.SetWeak(&g2s1, &WeakPointerCallback,
v8::WeakCallbackType::kParameter);
g2s2.handle.SetWeak(&g2s2, &WeakPointerCallback,
v8::WeakCallbackType::kParameter);
CHECK(g2s1.handle.IsWeak());
CHECK(g2s2.handle.IsWeak());
g3s1.handle.Reset(iso, Object::New(iso));
g3s2.handle.Reset(iso, Object::New(iso));
g3s1.handle.SetWeak(&g3s1, &WeakPointerCallback,
v8::WeakCallbackType::kParameter);
g3s2.handle.SetWeak(&g3s2, &WeakPointerCallback,
v8::WeakCallbackType::kParameter);
CHECK(g3s1.handle.IsWeak());
CHECK(g3s2.handle.IsWeak());
g4s1.handle.Reset(iso, Object::New(iso));
g4s2.handle.Reset(iso, Object::New(iso));
g4s1.handle.SetWeak(&g4s1, &WeakPointerCallback,
v8::WeakCallbackType::kParameter);
g4s2.handle.SetWeak(&g4s2, &WeakPointerCallback,
v8::WeakCallbackType::kParameter);
CHECK(g4s1.handle.IsWeak());
CHECK(g4s2.handle.IsWeak());
}
WeakCallCounterAndPersistent<Value> root(&counter);
root.handle.Reset(iso, g1s1.handle); // make a root.
// Connect groups. We're building the following cycle:
// G1: { g1s1, g2s1 }, g1s1 implicitly references g2s1, ditto for other
// groups.
{
UniqueId id1 = MakeUniqueId(g1s1.handle);
UniqueId id2 = MakeUniqueId(g2s1.handle);
UniqueId id3 = MakeUniqueId(g3s1.handle);
UniqueId id4 = MakeUniqueId(g4s1.handle);
iso->SetObjectGroupId(g1s1.handle, id1);
iso->SetObjectGroupId(g1s2.handle, id1);
iso->SetReferenceFromGroup(id1, g2s1.handle);
iso->SetObjectGroupId(g2s1.handle, id2);
iso->SetObjectGroupId(g2s2.handle, id2);
iso->SetReferenceFromGroup(id2, g3s1.handle);
iso->SetObjectGroupId(g3s1.handle, id3);
iso->SetObjectGroupId(g3s2.handle, id3);
iso->SetReferenceFromGroup(id3, g4s1.handle);
iso->SetObjectGroupId(g4s1.handle, id4);
iso->SetObjectGroupId(g4s2.handle, id4);
iso->SetReferenceFromGroup(id4, g1s1.handle);
}
// Do a single full GC
v8::internal::Heap* heap =
reinterpret_cast<v8::internal::Isolate*>(iso)->heap();
heap->CollectAllGarbage();
// All object should be alive.
CHECK_EQ(0, counter.NumberOfWeakCalls());
// Weaken the root.
root.handle.SetWeak(&root, &WeakPointerCallback,
v8::WeakCallbackType::kParameter);
// Groups are deleted, rebuild groups.
{
UniqueId id1 = MakeUniqueId(g1s1.handle);
UniqueId id2 = MakeUniqueId(g2s1.handle);
UniqueId id3 = MakeUniqueId(g3s1.handle);
UniqueId id4 = MakeUniqueId(g4s1.handle);
iso->SetObjectGroupId(g1s1.handle, id1);
iso->SetObjectGroupId(g1s2.handle, id1);
iso->SetReferenceFromGroup(id1, g2s1.handle);
iso->SetObjectGroupId(g2s1.handle, id2);
iso->SetObjectGroupId(g2s2.handle, id2);
iso->SetReferenceFromGroup(id2, g3s1.handle);
iso->SetObjectGroupId(g3s1.handle, id3);
iso->SetObjectGroupId(g3s2.handle, id3);
iso->SetReferenceFromGroup(id3, g4s1.handle);
iso->SetObjectGroupId(g4s1.handle, id4);
iso->SetObjectGroupId(g4s2.handle, id4);
iso->SetReferenceFromGroup(id4, g1s1.handle);
}
heap->CollectAllGarbage();
// All objects should be gone. 9 global handles in total.
CHECK_EQ(9, counter.NumberOfWeakCalls());
}
// TODO(mstarzinger): This should be a THREADED_TEST but causes failures
// on the buildbots, so was made non-threaded for the time being.
TEST(ApiObjectGroupsCycleForScavenger) {
i::FLAG_stress_compaction = false;
i::FLAG_gc_global = false;
LocalContext env;
v8::Isolate* iso = env->GetIsolate();
HandleScope scope(iso);
WeakCallCounter counter(1234);
WeakCallCounterAndPersistent<Value> g1s1(&counter);
WeakCallCounterAndPersistent<Value> g1s2(&counter);
WeakCallCounterAndPersistent<Value> g2s1(&counter);
WeakCallCounterAndPersistent<Value> g2s2(&counter);
WeakCallCounterAndPersistent<Value> g3s1(&counter);
WeakCallCounterAndPersistent<Value> g3s2(&counter);
{
HandleScope scope(iso);
g1s1.handle.Reset(iso, Object::New(iso));
g1s2.handle.Reset(iso, Object::New(iso));
g1s1.handle.SetWeak(&g1s1, &WeakPointerCallback,
v8::WeakCallbackType::kParameter);
g1s2.handle.SetWeak(&g1s2, &WeakPointerCallback,
v8::WeakCallbackType::kParameter);
g2s1.handle.Reset(iso, Object::New(iso));
g2s2.handle.Reset(iso, Object::New(iso));
g2s1.handle.SetWeak(&g2s1, &WeakPointerCallback,
v8::WeakCallbackType::kParameter);
g2s2.handle.SetWeak(&g2s2, &WeakPointerCallback,
v8::WeakCallbackType::kParameter);
g3s1.handle.Reset(iso, Object::New(iso));
g3s2.handle.Reset(iso, Object::New(iso));
g3s1.handle.SetWeak(&g3s1, &WeakPointerCallback,
v8::WeakCallbackType::kParameter);
g3s2.handle.SetWeak(&g3s2, &WeakPointerCallback,
v8::WeakCallbackType::kParameter);
}
// Make a root.
WeakCallCounterAndPersistent<Value> root(&counter);
root.handle.Reset(iso, g1s1.handle);
root.handle.MarkPartiallyDependent();
// Connect groups. We're building the following cycle:
// G1: { g1s1, g2s1 }, g1s1 implicitly references g2s1, ditto for other
// groups.
{
HandleScope handle_scope(iso);
g1s1.handle.MarkPartiallyDependent();
g1s2.handle.MarkPartiallyDependent();
g2s1.handle.MarkPartiallyDependent();
g2s2.handle.MarkPartiallyDependent();
g3s1.handle.MarkPartiallyDependent();
g3s2.handle.MarkPartiallyDependent();
iso->SetObjectGroupId(g1s1.handle, UniqueId(1));
iso->SetObjectGroupId(g1s2.handle, UniqueId(1));
Local<Object>::New(iso, g1s1.handle.As<Object>())
->Set(v8_str("x"), Local<Value>::New(iso, g2s1.handle));
iso->SetObjectGroupId(g2s1.handle, UniqueId(2));
iso->SetObjectGroupId(g2s2.handle, UniqueId(2));
Local<Object>::New(iso, g2s1.handle.As<Object>())
->Set(v8_str("x"), Local<Value>::New(iso, g3s1.handle));
iso->SetObjectGroupId(g3s1.handle, UniqueId(3));
iso->SetObjectGroupId(g3s2.handle, UniqueId(3));
Local<Object>::New(iso, g3s1.handle.As<Object>())
->Set(v8_str("x"), Local<Value>::New(iso, g1s1.handle));
}
v8::internal::Heap* heap =
reinterpret_cast<v8::internal::Isolate*>(iso)->heap();
heap->CollectAllGarbage();
// All objects should be alive.
CHECK_EQ(0, counter.NumberOfWeakCalls());
// Weaken the root.
root.handle.SetWeak(&root, &WeakPointerCallback,
v8::WeakCallbackType::kParameter);
root.handle.MarkPartiallyDependent();
// Groups are deleted, rebuild groups.
{
HandleScope handle_scope(iso);
g1s1.handle.MarkPartiallyDependent();
g1s2.handle.MarkPartiallyDependent();
g2s1.handle.MarkPartiallyDependent();
g2s2.handle.MarkPartiallyDependent();
g3s1.handle.MarkPartiallyDependent();
g3s2.handle.MarkPartiallyDependent();
iso->SetObjectGroupId(g1s1.handle, UniqueId(1));
iso->SetObjectGroupId(g1s2.handle, UniqueId(1));
Local<Object>::New(iso, g1s1.handle.As<Object>())
->Set(v8_str("x"), Local<Value>::New(iso, g2s1.handle));
iso->SetObjectGroupId(g2s1.handle, UniqueId(2));
iso->SetObjectGroupId(g2s2.handle, UniqueId(2));
Local<Object>::New(iso, g2s1.handle.As<Object>())
->Set(v8_str("x"), Local<Value>::New(iso, g3s1.handle));
iso->SetObjectGroupId(g3s1.handle, UniqueId(3));
iso->SetObjectGroupId(g3s2.handle, UniqueId(3));
Local<Object>::New(iso, g3s1.handle.As<Object>())
->Set(v8_str("x"), Local<Value>::New(iso, g1s1.handle));
}
heap->CollectAllGarbage();
// All objects should be gone. 7 global handles in total.
CHECK_EQ(7, counter.NumberOfWeakCalls());
}
THREADED_TEST(ScriptException) {
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
Local<Script> script = v8_compile("throw 'panama!';");
v8::TryCatch try_catch(env->GetIsolate());
Local<Value> result = script->Run();
CHECK(result.IsEmpty());
CHECK(try_catch.HasCaught());
String::Utf8Value exception_value(try_catch.Exception());
CHECK_EQ(0, strcmp(*exception_value, "panama!"));
}
TEST(TryCatchCustomException) {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
v8::TryCatch try_catch(isolate);
CompileRun(
"function CustomError() { this.a = 'b'; }"
"(function f() { throw new CustomError(); })();");
CHECK(try_catch.HasCaught());
CHECK(try_catch.Exception()
->ToObject(isolate)
->Get(v8_str("a"))
->Equals(v8_str("b")));
}
bool message_received;
static void check_message_0(v8::Handle<v8::Message> message,
v8::Handle<Value> data) {
CHECK_EQ(5.76, data->NumberValue());
CHECK_EQ(6.75, message->GetScriptOrigin().ResourceName()->NumberValue());
CHECK(!message->IsSharedCrossOrigin());
message_received = true;
}
THREADED_TEST(MessageHandler0) {
message_received = false;
v8::HandleScope scope(CcTest::isolate());
CHECK(!message_received);
LocalContext context;
v8::V8::AddMessageListener(check_message_0, v8_num(5.76));
v8::Handle<v8::Script> script = CompileWithOrigin("throw 'error'", "6.75");
script->Run();
CHECK(message_received);
// clear out the message listener
v8::V8::RemoveMessageListeners(check_message_0);
}
static void check_message_1(v8::Handle<v8::Message> message,
v8::Handle<Value> data) {
CHECK(data->IsNumber());
CHECK_EQ(1337, data->Int32Value());
CHECK(!message->IsSharedCrossOrigin());
message_received = true;
}
TEST(MessageHandler1) {
message_received = false;
v8::HandleScope scope(CcTest::isolate());
CHECK(!message_received);
v8::V8::AddMessageListener(check_message_1);
LocalContext context;
CompileRun("throw 1337;");
CHECK(message_received);
// clear out the message listener
v8::V8::RemoveMessageListeners(check_message_1);
}
static void check_message_2(v8::Handle<v8::Message> message,
v8::Handle<Value> data) {
LocalContext context;
CHECK(data->IsObject());
v8::Local<v8::Value> hidden_property =
v8::Object::Cast(*data)->GetHiddenValue(v8_str("hidden key"));
CHECK(v8_str("hidden value")->Equals(hidden_property));
CHECK(!message->IsSharedCrossOrigin());
message_received = true;
}
TEST(MessageHandler2) {
message_received = false;
v8::HandleScope scope(CcTest::isolate());
CHECK(!message_received);
v8::V8::AddMessageListener(check_message_2);
LocalContext context;
v8::Local<v8::Value> error = v8::Exception::Error(v8_str("custom error"));
v8::Object::Cast(*error)
->SetHiddenValue(v8_str("hidden key"), v8_str("hidden value"));
context->Global()->Set(v8_str("error"), error);
CompileRun("throw error;");
CHECK(message_received);
// clear out the message listener
v8::V8::RemoveMessageListeners(check_message_2);
}
static void check_message_3(v8::Handle<v8::Message> message,
v8::Handle<Value> data) {
CHECK(message->IsSharedCrossOrigin());
CHECK(message->GetScriptOrigin().Options().IsSharedCrossOrigin());
CHECK(message->GetScriptOrigin().Options().IsEmbedderDebugScript());
CHECK(message->GetScriptOrigin().Options().IsOpaque());
CHECK_EQ(6.75, message->GetScriptOrigin().ResourceName()->NumberValue());
CHECK_EQ(7.40, message->GetScriptOrigin().SourceMapUrl()->NumberValue());
message_received = true;
}
TEST(MessageHandler3) {
message_received = false;
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
CHECK(!message_received);
v8::V8::AddMessageListener(check_message_3);
LocalContext context;
v8::ScriptOrigin origin = v8::ScriptOrigin(
v8_str("6.75"), v8::Integer::New(isolate, 1),
v8::Integer::New(isolate, 2), v8::True(isolate), Handle<v8::Integer>(),
v8::True(isolate), v8_str("7.40"), v8::True(isolate));
v8::Handle<v8::Script> script =
Script::Compile(v8_str("throw 'error'"), &origin);
script->Run();
CHECK(message_received);
// clear out the message listener
v8::V8::RemoveMessageListeners(check_message_3);
}
static void check_message_4(v8::Handle<v8::Message> message,
v8::Handle<Value> data) {
CHECK(!message->IsSharedCrossOrigin());
CHECK_EQ(6.75, message->GetScriptOrigin().ResourceName()->NumberValue());
message_received = true;
}
TEST(MessageHandler4) {
message_received = false;
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
CHECK(!message_received);
v8::V8::AddMessageListener(check_message_4);
LocalContext context;
v8::ScriptOrigin origin =
v8::ScriptOrigin(v8_str("6.75"), v8::Integer::New(isolate, 1),
v8::Integer::New(isolate, 2), v8::False(isolate));
v8::Handle<v8::Script> script =
Script::Compile(v8_str("throw 'error'"), &origin);
script->Run();
CHECK(message_received);
// clear out the message listener
v8::V8::RemoveMessageListeners(check_message_4);
}
static void check_message_5a(v8::Handle<v8::Message> message,
v8::Handle<Value> data) {
CHECK(message->IsSharedCrossOrigin());
CHECK_EQ(6.75, message->GetScriptOrigin().ResourceName()->NumberValue());
message_received = true;
}
static void check_message_5b(v8::Handle<v8::Message> message,
v8::Handle<Value> data) {
CHECK(!message->IsSharedCrossOrigin());
CHECK_EQ(6.75, message->GetScriptOrigin().ResourceName()->NumberValue());
message_received = true;
}
TEST(MessageHandler5) {
message_received = false;
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
CHECK(!message_received);
v8::V8::AddMessageListener(check_message_5a);
LocalContext context;
v8::ScriptOrigin origin1 =
v8::ScriptOrigin(v8_str("6.75"), v8::Integer::New(isolate, 1),
v8::Integer::New(isolate, 2), v8::True(isolate));
v8::Handle<v8::Script> script =
Script::Compile(v8_str("throw 'error'"), &origin1);
script->Run();
CHECK(message_received);
// clear out the message listener
v8::V8::RemoveMessageListeners(check_message_5a);
message_received = false;
v8::V8::AddMessageListener(check_message_5b);
v8::ScriptOrigin origin2 =
v8::ScriptOrigin(v8_str("6.75"), v8::Integer::New(isolate, 1),
v8::Integer::New(isolate, 2), v8::False(isolate));
script = Script::Compile(v8_str("throw 'error'"), &origin2);
script->Run();
CHECK(message_received);
// clear out the message listener
v8::V8::RemoveMessageListeners(check_message_5b);
}
TEST(NativeWeakMap) {
v8::Isolate* isolate = CcTest::isolate();
HandleScope scope(isolate);
Local<v8::NativeWeakMap> weak_map(v8::NativeWeakMap::New(isolate));
CHECK(!weak_map.IsEmpty());
LocalContext env;
Local<Object> value = v8::Object::New(isolate);
Local<Object> local1 = v8::Object::New(isolate);
CHECK(!weak_map->Has(local1));
CHECK(weak_map->Get(local1)->IsUndefined());
weak_map->Set(local1, value);
CHECK(weak_map->Has(local1));
CHECK(value->Equals(weak_map->Get(local1)));
WeakCallCounter counter(1234);
WeakCallCounterAndPersistent<Value> o1(&counter);
WeakCallCounterAndPersistent<Value> o2(&counter);
WeakCallCounterAndPersistent<Value> s1(&counter);
{
HandleScope scope(isolate);
Local<v8::Object> obj1 = v8::Object::New(isolate);
Local<v8::Object> obj2 = v8::Object::New(isolate);
Local<v8::Symbol> sym1 = v8::Symbol::New(isolate);
weak_map->Set(obj1, value);
weak_map->Set(obj2, value);
weak_map->Set(sym1, value);
o1.handle.Reset(isolate, obj1);
o2.handle.Reset(isolate, obj2);
s1.handle.Reset(isolate, sym1);
CHECK(weak_map->Has(local1));
CHECK(weak_map->Has(obj1));
CHECK(weak_map->Has(obj2));
CHECK(weak_map->Has(sym1));
CHECK(value->Equals(weak_map->Get(local1)));
CHECK(value->Equals(weak_map->Get(obj1)));
CHECK(value->Equals(weak_map->Get(obj2)));
CHECK(value->Equals(weak_map->Get(sym1)));
}
CcTest::heap()->CollectAllGarbage();
{
HandleScope scope(isolate);
CHECK(value->Equals(weak_map->Get(local1)));
CHECK(value->Equals(weak_map->Get(Local<Value>::New(isolate, o1.handle))));
CHECK(value->Equals(weak_map->Get(Local<Value>::New(isolate, o2.handle))));
CHECK(value->Equals(weak_map->Get(Local<Value>::New(isolate, s1.handle))));
}
o1.handle.SetWeak(&o1, &WeakPointerCallback,
v8::WeakCallbackType::kParameter);
o2.handle.SetWeak(&o2, &WeakPointerCallback,
v8::WeakCallbackType::kParameter);
s1.handle.SetWeak(&s1, &WeakPointerCallback,
v8::WeakCallbackType::kParameter);
CcTest::heap()->CollectAllGarbage();
CHECK_EQ(3, counter.NumberOfWeakCalls());
CHECK(o1.handle.IsEmpty());
CHECK(o2.handle.IsEmpty());
CHECK(s1.handle.IsEmpty());
CHECK(value->Equals(weak_map->Get(local1)));
CHECK(weak_map->Delete(local1));
CHECK(!weak_map->Has(local1));
CHECK(weak_map->Get(local1)->IsUndefined());
}
THREADED_TEST(GetSetProperty) {
LocalContext context;
v8::Isolate* isolate = context->GetIsolate();
v8::HandleScope scope(isolate);
context->Global()->Set(v8_str("foo"), v8_num(14));
context->Global()->Set(v8_str("12"), v8_num(92));
context->Global()->Set(v8::Integer::New(isolate, 16), v8_num(32));
context->Global()->Set(v8_num(13), v8_num(56));
Local<Value> foo = CompileRun("this.foo");
CHECK_EQ(14, foo->Int32Value());
Local<Value> twelve = CompileRun("this[12]");
CHECK_EQ(92, twelve->Int32Value());
Local<Value> sixteen = CompileRun("this[16]");
CHECK_EQ(32, sixteen->Int32Value());
Local<Value> thirteen = CompileRun("this[13]");
CHECK_EQ(56, thirteen->Int32Value());
CHECK_EQ(92,
context->Global()->Get(v8::Integer::New(isolate, 12))->Int32Value());
CHECK_EQ(92, context->Global()->Get(v8_str("12"))->Int32Value());
CHECK_EQ(92, context->Global()->Get(v8_num(12))->Int32Value());
CHECK_EQ(32,
context->Global()->Get(v8::Integer::New(isolate, 16))->Int32Value());
CHECK_EQ(32, context->Global()->Get(v8_str("16"))->Int32Value());
CHECK_EQ(32, context->Global()->Get(v8_num(16))->Int32Value());
CHECK_EQ(56,
context->Global()->Get(v8::Integer::New(isolate, 13))->Int32Value());
CHECK_EQ(56, context->Global()->Get(v8_str("13"))->Int32Value());
CHECK_EQ(56, context->Global()->Get(v8_num(13))->Int32Value());
}
THREADED_TEST(PropertyAttributes) {
LocalContext context;
v8::HandleScope scope(context->GetIsolate());
// none
Local<String> prop = v8_str("none");
context->Global()->Set(prop, v8_num(7));
CHECK_EQ(v8::None, context->Global()->GetPropertyAttributes(prop));
// read-only
prop = v8_str("read_only");
context->Global()->ForceSet(prop, v8_num(7), v8::ReadOnly);
CHECK_EQ(7, context->Global()->Get(prop)->Int32Value());
CHECK_EQ(v8::ReadOnly, context->Global()->GetPropertyAttributes(prop));
CompileRun("read_only = 9");
CHECK_EQ(7, context->Global()->Get(prop)->Int32Value());
context->Global()->Set(prop, v8_num(10));
CHECK_EQ(7, context->Global()->Get(prop)->Int32Value());
// dont-delete
prop = v8_str("dont_delete");
context->Global()->ForceSet(prop, v8_num(13), v8::DontDelete);
CHECK_EQ(13, context->Global()->Get(prop)->Int32Value());
CompileRun("delete dont_delete");
CHECK_EQ(13, context->Global()->Get(prop)->Int32Value());
CHECK_EQ(v8::DontDelete, context->Global()->GetPropertyAttributes(prop));
// dont-enum
prop = v8_str("dont_enum");
context->Global()->ForceSet(prop, v8_num(28), v8::DontEnum);
CHECK_EQ(v8::DontEnum, context->Global()->GetPropertyAttributes(prop));
// absent
prop = v8_str("absent");
CHECK_EQ(v8::None, context->Global()->GetPropertyAttributes(prop));
Local<Value> fake_prop = v8_num(1);
CHECK_EQ(v8::None, context->Global()->GetPropertyAttributes(fake_prop));
// exception
TryCatch try_catch(context->GetIsolate());
Local<Value> exception =
CompileRun("({ toString: function() { throw 'exception';} })");
CHECK_EQ(v8::None, context->Global()->GetPropertyAttributes(exception));
CHECK(try_catch.HasCaught());
String::Utf8Value exception_value(try_catch.Exception());
CHECK_EQ(0, strcmp("exception", *exception_value));
try_catch.Reset();
}
THREADED_TEST(Array) {
LocalContext context;
v8::HandleScope scope(context->GetIsolate());
Local<v8::Array> array = v8::Array::New(context->GetIsolate());
CHECK_EQ(0u, array->Length());
CHECK(array->Get(0)->IsUndefined());
CHECK(!array->Has(0));
CHECK(array->Get(100)->IsUndefined());
CHECK(!array->Has(100));
array->Set(2, v8_num(7));
CHECK_EQ(3u, array->Length());
CHECK(!array->Has(0));
CHECK(!array->Has(1));
CHECK(array->Has(2));
CHECK_EQ(7, array->Get(2)->Int32Value());
Local<Value> obj = CompileRun("[1, 2, 3]");
Local<v8::Array> arr = obj.As<v8::Array>();
CHECK_EQ(3u, arr->Length());
CHECK_EQ(1, arr->Get(0)->Int32Value());
CHECK_EQ(2, arr->Get(1)->Int32Value());
CHECK_EQ(3, arr->Get(2)->Int32Value());
array = v8::Array::New(context->GetIsolate(), 27);
CHECK_EQ(27u, array->Length());
array = v8::Array::New(context->GetIsolate(), -27);
CHECK_EQ(0u, array->Length());
}
void HandleF(const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::EscapableHandleScope scope(args.GetIsolate());
ApiTestFuzzer::Fuzz();
Local<v8::Array> result = v8::Array::New(args.GetIsolate(), args.Length());
for (int i = 0; i < args.Length(); i++) result->Set(i, args[i]);
args.GetReturnValue().Set(scope.Escape(result));
}
THREADED_TEST(Vector) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
Local<ObjectTemplate> global = ObjectTemplate::New(isolate);
global->Set(v8_str("f"), v8::FunctionTemplate::New(isolate, HandleF));
LocalContext context(0, global);
const char* fun = "f()";
Local<v8::Array> a0 = CompileRun(fun).As<v8::Array>();
CHECK_EQ(0u, a0->Length());
const char* fun2 = "f(11)";
Local<v8::Array> a1 = CompileRun(fun2).As<v8::Array>();
CHECK_EQ(1u, a1->Length());
CHECK_EQ(11, a1->Get(0)->Int32Value());
const char* fun3 = "f(12, 13)";
Local<v8::Array> a2 = CompileRun(fun3).As<v8::Array>();
CHECK_EQ(2u, a2->Length());
CHECK_EQ(12, a2->Get(0)->Int32Value());
CHECK_EQ(13, a2->Get(1)->Int32Value());
const char* fun4 = "f(14, 15, 16)";
Local<v8::Array> a3 = CompileRun(fun4).As<v8::Array>();
CHECK_EQ(3u, a3->Length());
CHECK_EQ(14, a3->Get(0)->Int32Value());
CHECK_EQ(15, a3->Get(1)->Int32Value());
CHECK_EQ(16, a3->Get(2)->Int32Value());
const char* fun5 = "f(17, 18, 19, 20)";
Local<v8::Array> a4 = CompileRun(fun5).As<v8::Array>();
CHECK_EQ(4u, a4->Length());
CHECK_EQ(17, a4->Get(0)->Int32Value());
CHECK_EQ(18, a4->Get(1)->Int32Value());
CHECK_EQ(19, a4->Get(2)->Int32Value());
CHECK_EQ(20, a4->Get(3)->Int32Value());
}
THREADED_TEST(FunctionCall) {
LocalContext context;
v8::Isolate* isolate = context->GetIsolate();
v8::HandleScope scope(isolate);
CompileRun(
"function Foo() {"
" var result = [];"
" for (var i = 0; i < arguments.length; i++) {"
" result.push(arguments[i]);"
" }"
" return result;"
"}"
"function ReturnThisSloppy() {"
" return this;"
"}"
"function ReturnThisStrict() {"
" 'use strict';"
" return this;"
"}");
Local<Function> Foo =
Local<Function>::Cast(context->Global()->Get(v8_str("Foo")));
Local<Function> ReturnThisSloppy =
Local<Function>::Cast(context->Global()->Get(v8_str("ReturnThisSloppy")));
Local<Function> ReturnThisStrict =
Local<Function>::Cast(context->Global()->Get(v8_str("ReturnThisStrict")));
v8::Handle<Value>* args0 = NULL;
Local<v8::Array> a0 = Local<v8::Array>::Cast(Foo->Call(Foo, 0, args0));
CHECK_EQ(0u, a0->Length());
v8::Handle<Value> args1[] = {v8_num(1.1)};
Local<v8::Array> a1 = Local<v8::Array>::Cast(Foo->Call(Foo, 1, args1));
CHECK_EQ(1u, a1->Length());
CHECK_EQ(1.1, a1->Get(v8::Integer::New(isolate, 0))->NumberValue());
v8::Handle<Value> args2[] = {v8_num(2.2), v8_num(3.3)};
Local<v8::Array> a2 = Local<v8::Array>::Cast(Foo->Call(Foo, 2, args2));
CHECK_EQ(2u, a2->Length());
CHECK_EQ(2.2, a2->Get(v8::Integer::New(isolate, 0))->NumberValue());
CHECK_EQ(3.3, a2->Get(v8::Integer::New(isolate, 1))->NumberValue());
v8::Handle<Value> args3[] = {v8_num(4.4), v8_num(5.5), v8_num(6.6)};
Local<v8::Array> a3 = Local<v8::Array>::Cast(Foo->Call(Foo, 3, args3));
CHECK_EQ(3u, a3->Length());
CHECK_EQ(4.4, a3->Get(v8::Integer::New(isolate, 0))->NumberValue());
CHECK_EQ(5.5, a3->Get(v8::Integer::New(isolate, 1))->NumberValue());
CHECK_EQ(6.6, a3->Get(v8::Integer::New(isolate, 2))->NumberValue());
v8::Handle<Value> args4[] = {v8_num(7.7), v8_num(8.8), v8_num(9.9),
v8_num(10.11)};
Local<v8::Array> a4 = Local<v8::Array>::Cast(Foo->Call(Foo, 4, args4));
CHECK_EQ(4u, a4->Length());
CHECK_EQ(7.7, a4->Get(v8::Integer::New(isolate, 0))->NumberValue());
CHECK_EQ(8.8, a4->Get(v8::Integer::New(isolate, 1))->NumberValue());
CHECK_EQ(9.9, a4->Get(v8::Integer::New(isolate, 2))->NumberValue());
CHECK_EQ(10.11, a4->Get(v8::Integer::New(isolate, 3))->NumberValue());
Local<v8::Value> r1 = ReturnThisSloppy->Call(v8::Undefined(isolate), 0, NULL);
CHECK(r1->StrictEquals(context->Global()));
Local<v8::Value> r2 = ReturnThisSloppy->Call(v8::Null(isolate), 0, NULL);
CHECK(r2->StrictEquals(context->Global()));
Local<v8::Value> r3 = ReturnThisSloppy->Call(v8_num(42), 0, NULL);
CHECK(r3->IsNumberObject());
CHECK_EQ(42.0, r3.As<v8::NumberObject>()->ValueOf());
Local<v8::Value> r4 = ReturnThisSloppy->Call(v8_str("hello"), 0, NULL);
CHECK(r4->IsStringObject());
CHECK(r4.As<v8::StringObject>()->ValueOf()->StrictEquals(v8_str("hello")));
Local<v8::Value> r5 = ReturnThisSloppy->Call(v8::True(isolate), 0, NULL);
CHECK(r5->IsBooleanObject());
CHECK(r5.As<v8::BooleanObject>()->ValueOf());
Local<v8::Value> r6 = ReturnThisStrict->Call(v8::Undefined(isolate), 0, NULL);
CHECK(r6->IsUndefined());
Local<v8::Value> r7 = ReturnThisStrict->Call(v8::Null(isolate), 0, NULL);
CHECK(r7->IsNull());
Local<v8::Value> r8 = ReturnThisStrict->Call(v8_num(42), 0, NULL);
CHECK(r8->StrictEquals(v8_num(42)));
Local<v8::Value> r9 = ReturnThisStrict->Call(v8_str("hello"), 0, NULL);
CHECK(r9->StrictEquals(v8_str("hello")));
Local<v8::Value> r10 = ReturnThisStrict->Call(v8::True(isolate), 0, NULL);
CHECK(r10->StrictEquals(v8::True(isolate)));
}
THREADED_TEST(ConstructCall) {
LocalContext context;
v8::Isolate* isolate = context->GetIsolate();
v8::HandleScope scope(isolate);
CompileRun(
"function Foo() {"
" var result = [];"
" for (var i = 0; i < arguments.length; i++) {"
" result.push(arguments[i]);"
" }"
" return result;"
"}");
Local<Function> Foo =
Local<Function>::Cast(context->Global()->Get(v8_str("Foo")));
v8::Handle<Value>* args0 = NULL;
Local<v8::Array> a0 = Local<v8::Array>::Cast(Foo->NewInstance(0, args0));
CHECK_EQ(0u, a0->Length());
v8::Handle<Value> args1[] = {v8_num(1.1)};
Local<v8::Array> a1 = Local<v8::Array>::Cast(Foo->NewInstance(1, args1));
CHECK_EQ(1u, a1->Length());
CHECK_EQ(1.1, a1->Get(v8::Integer::New(isolate, 0))->NumberValue());
v8::Handle<Value> args2[] = {v8_num(2.2), v8_num(3.3)};
Local<v8::Array> a2 = Local<v8::Array>::Cast(Foo->NewInstance(2, args2));
CHECK_EQ(2u, a2->Length());
CHECK_EQ(2.2, a2->Get(v8::Integer::New(isolate, 0))->NumberValue());
CHECK_EQ(3.3, a2->Get(v8::Integer::New(isolate, 1))->NumberValue());
v8::Handle<Value> args3[] = {v8_num(4.4), v8_num(5.5), v8_num(6.6)};
Local<v8::Array> a3 = Local<v8::Array>::Cast(Foo->NewInstance(3, args3));
CHECK_EQ(3u, a3->Length());
CHECK_EQ(4.4, a3->Get(v8::Integer::New(isolate, 0))->NumberValue());
CHECK_EQ(5.5, a3->Get(v8::Integer::New(isolate, 1))->NumberValue());
CHECK_EQ(6.6, a3->Get(v8::Integer::New(isolate, 2))->NumberValue());
v8::Handle<Value> args4[] = {v8_num(7.7), v8_num(8.8), v8_num(9.9),
v8_num(10.11)};
Local<v8::Array> a4 = Local<v8::Array>::Cast(Foo->NewInstance(4, args4));
CHECK_EQ(4u, a4->Length());
CHECK_EQ(7.7, a4->Get(v8::Integer::New(isolate, 0))->NumberValue());
CHECK_EQ(8.8, a4->Get(v8::Integer::New(isolate, 1))->NumberValue());
CHECK_EQ(9.9, a4->Get(v8::Integer::New(isolate, 2))->NumberValue());
CHECK_EQ(10.11, a4->Get(v8::Integer::New(isolate, 3))->NumberValue());
}
static void CheckUncle(v8::TryCatch* try_catch) {
CHECK(try_catch->HasCaught());
String::Utf8Value str_value(try_catch->Exception());
CHECK_EQ(0, strcmp(*str_value, "uncle?"));
try_catch->Reset();
}
THREADED_TEST(ConversionNumber) {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
// Very large number.
CompileRun("var obj = Math.pow(2,32) * 1237;");
Local<Value> obj = env->Global()->Get(v8_str("obj"));
CHECK_EQ(5312874545152.0, obj->ToNumber(isolate)->Value());
CHECK_EQ(0, obj->ToInt32(isolate)->Value());
CHECK(0u ==
obj->ToUint32(isolate)->Value()); // NOLINT - no CHECK_EQ for unsigned.
// Large number.
CompileRun("var obj = -1234567890123;");
obj = env->Global()->Get(v8_str("obj"));
CHECK_EQ(-1234567890123.0, obj->ToNumber(isolate)->Value());
CHECK_EQ(-1912276171, obj->ToInt32(isolate)->Value());
CHECK(2382691125u == obj->ToUint32(isolate)->Value()); // NOLINT
// Small positive integer.
CompileRun("var obj = 42;");
obj = env->Global()->Get(v8_str("obj"));
CHECK_EQ(42.0, obj->ToNumber(isolate)->Value());
CHECK_EQ(42, obj->ToInt32(isolate)->Value());
CHECK(42u == obj->ToUint32(isolate)->Value()); // NOLINT
// Negative integer.
CompileRun("var obj = -37;");
obj = env->Global()->Get(v8_str("obj"));
CHECK_EQ(-37.0, obj->ToNumber(isolate)->Value());
CHECK_EQ(-37, obj->ToInt32(isolate)->Value());
CHECK(4294967259u == obj->ToUint32(isolate)->Value()); // NOLINT
// Positive non-int32 integer.
CompileRun("var obj = 0x81234567;");
obj = env->Global()->Get(v8_str("obj"));
CHECK_EQ(2166572391.0, obj->ToNumber(isolate)->Value());
CHECK_EQ(-2128394905, obj->ToInt32(isolate)->Value());
CHECK(2166572391u == obj->ToUint32(isolate)->Value()); // NOLINT
// Fraction.
CompileRun("var obj = 42.3;");
obj = env->Global()->Get(v8_str("obj"));
CHECK_EQ(42.3, obj->ToNumber(isolate)->Value());
CHECK_EQ(42, obj->ToInt32(isolate)->Value());
CHECK(42u == obj->ToUint32(isolate)->Value()); // NOLINT
// Large negative fraction.
CompileRun("var obj = -5726623061.75;");
obj = env->Global()->Get(v8_str("obj"));
CHECK_EQ(-5726623061.75, obj->ToNumber(isolate)->Value());
CHECK_EQ(-1431655765, obj->ToInt32(isolate)->Value());
CHECK(2863311531u == obj->ToUint32(isolate)->Value()); // NOLINT
}
THREADED_TEST(isNumberType) {
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
// Very large number.
CompileRun("var obj = Math.pow(2,32) * 1237;");
Local<Value> obj = env->Global()->Get(v8_str("obj"));
CHECK(!obj->IsInt32());
CHECK(!obj->IsUint32());
// Large negative number.
CompileRun("var obj = -1234567890123;");
obj = env->Global()->Get(v8_str("obj"));
CHECK(!obj->IsInt32());
CHECK(!obj->IsUint32());
// Small positive integer.
CompileRun("var obj = 42;");
obj = env->Global()->Get(v8_str("obj"));
CHECK(obj->IsInt32());
CHECK(obj->IsUint32());
// Negative integer.
CompileRun("var obj = -37;");
obj = env->Global()->Get(v8_str("obj"));
CHECK(obj->IsInt32());
CHECK(!obj->IsUint32());
// Positive non-int32 integer.
CompileRun("var obj = 0x81234567;");
obj = env->Global()->Get(v8_str("obj"));
CHECK(!obj->IsInt32());
CHECK(obj->IsUint32());
// Fraction.
CompileRun("var obj = 42.3;");
obj = env->Global()->Get(v8_str("obj"));
CHECK(!obj->IsInt32());
CHECK(!obj->IsUint32());
// Large negative fraction.
CompileRun("var obj = -5726623061.75;");
obj = env->Global()->Get(v8_str("obj"));
CHECK(!obj->IsInt32());
CHECK(!obj->IsUint32());
// Positive zero
CompileRun("var obj = 0.0;");
obj = env->Global()->Get(v8_str("obj"));
CHECK(obj->IsInt32());
CHECK(obj->IsUint32());
// Positive zero
CompileRun("var obj = -0.0;");
obj = env->Global()->Get(v8_str("obj"));
CHECK(!obj->IsInt32());
CHECK(!obj->IsUint32());
}
THREADED_TEST(ConversionException) {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
CompileRun(
"function TestClass() { };"
"TestClass.prototype.toString = function () { throw 'uncle?'; };"
"var obj = new TestClass();");
Local<Value> obj = env->Global()->Get(v8_str("obj"));
v8::TryCatch try_catch(isolate);
Local<Value> to_string_result = obj->ToString(isolate);
CHECK(to_string_result.IsEmpty());
CheckUncle(&try_catch);
Local<Value> to_number_result = obj->ToNumber(isolate);
CHECK(to_number_result.IsEmpty());
CheckUncle(&try_catch);
Local<Value> to_integer_result = obj->ToInteger(isolate);
CHECK(to_integer_result.IsEmpty());
CheckUncle(&try_catch);
Local<Value> to_uint32_result = obj->ToUint32(isolate);
CHECK(to_uint32_result.IsEmpty());
CheckUncle(&try_catch);
Local<Value> to_int32_result = obj->ToInt32(isolate);
CHECK(to_int32_result.IsEmpty());
CheckUncle(&try_catch);
Local<Value> to_object_result = v8::Undefined(isolate)->ToObject(isolate);
CHECK(to_object_result.IsEmpty());
CHECK(try_catch.HasCaught());
try_catch.Reset();
int32_t int32_value = obj->Int32Value();
CHECK_EQ(0, int32_value);
CheckUncle(&try_catch);
uint32_t uint32_value = obj->Uint32Value();
CHECK_EQ(0u, uint32_value);
CheckUncle(&try_catch);
double number_value = obj->NumberValue();
CHECK(std::isnan(number_value));
CheckUncle(&try_catch);
int64_t integer_value = obj->IntegerValue();
CHECK_EQ(0, integer_value);
CheckUncle(&try_catch);
}
void ThrowFromC(const v8::FunctionCallbackInfo<v8::Value>& args) {
ApiTestFuzzer::Fuzz();
args.GetIsolate()->ThrowException(v8_str("konto"));
}
void CCatcher(const v8::FunctionCallbackInfo<v8::Value>& args) {
if (args.Length() < 1) {
args.GetReturnValue().Set(false);
return;
}
v8::HandleScope scope(args.GetIsolate());
v8::TryCatch try_catch(args.GetIsolate());
Local<Value> result = CompileRun(args[0]->ToString(args.GetIsolate()));
CHECK(!try_catch.HasCaught() || result.IsEmpty());
args.GetReturnValue().Set(try_catch.HasCaught());
}
THREADED_TEST(APICatch) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
templ->Set(v8_str("ThrowFromC"),
v8::FunctionTemplate::New(isolate, ThrowFromC));
LocalContext context(0, templ);
CompileRun(
"var thrown = false;"
"try {"
" ThrowFromC();"
"} catch (e) {"
" thrown = true;"
"}");
Local<Value> thrown = context->Global()->Get(v8_str("thrown"));
CHECK(thrown->BooleanValue());
}
THREADED_TEST(APIThrowTryCatch) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
templ->Set(v8_str("ThrowFromC"),
v8::FunctionTemplate::New(isolate, ThrowFromC));
LocalContext context(0, templ);
v8::TryCatch try_catch(isolate);
CompileRun("ThrowFromC();");
CHECK(try_catch.HasCaught());
}
// Test that a try-finally block doesn't shadow a try-catch block
// when setting up an external handler.
//
// BUG(271): Some of the exception propagation does not work on the
// ARM simulator because the simulator separates the C++ stack and the
// JS stack. This test therefore fails on the simulator. The test is
// not threaded to allow the threading tests to run on the simulator.
TEST(TryCatchInTryFinally) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
templ->Set(v8_str("CCatcher"), v8::FunctionTemplate::New(isolate, CCatcher));
LocalContext context(0, templ);
Local<Value> result = CompileRun(
"try {"
" try {"
" CCatcher('throw 7;');"
" } finally {"
" }"
"} catch (e) {"
"}");
CHECK(result->IsTrue());
}
static void check_reference_error_message(v8::Handle<v8::Message> message,
v8::Handle<v8::Value> data) {
const char* reference_error = "Uncaught ReferenceError: asdf is not defined";
CHECK(message->Get()->Equals(v8_str(reference_error)));
}
static void Fail(const v8::FunctionCallbackInfo<v8::Value>& args) {
ApiTestFuzzer::Fuzz();
CHECK(false);
}
// Test that overwritten methods are not invoked on uncaught exception
// formatting. However, they are invoked when performing normal error
// string conversions.
TEST(APIThrowMessageOverwrittenToString) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::V8::AddMessageListener(check_reference_error_message);
Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
templ->Set(v8_str("fail"), v8::FunctionTemplate::New(isolate, Fail));
LocalContext context(NULL, templ);
CompileRun("asdf;");
CompileRun(
"var limit = {};"
"limit.valueOf = fail;"
"Error.stackTraceLimit = limit;");
CompileRun("asdf");
CompileRun("Array.prototype.pop = fail;");
CompileRun("Object.prototype.hasOwnProperty = fail;");
CompileRun("Object.prototype.toString = function f() { return 'Yikes'; }");
CompileRun("Number.prototype.toString = function f() { return 'Yikes'; }");
CompileRun("String.prototype.toString = function f() { return 'Yikes'; }");
CompileRun(
"ReferenceError.prototype.toString ="
" function() { return 'Whoops' }");
CompileRun("asdf;");
CompileRun("ReferenceError.prototype.constructor.name = void 0;");
CompileRun("asdf;");
CompileRun("ReferenceError.prototype.constructor = void 0;");
CompileRun("asdf;");
CompileRun("ReferenceError.prototype.__proto__ = new Object();");
CompileRun("asdf;");
CompileRun("ReferenceError.prototype = new Object();");
CompileRun("asdf;");
v8::Handle<Value> string = CompileRun("try { asdf; } catch(e) { e + ''; }");
CHECK(string->Equals(v8_str("Whoops")));
CompileRun(
"ReferenceError.prototype.constructor = new Object();"
"ReferenceError.prototype.constructor.name = 1;"
"Number.prototype.toString = function() { return 'Whoops'; };"
"ReferenceError.prototype.toString = Object.prototype.toString;");
CompileRun("asdf;");
v8::V8::RemoveMessageListeners(check_reference_error_message);
}
static void check_custom_error_tostring(v8::Handle<v8::Message> message,
v8::Handle<v8::Value> data) {
const char* uncaught_error = "Uncaught MyError toString";
CHECK(message->Get()->Equals(v8_str(uncaught_error)));
}
TEST(CustomErrorToString) {
LocalContext context;
v8::HandleScope scope(context->GetIsolate());
v8::V8::AddMessageListener(check_custom_error_tostring);
CompileRun(
"function MyError(name, message) { "
" this.name = name; "
" this.message = message; "
"} "
"MyError.prototype = Object.create(Error.prototype); "
"MyError.prototype.toString = function() { "
" return 'MyError toString'; "
"}; "
"throw new MyError('my name', 'my message'); ");
v8::V8::RemoveMessageListeners(check_custom_error_tostring);
}
static void check_custom_error_message(v8::Handle<v8::Message> message,
v8::Handle<v8::Value> data) {
const char* uncaught_error = "Uncaught MyError: my message";
printf("%s\n", *v8::String::Utf8Value(message->Get()));
CHECK(message->Get()->Equals(v8_str(uncaught_error)));
}
TEST(CustomErrorMessage) {
LocalContext context;
v8::HandleScope scope(context->GetIsolate());
v8::V8::AddMessageListener(check_custom_error_message);
// Handlebars.
CompileRun(
"function MyError(msg) { "
" this.name = 'MyError'; "
" this.message = msg; "
"} "
"MyError.prototype = new Error(); "
"throw new MyError('my message'); ");
// Closure.
CompileRun(
"function MyError(msg) { "
" this.name = 'MyError'; "
" this.message = msg; "
"} "
"inherits = function(childCtor, parentCtor) { "
" function tempCtor() {}; "
" tempCtor.prototype = parentCtor.prototype; "
" childCtor.superClass_ = parentCtor.prototype; "
" childCtor.prototype = new tempCtor(); "
" childCtor.prototype.constructor = childCtor; "
"}; "
"inherits(MyError, Error); "
"throw new MyError('my message'); ");
// Object.create.
CompileRun(
"function MyError(msg) { "
" this.name = 'MyError'; "
" this.message = msg; "
"} "
"MyError.prototype = Object.create(Error.prototype); "
"throw new MyError('my message'); ");
v8::V8::RemoveMessageListeners(check_custom_error_message);
}
static void check_custom_rethrowing_message(v8::Handle<v8::Message> message,
v8::Handle<v8::Value> data) {
const char* uncaught_error = "Uncaught exception";
CHECK(message->Get()->Equals(v8_str(uncaught_error)));
}
TEST(CustomErrorRethrowsOnToString) {
LocalContext context;
v8::HandleScope scope(context->GetIsolate());
v8::V8::AddMessageListener(check_custom_rethrowing_message);
CompileRun(
"var e = { toString: function() { throw e; } };"
"try { throw e; } finally {}");
v8::V8::RemoveMessageListeners(check_custom_rethrowing_message);
}
static void receive_message(v8::Handle<v8::Message> message,
v8::Handle<v8::Value> data) {
message->Get();
message_received = true;
}
TEST(APIThrowMessage) {
message_received = false;
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::V8::AddMessageListener(receive_message);
Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
templ->Set(v8_str("ThrowFromC"),
v8::FunctionTemplate::New(isolate, ThrowFromC));
LocalContext context(0, templ);
CompileRun("ThrowFromC();");
CHECK(message_received);
v8::V8::RemoveMessageListeners(receive_message);
}
TEST(APIThrowMessageAndVerboseTryCatch) {
message_received = false;
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::V8::AddMessageListener(receive_message);
Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
templ->Set(v8_str("ThrowFromC"),
v8::FunctionTemplate::New(isolate, ThrowFromC));
LocalContext context(0, templ);
v8::TryCatch try_catch(isolate);
try_catch.SetVerbose(true);
Local<Value> result = CompileRun("ThrowFromC();");
CHECK(try_catch.HasCaught());
CHECK(result.IsEmpty());
CHECK(message_received);
v8::V8::RemoveMessageListeners(receive_message);
}
TEST(APIStackOverflowAndVerboseTryCatch) {
message_received = false;
LocalContext context;
v8::HandleScope scope(context->GetIsolate());
v8::V8::AddMessageListener(receive_message);
v8::TryCatch try_catch(context->GetIsolate());
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::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
templ->Set(v8_str("ThrowFromC"),
v8::FunctionTemplate::New(isolate, ThrowFromC));
LocalContext context(0, templ);
v8::TryCatch try_catch(isolate);
Local<Value> result = CompileRun("ThrowFromC(); throw 'panama';");
CHECK(result.IsEmpty());
CHECK(try_catch.HasCaught());
String::Utf8Value exception_value(try_catch.Exception());
CHECK_EQ(0, strcmp("konto", *exception_value));
}
void CThrowCountDown(const v8::FunctionCallbackInfo<v8::Value>& args) {
ApiTestFuzzer::Fuzz();
CHECK_EQ(4, args.Length());
int count = args[0]->Int32Value();
int cInterval = args[2]->Int32Value();
if (count == 0) {
args.GetIsolate()->ThrowException(v8_str("FromC"));
return;
} else {
Local<v8::Object> global = args.GetIsolate()->GetCurrentContext()->Global();
Local<Value> fun = global->Get(v8_str("JSThrowCountDown"));
v8::Handle<Value> argv[] = {v8_num(count - 1), args[1], args[2], args[3]};
if (count % cInterval == 0) {
v8::TryCatch try_catch(args.GetIsolate());
Local<Value> result = fun.As<Function>()->Call(global, 4, argv);
int expected = args[3]->Int32Value();
if (try_catch.HasCaught()) {
CHECK_EQ(expected, count);
CHECK(result.IsEmpty());
CHECK(!CcTest::i_isolate()->has_scheduled_exception());
} else {
CHECK_NE(expected, count);
}
args.GetReturnValue().Set(result);
return;
} else {
args.GetReturnValue().Set(fun.As<Function>()->Call(global, 4, argv));
return;
}
}
}
void JSCheck(const v8::FunctionCallbackInfo<v8::Value>& args) {
ApiTestFuzzer::Fuzz();
CHECK_EQ(3, args.Length());
bool equality = args[0]->BooleanValue();
int count = args[1]->Int32Value();
int expected = args[2]->Int32Value();
if (equality) {
CHECK_EQ(count, expected);
} else {
CHECK_NE(count, expected);
}
}
THREADED_TEST(EvalInTryFinally) {
LocalContext context;
v8::HandleScope scope(context->GetIsolate());
v8::TryCatch try_catch(context->GetIsolate());
CompileRun(
"(function() {"
" try {"
" eval('asldkf (*&^&*^');"
" } finally {"
" return;"
" }"
"})()");
CHECK(!try_catch.HasCaught());
}
// This test works by making a stack of alternating JavaScript and C
// activations. These activations set up exception handlers with regular
// intervals, one interval for C activations and another for JavaScript
// activations. When enough activations have been created an exception is
// thrown and we check that the right activation catches the exception and that
// no other activations do. The right activation is always the topmost one with
// a handler, regardless of whether it is in JavaScript or C.
//
// The notation used to describe a test case looks like this:
//
// *JS[4] *C[3] @JS[2] C[1] JS[0]
//
// Each entry is an activation, either JS or C. The index is the count at that
// level. Stars identify activations with exception handlers, the @ identifies
// the exception handler that should catch the exception.
//
// BUG(271): Some of the exception propagation does not work on the
// ARM simulator because the simulator separates the C++ stack and the
// JS stack. This test therefore fails on the simulator. The test is
// not threaded to allow the threading tests to run on the simulator.
TEST(ExceptionOrder) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
templ->Set(v8_str("check"), v8::FunctionTemplate::New(isolate, JSCheck));
templ->Set(v8_str("CThrowCountDown"),
v8::FunctionTemplate::New(isolate, CThrowCountDown));
LocalContext context(0, templ);
CompileRun(
"function JSThrowCountDown(count, jsInterval, cInterval, expected) {"
" if (count == 0) throw 'FromJS';"
" if (count % jsInterval == 0) {"
" try {"
" var value = CThrowCountDown(count - 1,"
" jsInterval,"
" cInterval,"
" expected);"
" check(false, count, expected);"
" return value;"
" } catch (e) {"
" check(true, count, expected);"
" }"
" } else {"
" return CThrowCountDown(count - 1, jsInterval, cInterval, expected);"
" }"
"}");
Local<Function> fun =
Local<Function>::Cast(context->Global()->Get(v8_str("JSThrowCountDown")));
const int argc = 4;
// count jsInterval cInterval expected
// *JS[4] *C[3] @JS[2] C[1] JS[0]
v8::Handle<Value> a0[argc] = {v8_num(4), v8_num(2), v8_num(3), v8_num(2)};
fun->Call(fun, argc, a0);
// JS[5] *C[4] JS[3] @C[2] JS[1] C[0]
v8::Handle<Value> a1[argc] = {v8_num(5), v8_num(6), v8_num(1), v8_num(2)};
fun->Call(fun, argc, a1);
// JS[6] @C[5] JS[4] C[3] JS[2] C[1] JS[0]
v8::Handle<Value> a2[argc] = {v8_num(6), v8_num(7), v8_num(5), v8_num(5)};
fun->Call(fun, argc, a2);
// @JS[6] C[5] JS[4] C[3] JS[2] C[1] JS[0]
v8::Handle<Value> a3[argc] = {v8_num(6), v8_num(6), v8_num(7), v8_num(6)};
fun->Call(fun, argc, a3);
// JS[6] *C[5] @JS[4] C[3] JS[2] C[1] JS[0]
v8::Handle<Value> a4[argc] = {v8_num(6), v8_num(4), v8_num(5), v8_num(4)};
fun->Call(fun, argc, a4);
// JS[6] C[5] *JS[4] @C[3] JS[2] C[1] JS[0]
v8::Handle<Value> a5[argc] = {v8_num(6), v8_num(4), v8_num(3), v8_num(3)};
fun->Call(fun, argc, a5);
}
void ThrowValue(const v8::FunctionCallbackInfo<v8::Value>& args) {
ApiTestFuzzer::Fuzz();
CHECK_EQ(1, args.Length());
args.GetIsolate()->ThrowException(args[0]);
}
THREADED_TEST(ThrowValues) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
templ->Set(v8_str("Throw"), v8::FunctionTemplate::New(isolate, ThrowValue));
LocalContext context(0, templ);
v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun(
"function Run(obj) {"
" try {"
" Throw(obj);"
" } catch (e) {"
" return e;"
" }"
" return 'no exception';"
"}"
"[Run('str'), Run(1), Run(0), Run(null), Run(void 0)];"));
CHECK_EQ(5u, result->Length());
CHECK(result->Get(v8::Integer::New(isolate, 0))->IsString());
CHECK(result->Get(v8::Integer::New(isolate, 1))->IsNumber());
CHECK_EQ(1, result->Get(v8::Integer::New(isolate, 1))->Int32Value());
CHECK(result->Get(v8::Integer::New(isolate, 2))->IsNumber());
CHECK_EQ(0, result->Get(v8::Integer::New(isolate, 2))->Int32Value());
CHECK(result->Get(v8::Integer::New(isolate, 3))->IsNull());
CHECK(result->Get(v8::Integer::New(isolate, 4))->IsUndefined());
}
THREADED_TEST(CatchZero) {
LocalContext context;
v8::HandleScope scope(context->GetIsolate());
v8::TryCatch try_catch(context->GetIsolate());
CHECK(!try_catch.HasCaught());
CompileRun("throw 10");
CHECK(try_catch.HasCaught());
CHECK_EQ(10, try_catch.Exception()->Int32Value());
try_catch.Reset();
CHECK(!try_catch.HasCaught());
CompileRun("throw 0");
CHECK(try_catch.HasCaught());
CHECK_EQ(0, try_catch.Exception()->Int32Value());
}
THREADED_TEST(CatchExceptionFromWith) {
LocalContext context;
v8::HandleScope scope(context->GetIsolate());
v8::TryCatch try_catch(context->GetIsolate());
CHECK(!try_catch.HasCaught());
CompileRun("var o = {}; with (o) { throw 42; }");
CHECK(try_catch.HasCaught());
}
THREADED_TEST(TryCatchAndFinallyHidingException) {
LocalContext context;
v8::HandleScope scope(context->GetIsolate());
v8::TryCatch try_catch(context->GetIsolate());
CHECK(!try_catch.HasCaught());
CompileRun("function f(k) { try { this[k]; } finally { return 0; } };");
CompileRun("f({toString: function() { throw 42; }});");
CHECK(!try_catch.HasCaught());
}
void WithTryCatch(const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::TryCatch try_catch(args.GetIsolate());
}
THREADED_TEST(TryCatchAndFinally) {
LocalContext context;
v8::Isolate* isolate = context->GetIsolate();
v8::HandleScope scope(isolate);
context->Global()->Set(
v8_str("native_with_try_catch"),
v8::FunctionTemplate::New(isolate, WithTryCatch)->GetFunction());
v8::TryCatch try_catch(isolate);
CHECK(!try_catch.HasCaught());
CompileRun(
"try {\n"
" throw new Error('a');\n"
"} finally {\n"
" native_with_try_catch();\n"
"}\n");
CHECK(try_catch.HasCaught());
}
static void TryCatchNested1Helper(int depth) {
if (depth > 0) {
v8::TryCatch try_catch(CcTest::isolate());
try_catch.SetVerbose(true);
TryCatchNested1Helper(depth - 1);
CHECK(try_catch.HasCaught());
try_catch.ReThrow();
} else {
CcTest::isolate()->ThrowException(v8_str("E1"));
}
}
static void TryCatchNested2Helper(int depth) {
if (depth > 0) {
v8::TryCatch try_catch(CcTest::isolate());
try_catch.SetVerbose(true);
TryCatchNested2Helper(depth - 1);
CHECK(try_catch.HasCaught());
try_catch.ReThrow();
} else {
CompileRun("throw 'E2';");
}
}
TEST(TryCatchNested) {
v8::V8::Initialize();
LocalContext context;
v8::HandleScope scope(context->GetIsolate());
{
// Test nested try-catch with a native throw in the end.
v8::TryCatch try_catch(context->GetIsolate());
TryCatchNested1Helper(5);
CHECK(try_catch.HasCaught());
CHECK_EQ(0, strcmp(*v8::String::Utf8Value(try_catch.Exception()), "E1"));
}
{
// Test nested try-catch with a JavaScript throw in the end.
v8::TryCatch try_catch(context->GetIsolate());
TryCatchNested2Helper(5);
CHECK(try_catch.HasCaught());
CHECK_EQ(0, strcmp(*v8::String::Utf8Value(try_catch.Exception()), "E2"));
}
}
void TryCatchMixedNestingCheck(v8::TryCatch* try_catch) {
CHECK(try_catch->HasCaught());
Handle<Message> message = try_catch->Message();
Handle<Value> resource = message->GetScriptOrigin().ResourceName();
CHECK_EQ(0, strcmp(*v8::String::Utf8Value(resource), "inner"));
CHECK_EQ(0,
strcmp(*v8::String::Utf8Value(message->Get()), "Uncaught Error: a"));
CHECK_EQ(1, message->GetLineNumber());
CHECK_EQ(0, message->GetStartColumn());
}
void TryCatchMixedNestingHelper(
const v8::FunctionCallbackInfo<v8::Value>& args) {
ApiTestFuzzer::Fuzz();
v8::TryCatch try_catch(args.GetIsolate());
CompileRunWithOrigin("throw new Error('a');\n", "inner", 0, 0);
CHECK(try_catch.HasCaught());
TryCatchMixedNestingCheck(&try_catch);
try_catch.ReThrow();
}
// This test ensures that an outer TryCatch in the following situation:
// C++/TryCatch -> JS -> C++/TryCatch -> JS w/ SyntaxError
// does not clobber the Message object generated for the inner TryCatch.
// This exercises the ability of TryCatch.ReThrow() to restore the
// inner pending Message before throwing the exception again.
TEST(TryCatchMixedNesting) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::V8::Initialize();
v8::TryCatch try_catch(isolate);
Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
templ->Set(v8_str("TryCatchMixedNestingHelper"),
v8::FunctionTemplate::New(isolate, TryCatchMixedNestingHelper));
LocalContext context(0, templ);
CompileRunWithOrigin("TryCatchMixedNestingHelper();\n", "outer", 1, 1);
TryCatchMixedNestingCheck(&try_catch);
}
void TryCatchNativeHelper(const v8::FunctionCallbackInfo<v8::Value>& args) {
ApiTestFuzzer::Fuzz();
v8::TryCatch try_catch(args.GetIsolate());
args.GetIsolate()->ThrowException(v8_str("boom"));
CHECK(try_catch.HasCaught());
}
TEST(TryCatchNative) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::V8::Initialize();
v8::TryCatch try_catch(isolate);
Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
templ->Set(v8_str("TryCatchNativeHelper"),
v8::FunctionTemplate::New(isolate, TryCatchNativeHelper));
LocalContext context(0, templ);
CompileRun("TryCatchNativeHelper();");
CHECK(!try_catch.HasCaught());
}
void TryCatchNativeResetHelper(
const v8::FunctionCallbackInfo<v8::Value>& args) {
ApiTestFuzzer::Fuzz();
v8::TryCatch try_catch(args.GetIsolate());
args.GetIsolate()->ThrowException(v8_str("boom"));
CHECK(try_catch.HasCaught());
try_catch.Reset();
CHECK(!try_catch.HasCaught());
}
TEST(TryCatchNativeReset) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::V8::Initialize();
v8::TryCatch try_catch(isolate);
Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
templ->Set(v8_str("TryCatchNativeResetHelper"),
v8::FunctionTemplate::New(isolate, TryCatchNativeResetHelper));
LocalContext context(0, templ);
CompileRun("TryCatchNativeResetHelper();");
CHECK(!try_catch.HasCaught());
}
THREADED_TEST(Equality) {
LocalContext context;
v8::Isolate* isolate = context->GetIsolate();
v8::HandleScope scope(context->GetIsolate());
// Check that equality works at all before relying on CHECK_EQ
CHECK(v8_str("a")->Equals(v8_str("a")));
CHECK(!v8_str("a")->Equals(v8_str("b")));
CHECK(v8_str("a")->Equals(v8_str("a")));
CHECK(!v8_str("a")->Equals(v8_str("b")));
CHECK(v8_num(1)->Equals(v8_num(1)));
CHECK(v8_num(1.00)->Equals(v8_num(1)));
CHECK(!v8_num(1)->Equals(v8_num(2)));
// Assume String is not internalized.
CHECK(v8_str("a")->StrictEquals(v8_str("a")));
CHECK(!v8_str("a")->StrictEquals(v8_str("b")));
CHECK(!v8_str("5")->StrictEquals(v8_num(5)));
CHECK(v8_num(1)->StrictEquals(v8_num(1)));
CHECK(!v8_num(1)->StrictEquals(v8_num(2)));
CHECK(v8_num(0.0)->StrictEquals(v8_num(-0.0)));
Local<Value> not_a_number = v8_num(std::numeric_limits<double>::quiet_NaN());
CHECK(!not_a_number->StrictEquals(not_a_number));
CHECK(v8::False(isolate)->StrictEquals(v8::False(isolate)));
CHECK(!v8::False(isolate)->StrictEquals(v8::Undefined(isolate)));
v8::Handle<v8::Object> obj = v8::Object::New(isolate);
v8::Persistent<v8::Object> alias(isolate, obj);
CHECK(v8::Local<v8::Object>::New(isolate, alias)->StrictEquals(obj));
alias.Reset();
CHECK(v8_str("a")->SameValue(v8_str("a")));
CHECK(!v8_str("a")->SameValue(v8_str("b")));
CHECK(!v8_str("5")->SameValue(v8_num(5)));
CHECK(v8_num(1)->SameValue(v8_num(1)));
CHECK(!v8_num(1)->SameValue(v8_num(2)));
CHECK(!v8_num(0.0)->SameValue(v8_num(-0.0)));
CHECK(not_a_number->SameValue(not_a_number));
CHECK(v8::False(isolate)->SameValue(v8::False(isolate)));
CHECK(!v8::False(isolate)->SameValue(v8::Undefined(isolate)));
}
THREADED_TEST(MultiRun) {
LocalContext context;
v8::HandleScope scope(context->GetIsolate());
Local<Script> script = v8_compile("x");
for (int i = 0; i < 10; i++) script->Run();
}
static void GetXValue(Local<String> name,
const v8::PropertyCallbackInfo<v8::Value>& info) {
ApiTestFuzzer::Fuzz();
CHECK(info.Data()->Equals(v8_str("donut")));
CHECK(name->Equals(v8_str("x")));
info.GetReturnValue().Set(name);
}
THREADED_TEST(SimplePropertyRead) {
LocalContext context;
v8::Isolate* isolate = context->GetIsolate();
v8::HandleScope scope(isolate);
Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"));
context->Global()->Set(v8_str("obj"), templ->NewInstance());
Local<Script> script = v8_compile("obj.x");
for (int i = 0; i < 10; i++) {
Local<Value> result = script->Run();
CHECK(result->Equals(v8_str("x")));
}
}
THREADED_TEST(DefinePropertyOnAPIAccessor) {
LocalContext context;
v8::Isolate* isolate = context->GetIsolate();
v8::HandleScope scope(isolate);
Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"));
context->Global()->Set(v8_str("obj"), templ->NewInstance());
// Uses getOwnPropertyDescriptor to check the configurable status
Local<Script> script_desc = v8_compile(
"var prop = Object.getOwnPropertyDescriptor( "
"obj, 'x');"
"prop.configurable;");
Local<Value> result = script_desc->Run();
CHECK_EQ(result->BooleanValue(), true);
// Redefine get - but still configurable
Local<Script> script_define = v8_compile(
"var desc = { get: function(){return 42; },"
" configurable: true };"
"Object.defineProperty(obj, 'x', desc);"
"obj.x");
result = script_define->Run();
CHECK(result->Equals(v8_num(42)));
// Check that the accessor is still configurable
result = script_desc->Run();
CHECK_EQ(result->BooleanValue(), true);
// Redefine to a non-configurable
script_define = v8_compile(
"var desc = { get: function(){return 43; },"
" configurable: false };"
"Object.defineProperty(obj, 'x', desc);"
"obj.x");
result = script_define->Run();
CHECK(result->Equals(v8_num(43)));
result = script_desc->Run();
CHECK_EQ(result->BooleanValue(), false);
// Make sure that it is not possible to redefine again
v8::TryCatch try_catch(isolate);
result = script_define->Run();
CHECK(try_catch.HasCaught());
String::Utf8Value exception_value(try_catch.Exception());
CHECK_EQ(0,
strcmp(*exception_value, "TypeError: Cannot redefine property: x"));
}
THREADED_TEST(DefinePropertyOnDefineGetterSetter) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"));
LocalContext context;
context->Global()->Set(v8_str("obj"), templ->NewInstance());
Local<Script> script_desc = v8_compile(
"var prop ="
"Object.getOwnPropertyDescriptor( "
"obj, 'x');"
"prop.configurable;");
Local<Value> result = script_desc->Run();
CHECK_EQ(result->BooleanValue(), true);
Local<Script> script_define = v8_compile(
"var desc = {get: function(){return 42; },"
" configurable: true };"
"Object.defineProperty(obj, 'x', desc);"
"obj.x");
result = script_define->Run();
CHECK(result->Equals(v8_num(42)));
result = script_desc->Run();
CHECK_EQ(result->BooleanValue(), true);
script_define = v8_compile(
"var desc = {get: function(){return 43; },"
" configurable: false };"
"Object.defineProperty(obj, 'x', desc);"
"obj.x");
result = script_define->Run();
CHECK(result->Equals(v8_num(43)));
result = script_desc->Run();
CHECK_EQ(result->BooleanValue(), false);
v8::TryCatch try_catch(isolate);
result = script_define->Run();
CHECK(try_catch.HasCaught());
String::Utf8Value exception_value(try_catch.Exception());
CHECK_EQ(0,
strcmp(*exception_value, "TypeError: Cannot redefine property: x"));
}
static v8::Handle<v8::Object> GetGlobalProperty(LocalContext* context,
char const* name) {
return v8::Handle<v8::Object>::Cast((*context)->Global()->Get(v8_str(name)));
}
THREADED_TEST(DefineAPIAccessorOnObject) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
LocalContext context;
context->Global()->Set(v8_str("obj1"), templ->NewInstance());
CompileRun("var obj2 = {};");
CHECK(CompileRun("obj1.x")->IsUndefined());
CHECK(CompileRun("obj2.x")->IsUndefined());
CHECK(GetGlobalProperty(&context, "obj1")
->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
ExpectString("obj1.x", "x");
CHECK(CompileRun("obj2.x")->IsUndefined());
CHECK(GetGlobalProperty(&context, "obj2")
->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
ExpectString("obj1.x", "x");
ExpectString("obj2.x", "x");
ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
CompileRun(
"Object.defineProperty(obj1, 'x',"
"{ get: function() { return 'y'; }, configurable: true })");
ExpectString("obj1.x", "y");
ExpectString("obj2.x", "x");
CompileRun(
"Object.defineProperty(obj2, 'x',"
"{ get: function() { return 'y'; }, configurable: true })");
ExpectString("obj1.x", "y");
ExpectString("obj2.x", "y");
ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
CHECK(GetGlobalProperty(&context, "obj1")
->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
CHECK(GetGlobalProperty(&context, "obj2")
->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
ExpectString("obj1.x", "x");
ExpectString("obj2.x", "x");
ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
// Define getters/setters, but now make them not configurable.
CompileRun(
"Object.defineProperty(obj1, 'x',"
"{ get: function() { return 'z'; }, configurable: false })");
CompileRun(
"Object.defineProperty(obj2, 'x',"
"{ get: function() { return 'z'; }, configurable: false })");
ExpectTrue("!Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
ExpectTrue("!Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
ExpectString("obj1.x", "z");
ExpectString("obj2.x", "z");
CHECK(!GetGlobalProperty(&context, "obj1")
->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
CHECK(!GetGlobalProperty(&context, "obj2")
->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
ExpectString("obj1.x", "z");
ExpectString("obj2.x", "z");
}
THREADED_TEST(DontDeleteAPIAccessorsCannotBeOverriden) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
LocalContext context;
context->Global()->Set(v8_str("obj1"), templ->NewInstance());
CompileRun("var obj2 = {};");
CHECK(GetGlobalProperty(&context, "obj1")
->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"),
v8::DEFAULT, v8::DontDelete));
CHECK(GetGlobalProperty(&context, "obj2")
->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"),
v8::DEFAULT, v8::DontDelete));
ExpectString("obj1.x", "x");
ExpectString("obj2.x", "x");
ExpectTrue("!Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
ExpectTrue("!Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
CHECK(!GetGlobalProperty(&context, "obj1")
->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
CHECK(!GetGlobalProperty(&context, "obj2")
->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
{
v8::TryCatch try_catch(isolate);
CompileRun(
"Object.defineProperty(obj1, 'x',"
"{get: function() { return 'func'; }})");
CHECK(try_catch.HasCaught());
String::Utf8Value exception_value(try_catch.Exception());
CHECK_EQ(
0, strcmp(*exception_value, "TypeError: Cannot redefine property: x"));
}
{
v8::TryCatch try_catch(isolate);
CompileRun(
"Object.defineProperty(obj2, 'x',"
"{get: function() { return 'func'; }})");
CHECK(try_catch.HasCaught());
String::Utf8Value exception_value(try_catch.Exception());
CHECK_EQ(
0, strcmp(*exception_value, "TypeError: Cannot redefine property: x"));
}
}
static void Get239Value(Local<String> name,
const v8::PropertyCallbackInfo<v8::Value>& info) {
ApiTestFuzzer::Fuzz();
CHECK(info.Data()->Equals(v8_str("donut")));
CHECK(name->Equals(v8_str("239")));
info.GetReturnValue().Set(name);
}
THREADED_TEST(ElementAPIAccessor) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
LocalContext context;
context->Global()->Set(v8_str("obj1"), templ->NewInstance());
CompileRun("var obj2 = {};");
CHECK(GetGlobalProperty(&context, "obj1")
->SetAccessor(v8_str("239"), Get239Value, NULL, v8_str("donut")));
CHECK(GetGlobalProperty(&context, "obj2")
->SetAccessor(v8_str("239"), Get239Value, NULL, v8_str("donut")));
ExpectString("obj1[239]", "239");
ExpectString("obj2[239]", "239");
ExpectString("obj1['239']", "239");
ExpectString("obj2['239']", "239");
}
v8::Persistent<Value> xValue;
static void SetXValue(Local<String> name, Local<Value> value,
const v8::PropertyCallbackInfo<void>& info) {
CHECK(value->Equals(v8_num(4)));
CHECK(info.Data()->Equals(v8_str("donut")));
CHECK(name->Equals(v8_str("x")));
CHECK(xValue.IsEmpty());
xValue.Reset(info.GetIsolate(), value);
}
THREADED_TEST(SimplePropertyWrite) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
templ->SetAccessor(v8_str("x"), GetXValue, SetXValue, v8_str("donut"));
LocalContext context;
context->Global()->Set(v8_str("obj"), templ->NewInstance());
Local<Script> script = v8_compile("obj.x = 4");
for (int i = 0; i < 10; i++) {
CHECK(xValue.IsEmpty());
script->Run();
CHECK(v8_num(4)->Equals(Local<Value>::New(CcTest::isolate(), xValue)));
xValue.Reset();
}
}
THREADED_TEST(SetterOnly) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
templ->SetAccessor(v8_str("x"), NULL, SetXValue, v8_str("donut"));
LocalContext context;
context->Global()->Set(v8_str("obj"), templ->NewInstance());
Local<Script> script = v8_compile("obj.x = 4; obj.x");
for (int i = 0; i < 10; i++) {
CHECK(xValue.IsEmpty());
script->Run();
CHECK(v8_num(4)->Equals(Local<Value>::New(CcTest::isolate(), xValue)));
xValue.Reset();
}
}
THREADED_TEST(NoAccessors) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
templ->SetAccessor(v8_str("x"), static_cast<v8::AccessorGetterCallback>(NULL),
NULL, v8_str("donut"));
LocalContext context;
context->Global()->Set(v8_str("obj"), templ->NewInstance());
Local<Script> script = v8_compile("obj.x = 4; obj.x");
for (int i = 0; i < 10; i++) {
script->Run();
}
}
THREADED_TEST(MultiContexts) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Handle<ObjectTemplate> templ = ObjectTemplate::New(isolate);
templ->Set(v8_str("dummy"),
v8::FunctionTemplate::New(isolate, DummyCallHandler));
Local<String> password = v8_str("Password");
// Create an environment
LocalContext context0(0, templ);
context0->SetSecurityToken(password);
v8::Handle<v8::Object> global0 = context0->Global();
global0->Set(v8_str("custom"), v8_num(1234));
CHECK_EQ(1234, global0->Get(v8_str("custom"))->Int32Value());
// Create an independent environment
LocalContext context1(0, templ);
context1->SetSecurityToken(password);
v8::Handle<v8::Object> global1 = context1->Global();
global1->Set(v8_str("custom"), v8_num(1234));
CHECK(!global0->Equals(global1));
CHECK_EQ(1234, global0->Get(v8_str("custom"))->Int32Value());
CHECK_EQ(1234, global1->Get(v8_str("custom"))->Int32Value());
// Now create a new context with the old global
LocalContext context2(0, templ, global1);
context2->SetSecurityToken(password);
v8::Handle<v8::Object> global2 = context2->Global();
CHECK(global1->Equals(global2));
CHECK_EQ(0, global1->Get(v8_str("custom"))->Int32Value());
CHECK_EQ(0, global2->Get(v8_str("custom"))->Int32Value());
}
THREADED_TEST(FunctionPrototypeAcrossContexts) {
// Make sure that functions created by cloning boilerplates cannot
// communicate through their __proto__ field.
v8::HandleScope scope(CcTest::isolate());
LocalContext env0;
v8::Handle<v8::Object> global0 = env0->Global();
v8::Handle<v8::Object> object0 =
global0->Get(v8_str("Object")).As<v8::Object>();
v8::Handle<v8::Object> tostring0 =
object0->Get(v8_str("toString")).As<v8::Object>();
v8::Handle<v8::Object> proto0 =
tostring0->Get(v8_str("__proto__")).As<v8::Object>();
proto0->Set(v8_str("custom"), v8_num(1234));
LocalContext env1;
v8::Handle<v8::Object> global1 = env1->Global();
v8::Handle<v8::Object> object1 =
global1->Get(v8_str("Object")).As<v8::Object>();
v8::Handle<v8::Object> tostring1 =
object1->Get(v8_str("toString")).As<v8::Object>();
v8::Handle<v8::Object> proto1 =
tostring1->Get(v8_str("__proto__")).As<v8::Object>();
CHECK(!proto1->Has(v8_str("custom")));
}
THREADED_TEST(Regress892105) {
// Make sure that object and array literals created by cloning
// boilerplates cannot communicate through their __proto__
// field. This is rather difficult to check, but we try to add stuff
// to Object.prototype and Array.prototype and create a new
// environment. This should succeed.
v8::HandleScope scope(CcTest::isolate());
Local<String> source = v8_str(
"Object.prototype.obj = 1234;"
"Array.prototype.arr = 4567;"
"8901");
LocalContext env0;
Local<Script> script0 = v8_compile(source);
CHECK_EQ(8901.0, script0->Run()->NumberValue());
LocalContext env1;
Local<Script> script1 = v8_compile(source);
CHECK_EQ(8901.0, script1->Run()->NumberValue());
}
THREADED_TEST(UndetectableObject) {
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
Local<v8::FunctionTemplate> desc =
v8::FunctionTemplate::New(env->GetIsolate());
desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable
Local<v8::Object> obj = desc->GetFunction()->NewInstance();
env->Global()->Set(v8_str("undetectable"), obj);
ExpectString("undetectable.toString()", "[object Object]");
ExpectString("typeof undetectable", "undefined");
ExpectString("typeof(undetectable)", "undefined");
ExpectBoolean("typeof undetectable == 'undefined'", true);
ExpectBoolean("typeof undetectable == 'object'", false);
ExpectBoolean("if (undetectable) { true; } else { false; }", false);
ExpectBoolean("!undetectable", true);
ExpectObject("true&&undetectable", obj);
ExpectBoolean("false&&undetectable", false);
ExpectBoolean("true||undetectable", true);
ExpectObject("false||undetectable", obj);
ExpectObject("undetectable&&true", obj);
ExpectObject("undetectable&&false", obj);
ExpectBoolean("undetectable||true", true);
ExpectBoolean("undetectable||false", false);
ExpectBoolean("undetectable==null", true);
ExpectBoolean("null==undetectable", true);
ExpectBoolean("undetectable==undefined", true);
ExpectBoolean("undefined==undetectable", true);
ExpectBoolean("undetectable==undetectable", true);
ExpectBoolean("undetectable===null", false);
ExpectBoolean("null===undetectable", false);
ExpectBoolean("undetectable===undefined", false);
ExpectBoolean("undefined===undetectable", false);
ExpectBoolean("undetectable===undetectable", true);
}
THREADED_TEST(VoidLiteral) {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
Local<v8::FunctionTemplate> desc = v8::FunctionTemplate::New(isolate);
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) {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
Local<v8::FunctionTemplate> desc = v8::FunctionTemplate::New(isolate);
desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable
Local<v8::Object> obj = desc->GetFunction()->NewInstance();
env->Global()->Set(v8_str("undetectable"), obj);
Local<String> source = v8_str(
"undetectable.x = 42;"
"undetectable.x");
Local<Script> script = v8_compile(source);
CHECK(v8::Integer::New(isolate, 42)->Equals(script->Run()));
ExpectBoolean("Object.isExtensible(undetectable)", true);
source = v8_str("Object.preventExtensions(undetectable);");
script = v8_compile(source);
script->Run();
ExpectBoolean("Object.isExtensible(undetectable)", false);
source = v8_str("undetectable.y = 2000;");
script = v8_compile(source);
script->Run();
ExpectBoolean("undetectable.y == undefined", true);
}
// The point of this test is type checking. We run it only so compilers
// don't complain about an unused function.
TEST(PersistentHandles) {
LocalContext env;
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
Local<String> str = v8_str("foo");
v8::Persistent<String> p_str(isolate, str);
p_str.Reset();
Local<Script> scr = v8_compile("");
v8::Persistent<Script> p_scr(isolate, scr);
p_scr.Reset();
Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
v8::Persistent<ObjectTemplate> p_templ(isolate, templ);
p_templ.Reset();
}
static void HandleLogDelegator(
const v8::FunctionCallbackInfo<v8::Value>& args) {
ApiTestFuzzer::Fuzz();
}
THREADED_TEST(GlobalObjectTemplate) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope handle_scope(isolate);
Local<ObjectTemplate> global_template = ObjectTemplate::New(isolate);
global_template->Set(v8_str("JSNI_Log"),
v8::FunctionTemplate::New(isolate, HandleLogDelegator));
v8::Local<Context> context = Context::New(isolate, 0, global_template);
Context::Scope context_scope(context);
CompileRun("JSNI_Log('LOG')");
}
static const char* kSimpleExtensionSource =
"function Foo() {"
" return 4;"
"}";
TEST(SimpleExtensions) {
v8::HandleScope handle_scope(CcTest::isolate());
v8::RegisterExtension(new Extension("simpletest", kSimpleExtensionSource));
const char* extension_names[] = {"simpletest"};
v8::ExtensionConfiguration extensions(1, extension_names);
v8::Handle<Context> context = Context::New(CcTest::isolate(), &extensions);
Context::Scope lock(context);
v8::Handle<Value> result = CompileRun("Foo()");
CHECK(result->Equals(v8::Integer::New(CcTest::isolate(), 4)));
}
static const char* kStackTraceFromExtensionSource =
"function foo() {"
" throw new Error();"
"}"
"function bar() {"
" foo();"
"}";
TEST(StackTraceInExtension) {
v8::HandleScope handle_scope(CcTest::isolate());
v8::RegisterExtension(
new Extension("stacktracetest", kStackTraceFromExtensionSource));
const char* extension_names[] = {"stacktracetest"};
v8::ExtensionConfiguration extensions(1, extension_names);
v8::Handle<Context> context = Context::New(CcTest::isolate(), &extensions);
Context::Scope lock(context);
CompileRun(
"function user() { bar(); }"
"var error;"
"try{ user(); } catch (e) { error = e; }");
CHECK_EQ(-1, CompileRun("error.stack.indexOf('foo')")->Int32Value());
CHECK_EQ(-1, CompileRun("error.stack.indexOf('bar')")->Int32Value());
CHECK_NE(-1, CompileRun("error.stack.indexOf('user')")->Int32Value());
}
TEST(NullExtensions) {
v8::HandleScope handle_scope(CcTest::isolate());
v8::RegisterExtension(new Extension("nulltest", NULL));
const char* extension_names[] = {"nulltest"};
v8::ExtensionConfiguration extensions(1, extension_names);
v8::Handle<Context> context = Context::New(CcTest::isolate(), &extensions);
Context::Scope lock(context);
v8::Handle<Value> result = CompileRun("1+3");
CHECK(result->Equals(v8::Integer::New(CcTest::isolate(), 4)));
}
static const char* kEmbeddedExtensionSource =
"function Ret54321(){return 54321;}~~@@$"
"$%% THIS IS A SERIES OF NON-NULL-TERMINATED STRINGS.";
static const int kEmbeddedExtensionSourceValidLen = 34;
TEST(ExtensionMissingSourceLength) {
v8::HandleScope handle_scope(CcTest::isolate());
v8::RegisterExtension(
new Extension("srclentest_fail", kEmbeddedExtensionSource));
const char* extension_names[] = {"srclentest_fail"};
v8::ExtensionConfiguration extensions(1, extension_names);
v8::Handle<Context> context = Context::New(CcTest::isolate(), &extensions);
CHECK(0 == *context);
}
TEST(ExtensionWithSourceLength) {
for (int source_len = kEmbeddedExtensionSourceValidLen - 1;
source_len <= kEmbeddedExtensionSourceValidLen + 1; ++source_len) {
v8::HandleScope handle_scope(CcTest::isolate());
i::ScopedVector<char> extension_name(32);
i::SNPrintF(extension_name, "ext #%d", source_len);
v8::RegisterExtension(new Extension(
extension_name.start(), kEmbeddedExtensionSource, 0, 0, source_len));
const char* extension_names[1] = {extension_name.start()};
v8::ExtensionConfiguration extensions(1, extension_names);
v8::Handle<Context> context = Context::New(CcTest::isolate(), &extensions);
if (source_len == kEmbeddedExtensionSourceValidLen) {
Context::Scope lock(context);
v8::Handle<Value> result = CompileRun("Ret54321()");
CHECK(v8::Integer::New(CcTest::isolate(), 54321)->Equals(result));
} else {
// Anything but exactly the right length should fail to compile.
CHECK(0 == *context);
}
}
}
static const char* kEvalExtensionSource1 =
"function UseEval1() {"
" var x = 42;"
" return eval('x');"
"}";
static const char* kEvalExtensionSource2 =
"(function() {"
" var x = 42;"
" function e() {"
" return eval('x');"
" }"
" this.UseEval2 = e;"
"})()";
TEST(UseEvalFromExtension) {
v8::HandleScope handle_scope(CcTest::isolate());
v8::RegisterExtension(new Extension("evaltest1", kEvalExtensionSource1));
v8::RegisterExtension(new Extension("evaltest2", kEvalExtensionSource2));
const char* extension_names[] = {"evaltest1", "evaltest2"};
v8::ExtensionConfiguration extensions(2, extension_names);
v8::Handle<Context> context = Context::New(CcTest::isolate(), &extensions);
Context::Scope lock(context);
v8::Handle<Value> result = CompileRun("UseEval1()");
CHECK(result->Equals(v8::Integer::New(CcTest::isolate(), 42)));
result = CompileRun("UseEval2()");
CHECK(result->Equals(v8::Integer::New(CcTest::isolate(), 42)));
}
static const char* kWithExtensionSource1 =
"function UseWith1() {"
" var x = 42;"
" with({x:87}) { return x; }"
"}";
static const char* kWithExtensionSource2 =
"(function() {"
" var x = 42;"
" function e() {"
" with ({x:87}) { return x; }"
" }"
" this.UseWith2 = e;"
"})()";
TEST(UseWithFromExtension) {
v8::HandleScope handle_scope(CcTest::isolate());
v8::RegisterExtension(new Extension("withtest1", kWithExtensionSource1));
v8::RegisterExtension(new Extension("withtest2", kWithExtensionSource2));
const char* extension_names[] = {"withtest1", "withtest2"};
v8::ExtensionConfiguration extensions(2, extension_names);
v8::Handle<Context> context = Context::New(CcTest::isolate(), &extensions);
Context::Scope lock(context);
v8::Handle<Value> result = CompileRun("UseWith1()");
CHECK(result->Equals(v8::Integer::New(CcTest::isolate(), 87)));
result = CompileRun("UseWith2()");
CHECK(result->Equals(v8::Integer::New(CcTest::isolate(), 87)));
}
TEST(AutoExtensions) {
v8::HandleScope handle_scope(CcTest::isolate());
Extension* extension = new Extension("autotest", kSimpleExtensionSource);
extension->set_auto_enable(true);
v8::RegisterExtension(extension);
v8::Handle<Context> context = Context::New(CcTest::isolate());
Context::Scope lock(context);
v8::Handle<Value> result = CompileRun("Foo()");
CHECK(result->Equals(v8::Integer::New(CcTest::isolate(), 4)));
}
static const char* kSyntaxErrorInExtensionSource = "[";
// Test that a syntax error in an extension does not cause a fatal
// error but results in an empty context.
TEST(SyntaxErrorExtensions) {
v8::HandleScope handle_scope(CcTest::isolate());
v8::RegisterExtension(
new Extension("syntaxerror", kSyntaxErrorInExtensionSource));
const char* extension_names[] = {"syntaxerror"};
v8::ExtensionConfiguration extensions(1, extension_names);
v8::Handle<Context> context = Context::New(CcTest::isolate(), &extensions);
CHECK(context.IsEmpty());
}
static const char* kExceptionInExtensionSource = "throw 42";
// Test that an exception when installing an extension does not cause
// a fatal error but results in an empty context.
TEST(ExceptionExtensions) {
v8::HandleScope handle_scope(CcTest::isolate());
v8::RegisterExtension(
new Extension("exception", kExceptionInExtensionSource));
const char* extension_names[] = {"exception"};
v8::ExtensionConfiguration extensions(1, extension_names);
v8::Handle<Context> context = Context::New(CcTest::isolate(), &extensions);
CHECK(context.IsEmpty());
}
static const char* kNativeCallInExtensionSource =
"function call_runtime_last_index_of(x) {"
" return %StringLastIndexOf(x, 'bob', 10);"
"}";
static const char* kNativeCallTest =
"call_runtime_last_index_of('bobbobboellebobboellebobbob');";
// Test that a native runtime calls are supported in extensions.
TEST(NativeCallInExtensions) {
v8::HandleScope handle_scope(CcTest::isolate());
v8::RegisterExtension(
new Extension("nativecall", kNativeCallInExtensionSource));
const char* extension_names[] = {"nativecall"};
v8::ExtensionConfiguration extensions(1, extension_names);
v8::Handle<Context> context = Context::New(CcTest::isolate(), &extensions);
Context::Scope lock(context);
v8::Handle<Value> result = CompileRun(kNativeCallTest);
CHECK(result->Equals(v8::Integer::New(CcTest::isolate(), 3)));
}
class NativeFunctionExtension : public Extension {
public:
NativeFunctionExtension(const char* name, const char* source,
v8::FunctionCallback fun = &Echo)
: Extension(name, source), function_(fun) {}
virtual v8::Handle<v8::FunctionTemplate> GetNativeFunctionTemplate(
v8::Isolate* isolate, v8::Handle<v8::String> name) {
return v8::FunctionTemplate::New(isolate, function_);
}
static void Echo(const v8::FunctionCallbackInfo<v8::Value>& args) {
if (args.Length() >= 1) args.GetReturnValue().Set(args[0]);
}
private:
v8::FunctionCallback function_;
};
TEST(NativeFunctionDeclaration) {
v8::HandleScope handle_scope(CcTest::isolate());
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(CcTest::isolate(), &extensions);
Context::Scope lock(context);
v8::Handle<Value> result = CompileRun("foo(42);");
CHECK(result->Equals(v8::Integer::New(CcTest::isolate(), 42)));
}
TEST(NativeFunctionDeclarationError) {
v8::HandleScope handle_scope(CcTest::isolate());
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(CcTest::isolate(), &extensions);
CHECK(context.IsEmpty());
}
TEST(NativeFunctionDeclarationErrorEscape) {
v8::HandleScope handle_scope(CcTest::isolate());
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(CcTest::isolate(), &extensions);
CHECK(context.IsEmpty());
}
static void CheckDependencies(const char* name, const char* expected) {
v8::HandleScope handle_scope(CcTest::isolate());
v8::ExtensionConfiguration config(1, &name);
LocalContext context(&config);
CHECK(String::NewFromUtf8(CcTest::isolate(), expected)
->Equals(context->Global()->Get(v8_str("loaded"))));
}
/*
* Configuration:
*
* /-- B <--\
* A <- -- D <-- E
* \-- C <--/
*/
THREADED_TEST(ExtensionDependency) {
static const char* kEDeps[] = {"D"};
v8::RegisterExtension(new Extension("E", "this.loaded += 'E';", 1, kEDeps));
static const char* kDDeps[] = {"B", "C"};
v8::RegisterExtension(new Extension("D", "this.loaded += 'D';", 2, kDDeps));
static const char* kBCDeps[] = {"A"};
v8::RegisterExtension(new Extension("B", "this.loaded += 'B';", 1, kBCDeps));
v8::RegisterExtension(new Extension("C", "this.loaded += 'C';", 1, kBCDeps));
v8::RegisterExtension(new Extension("A", "this.loaded += 'A';"));
CheckDependencies("A", "undefinedA");
CheckDependencies("B", "undefinedAB");
CheckDependencies("C", "undefinedAC");
CheckDependencies("D", "undefinedABCD");
CheckDependencies("E", "undefinedABCDE");
v8::HandleScope handle_scope(CcTest::isolate());
static const char* exts[2] = {"C", "E"};
v8::ExtensionConfiguration config(2, exts);
LocalContext context(&config);
CHECK(v8_str("undefinedACBDE")
->Equals(context->Global()->Get(v8_str("loaded"))));
}
static const char* kExtensionTestScript =
"native function A();"
"native function B();"
"native function C();"
"function Foo(i) {"
" if (i == 0) return A();"
" if (i == 1) return B();"
" if (i == 2) return C();"
"}";
static void CallFun(const v8::FunctionCallbackInfo<v8::Value>& args) {
ApiTestFuzzer::Fuzz();
if (args.IsConstructCall()) {
args.This()->Set(v8_str("data"), args.Data());
args.GetReturnValue().SetNull();
return;
}
args.GetReturnValue().Set(args.Data());
}
class FunctionExtension : public Extension {
public:
FunctionExtension() : Extension("functiontest", kExtensionTestScript) {}
virtual v8::Handle<v8::FunctionTemplate> GetNativeFunctionTemplate(
v8::Isolate* isolate, v8::Handle<String> name);
};
static int lookup_count = 0;
v8::Handle<v8::FunctionTemplate> FunctionExtension::GetNativeFunctionTemplate(
v8::Isolate* isolate, v8::Handle<String> name) {
lookup_count++;
if (name->Equals(v8_str("A"))) {
return v8::FunctionTemplate::New(isolate, CallFun,
v8::Integer::New(isolate, 8));
} else if (name->Equals(v8_str("B"))) {
return v8::FunctionTemplate::New(isolate, CallFun,
v8::Integer::New(isolate, 7));
} else if (name->Equals(v8_str("C"))) {
return v8::FunctionTemplate::New(isolate, CallFun,
v8::Integer::New(isolate, 6));
} else {
return v8::Handle<v8::FunctionTemplate>();
}
}
THREADED_TEST(FunctionLookup) {
v8::RegisterExtension(new FunctionExtension());
v8::HandleScope handle_scope(CcTest::isolate());
static const char* exts[1] = {"functiontest"};
v8::ExtensionConfiguration config(1, exts);
LocalContext context(&config);
CHECK_EQ(3, lookup_count);
CHECK(v8::Integer::New(CcTest::isolate(), 8)->Equals(CompileRun("Foo(0)")));
CHECK(v8::Integer::New(CcTest::isolate(), 7)->Equals(CompileRun("Foo(1)")));
CHECK(v8::Integer::New(CcTest::isolate(), 6)->Equals(CompileRun("Foo(2)")));
}
THREADED_TEST(NativeFunctionConstructCall) {
v8::RegisterExtension(new FunctionExtension());
v8::HandleScope handle_scope(CcTest::isolate());
static const char* exts[1] = {"functiontest"};
v8::ExtensionConfiguration config(1, exts);
LocalContext context(&config);
for (int i = 0; i < 10; i++) {
// Run a few times to ensure that allocation of objects doesn't
// change behavior of a constructor function.
CHECK(v8::Integer::New(CcTest::isolate(), 8)
->Equals(CompileRun("(new A()).data")));
CHECK(v8::Integer::New(CcTest::isolate(), 7)
->Equals(CompileRun("(new B()).data")));
CHECK(v8::Integer::New(CcTest::isolate(), 6)
->Equals(CompileRun("(new C()).data")));
}
}
static const char* last_location;
static const char* last_message;
void StoringErrorCallback(const char* location, const char* message) {
if (last_location == NULL) {
last_location = location;
last_message = message;
}
}
// ErrorReporting creates a circular extensions configuration and
// tests that the fatal error handler gets called. This renders V8
// unusable and therefore this test cannot be run in parallel.
TEST(ErrorReporting) {
v8::V8::SetFatalErrorHandler(StoringErrorCallback);
static const char* aDeps[] = {"B"};
v8::RegisterExtension(new Extension("A", "", 1, aDeps));
static const char* bDeps[] = {"A"};
v8::RegisterExtension(new Extension("B", "", 1, bDeps));
last_location = NULL;
v8::ExtensionConfiguration config(1, bDeps);
v8::Handle<Context> context = Context::New(CcTest::isolate(), &config);
CHECK(context.IsEmpty());
CHECK(last_location);
}
static void MissingScriptInfoMessageListener(v8::Handle<v8::Message> message,
v8::Handle<Value> data) {
CHECK(message->GetScriptOrigin().ResourceName()->IsUndefined());
CHECK(v8::Undefined(CcTest::isolate())
->Equals(message->GetScriptOrigin().ResourceName()));
message->GetLineNumber();
message->GetSourceLine();
}
THREADED_TEST(ErrorWithMissingScriptInfo) {
LocalContext context;
v8::HandleScope scope(context->GetIsolate());
v8::V8::AddMessageListener(MissingScriptInfoMessageListener);
CompileRun("throw Error()");
v8::V8::RemoveMessageListeners(MissingScriptInfoMessageListener);
}
struct FlagAndPersistent {
bool flag;
v8::Global<v8::Object> handle;
};
static void SetFlag(const v8::WeakCallbackInfo<FlagAndPersistent>& data) {
data.GetParameter()->flag = true;
data.GetParameter()->handle.Reset();
}
static void IndependentWeakHandle(bool global_gc, bool interlinked) {
v8::Isolate* iso = CcTest::isolate();
v8::HandleScope scope(iso);
v8::Handle<Context> context = Context::New(iso);
Context::Scope context_scope(context);
FlagAndPersistent object_a, object_b;
intptr_t big_heap_size;
{
v8::HandleScope handle_scope(iso);
Local<Object> a(v8::Object::New(iso));
Local<Object> b(v8::Object::New(iso));
object_a.handle.Reset(iso, a);
object_b.handle.Reset(iso, b);
if (interlinked) {
a->Set(v8_str("x"), b);
b->Set(v8_str("x"), a);
}
if (global_gc) {
CcTest::heap()->CollectAllGarbage();
} else {
CcTest::heap()->CollectGarbage(i::NEW_SPACE);
}
// We are relying on this creating a big flag array and reserving the space
// up front.
v8::Handle<Value> big_array = CompileRun("new Array(50000)");
a->Set(v8_str("y"), big_array);
big_heap_size = CcTest::heap()->SizeOfObjects();
}
object_a.flag = false;
object_b.flag = false;
object_a.handle.SetWeak(&object_a, &SetFlag,
v8::WeakCallbackType::kParameter);
object_b.handle.SetWeak(&object_b, &SetFlag,
v8::WeakCallbackType::kParameter);
CHECK(!object_b.handle.IsIndependent());
object_a.handle.MarkIndependent();
object_b.handle.MarkIndependent();
CHECK(object_b.handle.IsIndependent());
if (global_gc) {
CcTest::heap()->CollectAllGarbage();
} else {
CcTest::heap()->CollectGarbage(i::NEW_SPACE);
}
// A single GC should be enough to reclaim the memory, since we are using
// phantom handles.
CHECK_LT(CcTest::heap()->SizeOfObjects(), big_heap_size - 200000);
CHECK(object_a.flag);
CHECK(object_b.flag);
}
TEST(IndependentWeakHandle) {
IndependentWeakHandle(false, false);
IndependentWeakHandle(false, true);
IndependentWeakHandle(true, false);
IndependentWeakHandle(true, true);
}
class Trivial {
public:
explicit Trivial(int x) : x_(x) {}
int x() { return x_; }
void set_x(int x) { x_ = x; }
private:
int x_;
};
class Trivial2 {
public:
Trivial2(int x, int y) : y_(y), x_(x) {}
int x() { return x_; }
void set_x(int x) { x_ = x; }
int y() { return y_; }
void set_y(int y) { y_ = y; }
private:
int y_;
int x_;
};
void CheckInternalFields(
const v8::WeakCallbackInfo<v8::Persistent<v8::Object>>& data) {
v8::Persistent<v8::Object>* handle = data.GetParameter();
handle->Reset();
Trivial* t1 = reinterpret_cast<Trivial*>(data.GetInternalField1());
Trivial2* t2 = reinterpret_cast<Trivial2*>(data.GetInternalField2());
CHECK_EQ(42, t1->x());
CHECK_EQ(103, t2->x());
t1->set_x(1729);
t2->set_x(33550336);
}
void InternalFieldCallback(bool global_gc) {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate);
Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate();
Trivial* t1;
Trivial2* t2;
instance_templ->SetInternalFieldCount(2);
{
v8::HandleScope scope(isolate);
Local<v8::Object> obj = templ->GetFunction()->NewInstance();
v8::Persistent<v8::Object> handle(isolate, obj);
CHECK_EQ(2, obj->InternalFieldCount());
CHECK(obj->GetInternalField(0)->IsUndefined());
t1 = new Trivial(42);
t2 = new Trivial2(103, 9);
obj->SetAlignedPointerInInternalField(0, t1);
t1 = reinterpret_cast<Trivial*>(obj->GetAlignedPointerFromInternalField(0));
CHECK_EQ(42, t1->x());
obj->SetAlignedPointerInInternalField(1, t2);
t2 =
reinterpret_cast<Trivial2*>(obj->GetAlignedPointerFromInternalField(1));
CHECK_EQ(103, t2->x());
handle.SetWeak<v8::Persistent<v8::Object>>(
&handle, CheckInternalFields, v8::WeakCallbackType::kInternalFields);
if (!global_gc) {
handle.MarkIndependent();
}
}
if (global_gc) {
CcTest::heap()->CollectAllGarbage();
} else {
CcTest::heap()->CollectGarbage(i::NEW_SPACE);
}
CHECK_EQ(1729, t1->x());
CHECK_EQ(33550336, t2->x());
delete t1;
delete t2;
}
THREADED_TEST(InternalFieldCallback) {
InternalFieldCallback(false);
InternalFieldCallback(true);
}
static void ResetUseValueAndSetFlag(
const v8::WeakCallbackInfo<FlagAndPersistent>& data) {
// Blink will reset the handle, and then use the other handle, so they
// can't use the same backing slot.
data.GetParameter()->handle.Reset();
data.GetParameter()->flag = true;
}
static void ResetWeakHandle(bool global_gc) {
v8::Isolate* iso = CcTest::isolate();
v8::HandleScope scope(iso);
v8::Handle<Context> context = Context::New(iso);
Context::Scope context_scope(context);
FlagAndPersistent object_a, object_b;
{
v8::HandleScope handle_scope(iso);
Local<Object> a(v8::Object::New(iso));
Local<Object> b(v8::Object::New(iso));
object_a.handle.Reset(iso, a);
object_b.handle.Reset(iso, b);
if (global_gc) {
CcTest::heap()->CollectAllGarbage(
TestHeap::Heap::kAbortIncrementalMarkingMask);
} else {
CcTest::heap()->CollectGarbage(i::NEW_SPACE);
}
}
object_a.flag = false;
object_b.flag = false;
object_a.handle.SetWeak(&object_a, &ResetUseValueAndSetFlag,
v8::WeakCallbackType::kParameter);
object_b.handle.SetWeak(&object_b, &ResetUseValueAndSetFlag,
v8::WeakCallbackType::kParameter);
if (!global_gc) {
object_a.handle.MarkIndependent();
object_b.handle.MarkIndependent();
CHECK(object_b.handle.IsIndependent());
}
if (global_gc) {
CcTest::heap()->CollectAllGarbage(
TestHeap::Heap::kAbortIncrementalMarkingMask);
} else {
CcTest::heap()->CollectGarbage(i::NEW_SPACE);
}
CHECK(object_a.flag);
CHECK(object_b.flag);
}
THREADED_TEST(ResetWeakHandle) {
ResetWeakHandle(false);
ResetWeakHandle(true);
}
static void InvokeScavenge() { CcTest::heap()->CollectGarbage(i::NEW_SPACE); }
static void InvokeMarkSweep() { CcTest::heap()->CollectAllGarbage(); }
static void ForceScavenge2(
const v8::WeakCallbackInfo<FlagAndPersistent>& data) {
data.GetParameter()->flag = true;
InvokeScavenge();
}
static void ForceScavenge1(
const v8::WeakCallbackInfo<FlagAndPersistent>& data) {
data.GetParameter()->handle.Reset();
data.SetSecondPassCallback(ForceScavenge2);
}
static void ForceMarkSweep2(
const v8::WeakCallbackInfo<FlagAndPersistent>& data) {
data.GetParameter()->flag = true;
InvokeMarkSweep();
}
static void ForceMarkSweep1(
const v8::WeakCallbackInfo<FlagAndPersistent>& data) {
data.GetParameter()->handle.Reset();
data.SetSecondPassCallback(ForceMarkSweep2);
}
THREADED_TEST(GCFromWeakCallbacks) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Handle<Context> context = Context::New(isolate);
Context::Scope context_scope(context);
static const int kNumberOfGCTypes = 2;
typedef v8::WeakCallbackInfo<FlagAndPersistent>::Callback Callback;
Callback gc_forcing_callback[kNumberOfGCTypes] = {&ForceScavenge1,
&ForceMarkSweep1};
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++) {
FlagAndPersistent object;
{
v8::HandleScope handle_scope(isolate);
object.handle.Reset(isolate, v8::Object::New(isolate));
}
object.flag = false;
object.handle.SetWeak(&object, gc_forcing_callback[inner_gc],
v8::WeakCallbackType::kParameter);
object.handle.MarkIndependent();
invoke_gc[outer_gc]();
CHECK(object.flag);
}
}
}
v8::Handle<Function> args_fun;
static void ArgumentsTestCallback(
const v8::FunctionCallbackInfo<v8::Value>& args) {
ApiTestFuzzer::Fuzz();
v8::Isolate* isolate = args.GetIsolate();
CHECK(args_fun->Equals(args.Callee()));
CHECK_EQ(3, args.Length());
CHECK(v8::Integer::New(isolate, 1)->Equals(args[0]));
CHECK(v8::Integer::New(isolate, 2)->Equals(args[1]));
CHECK(v8::Integer::New(isolate, 3)->Equals(args[2]));
CHECK(v8::Undefined(isolate)->Equals(args[3]));
v8::HandleScope scope(args.GetIsolate());
CcTest::heap()->CollectAllGarbage();
}
THREADED_TEST(Arguments) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::ObjectTemplate> global = ObjectTemplate::New(isolate);
global->Set(v8_str("f"),
v8::FunctionTemplate::New(isolate, ArgumentsTestCallback));
LocalContext context(NULL, global);
args_fun = context->Global()->Get(v8_str("f")).As<Function>();
v8_compile("f(1, 2, 3)")->Run();
}
static int p_getter_count;
static int p_getter_count2;
static void PGetter(Local<String> name,
const v8::PropertyCallbackInfo<v8::Value>& info) {
ApiTestFuzzer::Fuzz();
p_getter_count++;
v8::Handle<v8::Object> global =
info.GetIsolate()->GetCurrentContext()->Global();
CHECK(info.Holder()->Equals(global->Get(v8_str("o1"))));
if (name->Equals(v8_str("p1"))) {
CHECK(info.This()->Equals(global->Get(v8_str("o1"))));
} else if (name->Equals(v8_str("p2"))) {
CHECK(info.This()->Equals(global->Get(v8_str("o2"))));
} else if (name->Equals(v8_str("p3"))) {
CHECK(info.This()->Equals(global->Get(v8_str("o3"))));
} else if (name->Equals(v8_str("p4"))) {
CHECK(info.This()->Equals(global->Get(v8_str("o4"))));
}
}
static void RunHolderTest(v8::Handle<v8::ObjectTemplate> obj) {
ApiTestFuzzer::Fuzz();
LocalContext context;
context->Global()->Set(v8_str("o1"), obj->NewInstance());
CompileRun(
"o1.__proto__ = { };"
"var o2 = { __proto__: o1 };"
"var o3 = { __proto__: o2 };"
"var o4 = { __proto__: o3 };"
"for (var i = 0; i < 10; i++) o4.p4;"
"for (var i = 0; i < 10; i++) o3.p3;"
"for (var i = 0; i < 10; i++) o2.p2;"
"for (var i = 0; i < 10; i++) o1.p1;");
}
static void PGetter2(Local<Name> name,
const v8::PropertyCallbackInfo<v8::Value>& info) {
ApiTestFuzzer::Fuzz();
p_getter_count2++;
v8::Handle<v8::Object> global =
info.GetIsolate()->GetCurrentContext()->Global();
CHECK(info.Holder()->Equals(global->Get(v8_str("o1"))));
if (name->Equals(v8_str("p1"))) {
CHECK(info.This()->Equals(global->Get(v8_str("o1"))));
} else if (name->Equals(v8_str("p2"))) {
CHECK(info.This()->Equals(global->Get(v8_str("o2"))));
} else if (name->Equals(v8_str("p3"))) {
CHECK(info.This()->Equals(global->Get(v8_str("o3"))));
} else if (name->Equals(v8_str("p4"))) {
CHECK(info.This()->Equals(global->Get(v8_str("o4"))));
}
}
THREADED_TEST(GetterHolders) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(isolate);
obj->SetAccessor(v8_str("p1"), PGetter);
obj->SetAccessor(v8_str("p2"), PGetter);
obj->SetAccessor(v8_str("p3"), PGetter);
obj->SetAccessor(v8_str("p4"), PGetter);
p_getter_count = 0;
RunHolderTest(obj);
CHECK_EQ(40, p_getter_count);
}
THREADED_TEST(PreInterceptorHolders) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(isolate);
obj->SetHandler(v8::NamedPropertyHandlerConfiguration(PGetter2));
p_getter_count2 = 0;
RunHolderTest(obj);
CHECK_EQ(40, p_getter_count2);
}
THREADED_TEST(ObjectInstantiation) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
templ->SetAccessor(v8_str("t"), PGetter2);
LocalContext context;
context->Global()->Set(v8_str("o"), templ->NewInstance());
for (int i = 0; i < 100; i++) {
v8::HandleScope inner_scope(CcTest::isolate());
v8::Handle<v8::Object> obj = templ->NewInstance();
CHECK(!obj->Equals(context->Global()->Get(v8_str("o"))));
context->Global()->Set(v8_str("o2"), obj);
v8::Handle<Value> value =
CompileRun("o.__proto__ === o2.__proto__");
CHECK(v8::True(isolate)->Equals(value));
context->Global()->Set(v8_str("o"), obj);
}
}
static int StrCmp16(uint16_t* a, uint16_t* b) {
while (true) {
if (*a == 0 && *b == 0) return 0;
if (*a != *b) return 0 + *a - *b;
a++;
b++;
}
}
static int StrNCmp16(uint16_t* a, uint16_t* b, int n) {
while (true) {
if (n-- == 0) return 0;
if (*a == 0 && *b == 0) return 0;
if (*a != *b) return 0 + *a - *b;
a++;
b++;
}
}
int GetUtf8Length(Handle<String> str) {
int len = str->Utf8Length();
if (len < 0) {
i::Handle<i::String> istr(v8::Utils::OpenHandle(*str));
i::String::Flatten(istr);
len = str->Utf8Length();
}
return len;
}
THREADED_TEST(StringWrite) {
LocalContext context;
v8::HandleScope scope(context->GetIsolate());
v8::Handle<String> str = v8_str("abcde");
// abc<Icelandic eth><Unicode snowman>.
v8::Handle<String> str2 = v8_str("abc\303\260\342\230\203");
v8::Handle<String> str3 = v8::String::NewFromUtf8(
context->GetIsolate(), "abc\0def", v8::String::kNormalString, 7);
// "ab" + lead surrogate + "cd" + trail surrogate + "ef"
uint16_t orphans[8] = { 0x61, 0x62, 0xd800, 0x63, 0x64, 0xdc00, 0x65, 0x66 };
v8::Handle<String> orphans_str = v8::String::NewFromTwoByte(
context->GetIsolate(), orphans, v8::String::kNormalString, 8);
// single lead surrogate
uint16_t lead[1] = { 0xd800 };
v8::Handle<String> lead_str = v8::String::NewFromTwoByte(
context->GetIsolate(), lead, v8::String::kNormalString, 1);
// single trail surrogate
uint16_t trail[1] = { 0xdc00 };
v8::Handle<String> trail_str = v8::String::NewFromTwoByte(
context->GetIsolate(), trail, v8::String::kNormalString, 1);
// surrogate pair
uint16_t pair[2] = { 0xd800, 0xdc00 };
v8::Handle<String> pair_str = v8::String::NewFromTwoByte(
context->GetIsolate(), pair, v8::String::kNormalString, 2);
const int kStride = 4; // Must match stride in for loops in JS below.
CompileRun(
"var left = '';"
"for (var i = 0; i < 0xd800; i += 4) {"
" left = left + String.fromCharCode(i);"
"}");
CompileRun(
"var right = '';"
"for (var i = 0; i < 0xd800; i += 4) {"
" right = String.fromCharCode(i) + right;"
"}");
v8::Handle<v8::Object> global = context->Global();
Handle<String> left_tree = global->Get(v8_str("left")).As<String>();
Handle<String> right_tree = global->Get(v8_str("right")).As<String>();
CHECK_EQ(5, str2->Length());
CHECK_EQ(0xd800 / kStride, left_tree->Length());
CHECK_EQ(0xd800 / kStride, right_tree->Length());
char buf[100];
char utf8buf[0xd800 * 3];
uint16_t wbuf[100];
int len;
int charlen;
memset(utf8buf, 0x1, 1000);
len = str2->WriteUtf8(utf8buf, sizeof(utf8buf), &charlen);
CHECK_EQ(9, len);
CHECK_EQ(5, charlen);
CHECK_EQ(0, strcmp(utf8buf, "abc\303\260\342\230\203"));
memset(utf8buf, 0x1, 1000);
len = str2->WriteUtf8(utf8buf, 8, &charlen);
CHECK_EQ(8, len);
CHECK_EQ(5, charlen);
CHECK_EQ(0, strncmp(utf8buf, "abc\303\260\342\230\203\1", 9));
memset(utf8buf, 0x1, 1000);
len = str2->WriteUtf8(utf8buf, 7, &charlen);
CHECK_EQ(5, len);
CHECK_EQ(4, charlen);
CHECK_EQ(0, strncmp(utf8buf, "abc\303\260\1", 5));
memset(utf8buf, 0x1, 1000);
len = str2->WriteUtf8(utf8buf, 6, &charlen);
CHECK_EQ(5, len);
CHECK_EQ(4, charlen);
CHECK_EQ(0, strncmp(utf8buf, "abc\303\260\1", 5));
memset(utf8buf, 0x1, 1000);
len = str2->WriteUtf8(utf8buf, 5, &charlen);
CHECK_EQ(5, len);
CHECK_EQ(4, charlen);
CHECK_EQ(0, strncmp(utf8buf, "abc\303\260\1", 5));
memset(utf8buf, 0x1, 1000);
len = str2->WriteUtf8(utf8buf, 4, &charlen);
CHECK_EQ(3, len);
CHECK_EQ(3, charlen);
CHECK_EQ(0, strncmp(utf8buf, "abc\1", 4));
memset(utf8buf, 0x1, 1000);
len = str2->WriteUtf8(utf8buf, 3, &charlen);
CHECK_EQ(3, len);
CHECK_EQ(3, charlen);
CHECK_EQ(0, strncmp(utf8buf, "abc\1", 4));
memset(utf8buf, 0x1, 1000);
len = str2->WriteUtf8(utf8buf, 2, &charlen);
CHECK_EQ(2, len);
CHECK_EQ(2, charlen);
CHECK_EQ(0, strncmp(utf8buf, "ab\1", 3));
// allow orphan surrogates by default
memset(utf8buf, 0x1, 1000);
len = orphans_str->WriteUtf8(utf8buf, sizeof(utf8buf), &charlen);
CHECK_EQ(13, len);
CHECK_EQ(8, charlen);
CHECK_EQ(0, strcmp(utf8buf, "ab\355\240\200cd\355\260\200ef"));
// replace orphan surrogates with unicode replacement character
memset(utf8buf, 0x1, 1000);
len = orphans_str->WriteUtf8(utf8buf,
sizeof(utf8buf),
&charlen,
String::REPLACE_INVALID_UTF8);
CHECK_EQ(13, len);
CHECK_EQ(8, charlen);
CHECK_EQ(0, strcmp(utf8buf, "ab\357\277\275cd\357\277\275ef"));
// replace single lead surrogate with unicode replacement character
memset(utf8buf, 0x1, 1000);
len = lead_str->WriteUtf8(utf8buf,
sizeof(utf8buf),
&charlen,
String::REPLACE_INVALID_UTF8);
CHECK_EQ(4, len);
CHECK_EQ(1, charlen);
CHECK_EQ(0, strcmp(utf8buf, "\357\277\275"));
// replace single trail surrogate with unicode replacement character
memset(utf8buf, 0x1, 1000);
len = trail_str->WriteUtf8(utf8buf,
sizeof(utf8buf),
&charlen,
String::REPLACE_INVALID_UTF8);
CHECK_EQ(4, len);
CHECK_EQ(1, charlen);
CHECK_EQ(0, strcmp(utf8buf, "\357\277\275"));
// do not replace / write anything if surrogate pair does not fit the buffer
// space
memset(utf8buf, 0x1, 1000);
len = pair_str->WriteUtf8(utf8buf,
3,
&charlen,
String::REPLACE_INVALID_UTF8);
CHECK_EQ(0, len);
CHECK_EQ(0, charlen);
memset(utf8buf, 0x1, sizeof(utf8buf));
len = GetUtf8Length(left_tree);
int utf8_expected =
(0x80 + (0x800 - 0x80) * 2 + (0xd800 - 0x800) * 3) / kStride;
CHECK_EQ(utf8_expected, len);
len = left_tree->WriteUtf8(utf8buf, utf8_expected, &charlen);
CHECK_EQ(utf8_expected, len);
CHECK_EQ(0xd800 / kStride, charlen);
CHECK_EQ(0xed, static_cast<unsigned char>(utf8buf[utf8_expected - 3]));
CHECK_EQ(0x9f, static_cast<unsigned char>(utf8buf[utf8_expected - 2]));
CHECK_EQ(0xc0 - kStride,
static_cast<unsigned char>(utf8buf[utf8_expected - 1]));
CHECK_EQ(1, utf8buf[utf8_expected]);
memset(utf8buf, 0x1, sizeof(utf8buf));
len = GetUtf8Length(right_tree);
CHECK_EQ(utf8_expected, len);
len = right_tree->WriteUtf8(utf8buf, utf8_expected, &charlen);
CHECK_EQ(utf8_expected, len);
CHECK_EQ(0xd800 / kStride, charlen);
CHECK_EQ(0xed, static_cast<unsigned char>(utf8buf[0]));
CHECK_EQ(0x9f, static_cast<unsigned char>(utf8buf[1]));
CHECK_EQ(0xc0 - kStride, static_cast<unsigned char>(utf8buf[2]));
CHECK_EQ(1, utf8buf[utf8_expected]);
memset(buf, 0x1, sizeof(buf));
memset(wbuf, 0x1, sizeof(wbuf));
len = str->WriteOneByte(reinterpret_cast<uint8_t*>(buf));
CHECK_EQ(5, len);
len = str->Write(wbuf);
CHECK_EQ(5, len);
CHECK_EQ(0, strcmp("abcde", buf));
uint16_t answer1[] = {'a', 'b', 'c', 'd', 'e', '\0'};
CHECK_EQ(0, StrCmp16(answer1, wbuf));
memset(buf, 0x1, sizeof(buf));
memset(wbuf, 0x1, sizeof(wbuf));
len = str->WriteOneByte(reinterpret_cast<uint8_t*>(buf), 0, 4);
CHECK_EQ(4, len);
len = str->Write(wbuf, 0, 4);
CHECK_EQ(4, len);
CHECK_EQ(0, strncmp("abcd\1", buf, 5));
uint16_t answer2[] = {'a', 'b', 'c', 'd', 0x101};
CHECK_EQ(0, StrNCmp16(answer2, wbuf, 5));
memset(buf, 0x1, sizeof(buf));
memset(wbuf, 0x1, sizeof(wbuf));
len = str->WriteOneByte(reinterpret_cast<uint8_t*>(buf), 0, 5);
CHECK_EQ(5, len);
len = str->Write(wbuf, 0, 5);
CHECK_EQ(5, len);
CHECK_EQ(0, strncmp("abcde\1", buf, 6));
uint16_t answer3[] = {'a', 'b', 'c', 'd', 'e', 0x101};
CHECK_EQ(0, StrNCmp16(answer3, wbuf, 6));
memset(buf, 0x1, sizeof(buf));
memset(wbuf, 0x1, sizeof(wbuf));
len = str->WriteOneByte(reinterpret_cast<uint8_t*>(buf), 0, 6);
CHECK_EQ(5, len);
len = str->Write(wbuf, 0, 6);
CHECK_EQ(5, len);
CHECK_EQ(0, strcmp("abcde", buf));
uint16_t answer4[] = {'a', 'b', 'c', 'd', 'e', '\0'};
CHECK_EQ(0, StrCmp16(answer4, wbuf));
memset(buf, 0x1, sizeof(buf));
memset(wbuf, 0x1, sizeof(wbuf));
len = str->WriteOneByte(reinterpret_cast<uint8_t*>(buf), 4, -1);
CHECK_EQ(1, len);
len = str->Write(wbuf, 4, -1);
CHECK_EQ(1, len);
CHECK_EQ(0, strcmp("e", buf));
uint16_t answer5[] = {'e', '\0'};
CHECK_EQ(0, StrCmp16(answer5, wbuf));
memset(buf, 0x1, sizeof(buf));
memset(wbuf, 0x1, sizeof(wbuf));
len = str->WriteOneByte(reinterpret_cast<uint8_t*>(buf), 4, 6);
CHECK_EQ(1, len);
len = str->Write(wbuf, 4, 6);
CHECK_EQ(1, len);
CHECK_EQ(0, strcmp("e", buf));
CHECK_EQ(0, StrCmp16(answer5, wbuf));
memset(buf, 0x1, sizeof(buf));
memset(wbuf, 0x1, sizeof(wbuf));
len = str->WriteOneByte(reinterpret_cast<uint8_t*>(buf), 4, 1);
CHECK_EQ(1, len);
len = str->Write(wbuf, 4, 1);
CHECK_EQ(1, len);
CHECK_EQ(0, strncmp("e\1", buf, 2));
uint16_t answer6[] = {'e', 0x101};
CHECK_EQ(0, StrNCmp16(answer6, wbuf, 2));
memset(buf, 0x1, sizeof(buf));
memset(wbuf, 0x1, sizeof(wbuf));
len = str->WriteOneByte(reinterpret_cast<uint8_t*>(buf), 3, 1);
CHECK_EQ(1, len);
len = str->Write(wbuf, 3, 1);
CHECK_EQ(1, len);
CHECK_EQ(0, strncmp("d\1", buf, 2));
uint16_t answer7[] = {'d', 0x101};
CHECK_EQ(0, StrNCmp16(answer7, wbuf, 2));
memset(wbuf, 0x1, sizeof(wbuf));
wbuf[5] = 'X';
len = str->Write(wbuf, 0, 6, String::NO_NULL_TERMINATION);
CHECK_EQ(5, len);
CHECK_EQ('X', wbuf[5]);
uint16_t answer8a[] = {'a', 'b', 'c', 'd', 'e'};
uint16_t answer8b[] = {'a', 'b', 'c', 'd', 'e', '\0'};
CHECK_EQ(0, StrNCmp16(answer8a, wbuf, 5));
CHECK_NE(0, StrCmp16(answer8b, wbuf));
wbuf[5] = '\0';
CHECK_EQ(0, StrCmp16(answer8b, wbuf));
memset(buf, 0x1, sizeof(buf));
buf[5] = 'X';
len = str->WriteOneByte(reinterpret_cast<uint8_t*>(buf),
0,
6,
String::NO_NULL_TERMINATION);
CHECK_EQ(5, len);
CHECK_EQ('X', buf[5]);
CHECK_EQ(0, strncmp("abcde", buf, 5));
CHECK_NE(0, strcmp("abcde", buf));
buf[5] = '\0';
CHECK_EQ(0, strcmp("abcde", buf));
memset(utf8buf, 0x1, sizeof(utf8buf));
utf8buf[8] = 'X';
len = str2->WriteUtf8(utf8buf, sizeof(utf8buf), &charlen,
String::NO_NULL_TERMINATION);
CHECK_EQ(8, len);
CHECK_EQ('X', utf8buf[8]);
CHECK_EQ(5, charlen);
CHECK_EQ(0, strncmp(utf8buf, "abc\303\260\342\230\203", 8));
CHECK_NE(0, strcmp(utf8buf, "abc\303\260\342\230\203"));
utf8buf[8] = '\0';
CHECK_EQ(0, strcmp(utf8buf, "abc\303\260\342\230\203"));
memset(utf8buf, 0x1, sizeof(utf8buf));
utf8buf[5] = 'X';
len = str->WriteUtf8(utf8buf, sizeof(utf8buf), &charlen,
String::NO_NULL_TERMINATION);
CHECK_EQ(5, len);
CHECK_EQ('X', utf8buf[5]); // Test that the sixth character is untouched.
CHECK_EQ(5, charlen);
utf8buf[5] = '\0';
CHECK_EQ(0, strcmp(utf8buf, "abcde"));
memset(buf, 0x1, sizeof(buf));
len = str3->WriteOneByte(reinterpret_cast<uint8_t*>(buf));
CHECK_EQ(7, len);
CHECK_EQ(0, strcmp("abc", buf));
CHECK_EQ(0, buf[3]);
CHECK_EQ(0, strcmp("def", buf + 4));
CHECK_EQ(0, str->WriteOneByte(NULL, 0, 0, String::NO_NULL_TERMINATION));
CHECK_EQ(0, str->WriteUtf8(NULL, 0, 0, String::NO_NULL_TERMINATION));
CHECK_EQ(0, str->Write(NULL, 0, 0, String::NO_NULL_TERMINATION));
}
static void Utf16Helper(
LocalContext& context, // NOLINT
const char* name,
const char* lengths_name,
int len) {
Local<v8::Array> a =
Local<v8::Array>::Cast(context->Global()->Get(v8_str(name)));
Local<v8::Array> alens =
Local<v8::Array>::Cast(context->Global()->Get(v8_str(lengths_name)));
for (int i = 0; i < len; i++) {
Local<v8::String> string =
Local<v8::String>::Cast(a->Get(i));
Local<v8::Number> expected_len =
Local<v8::Number>::Cast(alens->Get(i));
int length = GetUtf8Length(string);
CHECK_EQ(static_cast<int>(expected_len->Value()), length);
}
}
THREADED_TEST(Utf16) {
LocalContext context;
v8::HandleScope scope(context->GetIsolate());
CompileRun(
"var pad = '01234567890123456789';"
"var p = [];"
"var plens = [20, 3, 3];"
"p.push('01234567890123456789');"
"var lead = 0xd800;"
"var trail = 0xdc00;"
"p.push(String.fromCharCode(0xd800));"
"p.push(String.fromCharCode(0xdc00));"
"var a = [];"
"var b = [];"
"var c = [];"
"var alens = [];"
"for (var i = 0; i < 3; i++) {"
" p[1] = String.fromCharCode(lead++);"
" for (var j = 0; j < 3; j++) {"
" p[2] = String.fromCharCode(trail++);"
" a.push(p[i] + p[j]);"
" b.push(p[i] + p[j]);"
" c.push(p[i] + p[j]);"
" alens.push(plens[i] + plens[j]);"
" }"
"}"
"alens[5] -= 2;" // Here the surrogate pairs match up.
"var a2 = [];"
"var b2 = [];"
"var c2 = [];"
"var a2lens = [];"
"for (var m = 0; m < 9; m++) {"
" for (var n = 0; n < 9; n++) {"
" a2.push(a[m] + a[n]);"
" b2.push(b[m] + b[n]);"
" var newc = 'x' + c[m] + c[n] + 'y';"
" c2.push(newc.substring(1, newc.length - 1));"
" var utf = alens[m] + alens[n];" // And here.
// The 'n's that start with 0xdc.. are 6-8
// The 'm's that end with 0xd8.. are 1, 4 and 7
" if ((m % 3) == 1 && n >= 6) utf -= 2;"
" a2lens.push(utf);"
" }"
"}");
Utf16Helper(context, "a", "alens", 9);
Utf16Helper(context, "a2", "a2lens", 81);
}
static bool SameSymbol(Handle<String> s1, Handle<String> s2) {
i::Handle<i::String> is1(v8::Utils::OpenHandle(*s1));
i::Handle<i::String> is2(v8::Utils::OpenHandle(*s2));
return *is1 == *is2;
}
THREADED_TEST(Utf16Symbol) {
LocalContext context;
v8::HandleScope scope(context->GetIsolate());
Handle<String> symbol1 = v8::String::NewFromUtf8(
context->GetIsolate(), "abc", v8::String::kInternalizedString);
Handle<String> symbol2 = v8::String::NewFromUtf8(
context->GetIsolate(), "abc", v8::String::kInternalizedString);
CHECK(SameSymbol(symbol1, symbol2));
CompileRun(
"var sym0 = 'benedictus';"
"var sym0b = 'S\303\270ren';"
"var sym1 = '\355\240\201\355\260\207';"
"var sym2 = '\360\220\220\210';"
"var sym3 = 'x\355\240\201\355\260\207';"
"var sym4 = 'x\360\220\220\210';"
"if (sym1.length != 2) throw sym1;"
"if (sym1.charCodeAt(1) != 0xdc07) throw sym1.charCodeAt(1);"
"if (sym2.length != 2) throw sym2;"
"if (sym2.charCodeAt(1) != 0xdc08) throw sym2.charCodeAt(2);"
"if (sym3.length != 3) throw sym3;"
"if (sym3.charCodeAt(2) != 0xdc07) throw sym1.charCodeAt(2);"
"if (sym4.length != 3) throw sym4;"
"if (sym4.charCodeAt(2) != 0xdc08) throw sym2.charCodeAt(2);");
Handle<String> sym0 = v8::String::NewFromUtf8(
context->GetIsolate(), "benedictus", v8::String::kInternalizedString);
Handle<String> sym0b = v8::String::NewFromUtf8(
context->GetIsolate(), "S\303\270ren", v8::String::kInternalizedString);
Handle<String> sym1 =
v8::String::NewFromUtf8(context->GetIsolate(), "\355\240\201\355\260\207",
v8::String::kInternalizedString);
Handle<String> sym2 =
v8::String::NewFromUtf8(context->GetIsolate(), "\360\220\220\210",
v8::String::kInternalizedString);
Handle<String> sym3 = v8::String::NewFromUtf8(
context->GetIsolate(), "x\355\240\201\355\260\207",
v8::String::kInternalizedString);
Handle<String> sym4 =
v8::String::NewFromUtf8(context->GetIsolate(), "x\360\220\220\210",
v8::String::kInternalizedString);
v8::Local<v8::Object> global = context->Global();
Local<Value> s0 = global->Get(v8_str("sym0"));
Local<Value> s0b = global->Get(v8_str("sym0b"));
Local<Value> s1 = global->Get(v8_str("sym1"));
Local<Value> s2 = global->Get(v8_str("sym2"));
Local<Value> s3 = global->Get(v8_str("sym3"));
Local<Value> s4 = global->Get(v8_str("sym4"));
CHECK(SameSymbol(sym0, Handle<String>::Cast(s0)));
CHECK(SameSymbol(sym0b, Handle<String>::Cast(s0b)));
CHECK(SameSymbol(sym1, Handle<String>::Cast(s1)));
CHECK(SameSymbol(sym2, Handle<String>::Cast(s2)));
CHECK(SameSymbol(sym3, Handle<String>::Cast(s3)));
CHECK(SameSymbol(sym4, Handle<String>::Cast(s4)));
}
THREADED_TEST(ToArrayIndex) {
LocalContext context;
v8::Isolate* isolate = context->GetIsolate();
v8::HandleScope scope(isolate);
v8::Handle<String> str = v8_str("42");
v8::Handle<v8::Uint32> index = str->ToArrayIndex();
CHECK(!index.IsEmpty());
CHECK_EQ(42.0, index->Uint32Value());
str = v8_str("42asdf");
index = str->ToArrayIndex();
CHECK(index.IsEmpty());
str = v8_str("-42");
index = str->ToArrayIndex();
CHECK(index.IsEmpty());
str = v8_str("4294967294");
index = str->ToArrayIndex();
CHECK(!index.IsEmpty());
CHECK_EQ(4294967294.0, index->Uint32Value());
v8::Handle<v8::Number> num = v8::Number::New(isolate, 1);
index = num->ToArrayIndex();
CHECK(!index.IsEmpty());
CHECK_EQ(1.0, index->Uint32Value());
num = v8::Number::New(isolate, -1);
index = num->ToArrayIndex();
CHECK(index.IsEmpty());
v8::Handle<v8::Object> obj = v8::Object::New(isolate);
index = obj->ToArrayIndex();
CHECK(index.IsEmpty());
}
THREADED_TEST(ErrorConstruction) {
LocalContext context;
v8::HandleScope scope(context->GetIsolate());
v8::Handle<String> foo = v8_str("foo");
v8::Handle<String> message = v8_str("message");
v8::Handle<Value> range_error = v8::Exception::RangeError(foo);
CHECK(range_error->IsObject());
CHECK(range_error.As<v8::Object>()->Get(message)->Equals(foo));
v8::Handle<Value> reference_error = v8::Exception::ReferenceError(foo);
CHECK(reference_error->IsObject());
CHECK(reference_error.As<v8::Object>()->Get(message)->Equals(foo));
v8::Handle<Value> syntax_error = v8::Exception::SyntaxError(foo);
CHECK(syntax_error->IsObject());
CHECK(syntax_error.As<v8::Object>()->Get(message)->Equals(foo));
v8::Handle<Value> type_error = v8::Exception::TypeError(foo);
CHECK(type_error->IsObject());
CHECK(type_error.As<v8::Object>()->Get(message)->Equals(foo));
v8::Handle<Value> error = v8::Exception::Error(foo);
CHECK(error->IsObject());
CHECK(error.As<v8::Object>()->Get(message)->Equals(foo));
}
static void ThrowV8Exception(const v8::FunctionCallbackInfo<v8::Value>& info) {
ApiTestFuzzer::Fuzz();
v8::Handle<String> foo = v8_str("foo");
v8::Handle<String> message = v8_str("message");
v8::Handle<Value> error = v8::Exception::Error(foo);
CHECK(error->IsObject());
CHECK(error.As<v8::Object>()->Get(message)->Equals(foo));
info.GetIsolate()->ThrowException(error);
info.GetReturnValue().SetUndefined();
}
THREADED_TEST(ExceptionCreateMessage) {
LocalContext context;
v8::HandleScope scope(context->GetIsolate());
v8::Handle<String> foo_str = v8_str("foo");
v8::Handle<String> message_str = v8_str("message");
v8::V8::SetCaptureStackTraceForUncaughtExceptions(true);
Local<v8::FunctionTemplate> fun =
v8::FunctionTemplate::New(context->GetIsolate(), ThrowV8Exception);
v8::Local<v8::Object> global = context->Global();
global->Set(v8_str("throwV8Exception"), fun->GetFunction());
TryCatch try_catch(context->GetIsolate());
CompileRun(
"function f1() {\n"
" throwV8Exception();\n"
"};\n"
"f1();");
CHECK(try_catch.HasCaught());
v8::Handle<v8::Value> error = try_catch.Exception();
CHECK(error->IsObject());
CHECK(error.As<v8::Object>()->Get(message_str)->Equals(foo_str));
v8::Handle<v8::Message> message = v8::Exception::CreateMessage(error);
CHECK(!message.IsEmpty());
CHECK_EQ(2, message->GetLineNumber());
CHECK_EQ(2, message->GetStartColumn());
v8::Handle<v8::StackTrace> stackTrace = message->GetStackTrace();
CHECK(!stackTrace.IsEmpty());
CHECK_EQ(2, stackTrace->GetFrameCount());
stackTrace = v8::Exception::GetStackTrace(error);
CHECK(!stackTrace.IsEmpty());
CHECK_EQ(2, stackTrace->GetFrameCount());
v8::V8::SetCaptureStackTraceForUncaughtExceptions(false);
// Now check message location when SetCaptureStackTraceForUncaughtExceptions
// is false.
try_catch.Reset();
CompileRun(
"function f2() {\n"
" return throwV8Exception();\n"
"};\n"
"f2();");
CHECK(try_catch.HasCaught());
error = try_catch.Exception();
CHECK(error->IsObject());
CHECK(error.As<v8::Object>()->Get(message_str)->Equals(foo_str));
message = v8::Exception::CreateMessage(error);
CHECK(!message.IsEmpty());
CHECK_EQ(2, message->GetLineNumber());
CHECK_EQ(9, message->GetStartColumn());
// Should be empty stack trace.
stackTrace = message->GetStackTrace();
CHECK(stackTrace.IsEmpty());
CHECK(v8::Exception::GetStackTrace(error).IsEmpty());
}
static void YGetter(Local<String> name,
const v8::PropertyCallbackInfo<v8::Value>& info) {
ApiTestFuzzer::Fuzz();
info.GetReturnValue().Set(v8_num(10));
}
static void YSetter(Local<String> name,
Local<Value> value,
const v8::PropertyCallbackInfo<void>& info) {
Local<Object> this_obj = Local<Object>::Cast(info.This());
if (this_obj->Has(name)) this_obj->Delete(name);
this_obj->Set(name, value);
}
THREADED_TEST(DeleteAccessor) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(isolate);
obj->SetAccessor(v8_str("y"), YGetter, YSetter);
LocalContext context;
v8::Handle<v8::Object> holder = obj->NewInstance();
context->Global()->Set(v8_str("holder"), holder);
v8::Handle<Value> result = CompileRun(
"holder.y = 11; holder.y = 12; holder.y");
CHECK_EQ(12u, result->Uint32Value());
}
THREADED_TEST(TypeSwitch) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::FunctionTemplate> templ1 = v8::FunctionTemplate::New(isolate);
v8::Handle<v8::FunctionTemplate> templ2 = v8::FunctionTemplate::New(isolate);
v8::Handle<v8::FunctionTemplate> templ3 = v8::FunctionTemplate::New(isolate);
v8::Handle<v8::FunctionTemplate> templs[3] = { templ1, templ2, templ3 };
v8::Handle<v8::TypeSwitch> type_switch = v8::TypeSwitch::New(3, templs);
LocalContext context;
v8::Handle<v8::Object> obj0 = v8::Object::New(isolate);
v8::Handle<v8::Object> obj1 = templ1->GetFunction()->NewInstance();
v8::Handle<v8::Object> obj2 = templ2->GetFunction()->NewInstance();
v8::Handle<v8::Object> obj3 = templ3->GetFunction()->NewInstance();
for (int i = 0; i < 10; i++) {
CHECK_EQ(0, type_switch->match(obj0));
CHECK_EQ(1, type_switch->match(obj1));
CHECK_EQ(2, type_switch->match(obj2));
CHECK_EQ(3, type_switch->match(obj3));
CHECK_EQ(3, type_switch->match(obj3));
CHECK_EQ(2, type_switch->match(obj2));
CHECK_EQ(1, type_switch->match(obj1));
CHECK_EQ(0, type_switch->match(obj0));
}
}
static int trouble_nesting = 0;
static void TroubleCallback(const v8::FunctionCallbackInfo<v8::Value>& args) {
ApiTestFuzzer::Fuzz();
trouble_nesting++;
// Call a JS function that throws an uncaught exception.
Local<v8::Object> arg_this =
args.GetIsolate()->GetCurrentContext()->Global();
Local<Value> trouble_callee = (trouble_nesting == 3) ?
arg_this->Get(v8_str("trouble_callee")) :
arg_this->Get(v8_str("trouble_caller"));
CHECK(trouble_callee->IsFunction());
args.GetReturnValue().Set(
Function::Cast(*trouble_callee)->Call(arg_this, 0, NULL));
}
static int report_count = 0;
static void ApiUncaughtExceptionTestListener(v8::Handle<v8::Message>,
v8::Handle<Value>) {
report_count++;
}
// Counts uncaught exceptions, but other tests running in parallel
// also have uncaught exceptions.
TEST(ApiUncaughtException) {
report_count = 0;
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
v8::V8::AddMessageListener(ApiUncaughtExceptionTestListener);
Local<v8::FunctionTemplate> fun =
v8::FunctionTemplate::New(isolate, TroubleCallback);
v8::Local<v8::Object> global = env->Global();
global->Set(v8_str("trouble"), fun->GetFunction());
CompileRun(
"function trouble_callee() {"
" var x = null;"
" return x.foo;"
"};"
"function trouble_caller() {"
" trouble();"
"};");
Local<Value> trouble = global->Get(v8_str("trouble"));
CHECK(trouble->IsFunction());
Local<Value> trouble_callee = global->Get(v8_str("trouble_callee"));
CHECK(trouble_callee->IsFunction());
Local<Value> trouble_caller = global->Get(v8_str("trouble_caller"));
CHECK(trouble_caller->IsFunction());
Function::Cast(*trouble_caller)->Call(global, 0, NULL);
CHECK_EQ(1, report_count);
v8::V8::RemoveMessageListeners(ApiUncaughtExceptionTestListener);
}
TEST(ApiUncaughtExceptionInObjectObserve) {
v8::internal::FLAG_stack_size = 150;
report_count = 0;
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
v8::V8::AddMessageListener(ApiUncaughtExceptionTestListener);
CompileRun(
"var obj = {};"
"var observe_count = 0;"
"function observer1() { ++observe_count; };"
"function observer2() { ++observe_count; };"
"function observer_throws() { throw new Error(); };"
"function stack_overflow() { return (function f(x) { f(x+1); })(0); };"
"Object.observe(obj, observer_throws.bind());"
"Object.observe(obj, observer1);"
"Object.observe(obj, stack_overflow);"
"Object.observe(obj, observer2);"
"Object.observe(obj, observer_throws.bind());"
"obj.foo = 'bar';");
CHECK_EQ(3, report_count);
ExpectInt32("observe_count", 2);
v8::V8::RemoveMessageListeners(ApiUncaughtExceptionTestListener);
}
static const char* script_resource_name = "ExceptionInNativeScript.js";
static void ExceptionInNativeScriptTestListener(v8::Handle<v8::Message> message,
v8::Handle<Value>) {
v8::Handle<v8::Value> name_val = message->GetScriptOrigin().ResourceName();
CHECK(!name_val.IsEmpty() && name_val->IsString());
v8::String::Utf8Value name(message->GetScriptOrigin().ResourceName());
CHECK_EQ(0, strcmp(script_resource_name, *name));
CHECK_EQ(3, message->GetLineNumber());
v8::String::Utf8Value source_line(message->GetSourceLine());
CHECK_EQ(0, strcmp(" new o.foo();", *source_line));
}
TEST(ExceptionInNativeScript) {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
v8::V8::AddMessageListener(ExceptionInNativeScriptTestListener);
Local<v8::FunctionTemplate> fun =
v8::FunctionTemplate::New(isolate, TroubleCallback);
v8::Local<v8::Object> global = env->Global();
global->Set(v8_str("trouble"), fun->GetFunction());
CompileRunWithOrigin(
"function trouble() {\n"
" var o = {};\n"
" new o.foo();\n"
"};",
script_resource_name);
Local<Value> trouble = global->Get(v8_str("trouble"));
CHECK(trouble->IsFunction());
Function::Cast(*trouble)->Call(global, 0, NULL);
v8::V8::RemoveMessageListeners(ExceptionInNativeScriptTestListener);
}
TEST(CompilationErrorUsingTryCatchHandler) {
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
v8::TryCatch try_catch(env->GetIsolate());
v8_compile("This doesn't &*&@#$&*^ compile.");
CHECK(*try_catch.Exception());
CHECK(try_catch.HasCaught());
}
TEST(TryCatchFinallyUsingTryCatchHandler) {
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
v8::TryCatch try_catch(env->GetIsolate());
CompileRun("try { throw ''; } catch (e) {}");
CHECK(!try_catch.HasCaught());
CompileRun("try { throw ''; } finally {}");
CHECK(try_catch.HasCaught());
try_catch.Reset();
CompileRun(
"(function() {"
"try { throw ''; } finally { return; }"
"})()");
CHECK(!try_catch.HasCaught());
CompileRun(
"(function()"
" { try { throw ''; } finally { throw 0; }"
"})()");
CHECK(try_catch.HasCaught());
}
void CEvaluate(const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::HandleScope scope(args.GetIsolate());
CompileRun(args[0]->ToString(args.GetIsolate()));
}
TEST(TryCatchFinallyStoresMessageUsingTryCatchHandler) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
templ->Set(v8_str("CEvaluate"),
v8::FunctionTemplate::New(isolate, CEvaluate));
LocalContext context(0, templ);
v8::TryCatch try_catch(isolate);
CompileRun("try {"
" CEvaluate('throw 1;');"
"} finally {"
"}");
CHECK(try_catch.HasCaught());
CHECK(!try_catch.Message().IsEmpty());
String::Utf8Value exception_value(try_catch.Exception());
CHECK_EQ(0, strcmp(*exception_value, "1"));
try_catch.Reset();
CompileRun("try {"
" CEvaluate('throw 1;');"
"} finally {"
" throw 2;"
"}");
CHECK(try_catch.HasCaught());
CHECK(!try_catch.Message().IsEmpty());
String::Utf8Value finally_exception_value(try_catch.Exception());
CHECK_EQ(0, strcmp(*finally_exception_value, "2"));
}
// For use within the TestSecurityHandler() test.
static bool g_security_callback_result = false;
static bool SecurityTestCallback(Local<v8::Object> global, Local<Value> name,
v8::AccessType type, Local<Value> data) {
printf("a\n");
return g_security_callback_result;
}
// SecurityHandler can't be run twice
TEST(SecurityHandler) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope0(isolate);
v8::Handle<v8::ObjectTemplate> global_template =
v8::ObjectTemplate::New(isolate);
global_template->SetAccessCheckCallbacks(SecurityTestCallback, NULL);
// Create an environment
v8::Handle<Context> context0 = Context::New(isolate, NULL, global_template);
context0->Enter();
v8::Handle<v8::Object> global0 = context0->Global();
v8::Handle<Script> script0 = v8_compile("foo = 111");
script0->Run();
global0->Set(v8_str("0"), v8_num(999));
v8::Handle<Value> foo0 = global0->Get(v8_str("foo"));
CHECK_EQ(111, foo0->Int32Value());
v8::Handle<Value> z0 = global0->Get(v8_str("0"));
CHECK_EQ(999, z0->Int32Value());
// Create another environment, should fail security checks.
v8::HandleScope scope1(isolate);
v8::Handle<Context> context1 =
Context::New(isolate, NULL, global_template);
context1->Enter();
v8::Handle<v8::Object> global1 = context1->Global();
global1->Set(v8_str("othercontext"), global0);
// This set will fail the security check.
v8::Handle<Script> script1 =
v8_compile("othercontext.foo = 222; othercontext[0] = 888;");
script1->Run();
g_security_callback_result = true;
// This read will pass the security check.
v8::Handle<Value> foo1 = global0->Get(v8_str("foo"));
CHECK_EQ(111, foo1->Int32Value());
// This read will pass the security check.
v8::Handle<Value> z1 = global0->Get(v8_str("0"));
CHECK_EQ(999, z1->Int32Value());
// Create another environment, should pass security checks.
{
v8::HandleScope scope2(isolate);
LocalContext context2;
v8::Handle<v8::Object> global2 = context2->Global();
global2->Set(v8_str("othercontext"), global0);
v8::Handle<Script> script2 =
v8_compile("othercontext.foo = 333; othercontext[0] = 888;");
script2->Run();
v8::Handle<Value> foo2 = global0->Get(v8_str("foo"));
CHECK_EQ(333, foo2->Int32Value());
v8::Handle<Value> z2 = global0->Get(v8_str("0"));
CHECK_EQ(888, z2->Int32Value());
}
context1->Exit();
context0->Exit();
}
THREADED_TEST(SecurityChecks) {
LocalContext env1;
v8::HandleScope handle_scope(env1->GetIsolate());
v8::Handle<Context> env2 = Context::New(env1->GetIsolate());
Local<Value> foo = v8_str("foo");
Local<Value> bar = v8_str("bar");
// Set to the same domain.
env1->SetSecurityToken(foo);
// Create a function in env1.
CompileRun("spy=function(){return spy;}");
Local<Value> spy = env1->Global()->Get(v8_str("spy"));
CHECK(spy->IsFunction());
// Create another function accessing global objects.
CompileRun("spy2=function(){return new this.Array();}");
Local<Value> spy2 = env1->Global()->Get(v8_str("spy2"));
CHECK(spy2->IsFunction());
// Switch to env2 in the same domain and invoke spy on env2.
{
env2->SetSecurityToken(foo);
// Enter env2
Context::Scope scope_env2(env2);
Local<Value> result = Function::Cast(*spy)->Call(env2->Global(), 0, NULL);
CHECK(result->IsFunction());
}
{
env2->SetSecurityToken(bar);
Context::Scope scope_env2(env2);
// Call cross_domain_call, it should throw an exception
v8::TryCatch try_catch(env1->GetIsolate());
Function::Cast(*spy2)->Call(env2->Global(), 0, NULL);
CHECK(try_catch.HasCaught());
}
}
// Regression test case for issue 1183439.
THREADED_TEST(SecurityChecksForPrototypeChain) {
LocalContext current;
v8::HandleScope scope(current->GetIsolate());
v8::Handle<Context> other = Context::New(current->GetIsolate());
// Change context to be able to get to the Object function in the
// other context without hitting the security checks.
v8::Local<Value> other_object;
{ Context::Scope scope(other);
other_object = other->Global()->Get(v8_str("Object"));
other->Global()->Set(v8_num(42), v8_num(87));
}
current->Global()->Set(v8_str("other"), other->Global());
CHECK(v8_compile("other")->Run()->Equals(other->Global()));
// Make sure the security check fails here and we get an undefined
// result instead of getting the Object function. Repeat in a loop
// to make sure to exercise the IC code.
v8::Local<Script> access_other0 = v8_compile("other.Object");
v8::Local<Script> access_other1 = v8_compile("other[42]");
for (int i = 0; i < 5; i++) {
CHECK(access_other0->Run().IsEmpty());
CHECK(access_other1->Run().IsEmpty());
}
// Create an object that has 'other' in its prototype chain and make
// sure we cannot access the Object function indirectly through
// that. Repeat in a loop to make sure to exercise the IC code.
v8_compile("function F() { };"
"F.prototype = other;"
"var f = new F();")->Run();
v8::Local<Script> access_f0 = v8_compile("f.Object");
v8::Local<Script> access_f1 = v8_compile("f[42]");
for (int j = 0; j < 5; j++) {
CHECK(access_f0->Run().IsEmpty());
CHECK(access_f1->Run().IsEmpty());
}
// Now it gets hairy: Set the prototype for the other global object
// to be the current global object. The prototype chain for 'f' now
// goes through 'other' but ends up in the current global object.
{ Context::Scope scope(other);
other->Global()->Set(v8_str("__proto__"), current->Global());
}
// Set a named and an index property on the current global
// object. To force the lookup to go through the other global object,
// the properties must not exist in the other global object.
current->Global()->Set(v8_str("foo"), v8_num(100));
current->Global()->Set(v8_num(99), v8_num(101));
// Try to read the properties from f and make sure that the access
// gets stopped by the security checks on the other global object.
Local<Script> access_f2 = v8_compile("f.foo");
Local<Script> access_f3 = v8_compile("f[99]");
for (int k = 0; k < 5; k++) {
CHECK(access_f2->Run().IsEmpty());
CHECK(access_f3->Run().IsEmpty());
}
}
static bool security_check_with_gc_called;
static bool SecurityTestCallbackWithGC(Local<v8::Object> global,
Local<v8::Value> name,
v8::AccessType type, Local<Value> data) {
CcTest::heap()->CollectAllGarbage();
security_check_with_gc_called = true;
return true;
}
TEST(SecurityTestGCAllowed) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope handle_scope(isolate);
v8::Handle<v8::ObjectTemplate> object_template =
v8::ObjectTemplate::New(isolate);
object_template->SetAccessCheckCallbacks(SecurityTestCallbackWithGC, NULL);
v8::Handle<Context> context = Context::New(isolate);
v8::Context::Scope context_scope(context);
context->Global()->Set(v8_str("obj"), object_template->NewInstance());
security_check_with_gc_called = false;
CompileRun("obj[0] = new String(1002);");
CHECK(security_check_with_gc_called);
security_check_with_gc_called = false;
CHECK(CompileRun("obj[0]")->ToString(isolate)->Equals(v8_str("1002")));
CHECK(security_check_with_gc_called);
}
THREADED_TEST(CrossDomainDelete) {
LocalContext env1;
v8::HandleScope handle_scope(env1->GetIsolate());
v8::Handle<Context> env2 = Context::New(env1->GetIsolate());
Local<Value> foo = v8_str("foo");
Local<Value> bar = v8_str("bar");
// Set to the same domain.
env1->SetSecurityToken(foo);
env2->SetSecurityToken(foo);
env1->Global()->Set(v8_str("prop"), v8_num(3));
env2->Global()->Set(v8_str("env1"), env1->Global());
// Change env2 to a different domain and delete env1.prop.
env2->SetSecurityToken(bar);
{
Context::Scope scope_env2(env2);
Local<Value> result =
CompileRun("delete env1.prop");
CHECK(result.IsEmpty());
}
// Check that env1.prop still exists.
Local<Value> v = env1->Global()->Get(v8_str("prop"));
CHECK(v->IsNumber());
CHECK_EQ(3, v->Int32Value());
}
THREADED_TEST(CrossDomainIsPropertyEnumerable) {
LocalContext env1;
v8::HandleScope handle_scope(env1->GetIsolate());
v8::Handle<Context> env2 = Context::New(env1->GetIsolate());
Local<Value> foo = v8_str("foo");
Local<Value> bar = v8_str("bar");
// Set to the same domain.
env1->SetSecurityToken(foo);
env2->SetSecurityToken(foo);
env1->Global()->Set(v8_str("prop"), v8_num(3));
env2->Global()->Set(v8_str("env1"), env1->Global());
// env1.prop is enumerable in env2.
Local<String> test = v8_str("propertyIsEnumerable.call(env1, 'prop')");
{
Context::Scope scope_env2(env2);
Local<Value> result = CompileRun(test);
CHECK(result->IsTrue());
}
// Change env2 to a different domain and test again.
env2->SetSecurityToken(bar);
{
Context::Scope scope_env2(env2);
Local<Value> result = CompileRun(test);
CHECK(result.IsEmpty());
}
}
THREADED_TEST(CrossDomainForIn) {
LocalContext env1;
v8::HandleScope handle_scope(env1->GetIsolate());
v8::Handle<Context> env2 = Context::New(env1->GetIsolate());
Local<Value> foo = v8_str("foo");
Local<Value> bar = v8_str("bar");
// Set to the same domain.
env1->SetSecurityToken(foo);
env2->SetSecurityToken(foo);
env1->Global()->Set(v8_str("prop"), v8_num(3));
env2->Global()->Set(v8_str("env1"), env1->Global());
// Change env2 to a different domain and set env1's global object
// as the __proto__ of an object in env2 and enumerate properties
// in for-in. It shouldn't enumerate properties on env1's global
// object.
env2->SetSecurityToken(bar);
{
Context::Scope scope_env2(env2);
Local<Value> result = CompileRun(
"(function() {"
" var obj = { '__proto__': env1 };"
" try {"
" for (var p in obj) {"
" if (p == 'prop') return false;"
" }"
" return false;"
" } catch (e) {"
" return true;"
" }"
"})()");
CHECK(result->IsTrue());
}
}
TEST(ContextDetachGlobal) {
LocalContext env1;
v8::HandleScope handle_scope(env1->GetIsolate());
v8::Handle<Context> env2 = Context::New(env1->GetIsolate());
Local<v8::Object> global1 = env1->Global();
Local<Value> foo = v8_str("foo");
// Set to the same domain.
env1->SetSecurityToken(foo);
env2->SetSecurityToken(foo);
// Enter env2
env2->Enter();
// Create a function in env2 and add a reference to it in env1.
Local<v8::Object> global2 = env2->Global();
global2->Set(v8_str("prop"), v8::Integer::New(env2->GetIsolate(), 1));
CompileRun("function getProp() {return prop;}");
env1->Global()->Set(v8_str("getProp"),
global2->Get(v8_str("getProp")));
// Detach env2's global, and reuse the global object of env2
env2->Exit();
env2->DetachGlobal();
v8::Handle<Context> env3 = Context::New(env1->GetIsolate(),
0,
v8::Handle<v8::ObjectTemplate>(),
global2);
env3->SetSecurityToken(v8_str("bar"));
env3->Enter();
Local<v8::Object> global3 = env3->Global();
CHECK(global2->Equals(global3));
CHECK(global3->Get(v8_str("prop"))->IsUndefined());
CHECK(global3->Get(v8_str("getProp"))->IsUndefined());
global3->Set(v8_str("prop"), v8::Integer::New(env3->GetIsolate(), -1));
global3->Set(v8_str("prop2"), v8::Integer::New(env3->GetIsolate(), 2));
env3->Exit();
// Call getProp in env1, and it should return the value 1
{
Local<Value> get_prop = global1->Get(v8_str("getProp"));
CHECK(get_prop->IsFunction());
v8::TryCatch try_catch(env1->GetIsolate());
Local<Value> r = Function::Cast(*get_prop)->Call(global1, 0, NULL);
CHECK(!try_catch.HasCaught());
CHECK_EQ(1, r->Int32Value());
}
// Check that env3 is not accessible from env1
{
Local<Value> r = global3->Get(v8_str("prop2"));
CHECK(r.IsEmpty());
}
}
TEST(DetachGlobal) {
LocalContext env1;
v8::HandleScope scope(env1->GetIsolate());
// Create second environment.
v8::Handle<Context> env2 = Context::New(env1->GetIsolate());
Local<Value> foo = v8_str("foo");
// Set same security token for env1 and env2.
env1->SetSecurityToken(foo);
env2->SetSecurityToken(foo);
// Create a property on the global object in env2.
{
v8::Context::Scope scope(env2);
env2->Global()->Set(v8_str("p"), v8::Integer::New(env2->GetIsolate(), 42));
}
// Create a reference to env2 global from env1 global.
env1->Global()->Set(v8_str("other"), env2->Global());
// Check that we have access to other.p in env2 from env1.
Local<Value> result = CompileRun("other.p");
CHECK(result->IsInt32());
CHECK_EQ(42, result->Int32Value());
// Hold on to global from env2 and detach global from env2.
Local<v8::Object> global2 = env2->Global();
env2->DetachGlobal();
// Check that the global has been detached. No other.p property can
// be found.
result = CompileRun("other.p");
CHECK(result.IsEmpty());
// Reuse global2 for env3.
v8::Handle<Context> env3 = Context::New(env1->GetIsolate(),
0,
v8::Handle<v8::ObjectTemplate>(),
global2);
CHECK(global2->Equals(env3->Global()));
// Start by using the same security token for env3 as for env1 and env2.
env3->SetSecurityToken(foo);
// Create a property on the global object in env3.
{
v8::Context::Scope scope(env3);
env3->Global()->Set(v8_str("p"), v8::Integer::New(env3->GetIsolate(), 24));
}
// Check that other.p is now the property in env3 and that we have access.
result = CompileRun("other.p");
CHECK(result->IsInt32());
CHECK_EQ(24, result->Int32Value());
// Change security token for env3 to something different from env1 and env2.
env3->SetSecurityToken(v8_str("bar"));
// Check that we do not have access to other.p in env1. |other| is now
// the global object for env3 which has a different security token,
// so access should be blocked.
result = CompileRun("other.p");
CHECK(result.IsEmpty());
}
void GetThisX(const v8::FunctionCallbackInfo<v8::Value>& info) {
info.GetReturnValue().Set(
info.GetIsolate()->GetCurrentContext()->Global()->Get(v8_str("x")));
}
TEST(DetachedAccesses) {
LocalContext env1;
v8::HandleScope scope(env1->GetIsolate());
// Create second environment.
Local<ObjectTemplate> inner_global_template =
FunctionTemplate::New(env1->GetIsolate())->InstanceTemplate();
inner_global_template ->SetAccessorProperty(
v8_str("this_x"), FunctionTemplate::New(env1->GetIsolate(), GetThisX));
v8::Local<Context> env2 =
Context::New(env1->GetIsolate(), NULL, inner_global_template);
Local<Value> foo = v8_str("foo");
// Set same security token for env1 and env2.
env1->SetSecurityToken(foo);
env2->SetSecurityToken(foo);
env1->Global()->Set(v8_str("x"), v8_str("env1_x"));
{
v8::Context::Scope scope(env2);
env2->Global()->Set(v8_str("x"), v8_str("env2_x"));
CompileRun(
"function bound_x() { return x; }"
"function get_x() { return this.x; }"
"function get_x_w() { return (function() {return this.x;})(); }");
env1->Global()->Set(v8_str("bound_x"), CompileRun("bound_x"));
env1->Global()->Set(v8_str("get_x"), CompileRun("get_x"));
env1->Global()->Set(v8_str("get_x_w"), CompileRun("get_x_w"));
env1->Global()->Set(
v8_str("this_x"),
CompileRun("Object.getOwnPropertyDescriptor(this, 'this_x').get"));
}
Local<Object> env2_global = env2->Global();
env2_global->TurnOnAccessCheck();
env2->DetachGlobal();
Local<Value> result;
result = CompileRun("bound_x()");
CHECK(v8_str("env2_x")->Equals(result));
result = CompileRun("get_x()");
CHECK(result.IsEmpty());
result = CompileRun("get_x_w()");
CHECK(result.IsEmpty());
result = CompileRun("this_x()");
CHECK(v8_str("env2_x")->Equals(result));
// Reattach env2's proxy
env2 = Context::New(env1->GetIsolate(),
0,
v8::Handle<v8::ObjectTemplate>(),
env2_global);
env2->SetSecurityToken(foo);
{
v8::Context::Scope scope(env2);
env2->Global()->Set(v8_str("x"), v8_str("env3_x"));
env2->Global()->Set(v8_str("env1"), env1->Global());
result = CompileRun(
"results = [];"
"for (var i = 0; i < 4; i++ ) {"
" results.push(env1.bound_x());"
" results.push(env1.get_x());"
" results.push(env1.get_x_w());"
" results.push(env1.this_x());"
"}"
"results");
Local<v8::Array> results = Local<v8::Array>::Cast(result);
CHECK_EQ(16u, results->Length());
for (int i = 0; i < 16; i += 4) {
CHECK(v8_str("env2_x")->Equals(results->Get(i + 0)));
CHECK(v8_str("env1_x")->Equals(results->Get(i + 1)));
CHECK(v8_str("env3_x")->Equals(results->Get(i + 2)));
CHECK(v8_str("env2_x")->Equals(results->Get(i + 3)));
}
}
result = CompileRun(
"results = [];"
"for (var i = 0; i < 4; i++ ) {"
" results.push(bound_x());"
" results.push(get_x());"
" results.push(get_x_w());"
" results.push(this_x());"
"}"
"results");
Local<v8::Array> results = Local<v8::Array>::Cast(result);
CHECK_EQ(16u, results->Length());
for (int i = 0; i < 16; i += 4) {
CHECK(v8_str("env2_x")->Equals(results->Get(i + 0)));
CHECK(v8_str("env3_x")->Equals(results->Get(i + 1)));
CHECK(v8_str("env3_x")->Equals(results->Get(i + 2)));
CHECK(v8_str("env2_x")->Equals(results->Get(i + 3)));
}
result = CompileRun(
"results = [];"
"for (var i = 0; i < 4; i++ ) {"
" results.push(this.bound_x());"
" results.push(this.get_x());"
" results.push(this.get_x_w());"
" results.push(this.this_x());"
"}"
"results");
results = Local<v8::Array>::Cast(result);
CHECK_EQ(16u, results->Length());
for (int i = 0; i < 16; i += 4) {
CHECK(v8_str("env2_x")->Equals(results->Get(i + 0)));
CHECK(v8_str("env1_x")->Equals(results->Get(i + 1)));
CHECK(v8_str("env3_x")->Equals(results->Get(i + 2)));
CHECK(v8_str("env2_x")->Equals(results->Get(i + 3)));
}
}
static bool allowed_access = false;
static bool AccessBlocker(Local<v8::Object> global, Local<Value> name,
v8::AccessType type, Local<Value> data) {
return CcTest::isolate()->GetCurrentContext()->Global()->Equals(global) ||
allowed_access;
}
static int g_echo_value = -1;
static void EchoGetter(
Local<String> name,
const v8::PropertyCallbackInfo<v8::Value>& info) {
info.GetReturnValue().Set(v8_num(g_echo_value));
}
static void EchoSetter(Local<String> name,
Local<Value> value,
const v8::PropertyCallbackInfo<void>&) {
if (value->IsNumber())
g_echo_value = value->Int32Value();
}
static void UnreachableGetter(
Local<String> name,
const v8::PropertyCallbackInfo<v8::Value>& info) {
CHECK(false); // This function should not be called..
}
static void UnreachableSetter(Local<String>,
Local<Value>,
const v8::PropertyCallbackInfo<void>&) {
CHECK(false); // This function should nto be called.
}
static void UnreachableFunction(
const v8::FunctionCallbackInfo<v8::Value>& info) {
CHECK(false); // This function should not be called..
}
TEST(AccessControl) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope handle_scope(isolate);
v8::Handle<v8::ObjectTemplate> global_template =
v8::ObjectTemplate::New(isolate);
global_template->SetAccessCheckCallbacks(AccessBlocker, NULL);
// Add an accessor accessible by cross-domain JS code.
global_template->SetAccessor(
v8_str("accessible_prop"),
EchoGetter, EchoSetter,
v8::Handle<Value>(),
v8::AccessControl(v8::ALL_CAN_READ | v8::ALL_CAN_WRITE));
// Add an accessor that is not accessible by cross-domain JS code.
global_template->SetAccessor(v8_str("blocked_prop"),
UnreachableGetter, UnreachableSetter,
v8::Handle<Value>(),
v8::DEFAULT);
global_template->SetAccessorProperty(
v8_str("blocked_js_prop"),
v8::FunctionTemplate::New(isolate, UnreachableFunction),
v8::FunctionTemplate::New(isolate, UnreachableFunction),
v8::None,
v8::DEFAULT);
// Create an environment
v8::Local<Context> context0 = Context::New(isolate, NULL, global_template);
context0->Enter();
v8::Handle<v8::Object> global0 = context0->Global();
// Define a property with JS getter and setter.
CompileRun(
"function getter() { return 'getter'; };\n"
"function setter() { return 'setter'; }\n"
"Object.defineProperty(this, 'js_accessor_p', {get:getter, set:setter})");
Local<Value> getter = global0->Get(v8_str("getter"));
Local<Value> setter = global0->Get(v8_str("setter"));
// And define normal element.
global0->Set(239, v8_str("239"));
// Define an element with JS getter and setter.
CompileRun(
"function el_getter() { return 'el_getter'; };\n"
"function el_setter() { return 'el_setter'; };\n"
"Object.defineProperty(this, '42', {get: el_getter, set: el_setter});");
Local<Value> el_getter = global0->Get(v8_str("el_getter"));
Local<Value> el_setter = global0->Get(v8_str("el_setter"));
v8::HandleScope scope1(isolate);
v8::Local<Context> context1 = Context::New(isolate);
context1->Enter();
v8::Handle<v8::Object> global1 = context1->Global();
global1->Set(v8_str("other"), global0);
// Access blocked property.
CompileRun("other.blocked_prop = 1");
CHECK(CompileRun("other.blocked_prop").IsEmpty());
CHECK(CompileRun("Object.getOwnPropertyDescriptor(other, 'blocked_prop')")
.IsEmpty());
CHECK(
CompileRun("propertyIsEnumerable.call(other, 'blocked_prop')").IsEmpty());
// Access blocked element.
CHECK(CompileRun("other[239] = 1").IsEmpty());
CHECK(CompileRun("other[239]").IsEmpty());
CHECK(CompileRun("Object.getOwnPropertyDescriptor(other, '239')").IsEmpty());
CHECK(CompileRun("propertyIsEnumerable.call(other, '239')").IsEmpty());
allowed_access = true;
// Now we can enumerate the property.
ExpectTrue("propertyIsEnumerable.call(other, '239')");
allowed_access = false;
// Access a property with JS accessor.
CHECK(CompileRun("other.js_accessor_p = 2").IsEmpty());
CHECK(CompileRun("other.js_accessor_p").IsEmpty());
CHECK(CompileRun("Object.getOwnPropertyDescriptor(other, 'js_accessor_p')")
.IsEmpty());
allowed_access = true;
ExpectString("other.js_accessor_p", "getter");
ExpectObject(
"Object.getOwnPropertyDescriptor(other, 'js_accessor_p').get", getter);
ExpectObject(
"Object.getOwnPropertyDescriptor(other, 'js_accessor_p').set", setter);
ExpectUndefined(
"Object.getOwnPropertyDescriptor(other, 'js_accessor_p').value");
allowed_access = false;
// Access an element with JS accessor.
CHECK(CompileRun("other[42] = 2").IsEmpty());
CHECK(CompileRun("other[42]").IsEmpty());
CHECK(CompileRun("Object.getOwnPropertyDescriptor(other, '42')").IsEmpty());
allowed_access = true;
ExpectString("other[42]", "el_getter");
ExpectObject("Object.getOwnPropertyDescriptor(other, '42').get", el_getter);
ExpectObject("Object.getOwnPropertyDescriptor(other, '42').set", el_setter);
ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').value");
allowed_access = false;
v8::Handle<Value> value;
// Access accessible property
value = CompileRun("other.accessible_prop = 3");
CHECK(value->IsNumber());
CHECK_EQ(3, value->Int32Value());
CHECK_EQ(3, g_echo_value);
value = CompileRun("other.accessible_prop");
CHECK(value->IsNumber());
CHECK_EQ(3, value->Int32Value());
value = CompileRun(
"Object.getOwnPropertyDescriptor(other, 'accessible_prop').value");
CHECK(value->IsNumber());
CHECK_EQ(3, value->Int32Value());
value = CompileRun("propertyIsEnumerable.call(other, 'accessible_prop')");
CHECK(value->IsTrue());
// Enumeration doesn't enumerate accessors from inaccessible objects in
// the prototype chain even if the accessors are in themselves accessible.
value = CompileRun(
"(function() {"
" var obj = { '__proto__': other };"
" try {"
" for (var p in obj) {"
" if (p == 'accessible_prop' ||"
" p == 'blocked_js_prop' ||"
" p == 'blocked_js_prop') {"
" return false;"
" }"
" }"
" return false;"
" } catch (e) {"
" return true;"
" }"
"})()");
CHECK(value->IsTrue());
context1->Exit();
context0->Exit();
}
TEST(AccessControlES5) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope handle_scope(isolate);
v8::Handle<v8::ObjectTemplate> global_template =
v8::ObjectTemplate::New(isolate);
global_template->SetAccessCheckCallbacks(AccessBlocker, NULL);
// Add accessible accessor.
global_template->SetAccessor(
v8_str("accessible_prop"),
EchoGetter, EchoSetter,
v8::Handle<Value>(),
v8::AccessControl(v8::ALL_CAN_READ | v8::ALL_CAN_WRITE));
// Add an accessor that is not accessible by cross-domain JS code.
global_template->SetAccessor(v8_str("blocked_prop"),
UnreachableGetter, UnreachableSetter,
v8::Handle<Value>(),
v8::DEFAULT);
// Create an environment
v8::Local<Context> context0 = Context::New(isolate, NULL, global_template);
context0->Enter();
v8::Handle<v8::Object> global0 = context0->Global();
v8::Local<Context> context1 = Context::New(isolate);
context1->Enter();
v8::Handle<v8::Object> global1 = context1->Global();
global1->Set(v8_str("other"), global0);
// Regression test for issue 1154.
CHECK(CompileRun("Object.keys(other)").IsEmpty());
CHECK(CompileRun("other.blocked_prop").IsEmpty());
// Regression test for issue 1027.
CompileRun("Object.defineProperty(\n"
" other, 'blocked_prop', {configurable: false})");
CHECK(CompileRun("other.blocked_prop").IsEmpty());
CHECK(CompileRun("Object.getOwnPropertyDescriptor(other, 'blocked_prop')")
.IsEmpty());
// Regression test for issue 1171.
ExpectTrue("Object.isExtensible(other)");
CompileRun("Object.preventExtensions(other)");
ExpectTrue("Object.isExtensible(other)");
// Object.seal and Object.freeze.
CompileRun("Object.freeze(other)");
ExpectTrue("Object.isExtensible(other)");
CompileRun("Object.seal(other)");
ExpectTrue("Object.isExtensible(other)");
// Regression test for issue 1250.
// Make sure that we can set the accessible accessors value using normal
// assignment.
CompileRun("other.accessible_prop = 42");
CHECK_EQ(42, g_echo_value);
v8::Handle<Value> value;
CompileRun("Object.defineProperty(other, 'accessible_prop', {value: -1})");
value = CompileRun("other.accessible_prop == 42");
CHECK(value->IsTrue());
}
static bool AccessAlwaysBlocked(Local<v8::Object> global, Local<Value> name,
v8::AccessType type, Local<Value> data) {
i::PrintF("Access blocked.\n");
return false;
}
THREADED_TEST(AccessControlGetOwnPropertyNames) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope handle_scope(isolate);
v8::Handle<v8::ObjectTemplate> obj_template =
v8::ObjectTemplate::New(isolate);
obj_template->Set(v8_str("x"), v8::Integer::New(isolate, 42));
obj_template->SetAccessCheckCallbacks(AccessAlwaysBlocked, NULL);
// Create an environment
v8::Local<Context> context0 = Context::New(isolate, NULL, obj_template);
context0->Enter();
v8::Handle<v8::Object> global0 = context0->Global();
v8::HandleScope scope1(CcTest::isolate());
v8::Local<Context> context1 = Context::New(isolate);
context1->Enter();
v8::Handle<v8::Object> global1 = context1->Global();
global1->Set(v8_str("other"), global0);
global1->Set(v8_str("object"), obj_template->NewInstance());
v8::Handle<Value> value;
// Attempt to get the property names of the other global object and
// of an object that requires access checks. Accessing the other
// global object should be blocked by access checks on the global
// proxy object. Accessing the object that requires access checks
// is blocked by the access checks on the object itself.
value = CompileRun("Object.getOwnPropertyNames(other).length == 0");
CHECK(value.IsEmpty());
value = CompileRun("Object.getOwnPropertyNames(object).length == 0");
CHECK(value.IsEmpty());
context1->Exit();
context0->Exit();
}
TEST(SuperAccessControl) {
i::FLAG_allow_natives_syntax = true;
i::FLAG_harmony_classes = true;
i::FLAG_harmony_object_literals = true;
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope handle_scope(isolate);
v8::Handle<v8::ObjectTemplate> obj_template =
v8::ObjectTemplate::New(isolate);
obj_template->SetAccessCheckCallbacks(AccessAlwaysBlocked, NULL);
LocalContext env;
env->Global()->Set(v8_str("prohibited"), obj_template->NewInstance());
{
v8::TryCatch try_catch(isolate);
CompileRun(
"var f = { m() { return super.hasOwnProperty; } }.m;"
"var m = %ToMethod(f, prohibited);"
"m();");
CHECK(try_catch.HasCaught());
}
{
v8::TryCatch try_catch(isolate);
CompileRun(
"var f = {m() { return super[42]; } }.m;"
"var m = %ToMethod(f, prohibited);"
"m();");
CHECK(try_catch.HasCaught());
}
{
v8::TryCatch try_catch(isolate);
CompileRun(
"var f = {m() { super.hasOwnProperty = function () {}; } }.m;"
"var m = %ToMethod(f, prohibited);"
"m();");
CHECK(try_catch.HasCaught());
}
{
v8::TryCatch try_catch(isolate);
CompileRun(
"Object.defineProperty(Object.prototype, 'x', { set : function(){}});"
"var f = {"
" m() { "
" 'use strict';"
" super.x = function () {};"
" }"
"}.m;"
"var m = %ToMethod(f, prohibited);"
"m();");
CHECK(try_catch.HasCaught());
}
}
TEST(Regress470113) {
i::FLAG_harmony_classes = true;
i::FLAG_harmony_object_literals = true;
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope handle_scope(isolate);
v8::Handle<v8::ObjectTemplate> obj_template =
v8::ObjectTemplate::New(isolate);
obj_template->SetAccessCheckCallbacks(AccessAlwaysBlocked, NULL);
LocalContext env;
env->Global()->Set(v8_str("prohibited"), obj_template->NewInstance());
{
v8::TryCatch try_catch(isolate);
CompileRun(
"'use strict';\n"
"class C extends Object {\n"
" m() { super.powned = 'Powned!'; }\n"
"}\n"
"let c = new C();\n"
"c.m.call(prohibited)");
CHECK(try_catch.HasCaught());
}
}
static void ConstTenGetter(Local<String> name,
const v8::PropertyCallbackInfo<v8::Value>& info) {
info.GetReturnValue().Set(v8_num(10));
}
THREADED_TEST(CrossDomainAccessors) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope handle_scope(isolate);
v8::Handle<v8::FunctionTemplate> func_template =
v8::FunctionTemplate::New(isolate);
v8::Handle<v8::ObjectTemplate> global_template =
func_template->InstanceTemplate();
v8::Handle<v8::ObjectTemplate> proto_template =
func_template->PrototypeTemplate();
// Add an accessor to proto that's accessible by cross-domain JS code.
proto_template->SetAccessor(v8_str("accessible"),
ConstTenGetter, 0,
v8::Handle<Value>(),
v8::ALL_CAN_READ);
// Add an accessor that is not accessible by cross-domain JS code.
global_template->SetAccessor(v8_str("unreachable"),
UnreachableGetter, 0,
v8::Handle<Value>(),
v8::DEFAULT);
v8::Local<Context> context0 = Context::New(isolate, NULL, global_template);
context0->Enter();
Local<v8::Object> global = context0->Global();
// Add a normal property that shadows 'accessible'
global->Set(v8_str("accessible"), v8_num(11));
// Enter a new context.
v8::HandleScope scope1(CcTest::isolate());
v8::Local<Context> context1 = Context::New(isolate);
context1->Enter();
v8::Handle<v8::Object> global1 = context1->Global();
global1->Set(v8_str("other"), global);
// Should return 10, instead of 11
v8::Handle<Value> value = v8_compile("other.accessible")->Run();
CHECK(value->IsNumber());
CHECK_EQ(10, value->Int32Value());
value = v8_compile("other.unreachable")->Run();
CHECK(value.IsEmpty());
context1->Exit();
context0->Exit();
}
static int access_count = 0;
static bool AccessCounter(Local<v8::Object> global, Local<Value> name,
v8::AccessType type, Local<Value> data) {
access_count++;
return true;
}
// This one is too easily disturbed by other tests.
TEST(AccessControlIC) {
access_count = 0;
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope handle_scope(isolate);
// Create an environment.
v8::Local<Context> context0 = Context::New(isolate);
context0->Enter();
// Create an object that requires access-check functions to be
// called for cross-domain access.
v8::Handle<v8::ObjectTemplate> object_template =
v8::ObjectTemplate::New(isolate);
object_template->SetAccessCheckCallbacks(AccessCounter, NULL);
Local<v8::Object> object = object_template->NewInstance();
v8::HandleScope scope1(isolate);
// Create another environment.
v8::Local<Context> context1 = Context::New(isolate);
context1->Enter();
// Make easy access to the object from the other environment.
v8::Handle<v8::Object> global1 = context1->Global();
global1->Set(v8_str("obj"), object);
v8::Handle<Value> value;
// Check that the named access-control function is called every time.
CompileRun("function testProp(obj) {"
" for (var i = 0; i < 10; i++) obj.prop = 1;"
" for (var j = 0; j < 10; j++) obj.prop;"
" return obj.prop"
"}");
value = CompileRun("testProp(obj)");
CHECK(value->IsNumber());
CHECK_EQ(1, value->Int32Value());
CHECK_EQ(21, access_count);
// Check that the named access-control function is called every time.
CompileRun("var p = 'prop';"
"function testKeyed(obj) {"
" for (var i = 0; i < 10; i++) obj[p] = 1;"
" for (var j = 0; j < 10; j++) obj[p];"
" return obj[p];"
"}");
// Use obj which requires access checks. No inline caching is used
// in that case.
value = CompileRun("testKeyed(obj)");
CHECK(value->IsNumber());
CHECK_EQ(1, value->Int32Value());
CHECK_EQ(42, access_count);
// Force the inline caches into generic state and try again.
CompileRun("testKeyed({ a: 0 })");
CompileRun("testKeyed({ b: 0 })");
value = CompileRun("testKeyed(obj)");
CHECK(value->IsNumber());
CHECK_EQ(1, value->Int32Value());
CHECK_EQ(63, access_count);
// Check that the indexed access-control function is called every time.
access_count = 0;
CompileRun("function testIndexed(obj) {"
" for (var i = 0; i < 10; i++) obj[0] = 1;"
" for (var j = 0; j < 10; j++) obj[0];"
" return obj[0]"
"}");
value = CompileRun("testIndexed(obj)");
CHECK(value->IsNumber());
CHECK_EQ(1, value->Int32Value());
CHECK_EQ(21, access_count);
// Force the inline caches into generic state.
CompileRun("testIndexed(new Array(1))");
// Test that the indexed access check is called.
value = CompileRun("testIndexed(obj)");
CHECK(value->IsNumber());
CHECK_EQ(1, value->Int32Value());
CHECK_EQ(42, access_count);
access_count = 0;
// Check that the named access check is called when invoking
// functions on an object that requires access checks.
CompileRun("obj.f = function() {}");
CompileRun("function testCallNormal(obj) {"
" for (var i = 0; i < 10; i++) obj.f();"
"}");
CompileRun("testCallNormal(obj)");
printf("%i\n", access_count);
CHECK_EQ(11, access_count);
// Force obj into slow case.
value = CompileRun("delete obj.prop");
CHECK(value->BooleanValue());
// Force inline caches into dictionary probing mode.
CompileRun("var o = { x: 0 }; delete o.x; testProp(o);");
// Test that the named access check is called.
value = CompileRun("testProp(obj);");
CHECK(value->IsNumber());
CHECK_EQ(1, value->Int32Value());
CHECK_EQ(33, access_count);
// Force the call inline cache into dictionary probing mode.
CompileRun("o.f = function() {}; testCallNormal(o)");
// Test that the named access check is still called for each
// invocation of the function.
value = CompileRun("testCallNormal(obj)");
CHECK_EQ(43, access_count);
context1->Exit();
context0->Exit();
}
THREADED_TEST(Version) { v8::V8::GetVersion(); }
static void InstanceFunctionCallback(
const v8::FunctionCallbackInfo<v8::Value>& args) {
ApiTestFuzzer::Fuzz();
args.GetReturnValue().Set(v8_num(12));
}
THREADED_TEST(InstanceProperties) {
LocalContext context;
v8::Isolate* isolate = context->GetIsolate();
v8::HandleScope handle_scope(isolate);
Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(isolate);
Local<ObjectTemplate> instance = t->InstanceTemplate();
instance->Set(v8_str("x"), v8_num(42));
instance->Set(v8_str("f"),
v8::FunctionTemplate::New(isolate, InstanceFunctionCallback));
Local<Value> o = t->GetFunction()->NewInstance();
context->Global()->Set(v8_str("i"), o);
Local<Value> value = CompileRun("i.x");
CHECK_EQ(42, value->Int32Value());
value = CompileRun("i.f()");
CHECK_EQ(12, value->Int32Value());
}
static void GlobalObjectInstancePropertiesGet(
Local<Name> key, const v8::PropertyCallbackInfo<v8::Value>&) {
ApiTestFuzzer::Fuzz();
}
THREADED_TEST(GlobalObjectInstanceProperties) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope handle_scope(isolate);
Local<Value> global_object;
Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(isolate);
t->InstanceTemplate()->SetHandler(
v8::NamedPropertyHandlerConfiguration(GlobalObjectInstancePropertiesGet));
Local<ObjectTemplate> instance_template = t->InstanceTemplate();
instance_template->Set(v8_str("x"), v8_num(42));
instance_template->Set(v8_str("f"),
v8::FunctionTemplate::New(isolate,
InstanceFunctionCallback));
// The script to check how Crankshaft compiles missing global function
// invocations. function g is not defined and should throw on call.
const char* script =
"function wrapper(call) {"
" var x = 0, y = 1;"
" for (var i = 0; i < 1000; i++) {"
" x += i * 100;"
" y += i * 100;"
" }"
" if (call) g();"
"}"
"for (var i = 0; i < 17; i++) wrapper(false);"
"var thrown = 0;"
"try { wrapper(true); } catch (e) { thrown = 1; };"
"thrown";
{
LocalContext env(NULL, instance_template);
// Hold on to the global object so it can be used again in another
// environment initialization.
global_object = env->Global();
Local<Value> value = CompileRun("x");
CHECK_EQ(42, value->Int32Value());
value = CompileRun("f()");
CHECK_EQ(12, value->Int32Value());
value = CompileRun(script);
CHECK_EQ(1, value->Int32Value());
}
{
// Create new environment reusing the global object.
LocalContext env(NULL, instance_template, global_object);
Local<Value> value = CompileRun("x");
CHECK_EQ(42, value->Int32Value());
value = CompileRun("f()");
CHECK_EQ(12, value->Int32Value());
value = CompileRun(script);
CHECK_EQ(1, value->Int32Value());
}
}
THREADED_TEST(CallKnownGlobalReceiver) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope handle_scope(isolate);
Local<Value> global_object;
Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(isolate);
Local<ObjectTemplate> instance_template = t->InstanceTemplate();
// The script to check that we leave global object not
// global object proxy on stack when we deoptimize from inside
// arguments evaluation.
// To provoke error we need to both force deoptimization
// from arguments evaluation and to force CallIC to take
// CallIC_Miss code path that can't cope with global proxy.
const char* script =
"function bar(x, y) { try { } finally { } }"
"function baz(x) { try { } finally { } }"
"function bom(x) { try { } finally { } }"
"function foo(x) { bar([x], bom(2)); }"
"for (var i = 0; i < 10000; i++) foo(1);"
"foo";
Local<Value> foo;
{
LocalContext env(NULL, instance_template);
// Hold on to the global object so it can be used again in another
// environment initialization.
global_object = env->Global();
foo = CompileRun(script);
}
{
// Create new environment reusing the global object.
LocalContext env(NULL, instance_template, global_object);
env->Global()->Set(v8_str("foo"), foo);
CompileRun("foo()");
}
}
static void ShadowFunctionCallback(
const v8::FunctionCallbackInfo<v8::Value>& args) {
ApiTestFuzzer::Fuzz();
args.GetReturnValue().Set(v8_num(42));
}
static int shadow_y;
static int shadow_y_setter_call_count;
static int shadow_y_getter_call_count;
static void ShadowYSetter(Local<String>,
Local<Value>,
const v8::PropertyCallbackInfo<void>&) {
shadow_y_setter_call_count++;
shadow_y = 42;
}
static void ShadowYGetter(Local<String> name,
const v8::PropertyCallbackInfo<v8::Value>& info) {
ApiTestFuzzer::Fuzz();
shadow_y_getter_call_count++;
info.GetReturnValue().Set(v8_num(shadow_y));
}
static void ShadowIndexedGet(uint32_t index,
const v8::PropertyCallbackInfo<v8::Value>&) {
}
static void ShadowNamedGet(Local<Name> key,
const v8::PropertyCallbackInfo<v8::Value>&) {}
THREADED_TEST(ShadowObject) {
shadow_y = shadow_y_setter_call_count = shadow_y_getter_call_count = 0;
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope handle_scope(isolate);
Local<ObjectTemplate> global_template = v8::ObjectTemplate::New(isolate);
LocalContext context(NULL, global_template);
Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(isolate);
t->InstanceTemplate()->SetHandler(
v8::NamedPropertyHandlerConfiguration(ShadowNamedGet));
t->InstanceTemplate()->SetHandler(
v8::IndexedPropertyHandlerConfiguration(ShadowIndexedGet));
Local<ObjectTemplate> proto = t->PrototypeTemplate();
Local<ObjectTemplate> instance = t->InstanceTemplate();
proto->Set(v8_str("f"),
v8::FunctionTemplate::New(isolate,
ShadowFunctionCallback,
Local<Value>()));
proto->Set(v8_str("x"), v8_num(12));
instance->SetAccessor(v8_str("y"), ShadowYGetter, ShadowYSetter);
Local<Value> o = t->GetFunction()->NewInstance();
context->Global()->Set(v8_str("__proto__"), o);
Local<Value> value =
CompileRun("this.propertyIsEnumerable(0)");
CHECK(value->IsBoolean());
CHECK(!value->BooleanValue());
value = CompileRun("x");
CHECK_EQ(12, value->Int32Value());
value = CompileRun("f()");
CHECK_EQ(42, value->Int32Value());
CompileRun("y = 43");
CHECK_EQ(1, shadow_y_setter_call_count);
value = CompileRun("y");
CHECK_EQ(1, shadow_y_getter_call_count);
CHECK_EQ(42, value->Int32Value());
}
THREADED_TEST(HiddenPrototype) {
LocalContext context;
v8::Isolate* isolate = context->GetIsolate();
v8::HandleScope handle_scope(isolate);
Local<v8::FunctionTemplate> t0 = v8::FunctionTemplate::New(isolate);
t0->InstanceTemplate()->Set(v8_str("x"), v8_num(0));
Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New(isolate);
t1->SetHiddenPrototype(true);
t1->InstanceTemplate()->Set(v8_str("y"), v8_num(1));
Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New(isolate);
t2->SetHiddenPrototype(true);
t2->InstanceTemplate()->Set(v8_str("z"), v8_num(2));
Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New(isolate);
t3->InstanceTemplate()->Set(v8_str("u"), v8_num(3));
Local<v8::Object> o0 = t0->GetFunction()->NewInstance();
Local<v8::Object> o1 = t1->GetFunction()->NewInstance();
Local<v8::Object> o2 = t2->GetFunction()->NewInstance();
Local<v8::Object> o3 = t3->GetFunction()->NewInstance();
// Setting the prototype on an object skips hidden prototypes.
CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
o0->Set(v8_str("__proto__"), o1);
CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
o0->Set(v8_str("__proto__"), o2);
CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
o0->Set(v8_str("__proto__"), o3);
CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
CHECK_EQ(3, o0->Get(v8_str("u"))->Int32Value());
// Getting the prototype of o0 should get the first visible one
// which is o3. Therefore, z should not be defined on the prototype
// object.
Local<Value> proto = o0->Get(v8_str("__proto__"));
CHECK(proto->IsObject());
CHECK(proto.As<v8::Object>()->Get(v8_str("z"))->IsUndefined());
}
THREADED_TEST(HiddenPrototypeSet) {
LocalContext context;
v8::Isolate* isolate = context->GetIsolate();
v8::HandleScope handle_scope(isolate);
Local<v8::FunctionTemplate> ot = v8::FunctionTemplate::New(isolate);
Local<v8::FunctionTemplate> ht = v8::FunctionTemplate::New(isolate);
ht->SetHiddenPrototype(true);
Local<v8::FunctionTemplate> pt = v8::FunctionTemplate::New(isolate);
ht->InstanceTemplate()->Set(v8_str("x"), v8_num(0));
Local<v8::Object> o = ot->GetFunction()->NewInstance();
Local<v8::Object> h = ht->GetFunction()->NewInstance();
Local<v8::Object> p = pt->GetFunction()->NewInstance();
o->Set(v8_str("__proto__"), h);
h->Set(v8_str("__proto__"), p);
// Setting a property that exists on the hidden prototype goes there.
o->Set(v8_str("x"), v8_num(7));
CHECK_EQ(7, o->Get(v8_str("x"))->Int32Value());
CHECK_EQ(7, h->Get(v8_str("x"))->Int32Value());
CHECK(p->Get(v8_str("x"))->IsUndefined());
// Setting a new property should not be forwarded to the hidden prototype.
o->Set(v8_str("y"), v8_num(6));
CHECK_EQ(6, o->Get(v8_str("y"))->Int32Value());
CHECK(h->Get(v8_str("y"))->IsUndefined());
CHECK(p->Get(v8_str("y"))->IsUndefined());
// Setting a property that only exists on a prototype of the hidden prototype
// is treated normally again.
p->Set(v8_str("z"), v8_num(8));
CHECK_EQ(8, o->Get(v8_str("z"))->Int32Value());
CHECK_EQ(8, h->Get(v8_str("z"))->Int32Value());
CHECK_EQ(8, p->Get(v8_str("z"))->Int32Value());
o->Set(v8_str("z"), v8_num(9));
CHECK_EQ(9, o->Get(v8_str("z"))->Int32Value());
CHECK_EQ(8, h->Get(v8_str("z"))->Int32Value());
CHECK_EQ(8, p->Get(v8_str("z"))->Int32Value());
}
// Regression test for issue 2457.
THREADED_TEST(HiddenPrototypeIdentityHash) {
LocalContext context;
v8::HandleScope handle_scope(context->GetIsolate());
Handle<FunctionTemplate> t = FunctionTemplate::New(context->GetIsolate());
t->SetHiddenPrototype(true);
t->InstanceTemplate()->Set(v8_str("foo"), v8_num(75));
Handle<Object> p = t->GetFunction()->NewInstance();
Handle<Object> o = Object::New(context->GetIsolate());
o->SetPrototype(p);
int hash = o->GetIdentityHash();
USE(hash);
o->Set(v8_str("foo"), v8_num(42));
DCHECK_EQ(hash, o->GetIdentityHash());
}
THREADED_TEST(SetPrototype) {
LocalContext context;
v8::Isolate* isolate = context->GetIsolate();
v8::HandleScope handle_scope(isolate);
Local<v8::FunctionTemplate> t0 = v8::FunctionTemplate::New(isolate);
t0->InstanceTemplate()->Set(v8_str("x"), v8_num(0));
Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New(isolate);
t1->SetHiddenPrototype(true);
t1->InstanceTemplate()->Set(v8_str("y"), v8_num(1));
Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New(isolate);
t2->SetHiddenPrototype(true);
t2->InstanceTemplate()->Set(v8_str("z"), v8_num(2));
Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New(isolate);
t3->InstanceTemplate()->Set(v8_str("u"), v8_num(3));
Local<v8::Object> o0 = t0->GetFunction()->NewInstance();
Local<v8::Object> o1 = t1->GetFunction()->NewInstance();
Local<v8::Object> o2 = t2->GetFunction()->NewInstance();
Local<v8::Object> o3 = t3->GetFunction()->NewInstance();
// Setting the prototype on an object does not skip hidden prototypes.
CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
CHECK(o0->SetPrototype(o1));
CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
CHECK(o1->SetPrototype(o2));
CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
CHECK(o2->SetPrototype(o3));
CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
CHECK_EQ(3, o0->Get(v8_str("u"))->Int32Value());
// Getting the prototype of o0 should get the first visible one
// which is o3. Therefore, z should not be defined on the prototype
// object.
Local<Value> proto = o0->Get(v8_str("__proto__"));
CHECK(proto->IsObject());
CHECK(proto.As<v8::Object>()->Equals(o3));
// However, Object::GetPrototype ignores hidden prototype.
Local<Value> proto0 = o0->GetPrototype();
CHECK(proto0->IsObject());
CHECK(proto0.As<v8::Object>()->Equals(o1));
Local<Value> proto1 = o1->GetPrototype();
CHECK(proto1->IsObject());
CHECK(proto1.As<v8::Object>()->Equals(o2));
Local<Value> proto2 = o2->GetPrototype();
CHECK(proto2->IsObject());
CHECK(proto2.As<v8::Object>()->Equals(o3));
}
// Getting property names of an object with a prototype chain that
// triggers dictionary elements in GetOwnPropertyNames() shouldn't
// crash the runtime.
THREADED_TEST(Regress91517) {
i::FLAG_allow_natives_syntax = true;
LocalContext context;
v8::Isolate* isolate = context->GetIsolate();
v8::HandleScope handle_scope(isolate);
Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New(isolate);
t1->SetHiddenPrototype(true);
t1->InstanceTemplate()->Set(v8_str("foo"), v8_num(1));
Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New(isolate);
t2->SetHiddenPrototype(true);
t2->InstanceTemplate()->Set(v8_str("fuz1"), v8_num(2));
t2->InstanceTemplate()->Set(v8_str("objects"), v8::Object::New(isolate));
t2->InstanceTemplate()->Set(v8_str("fuz2"), v8_num(2));
Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New(isolate);
t3->SetHiddenPrototype(true);
t3->InstanceTemplate()->Set(v8_str("boo"), v8_num(3));
Local<v8::FunctionTemplate> t4 = v8::FunctionTemplate::New(isolate);
t4->InstanceTemplate()->Set(v8_str("baz"), v8_num(4));
// Force dictionary-based properties.
i::ScopedVector<char> name_buf(1024);
for (int i = 1; i <= 1000; i++) {
i::SNPrintF(name_buf, "sdf%d", i);
t2->InstanceTemplate()->Set(v8_str(name_buf.start()), v8_num(2));
}
Local<v8::Object> o1 = t1->GetFunction()->NewInstance();
Local<v8::Object> o2 = t2->GetFunction()->NewInstance();
Local<v8::Object> o3 = t3->GetFunction()->NewInstance();
Local<v8::Object> o4 = t4->GetFunction()->NewInstance();
// Create prototype chain of hidden prototypes.
CHECK(o4->SetPrototype(o3));
CHECK(o3->SetPrototype(o2));
CHECK(o2->SetPrototype(o1));
// Call the runtime version of GetOwnPropertyNames() on the natively
// created object through JavaScript.
context->Global()->Set(v8_str("obj"), o4);
// PROPERTY_ATTRIBUTES_NONE = 0
CompileRun("var names = %GetOwnPropertyNames(obj, 0);");
ExpectInt32("names.length", 1006);
ExpectTrue("names.indexOf(\"baz\") >= 0");
ExpectTrue("names.indexOf(\"boo\") >= 0");
ExpectTrue("names.indexOf(\"foo\") >= 0");
ExpectTrue("names.indexOf(\"fuz1\") >= 0");
ExpectTrue("names.indexOf(\"fuz2\") >= 0");
ExpectFalse("names[1005] == undefined");
}
// Getting property names of an object with a hidden and inherited
// prototype should not duplicate the accessor properties inherited.
THREADED_TEST(Regress269562) {
i::FLAG_allow_natives_syntax = true;
LocalContext context;
v8::HandleScope handle_scope(context->GetIsolate());
Local<v8::FunctionTemplate> t1 =
v8::FunctionTemplate::New(context->GetIsolate());
t1->SetHiddenPrototype(true);
Local<v8::ObjectTemplate> i1 = t1->InstanceTemplate();
i1->SetAccessor(v8_str("foo"),
SimpleAccessorGetter, SimpleAccessorSetter);
i1->SetAccessor(v8_str("bar"),
SimpleAccessorGetter, SimpleAccessorSetter);
i1->SetAccessor(v8_str("baz"),
SimpleAccessorGetter, SimpleAccessorSetter);
i1->Set(v8_str("n1"), v8_num(1));
i1->Set(v8_str("n2"), v8_num(2));
Local<v8::Object> o1 = t1->GetFunction()->NewInstance();
Local<v8::FunctionTemplate> t2 =
v8::FunctionTemplate::New(context->GetIsolate());
t2->SetHiddenPrototype(true);
// Inherit from t1 and mark prototype as hidden.
t2->Inherit(t1);
t2->InstanceTemplate()->Set(v8_str("mine"), v8_num(4));
Local<v8::Object> o2 = t2->GetFunction()->NewInstance();
CHECK(o2->SetPrototype(o1));
v8::Local<v8::Symbol> sym =
v8::Symbol::New(context->GetIsolate(), v8_str("s1"));
o1->Set(sym, v8_num(3));
o1->SetHiddenValue(
v8_str("h1"), v8::Integer::New(context->GetIsolate(), 2013));
// Call the runtime version of GetOwnPropertyNames() on
// the natively created object through JavaScript.
context->Global()->Set(v8_str("obj"), o2);
context->Global()->Set(v8_str("sym"), sym);
// PROPERTY_ATTRIBUTES_NONE = 0
CompileRun("var names = %GetOwnPropertyNames(obj, 0);");
ExpectInt32("names.length", 7);
ExpectTrue("names.indexOf(\"foo\") >= 0");
ExpectTrue("names.indexOf(\"bar\") >= 0");
ExpectTrue("names.indexOf(\"baz\") >= 0");
ExpectTrue("names.indexOf(\"n1\") >= 0");
ExpectTrue("names.indexOf(\"n2\") >= 0");
ExpectTrue("names.indexOf(sym) >= 0");
ExpectTrue("names.indexOf(\"mine\") >= 0");
}
THREADED_TEST(FunctionReadOnlyPrototype) {
LocalContext context;
v8::Isolate* isolate = context->GetIsolate();
v8::HandleScope handle_scope(isolate);
Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New(isolate);
t1->PrototypeTemplate()->Set(v8_str("x"), v8::Integer::New(isolate, 42));
t1->ReadOnlyPrototype();
context->Global()->Set(v8_str("func1"), t1->GetFunction());
// Configured value of ReadOnly flag.
CHECK(CompileRun(
"(function() {"
" descriptor = Object.getOwnPropertyDescriptor(func1, 'prototype');"
" return (descriptor['writable'] == false);"
"})()")->BooleanValue());
CHECK_EQ(42, CompileRun("func1.prototype.x")->Int32Value());
CHECK_EQ(42,
CompileRun("func1.prototype = {}; func1.prototype.x")->Int32Value());
Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New(isolate);
t2->PrototypeTemplate()->Set(v8_str("x"), v8::Integer::New(isolate, 42));
context->Global()->Set(v8_str("func2"), t2->GetFunction());
// Default value of ReadOnly flag.
CHECK(CompileRun(
"(function() {"
" descriptor = Object.getOwnPropertyDescriptor(func2, 'prototype');"
" return (descriptor['writable'] == true);"
"})()")->BooleanValue());
CHECK_EQ(42, CompileRun("func2.prototype.x")->Int32Value());
}
THREADED_TEST(SetPrototypeThrows) {
LocalContext context;
v8::Isolate* isolate = context->GetIsolate();
v8::HandleScope handle_scope(isolate);
Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(isolate);
Local<v8::Object> o0 = t->GetFunction()->NewInstance();
Local<v8::Object> o1 = t->GetFunction()->NewInstance();
CHECK(o0->SetPrototype(o1));
// If setting the prototype leads to the cycle, SetPrototype should
// return false and keep VM in sane state.
v8::TryCatch try_catch(isolate);
CHECK(!o1->SetPrototype(o0));
CHECK(!try_catch.HasCaught());
DCHECK(!CcTest::i_isolate()->has_pending_exception());
CHECK_EQ(42, CompileRun("function f() { return 42; }; f()")->Int32Value());
}
THREADED_TEST(FunctionRemovePrototype) {
LocalContext context;
v8::Isolate* isolate = context->GetIsolate();
v8::HandleScope handle_scope(isolate);
Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New(isolate);
t1->RemovePrototype();
Local<v8::Function> fun = t1->GetFunction();
context->Global()->Set(v8_str("fun"), fun);
CHECK(!CompileRun("'prototype' in fun")->BooleanValue());
v8::TryCatch try_catch(isolate);
CompileRun("new fun()");
CHECK(try_catch.HasCaught());
try_catch.Reset();
fun->NewInstance();
CHECK(try_catch.HasCaught());
}
THREADED_TEST(GetterSetterExceptions) {
LocalContext context;
v8::Isolate* isolate = context->GetIsolate();
v8::HandleScope handle_scope(isolate);
CompileRun(
"function Foo() { };"
"function Throw() { throw 5; };"
"var x = { };"
"x.__defineSetter__('set', Throw);"
"x.__defineGetter__('get', Throw);");
Local<v8::Object> x =
Local<v8::Object>::Cast(context->Global()->Get(v8_str("x")));
v8::TryCatch try_catch(isolate);
x->Set(v8_str("set"), v8::Integer::New(isolate, 8));
x->Get(v8_str("get"));
x->Set(v8_str("set"), v8::Integer::New(isolate, 8));
x->Get(v8_str("get"));
x->Set(v8_str("set"), v8::Integer::New(isolate, 8));
x->Get(v8_str("get"));
x->Set(v8_str("set"), v8::Integer::New(isolate, 8));
x->Get(v8_str("get"));
}
THREADED_TEST(Constructor) {
LocalContext context;
v8::Isolate* isolate = context->GetIsolate();
v8::HandleScope handle_scope(isolate);
Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate);
templ->SetClassName(v8_str("Fun"));
Local<Function> cons = templ->GetFunction();
context->Global()->Set(v8_str("Fun"), cons);
Local<v8::Object> inst = cons->NewInstance();
i::Handle<i::JSObject> obj(v8::Utils::OpenHandle(*inst));
CHECK(obj->IsJSObject());
Local<Value> value = CompileRun("(new Fun()).constructor === Fun");
CHECK(value->BooleanValue());
}
static void ConstructorCallback(
const v8::FunctionCallbackInfo<v8::Value>& args) {
ApiTestFuzzer::Fuzz();
Local<Object> This;
if (args.IsConstructCall()) {
Local<Object> Holder = args.Holder();
This = Object::New(args.GetIsolate());
Local<Value> proto = Holder->GetPrototype();
if (proto->IsObject()) {
This->SetPrototype(proto);
}
} else {
This = args.This();
}
This->Set(v8_str("a"), args[0]);
args.GetReturnValue().Set(This);
}
static void FakeConstructorCallback(
const v8::FunctionCallbackInfo<v8::Value>& args) {
ApiTestFuzzer::Fuzz();
args.GetReturnValue().Set(args[0]);
}
THREADED_TEST(ConstructorForObject) {
LocalContext context;
v8::Isolate* isolate = context->GetIsolate();
v8::HandleScope handle_scope(isolate);
{
Local<ObjectTemplate> instance_template = ObjectTemplate::New(isolate);
instance_template->SetCallAsFunctionHandler(ConstructorCallback);
Local<Object> instance = instance_template->NewInstance();
context->Global()->Set(v8_str("obj"), instance);
v8::TryCatch try_catch(isolate);
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::Utf8Value string_value1(value->ToString(isolate));
CHECK_EQ(0, strcmp("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::Utf8Value string_value2(value->ToString(isolate));
CHECK_EQ(0, strcmp("tipli", *string_value2));
// Call the Object's constructor with a Boolean.
value = CompileRun("(function() { var o = new obj(true); return o.a; })()");
CHECK(!try_catch.HasCaught());
CHECK(value->IsBoolean());
CHECK_EQ(true, value->BooleanValue());
Handle<Value> args3[] = {v8::True(isolate)};
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(isolate)};
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(isolate)};
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(isolate);
Local<Object> instance = instance_template->NewInstance();
context->Global()->Set(v8_str("obj2"), instance);
v8::TryCatch try_catch(isolate);
Local<Value> value;
CHECK(!try_catch.HasCaught());
value = CompileRun("new obj2(28)");
CHECK(try_catch.HasCaught());
String::Utf8Value exception_value1(try_catch.Exception());
CHECK_EQ(0, strcmp("TypeError: obj2 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::Utf8Value exception_value2(try_catch.Exception());
CHECK_EQ(
0, strcmp("TypeError: #<Object> is not a function", *exception_value2));
try_catch.Reset();
}
// Check the case when constructor throws exception.
{
Local<ObjectTemplate> instance_template = ObjectTemplate::New(isolate);
instance_template->SetCallAsFunctionHandler(ThrowValue);
Local<Object> instance = instance_template->NewInstance();
context->Global()->Set(v8_str("obj3"), instance);
v8::TryCatch try_catch(isolate);
Local<Value> value;
CHECK(!try_catch.HasCaught());
value = CompileRun("new obj3(22)");
CHECK(try_catch.HasCaught());
String::Utf8Value exception_value1(try_catch.Exception());
CHECK_EQ(0, strcmp("22", *exception_value1));
try_catch.Reset();
Local<Value> args[] = {v8_num(23)};
value = instance->CallAsConstructor(1, args);
CHECK(try_catch.HasCaught());
String::Utf8Value exception_value2(try_catch.Exception());
CHECK_EQ(0, strcmp("23", *exception_value2));
try_catch.Reset();
}
// Check whether constructor returns with an object or non-object.
{
Local<FunctionTemplate> function_template =
FunctionTemplate::New(isolate, FakeConstructorCallback);
Local<Function> function = function_template->GetFunction();
Local<Object> instance1 = function;
context->Global()->Set(v8_str("obj4"), instance1);
v8::TryCatch try_catch(isolate);
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(isolate);
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) {
LocalContext context;
v8::Isolate* isolate = context->GetIsolate();
v8::HandleScope handle_scope(isolate);
Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate);
templ->SetClassName(v8_str("Fun"));
Local<Function> cons = templ->GetFunction();
context->Global()->Set(v8_str("Fun"), cons);
Local<Value> value = CompileRun(
"function test() {"
" try {"
" (new Fun()).blah()"
" } catch (e) {"
" var str = String(e);"
// " if (str.indexOf('TypeError') == -1) return 1;"
// " if (str.indexOf('[object Fun]') != -1) return 2;"
// " if (str.indexOf('#<Fun>') == -1) return 3;"
" return 0;"
" }"
" return 4;"
"}"
"test();");
CHECK_EQ(0, value->Int32Value());
}
THREADED_TEST(EvalAliasedDynamic) {
LocalContext current;
v8::HandleScope scope(current->GetIsolate());
// Tests where aliased eval can only be resolved dynamically.
Local<Script> script = v8_compile(
"function f(x) { "
" var foo = 2;"
" with (x) { return eval('foo'); }"
"}"
"foo = 0;"
"result1 = f(new Object());"
"result2 = f(this);"
"var x = new Object();"
"x.eval = function(x) { return 1; };"
"result3 = f(x);");
script->Run();
CHECK_EQ(2, current->Global()->Get(v8_str("result1"))->Int32Value());
CHECK_EQ(0, current->Global()->Get(v8_str("result2"))->Int32Value());
CHECK_EQ(1, current->Global()->Get(v8_str("result3"))->Int32Value());
v8::TryCatch try_catch(current->GetIsolate());
script = v8_compile(
"function f(x) { "
" var bar = 2;"
" with (x) { return eval('bar'); }"
"}"
"result4 = f(this)");
script->Run();
CHECK(!try_catch.HasCaught());
CHECK_EQ(2, current->Global()->Get(v8_str("result4"))->Int32Value());
try_catch.Reset();
}
THREADED_TEST(CrossEval) {
v8::HandleScope scope(CcTest::isolate());
LocalContext other;
LocalContext current;
Local<String> token = v8_str("<security token>");
other->SetSecurityToken(token);
current->SetSecurityToken(token);
// Set up reference from current to other.
current->Global()->Set(v8_str("other"), other->Global());
// Check that new variables are introduced in other context.
Local<Script> script = v8_compile("other.eval('var foo = 1234')");
script->Run();
Local<Value> foo = other->Global()->Get(v8_str("foo"));
CHECK_EQ(1234, foo->Int32Value());
CHECK(!current->Global()->Has(v8_str("foo")));
// Check that writing to non-existing properties introduces them in
// the other context.
script = v8_compile("other.eval('na = 1234')");
script->Run();
CHECK_EQ(1234, other->Global()->Get(v8_str("na"))->Int32Value());
CHECK(!current->Global()->Has(v8_str("na")));
// Check that global variables in current context are not visible in other
// context.
v8::TryCatch try_catch(CcTest::isolate());
script = v8_compile("var bar = 42; other.eval('bar');");
Local<Value> result = script->Run();
CHECK(try_catch.HasCaught());
try_catch.Reset();
// Check that local variables in current context are not visible in other
// context.
script = v8_compile(
"(function() { "
" var baz = 87;"
" return other.eval('baz');"
"})();");
result = script->Run();
CHECK(try_catch.HasCaught());
try_catch.Reset();
// Check that global variables in the other environment are visible
// when evaluting code.
other->Global()->Set(v8_str("bis"), v8_num(1234));
script = v8_compile("other.eval('bis')");
CHECK_EQ(1234, script->Run()->Int32Value());
CHECK(!try_catch.HasCaught());
// Check that the 'this' pointer points to the global object evaluating
// code.
other->Global()->Set(v8_str("t"), other->Global());
script = v8_compile("other.eval('this == t')");
result = script->Run();
CHECK(result->IsTrue());
CHECK(!try_catch.HasCaught());
// Check that variables introduced in with-statement are not visible in
// other context.
script = v8_compile("with({x:2}){other.eval('x')}");
result = script->Run();
CHECK(try_catch.HasCaught());
try_catch.Reset();
// Check that you cannot use 'eval.call' with another object than the
// current global object.
script = v8_compile("other.y = 1; eval.call(other, 'y')");
result = script->Run();
CHECK(try_catch.HasCaught());
}
// Test that calling eval in a context which has been detached from
// its global proxy works.
THREADED_TEST(EvalInDetachedGlobal) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Local<Context> context0 = Context::New(isolate);
v8::Local<Context> context1 = Context::New(isolate);
// Set up function in context0 that uses eval from context0.
context0->Enter();
v8::Handle<v8::Value> fun = CompileRun(
"var x = 42;"
"(function() {"
" var e = eval;"
" return function(s) { return e(s); }"
"})()");
context0->Exit();
// Put the function into context1 and call it before and after
// detaching the global. Before detaching, the call succeeds and
// after detaching and exception is thrown.
context1->Enter();
context1->Global()->Set(v8_str("fun"), fun);
v8::Handle<v8::Value> x_value = CompileRun("fun('x')");
CHECK_EQ(42, x_value->Int32Value());
context0->DetachGlobal();
v8::TryCatch catcher(isolate);
x_value = CompileRun("fun('x')");
CHECK_EQ(42, x_value->Int32Value());
context1->Exit();
}
THREADED_TEST(CrossLazyLoad) {
v8::HandleScope scope(CcTest::isolate());
LocalContext other;
LocalContext current;
Local<String> token = v8_str("<security token>");
other->SetSecurityToken(token);
current->SetSecurityToken(token);
// Set up reference from current to other.
current->Global()->Set(v8_str("other"), other->Global());
// Trigger lazy loading in other context.
Local<Script> script = v8_compile("other.eval('new Date(42)')");
Local<Value> value = script->Run();
CHECK_EQ(42.0, value->NumberValue());
}
static void call_as_function(const v8::FunctionCallbackInfo<v8::Value>& args) {
ApiTestFuzzer::Fuzz();
if (args.IsConstructCall()) {
if (args[0]->IsInt32()) {
args.GetReturnValue().Set(v8_num(-args[0]->Int32Value()));
return;
}
}
args.GetReturnValue().Set(args[0]);
}
static void ReturnThis(const v8::FunctionCallbackInfo<v8::Value>& args) {
args.GetReturnValue().Set(args.This());
}
// Test that a call handler can be set for objects which will allow
// non-function objects created through the API to be called as
// functions.
THREADED_TEST(CallAsFunction) {
LocalContext context;
v8::Isolate* isolate = context->GetIsolate();
v8::HandleScope scope(isolate);
{
Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(isolate);
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(isolate);
Local<Value> value;
CHECK(!try_catch.HasCaught());
value = CompileRun("obj(42)");
CHECK(!try_catch.HasCaught());
CHECK_EQ(42, value->Int32Value());
value = CompileRun("(function(o){return o(49)})(obj)");
CHECK(!try_catch.HasCaught());
CHECK_EQ(49, value->Int32Value());
// test special case of call as function
value = CompileRun("[obj]['0'](45)");
CHECK(!try_catch.HasCaught());
CHECK_EQ(45, value->Int32Value());
value = CompileRun(
"obj.call = Function.prototype.call;"
"obj.call(null, 87)");
CHECK(!try_catch.HasCaught());
CHECK_EQ(87, value->Int32Value());
// Regression tests for bug #1116356: Calling call through call/apply
// must work for non-function receivers.
const char* apply_99 = "Function.prototype.call.apply(obj, [this, 99])";
value = CompileRun(apply_99);
CHECK(!try_catch.HasCaught());
CHECK_EQ(99, value->Int32Value());
const char* call_17 = "Function.prototype.call.call(obj, this, 17)";
value = CompileRun(call_17);
CHECK(!try_catch.HasCaught());
CHECK_EQ(17, value->Int32Value());
// Check that the call-as-function handler can be called through
// new.
value = CompileRun("new obj(43)");
CHECK(!try_catch.HasCaught());
CHECK_EQ(-43, value->Int32Value());
// Check that the call-as-function handler can be called through
// the API.
v8::Handle<Value> args[] = {v8_num(28)};
value = instance->CallAsFunction(instance, 1, args);
CHECK(!try_catch.HasCaught());
CHECK_EQ(28, value->Int32Value());
}
{
Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(isolate);
Local<ObjectTemplate> instance_template(t->InstanceTemplate());
USE(instance_template);
Local<v8::Object> instance = t->GetFunction()->NewInstance();
context->Global()->Set(v8_str("obj2"), instance);
v8::TryCatch try_catch(isolate);
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::Utf8Value exception_value1(try_catch.Exception());
// TODO(verwaest): Better message
CHECK_EQ(0, strcmp("TypeError: obj2 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::Utf8Value exception_value2(try_catch.Exception());
CHECK_EQ(0, strcmp("TypeError: [object Object] is not a function",
*exception_value2));
try_catch.Reset();
}
{
Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(isolate);
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(isolate);
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::Utf8Value exception_value1(try_catch.Exception());
CHECK_EQ(0, strcmp("22", *exception_value1));
try_catch.Reset();
v8::Handle<Value> args[] = {v8_num(23)};
value = instance->CallAsFunction(instance, 1, args);
CHECK(try_catch.HasCaught());
String::Utf8Value exception_value2(try_catch.Exception());
CHECK_EQ(0, strcmp("23", *exception_value2));
try_catch.Reset();
}
{
Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(isolate);
Local<ObjectTemplate> instance_template = t->InstanceTemplate();
instance_template->SetCallAsFunctionHandler(ReturnThis);
Local<v8::Object> instance = t->GetFunction()->NewInstance();
Local<v8::Value> a1 =
instance->CallAsFunction(v8::Undefined(isolate), 0, NULL);
CHECK(a1->StrictEquals(instance));
Local<v8::Value> a2 = instance->CallAsFunction(v8::Null(isolate), 0, NULL);
CHECK(a2->StrictEquals(instance));
Local<v8::Value> a3 = instance->CallAsFunction(v8_num(42), 0, NULL);
CHECK(a3->StrictEquals(instance));
Local<v8::Value> a4 = instance->CallAsFunction(v8_str("hello"), 0, NULL);
CHECK(a4->StrictEquals(instance));
Local<v8::Value> a5 = instance->CallAsFunction(v8::True(isolate), 0, NULL);
CHECK(a5->StrictEquals(instance));
}
{
CompileRun(
"function ReturnThisSloppy() {"
" return this;"
"}"
"function ReturnThisStrict() {"
" 'use strict';"
" return this;"
"}");
Local<Function> ReturnThisSloppy = Local<Function>::Cast(
context->Global()->Get(v8_str("ReturnThisSloppy")));
Local<Function> ReturnThisStrict = Local<Function>::Cast(
context->Global()->Get(v8_str("ReturnThisStrict")));
Local<v8::Value> a1 =
ReturnThisSloppy->CallAsFunction(v8::Undefined(isolate), 0, NULL);
CHECK(a1->StrictEquals(context->Global()));
Local<v8::Value> a2 =
ReturnThisSloppy->CallAsFunction(v8::Null(isolate), 0, NULL);
CHECK(a2->StrictEquals(context->Global()));
Local<v8::Value> a3 = ReturnThisSloppy->CallAsFunction(v8_num(42), 0, NULL);
CHECK(a3->IsNumberObject());
CHECK_EQ(42.0, a3.As<v8::NumberObject>()->ValueOf());
Local<v8::Value> a4 =
ReturnThisSloppy->CallAsFunction(v8_str("hello"), 0, NULL);
CHECK(a4->IsStringObject());
CHECK(a4.As<v8::StringObject>()->ValueOf()->StrictEquals(v8_str("hello")));
Local<v8::Value> a5 =
ReturnThisSloppy->CallAsFunction(v8::True(isolate), 0, NULL);
CHECK(a5->IsBooleanObject());
CHECK(a5.As<v8::BooleanObject>()->ValueOf());
Local<v8::Value> a6 =
ReturnThisStrict->CallAsFunction(v8::Undefined(isolate), 0, NULL);
CHECK(a6->IsUndefined());
Local<v8::Value> a7 =
ReturnThisStrict->CallAsFunction(v8::Null(isolate), 0, NULL);
CHECK(a7->IsNull());
Local<v8::Value> a8 = ReturnThisStrict->CallAsFunction(v8_num(42), 0, NULL);
CHECK(a8->StrictEquals(v8_num(42)));
Local<v8::Value> a9 =
ReturnThisStrict->CallAsFunction(v8_str("hello"), 0, NULL);
CHECK(a9->StrictEquals(v8_str("hello")));
Local<v8::Value> a10 =
ReturnThisStrict->CallAsFunction(v8::True(isolate), 0, NULL);
CHECK(a10->StrictEquals(v8::True(isolate)));
}
}
// Check whether a non-function object is callable.
THREADED_TEST(CallableObject) {
LocalContext context;
v8::Isolate* isolate = context->GetIsolate();
v8::HandleScope scope(isolate);
{
Local<ObjectTemplate> instance_template = ObjectTemplate::New(isolate);
instance_template->SetCallAsFunctionHandler(call_as_function);
Local<Object> instance = instance_template->NewInstance();
v8::TryCatch try_catch(isolate);
CHECK(instance->IsCallable());
CHECK(!try_catch.HasCaught());
}
{
Local<ObjectTemplate> instance_template = ObjectTemplate::New(isolate);
Local<Object> instance = instance_template->NewInstance();
v8::TryCatch try_catch(isolate);
CHECK(!instance->IsCallable());
CHECK(!try_catch.HasCaught());
}
{
Local<FunctionTemplate> function_template =
FunctionTemplate::New(isolate, call_as_function);
Local<Function> function = function_template->GetFunction();
Local<Object> instance = function;
v8::TryCatch try_catch(isolate);
CHECK(instance->IsCallable());
CHECK(!try_catch.HasCaught());
}
{
Local<FunctionTemplate> function_template = FunctionTemplate::New(isolate);
Local<Function> function = function_template->GetFunction();
Local<Object> instance = function;
v8::TryCatch try_catch(isolate);
CHECK(instance->IsCallable());
CHECK(!try_catch.HasCaught());
}
}
static int Recurse(v8::Isolate* isolate, int depth, int iterations) {
v8::HandleScope scope(isolate);
if (depth == 0) return v8::HandleScope::NumberOfHandles(isolate);
for (int i = 0; i < iterations; i++) {
Local<v8::Number> n(v8::Integer::New(isolate, 42));
}
return Recurse(isolate, depth - 1, iterations);
}
THREADED_TEST(HandleIteration) {
static const int kIterations = 500;
static const int kNesting = 200;
LocalContext context;
v8::Isolate* isolate = context->GetIsolate();
v8::HandleScope scope0(isolate);
CHECK_EQ(0, v8::HandleScope::NumberOfHandles(isolate));
{
v8::HandleScope scope1(isolate);
CHECK_EQ(0, v8::HandleScope::NumberOfHandles(isolate));
for (int i = 0; i < kIterations; i++) {
Local<v8::Number> n(v8::Integer::New(CcTest::isolate(), 42));
CHECK_EQ(i + 1, v8::HandleScope::NumberOfHandles(isolate));
}
CHECK_EQ(kIterations, v8::HandleScope::NumberOfHandles(isolate));
{
v8::HandleScope scope2(CcTest::isolate());
for (int j = 0; j < kIterations; j++) {
Local<v8::Number> n(v8::Integer::New(CcTest::isolate(), 42));
CHECK_EQ(j + 1 + kIterations,
v8::HandleScope::NumberOfHandles(isolate));
}
}
CHECK_EQ(kIterations, v8::HandleScope::NumberOfHandles(isolate));
}
CHECK_EQ(0, v8::HandleScope::NumberOfHandles(isolate));
CHECK_EQ(kNesting * kIterations, Recurse(isolate, kNesting, kIterations));
}
static void InterceptorCallICFastApi(
Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
ApiTestFuzzer::Fuzz();
CheckReturnValue(info, FUNCTION_ADDR(InterceptorCallICFastApi));
int* call_count =
reinterpret_cast<int*>(v8::External::Cast(*info.Data())->Value());
++(*call_count);
if ((*call_count) % 20 == 0) {
CcTest::heap()->CollectAllGarbage();
}
}
static void FastApiCallback_TrivialSignature(
const v8::FunctionCallbackInfo<v8::Value>& args) {
ApiTestFuzzer::Fuzz();
CheckReturnValue(args, FUNCTION_ADDR(FastApiCallback_TrivialSignature));
v8::Isolate* isolate = CcTest::isolate();
CHECK_EQ(isolate, args.GetIsolate());
CHECK(args.This()->Equals(args.Holder()));
CHECK(args.Data()->Equals(v8_str("method_data")));
args.GetReturnValue().Set(args[0]->Int32Value() + 1);
}
static void FastApiCallback_SimpleSignature(
const v8::FunctionCallbackInfo<v8::Value>& args) {
ApiTestFuzzer::Fuzz();
CheckReturnValue(args, FUNCTION_ADDR(FastApiCallback_SimpleSignature));
v8::Isolate* isolate = CcTest::isolate();
CHECK_EQ(isolate, args.GetIsolate());
CHECK(args.This()->GetPrototype()->Equals(args.Holder()));
CHECK(args.Data()->Equals(v8_str("method_data")));
// Note, we're using HasRealNamedProperty instead of Has to avoid
// invoking the interceptor again.
CHECK(args.Holder()->HasRealNamedProperty(v8_str("foo")));
args.GetReturnValue().Set(args[0]->Int32Value() + 1);
}
// Helper to maximize the odds of object moving.
static void GenerateSomeGarbage() {
CompileRun(
"var garbage;"
"for (var i = 0; i < 1000; i++) {"
" garbage = [1/i, \"garbage\" + i, garbage, {foo: garbage}];"
"}"
"garbage = undefined;");
}
void DirectApiCallback(const v8::FunctionCallbackInfo<v8::Value>& args) {
static int count = 0;
if (count++ % 3 == 0) {
CcTest::heap()->CollectAllGarbage();
// This should move the stub
GenerateSomeGarbage(); // This should ensure the old stub memory is flushed
}
}
THREADED_TEST(CallICFastApi_DirectCall_GCMoveStub) {
LocalContext context;
v8::Isolate* isolate = context->GetIsolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::ObjectTemplate> nativeobject_templ =
v8::ObjectTemplate::New(isolate);
nativeobject_templ->Set(isolate, "callback",
v8::FunctionTemplate::New(isolate,
DirectApiCallback));
v8::Local<v8::Object> nativeobject_obj = nativeobject_templ->NewInstance();
context->Global()->Set(v8_str("nativeobject"), nativeobject_obj);
// call the api function multiple times to ensure direct call stub creation.
CompileRun(
"function f() {"
" for (var i = 1; i <= 30; i++) {"
" nativeobject.callback();"
" }"
"}"
"f();");
}
void ThrowingDirectApiCallback(
const v8::FunctionCallbackInfo<v8::Value>& args) {
args.GetIsolate()->ThrowException(v8_str("g"));
}
THREADED_TEST(CallICFastApi_DirectCall_Throw) {
LocalContext context;
v8::Isolate* isolate = context->GetIsolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::ObjectTemplate> nativeobject_templ =
v8::ObjectTemplate::New(isolate);
nativeobject_templ->Set(isolate, "callback",
v8::FunctionTemplate::New(isolate,
ThrowingDirectApiCallback));
v8::Local<v8::Object> nativeobject_obj = nativeobject_templ->NewInstance();
context->Global()->Set(v8_str("nativeobject"), nativeobject_obj);
// call the api function multiple times to ensure direct call stub creation.
v8::Handle<Value> result = CompileRun(
"var result = '';"
"function f() {"
" for (var i = 1; i <= 5; i++) {"
" try { nativeobject.callback(); } catch (e) { result += e; }"
" }"
"}"
"f(); result;");
CHECK(v8_str("ggggg")->Equals(result));
}
static int p_getter_count_3;
static Handle<Value> DoDirectGetter() {
if (++p_getter_count_3 % 3 == 0) {
CcTest::heap()->CollectAllGarbage();
GenerateSomeGarbage();
}
return v8_str("Direct Getter Result");
}
static void DirectGetterCallback(
Local<String> name,
const v8::PropertyCallbackInfo<v8::Value>& info) {
CheckReturnValue(info, FUNCTION_ADDR(DirectGetterCallback));
info.GetReturnValue().Set(DoDirectGetter());
}
template<typename Accessor>
static void LoadICFastApi_DirectCall_GCMoveStub(Accessor accessor) {
LocalContext context;
v8::Isolate* isolate = context->GetIsolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::ObjectTemplate> obj = v8::ObjectTemplate::New(isolate);
obj->SetAccessor(v8_str("p1"), accessor);
context->Global()->Set(v8_str("o1"), obj->NewInstance());
p_getter_count_3 = 0;
v8::Handle<v8::Value> result = CompileRun(
"function f() {"
" for (var i = 0; i < 30; i++) o1.p1;"
" return o1.p1"
"}"
"f();");
CHECK(v8_str("Direct Getter Result")->Equals(result));
CHECK_EQ(31, p_getter_count_3);
}
THREADED_PROFILED_TEST(LoadICFastApi_DirectCall_GCMoveStub) {
LoadICFastApi_DirectCall_GCMoveStub(DirectGetterCallback);
}
void ThrowingDirectGetterCallback(
Local<String> name,
const v8::PropertyCallbackInfo<v8::Value>& info) {
info.GetIsolate()->ThrowException(v8_str("g"));
}
THREADED_TEST(LoadICFastApi_DirectCall_Throw) {
LocalContext context;
v8::Isolate* isolate = context->GetIsolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::ObjectTemplate> obj = v8::ObjectTemplate::New(isolate);
obj->SetAccessor(v8_str("p1"), ThrowingDirectGetterCallback);
context->Global()->Set(v8_str("o1"), obj->NewInstance());
v8::Handle<Value> result = CompileRun(
"var result = '';"
"for (var i = 0; i < 5; i++) {"
" try { o1.p1; } catch (e) { result += e; }"
"}"
"result;");
CHECK(v8_str("ggggg")->Equals(result));
}
THREADED_PROFILED_TEST(InterceptorCallICFastApi_TrivialSignature) {
int interceptor_call_count = 0;
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::FunctionTemplate> fun_templ =
v8::FunctionTemplate::New(isolate);
v8::Handle<v8::FunctionTemplate> method_templ =
v8::FunctionTemplate::New(isolate,
FastApiCallback_TrivialSignature,
v8_str("method_data"),
v8::Handle<v8::Signature>());
v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
proto_templ->Set(v8_str("method"), method_templ);
v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
templ->SetHandler(v8::NamedPropertyHandlerConfiguration(
InterceptorCallICFastApi, NULL, NULL, NULL, NULL,
v8::External::New(isolate, &interceptor_call_count)));
LocalContext context;
v8::Handle<v8::Function> fun = fun_templ->GetFunction();
GenerateSomeGarbage();
context->Global()->Set(v8_str("o"), fun->NewInstance());
CompileRun(
"var result = 0;"
"for (var i = 0; i < 100; i++) {"
" result = o.method(41);"
"}");
CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
CHECK_EQ(100, interceptor_call_count);
}
THREADED_PROFILED_TEST(InterceptorCallICFastApi_SimpleSignature) {
int interceptor_call_count = 0;
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::FunctionTemplate> fun_templ =
v8::FunctionTemplate::New(isolate);
v8::Handle<v8::FunctionTemplate> method_templ = v8::FunctionTemplate::New(
isolate, FastApiCallback_SimpleSignature, v8_str("method_data"),
v8::Signature::New(isolate, fun_templ));
v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
proto_templ->Set(v8_str("method"), method_templ);
fun_templ->SetHiddenPrototype(true);
v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
templ->SetHandler(v8::NamedPropertyHandlerConfiguration(
InterceptorCallICFastApi, NULL, NULL, NULL, NULL,
v8::External::New(isolate, &interceptor_call_count)));
LocalContext context;
v8::Handle<v8::Function> fun = fun_templ->GetFunction();
GenerateSomeGarbage();
context->Global()->Set(v8_str("o"), fun->NewInstance());
CompileRun(
"o.foo = 17;"
"var receiver = {};"
"receiver.__proto__ = o;"
"var result = 0;"
"for (var i = 0; i < 100; i++) {"
" result = receiver.method(41);"
"}");
CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
CHECK_EQ(100, interceptor_call_count);
}
THREADED_PROFILED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss1) {
int interceptor_call_count = 0;
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::FunctionTemplate> fun_templ =
v8::FunctionTemplate::New(isolate);
v8::Handle<v8::FunctionTemplate> method_templ = v8::FunctionTemplate::New(
isolate, FastApiCallback_SimpleSignature, v8_str("method_data"),
v8::Signature::New(isolate, fun_templ));
v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
proto_templ->Set(v8_str("method"), method_templ);
fun_templ->SetHiddenPrototype(true);
v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
templ->SetHandler(v8::NamedPropertyHandlerConfiguration(
InterceptorCallICFastApi, NULL, NULL, NULL, NULL,
v8::External::New(isolate, &interceptor_call_count)));
LocalContext context;
v8::Handle<v8::Function> fun = fun_templ->GetFunction();
GenerateSomeGarbage();
context->Global()->Set(v8_str("o"), fun->NewInstance());
CompileRun(
"o.foo = 17;"
"var receiver = {};"
"receiver.__proto__ = o;"
"var result = 0;"
"var saved_result = 0;"
"for (var i = 0; i < 100; i++) {"
" result = receiver.method(41);"
" if (i == 50) {"
" saved_result = result;"
" receiver = {method: function(x) { return x - 1 }};"
" }"
"}");
CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value());
CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
CHECK_GE(interceptor_call_count, 50);
}
THREADED_PROFILED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss2) {
int interceptor_call_count = 0;
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::FunctionTemplate> fun_templ =
v8::FunctionTemplate::New(isolate);
v8::Handle<v8::FunctionTemplate> method_templ = v8::FunctionTemplate::New(
isolate, FastApiCallback_SimpleSignature, v8_str("method_data"),
v8::Signature::New(isolate, fun_templ));
v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
proto_templ->Set(v8_str("method"), method_templ);
fun_templ->SetHiddenPrototype(true);
v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
templ->SetHandler(v8::NamedPropertyHandlerConfiguration(
InterceptorCallICFastApi, NULL, NULL, NULL, NULL,
v8::External::New(isolate, &interceptor_call_count)));
LocalContext context;
v8::Handle<v8::Function> fun = fun_templ->GetFunction();
GenerateSomeGarbage();
context->Global()->Set(v8_str("o"), fun->NewInstance());
CompileRun(
"o.foo = 17;"
"var receiver = {};"
"receiver.__proto__ = o;"
"var result = 0;"
"var saved_result = 0;"
"for (var i = 0; i < 100; i++) {"
" result = receiver.method(41);"
" if (i == 50) {"
" saved_result = result;"
" o.method = function(x) { return x - 1 };"
" }"
"}");
CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value());
CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
CHECK_GE(interceptor_call_count, 50);
}
THREADED_PROFILED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss3) {
int interceptor_call_count = 0;
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::FunctionTemplate> fun_templ =
v8::FunctionTemplate::New(isolate);
v8::Handle<v8::FunctionTemplate> method_templ = v8::FunctionTemplate::New(
isolate, FastApiCallback_SimpleSignature, v8_str("method_data"),
v8::Signature::New(isolate, fun_templ));
v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
proto_templ->Set(v8_str("method"), method_templ);
fun_templ->SetHiddenPrototype(true);
v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
templ->SetHandler(v8::NamedPropertyHandlerConfiguration(
InterceptorCallICFastApi, NULL, NULL, NULL, NULL,
v8::External::New(isolate, &interceptor_call_count)));
LocalContext context;
v8::Handle<v8::Function> fun = fun_templ->GetFunction();
GenerateSomeGarbage();
context->Global()->Set(v8_str("o"), fun->NewInstance());
v8::TryCatch try_catch(isolate);
CompileRun(
"o.foo = 17;"
"var receiver = {};"
"receiver.__proto__ = o;"
"var result = 0;"
"var saved_result = 0;"
"for (var i = 0; i < 100; i++) {"
" result = receiver.method(41);"
" if (i == 50) {"
" saved_result = result;"
" receiver = 333;"
" }"
"}");
CHECK(try_catch.HasCaught());
// TODO(verwaest): Adjust message.
CHECK(v8_str("TypeError: receiver.method is not a function")
->Equals(try_catch.Exception()->ToString(isolate)));
CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
CHECK_GE(interceptor_call_count, 50);
}
THREADED_PROFILED_TEST(InterceptorCallICFastApi_SimpleSignature_TypeError) {
int interceptor_call_count = 0;
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::FunctionTemplate> fun_templ =
v8::FunctionTemplate::New(isolate);
v8::Handle<v8::FunctionTemplate> method_templ = v8::FunctionTemplate::New(
isolate, FastApiCallback_SimpleSignature, v8_str("method_data"),
v8::Signature::New(isolate, fun_templ));
v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
proto_templ->Set(v8_str("method"), method_templ);
fun_templ->SetHiddenPrototype(true);
v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
templ->SetHandler(v8::NamedPropertyHandlerConfiguration(
InterceptorCallICFastApi, NULL, NULL, NULL, NULL,
v8::External::New(isolate, &interceptor_call_count)));
LocalContext context;
v8::Handle<v8::Function> fun = fun_templ->GetFunction();
GenerateSomeGarbage();
context->Global()->Set(v8_str("o"), fun->NewInstance());
v8::TryCatch try_catch(isolate);
CompileRun(
"o.foo = 17;"
"var receiver = {};"
"receiver.__proto__ = o;"
"var result = 0;"
"var saved_result = 0;"
"for (var i = 0; i < 100; i++) {"
" result = receiver.method(41);"
" if (i == 50) {"
" saved_result = result;"
" receiver = {method: receiver.method};"
" }"
"}");
CHECK(try_catch.HasCaught());
CHECK(v8_str("TypeError: Illegal invocation")
->Equals(try_catch.Exception()->ToString(isolate)));
CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
CHECK_GE(interceptor_call_count, 50);
}
THREADED_PROFILED_TEST(CallICFastApi_TrivialSignature) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::FunctionTemplate> fun_templ =
v8::FunctionTemplate::New(isolate);
v8::Handle<v8::FunctionTemplate> method_templ =
v8::FunctionTemplate::New(isolate,
FastApiCallback_TrivialSignature,
v8_str("method_data"),
v8::Handle<v8::Signature>());
v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
proto_templ->Set(v8_str("method"), method_templ);
v8::Handle<v8::ObjectTemplate> templ(fun_templ->InstanceTemplate());
USE(templ);
LocalContext context;
v8::Handle<v8::Function> fun = fun_templ->GetFunction();
GenerateSomeGarbage();
context->Global()->Set(v8_str("o"), fun->NewInstance());
CompileRun(
"var result = 0;"
"for (var i = 0; i < 100; i++) {"
" result = o.method(41);"
"}");
CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
}
THREADED_PROFILED_TEST(CallICFastApi_SimpleSignature) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::FunctionTemplate> fun_templ =
v8::FunctionTemplate::New(isolate);
v8::Handle<v8::FunctionTemplate> method_templ = v8::FunctionTemplate::New(
isolate, FastApiCallback_SimpleSignature, v8_str("method_data"),
v8::Signature::New(isolate, fun_templ));
v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
proto_templ->Set(v8_str("method"), method_templ);
fun_templ->SetHiddenPrototype(true);
v8::Handle<v8::ObjectTemplate> templ(fun_templ->InstanceTemplate());
CHECK(!templ.IsEmpty());
LocalContext context;
v8::Handle<v8::Function> fun = fun_templ->GetFunction();
GenerateSomeGarbage();
context->Global()->Set(v8_str("o"), fun->NewInstance());
CompileRun(
"o.foo = 17;"
"var receiver = {};"
"receiver.__proto__ = o;"
"var result = 0;"
"for (var i = 0; i < 100; i++) {"
" result = receiver.method(41);"
"}");
CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
}
THREADED_PROFILED_TEST(CallICFastApi_SimpleSignature_Miss1) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::FunctionTemplate> fun_templ =
v8::FunctionTemplate::New(isolate);
v8::Handle<v8::FunctionTemplate> method_templ = v8::FunctionTemplate::New(
isolate, FastApiCallback_SimpleSignature, v8_str("method_data"),
v8::Signature::New(isolate, fun_templ));
v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
proto_templ->Set(v8_str("method"), method_templ);
fun_templ->SetHiddenPrototype(true);
v8::Handle<v8::ObjectTemplate> templ(fun_templ->InstanceTemplate());
CHECK(!templ.IsEmpty());
LocalContext context;
v8::Handle<v8::Function> fun = fun_templ->GetFunction();
GenerateSomeGarbage();
context->Global()->Set(v8_str("o"), fun->NewInstance());
CompileRun(
"o.foo = 17;"
"var receiver = {};"
"receiver.__proto__ = o;"
"var result = 0;"
"var saved_result = 0;"
"for (var i = 0; i < 100; i++) {"
" result = receiver.method(41);"
" if (i == 50) {"
" saved_result = result;"
" receiver = {method: function(x) { return x - 1 }};"
" }"
"}");
CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value());
CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
}
THREADED_PROFILED_TEST(CallICFastApi_SimpleSignature_Miss2) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::FunctionTemplate> fun_templ =
v8::FunctionTemplate::New(isolate);
v8::Handle<v8::FunctionTemplate> method_templ = v8::FunctionTemplate::New(
isolate, FastApiCallback_SimpleSignature, v8_str("method_data"),
v8::Signature::New(isolate, fun_templ));
v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
proto_templ->Set(v8_str("method"), method_templ);
fun_templ->SetHiddenPrototype(true);
v8::Handle<v8::ObjectTemplate> templ(fun_templ->InstanceTemplate());
CHECK(!templ.IsEmpty());
LocalContext context;
v8::Handle<v8::Function> fun = fun_templ->GetFunction();
GenerateSomeGarbage();
context->Global()->Set(v8_str("o"), fun->NewInstance());
v8::TryCatch try_catch(isolate);
CompileRun(
"o.foo = 17;"
"var receiver = {};"
"receiver.__proto__ = o;"
"var result = 0;"
"var saved_result = 0;"
"for (var i = 0; i < 100; i++) {"
" result = receiver.method(41);"
" if (i == 50) {"
" saved_result = result;"
" receiver = 333;"
" }"
"}");
CHECK(try_catch.HasCaught());
// TODO(verwaest): Adjust message.
CHECK(v8_str("TypeError: receiver.method is not a function")
->Equals(try_catch.Exception()->ToString(isolate)));
CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
}
THREADED_PROFILED_TEST(CallICFastApi_SimpleSignature_TypeError) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::FunctionTemplate> fun_templ =
v8::FunctionTemplate::New(isolate);
v8::Handle<v8::FunctionTemplate> method_templ = v8::FunctionTemplate::New(
isolate, FastApiCallback_SimpleSignature, v8_str("method_data"),
v8::Signature::New(isolate, fun_templ));
v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
proto_templ->Set(v8_str("method"), method_templ);
fun_templ->SetHiddenPrototype(true);
v8::Handle<v8::ObjectTemplate> templ(fun_templ->InstanceTemplate());
CHECK(!templ.IsEmpty());
LocalContext context;
v8::Handle<v8::Function> fun = fun_templ->GetFunction();
GenerateSomeGarbage();
context->Global()->Set(v8_str("o"), fun->NewInstance());
v8::TryCatch try_catch(isolate);
CompileRun(
"o.foo = 17;"
"var receiver = {};"
"receiver.__proto__ = o;"
"var result = 0;"
"var saved_result = 0;"
"for (var i = 0; i < 100; i++) {"
" result = receiver.method(41);"
" if (i == 50) {"
" saved_result = result;"
" receiver = Object.create(receiver);"
" }"
"}");
CHECK(try_catch.HasCaught());
CHECK(v8_str("TypeError: Illegal invocation")
->Equals(try_catch.Exception()->ToString(isolate)));
CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
}
static void ThrowingGetter(Local<String> name,
const v8::PropertyCallbackInfo<v8::Value>& info) {
ApiTestFuzzer::Fuzz();
info.GetIsolate()->ThrowException(Handle<Value>());
info.GetReturnValue().SetUndefined();
}
THREADED_TEST(VariousGetPropertiesAndThrowingCallbacks) {
LocalContext context;
HandleScope scope(context->GetIsolate());
Local<FunctionTemplate> templ = FunctionTemplate::New(context->GetIsolate());
Local<ObjectTemplate> instance_templ = templ->InstanceTemplate();
instance_templ->SetAccessor(v8_str("f"), ThrowingGetter);
Local<Object> instance = templ->GetFunction()->NewInstance();
Local<Object> another = Object::New(context->GetIsolate());
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(context->GetIsolate());
Local<Value> result = instance->GetRealNamedProperty(v8_str("f"));
CHECK(try_catch.HasCaught());
try_catch.Reset();
CHECK(result.IsEmpty());
Maybe<PropertyAttribute> attr =
instance->GetRealNamedPropertyAttributes(v8_str("f"));
CHECK(!try_catch.HasCaught());
CHECK(Just(None) == attr);
result = another->GetRealNamedProperty(v8_str("f"));
CHECK(try_catch.HasCaught());
try_catch.Reset();
CHECK(result.IsEmpty());
attr = another->GetRealNamedPropertyAttributes(v8_str("f"));
CHECK(!try_catch.HasCaught());
CHECK(Just(None) == attr);
result = another->GetRealNamedPropertyInPrototypeChain(v8_str("f"));
CHECK(try_catch.HasCaught());
try_catch.Reset();
CHECK(result.IsEmpty());
attr = another->GetRealNamedPropertyAttributesInPrototypeChain(v8_str("f"));
CHECK(!try_catch.HasCaught());
CHECK(Just(None) == attr);
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());
attr = with_js_getter->GetRealNamedPropertyAttributes(v8_str("f"));
CHECK(!try_catch.HasCaught());
CHECK(Just(None) == attr);
result = with_js_getter->Get(v8_str("f"));
CHECK(try_catch.HasCaught());
try_catch.Reset();
CHECK(result.IsEmpty());
}
static void ThrowingCallbackWithTryCatch(
const v8::FunctionCallbackInfo<v8::Value>& args) {
TryCatch try_catch(args.GetIsolate());
// 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(!CcTest::i_isolate()->has_pending_exception());
CHECK(!CcTest::i_isolate()->has_scheduled_exception());
}
static int call_depth;
static void WithTryCatch(Handle<Message> message, Handle<Value> data) {
TryCatch try_catch(CcTest::isolate());
}
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) CcTest::isolate()->ThrowException(v8_str("ThrowViaApi"));
}
static void WebKitLike(Handle<Message> message, Handle<Value> data) {
Handle<String> errorMessageString = message->Get();
CHECK(!errorMessageString.IsEmpty());
message->GetStackTrace();
message->GetScriptOrigin().ResourceName();
}
THREADED_TEST(ExceptionsDoNotPropagatePastTryCatch) {
LocalContext context;
v8::Isolate* isolate = context->GetIsolate();
HandleScope scope(isolate);
Local<Function> func =
FunctionTemplate::New(isolate,
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 void ParentGetter(Local<String> name,
const v8::PropertyCallbackInfo<v8::Value>& info) {
ApiTestFuzzer::Fuzz();
info.GetReturnValue().Set(v8_num(1));
}
static void ChildGetter(Local<String> name,
const v8::PropertyCallbackInfo<v8::Value>& info) {
ApiTestFuzzer::Fuzz();
info.GetReturnValue().Set(v8_num(42));
}
THREADED_TEST(Overriding) {
LocalContext context;
v8::Isolate* isolate = context->GetIsolate();
v8::HandleScope scope(isolate);
// Parent template.
Local<v8::FunctionTemplate> parent_templ = v8::FunctionTemplate::New(isolate);
Local<ObjectTemplate> parent_instance_templ =
parent_templ->InstanceTemplate();
parent_instance_templ->SetAccessor(v8_str("f"), ParentGetter);
// Template that inherits from the parent template.
Local<v8::FunctionTemplate> child_templ = v8::FunctionTemplate::New(isolate);
Local<ObjectTemplate> child_instance_templ =
child_templ->InstanceTemplate();
child_templ->Inherit(parent_templ);
// Override 'f'. The child version of 'f' should get called for child
// instances.
child_instance_templ->SetAccessor(v8_str("f"), ChildGetter);
// Add 'g' twice. The 'g' added last should get called for instances.
child_instance_templ->SetAccessor(v8_str("g"), ParentGetter);
child_instance_templ->SetAccessor(v8_str("g"), ChildGetter);
// Add 'h' as an accessor to the proto template with ReadOnly attributes
// so 'h' can be shadowed on the instance object.
Local<ObjectTemplate> child_proto_templ = child_templ->PrototypeTemplate();
child_proto_templ->SetAccessor(v8_str("h"), ParentGetter, 0,
v8::Handle<Value>(), v8::DEFAULT, v8::ReadOnly);
// Add 'i' as an accessor to the instance template with ReadOnly attributes
// but the attribute does not have effect because it is duplicated with
// NULL setter.
child_instance_templ->SetAccessor(v8_str("i"), ChildGetter, 0,
v8::Handle<Value>(), v8::DEFAULT, v8::ReadOnly);
// Instantiate the child template.
Local<v8::Object> instance = child_templ->GetFunction()->NewInstance();
// Check that the child function overrides the parent one.
context->Global()->Set(v8_str("o"), instance);
Local<Value> value = v8_compile("o.f")->Run();
// Check that the 'g' that was added last is hit.
CHECK_EQ(42, value->Int32Value());
value = v8_compile("o.g")->Run();
CHECK_EQ(42, value->Int32Value());
// Check that 'h' cannot be shadowed.
value = v8_compile("o.h = 3; o.h")->Run();
CHECK_EQ(1, value->Int32Value());
// Check that 'i' cannot be shadowed or changed.
value = v8_compile("o.i = 3; o.i")->Run();
CHECK_EQ(42, value->Int32Value());
}
static void IsConstructHandler(
const v8::FunctionCallbackInfo<v8::Value>& args) {
ApiTestFuzzer::Fuzz();
args.GetReturnValue().Set(args.IsConstructCall());
}
THREADED_TEST(IsConstructCall) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
// Function template with call handler.
Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate);
templ->SetCallHandler(IsConstructHandler);
LocalContext context;
context->Global()->Set(v8_str("f"), templ->GetFunction());
Local<Value> value = v8_compile("f()")->Run();
CHECK(!value->BooleanValue());
value = v8_compile("new f()")->Run();
CHECK(value->BooleanValue());
}
THREADED_TEST(ObjectProtoToString) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate);
templ->SetClassName(v8_str("MyClass"));
LocalContext context;
Local<String> customized_tostring = v8_str("customized toString");
// Replace Object.prototype.toString
v8_compile("Object.prototype.toString = function() {"
" return 'customized toString';"
"}")->Run();
// Normal ToString call should call replaced Object.prototype.toString
Local<v8::Object> instance = templ->GetFunction()->NewInstance();
Local<String> value = instance->ToString(isolate);
CHECK(value->IsString() && value->Equals(customized_tostring));
// ObjectProtoToString should not call replace toString function.
value = instance->ObjectProtoToString();
CHECK(value->IsString() && value->Equals(v8_str("[object MyClass]")));
// Check global
value = context->Global()->ObjectProtoToString();
CHECK(value->IsString() && value->Equals(v8_str("[object global]")));
// Check ordinary object
Local<Value> object = v8_compile("new Object()")->Run();
value = object.As<v8::Object>()->ObjectProtoToString();
CHECK(value->IsString() && value->Equals(v8_str("[object Object]")));
}
TEST(ObjectProtoToStringES6) {
// TODO(dslomov, caitp): merge into ObjectProtoToString test once shipped.
i::FLAG_harmony_tostring = true;
LocalContext context;
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate);
templ->SetClassName(v8_str("MyClass"));
Local<String> customized_tostring = v8_str("customized toString");
// Replace Object.prototype.toString
CompileRun(
"Object.prototype.toString = function() {"
" return 'customized toString';"
"}");
// Normal ToString call should call replaced Object.prototype.toString
Local<v8::Object> instance = templ->GetFunction()->NewInstance();
Local<String> value = instance->ToString(isolate);
CHECK(value->IsString() && value->Equals(customized_tostring));
// ObjectProtoToString should not call replace toString function.
value = instance->ObjectProtoToString();
CHECK(value->IsString() && value->Equals(v8_str("[object MyClass]")));
// Check global
value = context->Global()->ObjectProtoToString();
CHECK(value->IsString() && value->Equals(v8_str("[object global]")));
// Check ordinary object
Local<Value> object = CompileRun("new Object()");
value = object.As<v8::Object>()->ObjectProtoToString();
CHECK(value->IsString() && value->Equals(v8_str("[object Object]")));
// Check that ES6 semantics using @@toStringTag work
Local<v8::Symbol> toStringTag = v8::Symbol::GetToStringTag(isolate);
#define TEST_TOSTRINGTAG(type, tag, expected) \
do { \
object = CompileRun("new " #type "()"); \
object.As<v8::Object>()->Set(toStringTag, v8_str(#tag)); \
value = object.As<v8::Object>()->ObjectProtoToString(); \
CHECK(value->IsString() && \
value->Equals(v8_str("[object " #expected "]"))); \
} while (0)
TEST_TOSTRINGTAG(Array, Object, Object);
TEST_TOSTRINGTAG(Object, Arguments, Arguments);
TEST_TOSTRINGTAG(Object, Array, Array);
TEST_TOSTRINGTAG(Object, Boolean, Boolean);
TEST_TOSTRINGTAG(Object, Date, Date);
TEST_TOSTRINGTAG(Object, Error, Error);
TEST_TOSTRINGTAG(Object, Function, Function);
TEST_TOSTRINGTAG(Object, Number, Number);
TEST_TOSTRINGTAG(Object, RegExp, RegExp);
TEST_TOSTRINGTAG(Object, String, String);
TEST_TOSTRINGTAG(Object, Foo, Foo);
#undef TEST_TOSTRINGTAG
Local<v8::RegExp> valueRegExp = v8::RegExp::New(v8_str("^$"),
v8::RegExp::kNone);
Local<Value> valueNumber = v8_num(123);
Local<v8::Symbol> valueSymbol = v8_symbol("TestSymbol");
Local<v8::Function> valueFunction =
CompileRun("(function fn() {})").As<v8::Function>();
Local<v8::Object> valueObject = v8::Object::New(v8::Isolate::GetCurrent());
Local<v8::Primitive> valueNull = v8::Null(v8::Isolate::GetCurrent());
Local<v8::Primitive> valueUndef = v8::Undefined(v8::Isolate::GetCurrent());
#define TEST_TOSTRINGTAG(type, tagValue, expected) \
do { \
object = CompileRun("new " #type "()"); \
object.As<v8::Object>()->Set(toStringTag, tagValue); \
value = object.As<v8::Object>()->ObjectProtoToString(); \
CHECK(value->IsString() && \
value->Equals(v8_str("[object " #expected "]"))); \
} while (0)
#define TEST_TOSTRINGTAG_TYPES(tagValue) \
TEST_TOSTRINGTAG(Array, tagValue, Array); \
TEST_TOSTRINGTAG(Object, tagValue, Object); \
TEST_TOSTRINGTAG(Function, tagValue, Function); \
TEST_TOSTRINGTAG(Date, tagValue, Date); \
TEST_TOSTRINGTAG(RegExp, tagValue, RegExp); \
TEST_TOSTRINGTAG(Error, tagValue, Error); \
// Test non-String-valued @@toStringTag
TEST_TOSTRINGTAG_TYPES(valueRegExp);
TEST_TOSTRINGTAG_TYPES(valueNumber);
TEST_TOSTRINGTAG_TYPES(valueSymbol);
TEST_TOSTRINGTAG_TYPES(valueFunction);
TEST_TOSTRINGTAG_TYPES(valueObject);
TEST_TOSTRINGTAG_TYPES(valueNull);
TEST_TOSTRINGTAG_TYPES(valueUndef);
#undef TEST_TOSTRINGTAG
#undef TEST_TOSTRINGTAG_TYPES
// @@toStringTag getter throws
Local<Value> obj = v8::Object::New(isolate);
obj.As<v8::Object>()->SetAccessor(toStringTag, ThrowingSymbolAccessorGetter);
{
TryCatch try_catch(isolate);
value = obj.As<v8::Object>()->ObjectProtoToString();
CHECK(value.IsEmpty());
CHECK(try_catch.HasCaught());
}
// @@toStringTag getter does not throw
obj = v8::Object::New(isolate);
obj.As<v8::Object>()->SetAccessor(
toStringTag, SymbolAccessorGetterReturnsDefault, 0, v8_str("Test"));
{
TryCatch try_catch(isolate);
value = obj.As<v8::Object>()->ObjectProtoToString();
CHECK(value->IsString() && value->Equals(v8_str("[object Test]")));
CHECK(!try_catch.HasCaught());
}
// JS @@toStringTag value
obj = CompileRun("obj = {}; obj[Symbol.toStringTag] = 'Test'; obj");
{
TryCatch try_catch(isolate);
value = obj.As<v8::Object>()->ObjectProtoToString();
CHECK(value->IsString() && value->Equals(v8_str("[object Test]")));
CHECK(!try_catch.HasCaught());
}
// JS @@toStringTag getter throws
obj = CompileRun(
"obj = {}; Object.defineProperty(obj, Symbol.toStringTag, {"
" get: function() { throw 'Test'; }"
"}); obj");
{
TryCatch try_catch(isolate);
value = obj.As<v8::Object>()->ObjectProtoToString();
CHECK(value.IsEmpty());
CHECK(try_catch.HasCaught());
}
// JS @@toStringTag getter does not throw
obj = CompileRun(
"obj = {}; Object.defineProperty(obj, Symbol.toStringTag, {"
" get: function() { return 'Test'; }"
"}); obj");
{
TryCatch try_catch(isolate);
value = obj.As<v8::Object>()->ObjectProtoToString();
CHECK(value->IsString() && value->Equals(v8_str("[object Test]")));
CHECK(!try_catch.HasCaught());
}
}
THREADED_TEST(ObjectGetConstructorName) {
v8::Isolate* isolate = CcTest::isolate();
LocalContext context;
v8::HandleScope scope(isolate);
v8_compile(
"function Parent() {};"
"function Child() {};"
"Child.prototype = new Parent();"
"var outer = { inner: function() { } };"
"var p = new Parent();"
"var c = new Child();"
"var x = new outer.inner();"
"var proto = Child.prototype;")->Run();
Local<v8::Value> p = context->Global()->Get(v8_str("p"));
CHECK(p->IsObject() &&
p->ToObject(isolate)->GetConstructorName()->Equals(v8_str("Parent")));
Local<v8::Value> c = context->Global()->Get(v8_str("c"));
CHECK(c->IsObject() &&
c->ToObject(isolate)->GetConstructorName()->Equals(v8_str("Child")));
Local<v8::Value> x = context->Global()->Get(v8_str("x"));
CHECK(x->IsObject() &&
x->ToObject(isolate)->GetConstructorName()->Equals(
v8_str("outer.inner")));
Local<v8::Value> child_prototype = context->Global()->Get(v8_str("proto"));
CHECK(child_prototype->IsObject() &&
child_prototype->ToObject(isolate)->GetConstructorName()->Equals(
v8_str("Parent")));
}
bool ApiTestFuzzer::fuzzing_ = false;
v8::base::Semaphore ApiTestFuzzer::all_tests_done_(0);
int ApiTestFuzzer::active_tests_;
int ApiTestFuzzer::tests_being_run_;
int ApiTestFuzzer::current_;
// We are in a callback and want to switch to another thread (if we
// are currently running the thread fuzzing test).
void ApiTestFuzzer::Fuzz() {
if (!fuzzing_) return;
ApiTestFuzzer* test = RegisterThreadedTest::nth(current_)->fuzzer_;
test->ContextSwitch();
}
// Let the next thread go. Since it is also waiting on the V8 lock it may
// not start immediately.
bool ApiTestFuzzer::NextThread() {
int test_position = GetNextTestNumber();
const char* test_name = RegisterThreadedTest::nth(current_)->name();
if (test_position == current_) {
if (kLogThreading)
printf("Stay with %s\n", test_name);
return false;
}
if (kLogThreading) {
printf("Switch from %s to %s\n",
test_name,
RegisterThreadedTest::nth(test_position)->name());
}
current_ = test_position;
RegisterThreadedTest::nth(current_)->fuzzer_->gate_.Signal();
return true;
}
void ApiTestFuzzer::Run() {
// When it is our turn...
gate_.Wait();
{
// ... get the V8 lock and start running the test.
v8::Locker locker(CcTest::isolate());
CallTest();
}
// This test finished.
active_ = false;
active_tests_--;
// If it was the last then signal that fact.
if (active_tests_ == 0) {
all_tests_done_.Signal();
} else {
// Otherwise select a new test and start that.
NextThread();
}
}
static unsigned linear_congruential_generator;
void ApiTestFuzzer::SetUp(PartOfTest part) {
linear_congruential_generator = i::FLAG_testing_prng_seed;
fuzzing_ = true;
int count = RegisterThreadedTest::count();
int start = count * part / (LAST_PART + 1);
int end = (count * (part + 1) / (LAST_PART + 1)) - 1;
active_tests_ = tests_being_run_ = end - start + 1;
for (int i = 0; i < tests_being_run_; i++) {
RegisterThreadedTest::nth(i)->fuzzer_ = new ApiTestFuzzer(i + start);
}
for (int i = 0; i < active_tests_; i++) {
RegisterThreadedTest::nth(i)->fuzzer_->Start();
}
}
static void CallTestNumber(int test_number) {
(RegisterThreadedTest::nth(test_number)->callback())();
}
void ApiTestFuzzer::RunAllTests() {
// Set off the first test.
current_ = -1;
NextThread();
// Wait till they are all done.
all_tests_done_.Wait();
}
int ApiTestFuzzer::GetNextTestNumber() {
int next_test;
do {
next_test = (linear_congruential_generator >> 16) % tests_being_run_;
linear_congruential_generator *= 1664525u;
linear_congruential_generator += 1013904223u;
} while (!RegisterThreadedTest::nth(next_test)->fuzzer_->active_);
return next_test;
}
void ApiTestFuzzer::ContextSwitch() {
// If the new thread is the same as the current thread there is nothing to do.
if (NextThread()) {
// Now it can start.
v8::Unlocker unlocker(CcTest::isolate());
// Wait till someone starts us again.
gate_.Wait();
// And we're off.
}
}
void ApiTestFuzzer::TearDown() {
fuzzing_ = false;
for (int i = 0; i < RegisterThreadedTest::count(); i++) {
ApiTestFuzzer *fuzzer = RegisterThreadedTest::nth(i)->fuzzer_;
if (fuzzer != NULL) fuzzer->Join();
}
}
// Lets not be needlessly self-referential.
TEST(Threading1) {
ApiTestFuzzer::SetUp(ApiTestFuzzer::FIRST_PART);
ApiTestFuzzer::RunAllTests();
ApiTestFuzzer::TearDown();
}
TEST(Threading2) {
ApiTestFuzzer::SetUp(ApiTestFuzzer::SECOND_PART);
ApiTestFuzzer::RunAllTests();
ApiTestFuzzer::TearDown();
}
TEST(Threading3) {
ApiTestFuzzer::SetUp(ApiTestFuzzer::THIRD_PART);
ApiTestFuzzer::RunAllTests();
ApiTestFuzzer::TearDown();
}
TEST(Threading4) {
ApiTestFuzzer::SetUp(ApiTestFuzzer::FOURTH_PART);
ApiTestFuzzer::RunAllTests();
ApiTestFuzzer::TearDown();
}
void ApiTestFuzzer::CallTest() {
v8::Isolate::Scope scope(CcTest::isolate());
if (kLogThreading)
printf("Start test %d\n", test_number_);
CallTestNumber(test_number_);
if (kLogThreading)
printf("End test %d\n", test_number_);
}
static void ThrowInJS(const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::Isolate* isolate = args.GetIsolate();
CHECK(v8::Locker::IsLocked(isolate));
ApiTestFuzzer::Fuzz();
v8::Unlocker unlocker(isolate);
const char* code = "throw 7;";
{
v8::Locker nested_locker(isolate);
v8::HandleScope scope(isolate);
v8::Handle<Value> exception;
{
v8::TryCatch try_catch(isolate);
v8::Handle<Value> value = CompileRun(code);
CHECK(value.IsEmpty());
CHECK(try_catch.HasCaught());
// Make sure to wrap the exception in a new handle because
// the handle returned from the TryCatch is destroyed
// when the TryCatch is destroyed.
exception = Local<Value>::New(isolate, try_catch.Exception());
}
args.GetIsolate()->ThrowException(exception);
}
}
static void ThrowInJSNoCatch(const v8::FunctionCallbackInfo<v8::Value>& args) {
CHECK(v8::Locker::IsLocked(CcTest::isolate()));
ApiTestFuzzer::Fuzz();
v8::Unlocker unlocker(CcTest::isolate());
const char* code = "throw 7;";
{
v8::Locker nested_locker(CcTest::isolate());
v8::HandleScope scope(args.GetIsolate());
v8::Handle<Value> value = CompileRun(code);
CHECK(value.IsEmpty());
args.GetReturnValue().Set(v8_str("foo"));
}
}
// These are locking tests that don't need to be run again
// as part of the locking aggregation tests.
TEST(NestedLockers) {
v8::Isolate* isolate = CcTest::isolate();
v8::Locker locker(isolate);
CHECK(v8::Locker::IsLocked(isolate));
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
Local<v8::FunctionTemplate> fun_templ =
v8::FunctionTemplate::New(isolate, ThrowInJS);
Local<Function> fun = fun_templ->GetFunction();
env->Global()->Set(v8_str("throw_in_js"), fun);
Local<Script> script = v8_compile("(function () {"
" try {"
" throw_in_js();"
" return 42;"
" } catch (e) {"
" return e * 13;"
" }"
"})();");
CHECK_EQ(91, script->Run()->Int32Value());
}
// These are locking tests that don't need to be run again
// as part of the locking aggregation tests.
TEST(NestedLockersNoTryCatch) {
v8::Locker locker(CcTest::isolate());
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
Local<v8::FunctionTemplate> fun_templ =
v8::FunctionTemplate::New(env->GetIsolate(), ThrowInJSNoCatch);
Local<Function> fun = fun_templ->GetFunction();
env->Global()->Set(v8_str("throw_in_js"), fun);
Local<Script> script = v8_compile("(function () {"
" try {"
" throw_in_js();"
" return 42;"
" } catch (e) {"
" return e * 13;"
" }"
"})();");
CHECK_EQ(91, script->Run()->Int32Value());
}
THREADED_TEST(RecursiveLocking) {
v8::Locker locker(CcTest::isolate());
{
v8::Locker locker2(CcTest::isolate());
CHECK(v8::Locker::IsLocked(CcTest::isolate()));
}
}
static void UnlockForAMoment(const v8::FunctionCallbackInfo<v8::Value>& args) {
ApiTestFuzzer::Fuzz();
v8::Unlocker unlocker(CcTest::isolate());
}
THREADED_TEST(LockUnlockLock) {
{
v8::Locker locker(CcTest::isolate());
v8::HandleScope scope(CcTest::isolate());
LocalContext env;
Local<v8::FunctionTemplate> fun_templ =
v8::FunctionTemplate::New(CcTest::isolate(), UnlockForAMoment);
Local<Function> fun = fun_templ->GetFunction();
env->Global()->Set(v8_str("unlock_for_a_moment"), fun);
Local<Script> script = v8_compile("(function () {"
" unlock_for_a_moment();"
" return 42;"
"})();");
CHECK_EQ(42, script->Run()->Int32Value());
}
{
v8::Locker locker(CcTest::isolate());
v8::HandleScope scope(CcTest::isolate());
LocalContext env;
Local<v8::FunctionTemplate> fun_templ =
v8::FunctionTemplate::New(CcTest::isolate(), UnlockForAMoment);
Local<Function> fun = fun_templ->GetFunction();
env->Global()->Set(v8_str("unlock_for_a_moment"), fun);
Local<Script> script = v8_compile("(function () {"
" unlock_for_a_moment();"
" return 42;"
"})();");
CHECK_EQ(42, script->Run()->Int32Value());
}
}
static int GetGlobalObjectsCount() {
int count = 0;
i::HeapIterator it(CcTest::heap());
for (i::HeapObject* object = it.next(); object != NULL; object = it.next())
if (object->IsJSGlobalObject()) count++;
return count;
}
static void CheckSurvivingGlobalObjectsCount(int expected) {
// We need to collect all garbage twice to be sure that everything
// has been collected. This is because inline caches are cleared in
// the first garbage collection but some of the maps have already
// been marked at that point. Therefore some of the maps are not
// collected until the second garbage collection.
CcTest::heap()->CollectAllGarbage();
CcTest::heap()->CollectAllGarbage(i::Heap::kMakeHeapIterableMask);
int count = GetGlobalObjectsCount();
#ifdef DEBUG
if (count != expected) CcTest::heap()->TracePathToGlobal();
#endif
CHECK_EQ(expected, count);
}
TEST(DontLeakGlobalObjects) {
// Regression test for issues 1139850 and 1174891.
i::FLAG_expose_gc = true;
v8::V8::Initialize();
for (int i = 0; i < 5; i++) {
{ v8::HandleScope scope(CcTest::isolate());
LocalContext context;
}
CcTest::isolate()->ContextDisposedNotification();
CheckSurvivingGlobalObjectsCount(0);
{ v8::HandleScope scope(CcTest::isolate());
LocalContext context;
v8_compile("Date")->Run();
}
CcTest::isolate()->ContextDisposedNotification();
CheckSurvivingGlobalObjectsCount(0);
{ v8::HandleScope scope(CcTest::isolate());
LocalContext context;
v8_compile("/aaa/")->Run();
}
CcTest::isolate()->ContextDisposedNotification();
CheckSurvivingGlobalObjectsCount(0);
{ v8::HandleScope scope(CcTest::isolate());
const char* extension_list[] = { "v8/gc" };
v8::ExtensionConfiguration extensions(1, extension_list);
LocalContext context(&extensions);
v8_compile("gc();")->Run();
}
CcTest::isolate()->ContextDisposedNotification();
CheckSurvivingGlobalObjectsCount(0);
}
}
TEST(CopyablePersistent) {
LocalContext context;
v8::Isolate* isolate = context->GetIsolate();
i::GlobalHandles* globals =
reinterpret_cast<i::Isolate*>(isolate)->global_handles();
int initial_handles = globals->global_handles_count();
typedef v8::Persistent<v8::Object, v8::CopyablePersistentTraits<v8::Object> >
CopyableObject;
{
CopyableObject handle1;
{
v8::HandleScope scope(isolate);
handle1.Reset(isolate, v8::Object::New(isolate));
}
CHECK_EQ(initial_handles + 1, globals->global_handles_count());
CopyableObject handle2;
handle2 = handle1;
CHECK(handle1 == handle2);
CHECK_EQ(initial_handles + 2, globals->global_handles_count());
CopyableObject handle3(handle2);
CHECK(handle1 == handle3);
CHECK_EQ(initial_handles + 3, globals->global_handles_count());
}
// Verify autodispose
CHECK_EQ(initial_handles, globals->global_handles_count());
}
static void WeakApiCallback(
const v8::WeakCallbackInfo<Persistent<v8::Object>>& data) {
data.GetParameter()->Reset();
delete data.GetParameter();
}
TEST(WeakCallbackApi) {
LocalContext context;
v8::Isolate* isolate = context->GetIsolate();
i::GlobalHandles* globals =
reinterpret_cast<i::Isolate*>(isolate)->global_handles();
int initial_handles = globals->global_handles_count();
{
v8::HandleScope scope(isolate);
v8::Local<v8::Object> obj = v8::Object::New(isolate);
obj->Set(v8_str("key"), v8::Integer::New(isolate, 231));
v8::Persistent<v8::Object>* handle =
new v8::Persistent<v8::Object>(isolate, obj);
handle->SetWeak<v8::Persistent<v8::Object>>(
handle, WeakApiCallback, v8::WeakCallbackType::kParameter);
}
reinterpret_cast<i::Isolate*>(isolate)->heap()->CollectAllGarbage(
i::Heap::kAbortIncrementalMarkingMask);
// Verify disposed.
CHECK_EQ(initial_handles, globals->global_handles_count());
}
v8::Persistent<v8::Object> some_object;
v8::Persistent<v8::Object> bad_handle;
void NewPersistentHandleCallback2(
const v8::WeakCallbackInfo<v8::Persistent<v8::Object>>& data) {
v8::HandleScope scope(data.GetIsolate());
bad_handle.Reset(data.GetIsolate(), some_object);
}
void NewPersistentHandleCallback1(
const v8::WeakCallbackInfo<v8::Persistent<v8::Object>>& data) {
data.GetParameter()->Reset();
data.SetSecondPassCallback(NewPersistentHandleCallback2);
}
THREADED_TEST(NewPersistentHandleFromWeakCallback) {
LocalContext context;
v8::Isolate* isolate = context->GetIsolate();
v8::Persistent<v8::Object> handle1, handle2;
{
v8::HandleScope scope(isolate);
some_object.Reset(isolate, v8::Object::New(isolate));
handle1.Reset(isolate, v8::Object::New(isolate));
handle2.Reset(isolate, v8::Object::New(isolate));
}
// Note: order is implementation dependent alas: currently
// global handle nodes are processed by PostGarbageCollectionProcessing
// in reverse allocation order, so if second allocated handle is deleted,
// weak callback of the first handle would be able to 'reallocate' it.
handle1.SetWeak(&handle1, NewPersistentHandleCallback1,
v8::WeakCallbackType::kParameter);
handle2.Reset();
CcTest::heap()->CollectAllGarbage();
}
v8::Persistent<v8::Object> to_be_disposed;
void DisposeAndForceGcCallback2(
const v8::WeakCallbackInfo<v8::Persistent<v8::Object>>& data) {
to_be_disposed.Reset();
CcTest::heap()->CollectAllGarbage();
}
void DisposeAndForceGcCallback1(
const v8::WeakCallbackInfo<v8::Persistent<v8::Object>>& data) {
data.GetParameter()->Reset();
data.SetSecondPassCallback(DisposeAndForceGcCallback2);
}
THREADED_TEST(DoNotUseDeletedNodesInSecondLevelGc) {
LocalContext context;
v8::Isolate* isolate = context->GetIsolate();
v8::Persistent<v8::Object> handle1, handle2;
{
v8::HandleScope scope(isolate);
handle1.Reset(isolate, v8::Object::New(isolate));
handle2.Reset(isolate, v8::Object::New(isolate));
}
handle1.SetWeak(&handle1, DisposeAndForceGcCallback1,
v8::WeakCallbackType::kParameter);
to_be_disposed.Reset(isolate, handle2);
CcTest::heap()->CollectAllGarbage();
}
void DisposingCallback(
const v8::WeakCallbackInfo<v8::Persistent<v8::Object>>& data) {
data.GetParameter()->Reset();
}
void HandleCreatingCallback2(
const v8::WeakCallbackInfo<v8::Persistent<v8::Object>>& data) {
v8::HandleScope scope(data.GetIsolate());
v8::Global<v8::Object>(data.GetIsolate(), v8::Object::New(data.GetIsolate()));
}
void HandleCreatingCallback1(
const v8::WeakCallbackInfo<v8::Persistent<v8::Object>>& data) {
data.GetParameter()->Reset();
data.SetSecondPassCallback(HandleCreatingCallback2);
}
THREADED_TEST(NoGlobalHandlesOrphaningDueToWeakCallback) {
LocalContext context;
v8::Isolate* isolate = context->GetIsolate();
v8::Persistent<v8::Object> handle1, handle2, handle3;
{
v8::HandleScope scope(isolate);
handle3.Reset(isolate, v8::Object::New(isolate));
handle2.Reset(isolate, v8::Object::New(isolate));
handle1.Reset(isolate, v8::Object::New(isolate));
}
handle2.SetWeak(&handle2, DisposingCallback,
v8::WeakCallbackType::kParameter);
handle3.SetWeak(&handle3, HandleCreatingCallback1,
v8::WeakCallbackType::kParameter);
CcTest::heap()->CollectAllGarbage();
}
THREADED_TEST(CheckForCrossContextObjectLiterals) {
v8::V8::Initialize();
const int nof = 2;
const char* sources[nof] = {
"try { [ 2, 3, 4 ].forEach(5); } catch(e) { e.toString(); }",
"Object()"
};
for (int i = 0; i < nof; i++) {
const char* source = sources[i];
{ v8::HandleScope scope(CcTest::isolate());
LocalContext context;
CompileRun(source);
}
{ v8::HandleScope scope(CcTest::isolate());
LocalContext context;
CompileRun(source);
}
}
}
static v8::Handle<Value> NestedScope(v8::Local<Context> env) {
v8::EscapableHandleScope inner(env->GetIsolate());
env->Enter();
v8::Local<Value> three = v8_num(3);
v8::Local<Value> value = inner.Escape(three);
env->Exit();
return value;
}
THREADED_TEST(NestedHandleScopeAndContexts) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope outer(isolate);
v8::Local<Context> env = Context::New(isolate);
env->Enter();
v8::Handle<Value> value = NestedScope(env);
v8::Handle<String> str(value->ToString(isolate));
CHECK(!str.IsEmpty());
env->Exit();
}
static bool MatchPointers(void* key1, void* key2) {
return key1 == key2;
}
struct SymbolInfo {
size_t id;
size_t size;
std::string name;
};
class SetFunctionEntryHookTest {
public:
SetFunctionEntryHookTest() {
CHECK(instance_ == NULL);
instance_ = this;
}
~SetFunctionEntryHookTest() {
CHECK(instance_ == this);
instance_ = NULL;
}
void Reset() {
symbols_.clear();
symbol_locations_.clear();
invocations_.clear();
}
void RunTest();
void OnJitEvent(const v8::JitCodeEvent* event);
static void JitEvent(const v8::JitCodeEvent* event) {
CHECK(instance_ != NULL);
instance_->OnJitEvent(event);
}
void OnEntryHook(uintptr_t function,
uintptr_t return_addr_location);
static void EntryHook(uintptr_t function,
uintptr_t return_addr_location) {
CHECK(instance_ != NULL);
instance_->OnEntryHook(function, return_addr_location);
}
static void RuntimeCallback(const v8::FunctionCallbackInfo<v8::Value>& args) {
CHECK(instance_ != NULL);
args.GetReturnValue().Set(v8_num(42));
}
void RunLoopInNewEnv(v8::Isolate* isolate);
// Records addr as location of symbol.
void InsertSymbolAt(i::Address addr, SymbolInfo* symbol);
// Finds the symbol containing addr
SymbolInfo* FindSymbolForAddr(i::Address addr);
// Returns the number of invocations where the caller name contains
// \p caller_name and the function name contains \p function_name.
int CountInvocations(const char* caller_name,
const char* function_name);
i::Handle<i::JSFunction> foo_func_;
i::Handle<i::JSFunction> bar_func_;
typedef std::map<size_t, SymbolInfo> SymbolMap;
typedef std::map<i::Address, SymbolInfo*> SymbolLocationMap;
typedef std::map<std::pair<SymbolInfo*, SymbolInfo*>, int> InvocationMap;
SymbolMap symbols_;
SymbolLocationMap symbol_locations_;
InvocationMap invocations_;
static SetFunctionEntryHookTest* instance_;
};
SetFunctionEntryHookTest* SetFunctionEntryHookTest::instance_ = NULL;
// Returns true if addr is in the range [start, start+len).
static bool Overlaps(i::Address start, size_t len, i::Address addr) {
if (start <= addr && start + len > addr)
return true;
return false;
}
void SetFunctionEntryHookTest::InsertSymbolAt(i::Address addr,
SymbolInfo* symbol) {
// Insert the symbol at the new location.
SymbolLocationMap::iterator it =
symbol_locations_.insert(std::make_pair(addr, symbol)).first;
// Now erase symbols to the left and right that overlap this one.
while (it != symbol_locations_.begin()) {
SymbolLocationMap::iterator left = it;
--left;
if (!Overlaps(left->first, left->second->size, addr))
break;
symbol_locations_.erase(left);
}
// Now erase symbols to the left and right that overlap this one.
while (true) {
SymbolLocationMap::iterator right = it;
++right;
if (right == symbol_locations_.end())
break;
if (!Overlaps(addr, symbol->size, right->first))
break;
symbol_locations_.erase(right);
}
}
void SetFunctionEntryHookTest::OnJitEvent(const v8::JitCodeEvent* event) {
switch (event->type) {
case v8::JitCodeEvent::CODE_ADDED: {
CHECK(event->code_start != NULL);
CHECK_NE(0, static_cast<int>(event->code_len));
CHECK(event->name.str != NULL);
size_t symbol_id = symbols_.size();
// Record the new symbol.
SymbolInfo& info = symbols_[symbol_id];
info.id = symbol_id;
info.size = event->code_len;
info.name.assign(event->name.str, event->name.str + event->name.len);
// And record it's location.
InsertSymbolAt(reinterpret_cast<i::Address>(event->code_start), &info);
}
break;
case v8::JitCodeEvent::CODE_MOVED: {
// We would like to never see code move that we haven't seen before,
// but the code creation event does not happen until the line endings
// have been calculated (this is so that we can report the line in the
// script at which the function source is found, see
// Compiler::RecordFunctionCompilation) and the line endings
// calculations can cause a GC, which can move the newly created code
// before its existence can be logged.
SymbolLocationMap::iterator it(
symbol_locations_.find(
reinterpret_cast<i::Address>(event->code_start)));
if (it != symbol_locations_.end()) {
// Found a symbol at this location, move it.
SymbolInfo* info = it->second;
symbol_locations_.erase(it);
InsertSymbolAt(reinterpret_cast<i::Address>(event->new_code_start),
info);
}
}
default:
break;
}
}
void SetFunctionEntryHookTest::OnEntryHook(
uintptr_t function, uintptr_t return_addr_location) {
// Get the function's code object.
i::Code* function_code = i::Code::GetCodeFromTargetAddress(
reinterpret_cast<i::Address>(function));
CHECK(function_code != NULL);
// Then try and look up the caller's code object.
i::Address caller = *reinterpret_cast<i::Address*>(return_addr_location);
// Count the invocation.
SymbolInfo* caller_symbol = FindSymbolForAddr(caller);
SymbolInfo* function_symbol =
FindSymbolForAddr(reinterpret_cast<i::Address>(function));
++invocations_[std::make_pair(caller_symbol, function_symbol)];
if (!bar_func_.is_null() && function_code == bar_func_->code()) {
// Check that we have a symbol for the "bar" function at the right location.
SymbolLocationMap::iterator it(
symbol_locations_.find(function_code->instruction_start()));
CHECK(it != symbol_locations_.end());
}
if (!foo_func_.is_null() && function_code == foo_func_->code()) {
// Check that we have a symbol for "foo" at the right location.
SymbolLocationMap::iterator it(
symbol_locations_.find(function_code->instruction_start()));
CHECK(it != symbol_locations_.end());
}
}
SymbolInfo* SetFunctionEntryHookTest::FindSymbolForAddr(i::Address addr) {
SymbolLocationMap::iterator it(symbol_locations_.lower_bound(addr));
// Do we have a direct hit on a symbol?
if (it != symbol_locations_.end()) {
if (it->first == addr)
return it->second;
}
// If not a direct hit, it'll have to be the previous symbol.
if (it == symbol_locations_.begin())
return NULL;
--it;
size_t offs = addr - it->first;
if (offs < it->second->size)
return it->second;
return NULL;
}
int SetFunctionEntryHookTest::CountInvocations(
const char* caller_name, const char* function_name) {
InvocationMap::iterator it(invocations_.begin());
int invocations = 0;
for (; it != invocations_.end(); ++it) {
SymbolInfo* caller = it->first.first;
SymbolInfo* function = it->first.second;
// Filter out non-matching functions.
if (function_name != NULL) {
if (function->name.find(function_name) == std::string::npos)
continue;
}
// Filter out non-matching callers.
if (caller_name != NULL) {
if (caller == NULL)
continue;
if (caller->name.find(caller_name) == std::string::npos)
continue;
}
// It matches add the invocation count to the tally.
invocations += it->second;
}
return invocations;
}
void SetFunctionEntryHookTest::RunLoopInNewEnv(v8::Isolate* isolate) {
v8::HandleScope outer(isolate);
v8::Local<Context> env = Context::New(isolate);
env->Enter();
Local<ObjectTemplate> t = ObjectTemplate::New(isolate);
t->Set(v8_str("asdf"), v8::FunctionTemplate::New(isolate, RuntimeCallback));
env->Global()->Set(v8_str("obj"), t->NewInstance());
const char* script =
"function bar() {\n"
" var sum = 0;\n"
" for (i = 0; i < 100; ++i)\n"
" sum = foo(i);\n"
" return sum;\n"
"}\n"
"function foo(i) { return i * i; }\n"
"// Invoke on the runtime function.\n"
"obj.asdf()";
CompileRun(script);
bar_func_ = i::Handle<i::JSFunction>::cast(
v8::Utils::OpenHandle(*env->Global()->Get(v8_str("bar"))));
DCHECK(!bar_func_.is_null());
foo_func_ =
i::Handle<i::JSFunction>::cast(
v8::Utils::OpenHandle(*env->Global()->Get(v8_str("foo"))));
DCHECK(!foo_func_.is_null());
v8::Handle<v8::Value> value = CompileRun("bar();");
CHECK(value->IsNumber());
CHECK_EQ(9801.0, v8::Number::Cast(*value)->Value());
// Test the optimized codegen path.
value = CompileRun("%OptimizeFunctionOnNextCall(foo);"
"bar();");
CHECK(value->IsNumber());
CHECK_EQ(9801.0, v8::Number::Cast(*value)->Value());
env->Exit();
}
void SetFunctionEntryHookTest::RunTest() {
// Work in a new isolate throughout.
v8::Isolate::CreateParams create_params;
create_params.entry_hook = EntryHook;
create_params.code_event_handler = JitEvent;
create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
v8::Isolate* isolate = v8::Isolate::New(create_params);
{
v8::Isolate::Scope scope(isolate);
RunLoopInNewEnv(isolate);
// Check the exepected invocation counts.
CHECK_EQ(2, CountInvocations(NULL, "bar"));
CHECK_EQ(200, CountInvocations("bar", "foo"));
CHECK_EQ(200, CountInvocations(NULL, "foo"));
// Verify that we have an entry hook on some specific stubs.
CHECK_NE(0, CountInvocations(NULL, "CEntryStub"));
CHECK_NE(0, CountInvocations(NULL, "JSEntryStub"));
CHECK_NE(0, CountInvocations(NULL, "JSEntryTrampoline"));
}
isolate->Dispose();
Reset();
// Make sure a second isolate is unaffected by the previous entry hook.
create_params = v8::Isolate::CreateParams();
create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
isolate = v8::Isolate::New(create_params);
{
v8::Isolate::Scope scope(isolate);
// Reset the entry count to zero and set the entry hook.
RunLoopInNewEnv(isolate);
// We should record no invocations in this isolate.
CHECK_EQ(0, static_cast<int>(invocations_.size()));
}
isolate->Dispose();
}
TEST(SetFunctionEntryHook) {
// FunctionEntryHook does not work well with experimental natives.
// Experimental natives are compiled during snapshot deserialization.
// This test breaks because InstallGetter (function from snapshot that
// only gets called from experimental natives) is compiled with entry hooks.
i::FLAG_allow_natives_syntax = true;
i::FLAG_use_inlining = false;
SetFunctionEntryHookTest test;
test.RunTest();
}
static i::HashMap* code_map = NULL;
static i::HashMap* jitcode_line_info = NULL;
static int saw_bar = 0;
static int move_events = 0;
static bool FunctionNameIs(const char* expected,
const v8::JitCodeEvent* event) {
// Log lines for functions are of the general form:
// "LazyCompile:<type><function_name>", where the type is one of
// "*", "~" or "".
static const char kPreamble[] = "LazyCompile:";
static size_t kPreambleLen = sizeof(kPreamble) - 1;
if (event->name.len < sizeof(kPreamble) - 1 ||
strncmp(kPreamble, event->name.str, kPreambleLen) != 0) {
return false;
}
const char* tail = event->name.str + kPreambleLen;
size_t tail_len = event->name.len - kPreambleLen;
size_t expected_len = strlen(expected);
if (tail_len > 1 && (*tail == '*' || *tail == '~')) {
--tail_len;
++tail;
}
// Check for tails like 'bar :1'.
if (tail_len > expected_len + 2 &&
tail[expected_len] == ' ' &&
tail[expected_len + 1] == ':' &&
tail[expected_len + 2] &&
!strncmp(tail, expected, expected_len)) {
return true;
}
if (tail_len != expected_len)
return false;
return strncmp(tail, expected, expected_len) == 0;
}
static void event_handler(const v8::JitCodeEvent* event) {
CHECK(event != NULL);
CHECK(code_map != NULL);
CHECK(jitcode_line_info != NULL);
class DummyJitCodeLineInfo {
};
switch (event->type) {
case v8::JitCodeEvent::CODE_ADDED: {
CHECK(event->code_start != NULL);
CHECK_NE(0, static_cast<int>(event->code_len));
CHECK(event->name.str != NULL);
i::HashMap::Entry* entry = code_map->LookupOrInsert(
event->code_start, i::ComputePointerHash(event->code_start));
entry->value = reinterpret_cast<void*>(event->code_len);
if (FunctionNameIs("bar", event)) {
++saw_bar;
}
}
break;
case v8::JitCodeEvent::CODE_MOVED: {
uint32_t hash = i::ComputePointerHash(event->code_start);
// We would like to never see code move that we haven't seen before,
// but the code creation event does not happen until the line endings
// have been calculated (this is so that we can report the line in the
// script at which the function source is found, see
// Compiler::RecordFunctionCompilation) and the line endings
// calculations can cause a GC, which can move the newly created code
// before its existence can be logged.
i::HashMap::Entry* entry = code_map->Lookup(event->code_start, hash);
if (entry != NULL) {
++move_events;
CHECK_EQ(reinterpret_cast<void*>(event->code_len), entry->value);
code_map->Remove(event->code_start, hash);
entry = code_map->LookupOrInsert(
event->new_code_start,
i::ComputePointerHash(event->new_code_start));
entry->value = reinterpret_cast<void*>(event->code_len);
}
}
break;
case v8::JitCodeEvent::CODE_REMOVED:
// Object/code removal events are currently not dispatched from the GC.
CHECK(false);
break;
// For CODE_START_LINE_INFO_RECORDING event, we will create one
// DummyJitCodeLineInfo data structure pointed by event->user_dat. We
// record it in jitcode_line_info.
case v8::JitCodeEvent::CODE_START_LINE_INFO_RECORDING: {
DummyJitCodeLineInfo* line_info = new DummyJitCodeLineInfo();
v8::JitCodeEvent* temp_event = const_cast<v8::JitCodeEvent*>(event);
temp_event->user_data = line_info;
i::HashMap::Entry* entry = jitcode_line_info->LookupOrInsert(
line_info, i::ComputePointerHash(line_info));
entry->value = reinterpret_cast<void*>(line_info);
}
break;
// For these two events, we will check whether the event->user_data
// data structure is created before during CODE_START_LINE_INFO_RECORDING
// event. And delete it in CODE_END_LINE_INFO_RECORDING event handling.
case v8::JitCodeEvent::CODE_END_LINE_INFO_RECORDING: {
CHECK(event->user_data != NULL);
uint32_t hash = i::ComputePointerHash(event->user_data);
i::HashMap::Entry* entry =
jitcode_line_info->Lookup(event->user_data, hash);
CHECK(entry != NULL);
delete reinterpret_cast<DummyJitCodeLineInfo*>(event->user_data);
}
break;
case v8::JitCodeEvent::CODE_ADD_LINE_POS_INFO: {
CHECK(event->user_data != NULL);
uint32_t hash = i::ComputePointerHash(event->user_data);
i::HashMap::Entry* entry =
jitcode_line_info->Lookup(event->user_data, hash);
CHECK(entry != NULL);
}
break;
default:
// Impossible event.
CHECK(false);
break;
}
}
UNINITIALIZED_TEST(SetJitCodeEventHandler) {
i::FLAG_stress_compaction = true;
i::FLAG_incremental_marking = false;
if (i::FLAG_never_compact) return;
const char* script =
"function bar() {"
" var sum = 0;"
" for (i = 0; i < 10; ++i)"
" sum = foo(i);"
" return sum;"
"}"
"function foo(i) { return i; };"
"bar();";
// Run this test in a new isolate to make sure we don't
// have remnants of state from other code.
v8::Isolate::CreateParams create_params;
create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
v8::Isolate* isolate = v8::Isolate::New(create_params);
isolate->Enter();
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
i::Heap* heap = i_isolate->heap();
// Start with a clean slate.
heap->CollectAllAvailableGarbage("TestSetJitCodeEventHandler_Prepare");
{
v8::HandleScope scope(isolate);
i::HashMap code(MatchPointers);
code_map = &code;
i::HashMap lineinfo(MatchPointers);
jitcode_line_info = &lineinfo;
saw_bar = 0;
move_events = 0;
isolate->SetJitCodeEventHandler(v8::kJitCodeEventDefault, event_handler);
// Generate new code objects sparsely distributed across several
// different fragmented code-space pages.
const int kIterations = 10;
for (int i = 0; i < kIterations; ++i) {
LocalContext env(isolate);
i::AlwaysAllocateScope always_allocate(i_isolate);
SimulateFullSpace(heap->code_space());
CompileRun(script);
// Keep a strong reference to the code object in the handle scope.
i::Handle<i::Code> bar_code(i::Handle<i::JSFunction>::cast(
v8::Utils::OpenHandle(*env->Global()->Get(v8_str("bar"))))->code());
i::Handle<i::Code> foo_code(i::Handle<i::JSFunction>::cast(
v8::Utils::OpenHandle(*env->Global()->Get(v8_str("foo"))))->code());
// Clear the compilation cache to get more wastage.
reinterpret_cast<i::Isolate*>(isolate)->compilation_cache()->Clear();
}
// Force code movement.
heap->CollectAllAvailableGarbage("TestSetJitCodeEventHandler_Move");
isolate->SetJitCodeEventHandler(v8::kJitCodeEventDefault, NULL);
CHECK_LE(kIterations, saw_bar);
CHECK_LT(0, move_events);
code_map = NULL;
jitcode_line_info = NULL;
}
isolate->Exit();
isolate->Dispose();
// Do this in a new isolate.
isolate = v8::Isolate::New(create_params);
isolate->Enter();
// Verify that we get callbacks for existing code objects when we
// request enumeration of existing code.
{
v8::HandleScope scope(isolate);
LocalContext env(isolate);
CompileRun(script);
// Now get code through initial iteration.
i::HashMap code(MatchPointers);
code_map = &code;
i::HashMap lineinfo(MatchPointers);
jitcode_line_info = &lineinfo;
isolate->SetJitCodeEventHandler(v8::kJitCodeEventEnumExisting,
event_handler);
isolate->SetJitCodeEventHandler(v8::kJitCodeEventDefault, NULL);
jitcode_line_info = NULL;
// We expect that we got some events. Note that if we could get code removal
// notifications, we could compare two collections, one created by listening
// from the time of creation of an isolate, and the other by subscribing
// with EnumExisting.
CHECK_LT(0u, code.occupancy());
code_map = NULL;
}
isolate->Exit();
isolate->Dispose();
}
THREADED_TEST(ExternalAllocatedMemory) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope outer(isolate);
v8::Local<Context> env(Context::New(isolate));
CHECK(!env.IsEmpty());
const int64_t kSize = 1024*1024;
int64_t baseline = isolate->AdjustAmountOfExternalAllocatedMemory(0);
CHECK_EQ(baseline + kSize,
isolate->AdjustAmountOfExternalAllocatedMemory(kSize));
CHECK_EQ(baseline,
isolate->AdjustAmountOfExternalAllocatedMemory(-kSize));
const int64_t kTriggerGCSize =
v8::internal::Internals::kExternalAllocationLimit + 1;
CHECK_EQ(baseline + kTriggerGCSize,
isolate->AdjustAmountOfExternalAllocatedMemory(kTriggerGCSize));
CHECK_EQ(baseline,
isolate->AdjustAmountOfExternalAllocatedMemory(-kTriggerGCSize));
}
// Regression test for issue 54, object templates with internal fields
// but no accessors or interceptors did not get their internal field
// count set on instances.
THREADED_TEST(Regress54) {
LocalContext context;
v8::Isolate* isolate = context->GetIsolate();
v8::HandleScope outer(isolate);
static v8::Persistent<v8::ObjectTemplate> templ;
if (templ.IsEmpty()) {
v8::EscapableHandleScope inner(isolate);
v8::Local<v8::ObjectTemplate> local = v8::ObjectTemplate::New(isolate);
local->SetInternalFieldCount(1);
templ.Reset(isolate, inner.Escape(local));
}
v8::Handle<v8::Object> result =
v8::Local<v8::ObjectTemplate>::New(isolate, templ)->NewInstance();
CHECK_EQ(1, result->InternalFieldCount());
}
// If part of the threaded tests, this test makes ThreadingTest fail
// on mac.
TEST(CatchStackOverflow) {
LocalContext context;
v8::HandleScope scope(context->GetIsolate());
v8::TryCatch try_catch(context->GetIsolate());
v8::Handle<v8::Value> result = CompileRun(
"function f() {"
" return f();"
"}"
""
"f();");
CHECK(result.IsEmpty());
}
static void CheckTryCatchSourceInfo(v8::Handle<v8::Script> script,
const char* resource_name,
int line_offset) {
v8::HandleScope scope(CcTest::isolate());
v8::TryCatch try_catch(CcTest::isolate());
v8::Handle<v8::Value> result = script->Run();
CHECK(result.IsEmpty());
CHECK(try_catch.HasCaught());
v8::Handle<v8::Message> message = try_catch.Message();
CHECK(!message.IsEmpty());
CHECK_EQ(10 + line_offset, message->GetLineNumber());
CHECK_EQ(91, message->GetStartPosition());
CHECK_EQ(92, message->GetEndPosition());
CHECK_EQ(2, message->GetStartColumn());
CHECK_EQ(3, message->GetEndColumn());
v8::String::Utf8Value line(message->GetSourceLine());
CHECK_EQ(0, strcmp(" throw 'nirk';", *line));
v8::String::Utf8Value name(message->GetScriptOrigin().ResourceName());
CHECK_EQ(0, strcmp(resource_name, *name));
}
THREADED_TEST(TryCatchSourceInfo) {
LocalContext context;
v8::HandleScope scope(context->GetIsolate());
v8::Local<v8::String> source = v8_str(
"function Foo() {\n"
" return Bar();\n"
"}\n"
"\n"
"function Bar() {\n"
" return Baz();\n"
"}\n"
"\n"
"function Baz() {\n"
" throw 'nirk';\n"
"}\n"
"\n"
"Foo();\n");
const char* resource_name;
v8::Handle<v8::Script> script;
resource_name = "test.js";
script = CompileWithOrigin(source, resource_name);
CheckTryCatchSourceInfo(script, resource_name, 0);
resource_name = "test1.js";
v8::ScriptOrigin origin1(
v8::String::NewFromUtf8(context->GetIsolate(), resource_name));
script = v8::Script::Compile(source, &origin1);
CheckTryCatchSourceInfo(script, resource_name, 0);
resource_name = "test2.js";
v8::ScriptOrigin origin2(
v8::String::NewFromUtf8(context->GetIsolate(), resource_name),
v8::Integer::New(context->GetIsolate(), 7));
script = v8::Script::Compile(source, &origin2);
CheckTryCatchSourceInfo(script, resource_name, 7);
}
THREADED_TEST(TryCatchSourceInfoForEOSError) {
LocalContext context;
v8::HandleScope scope(context->GetIsolate());
v8::TryCatch try_catch(context->GetIsolate());
v8::Script::Compile(v8_str("!\n"));
CHECK(try_catch.HasCaught());
v8::Handle<v8::Message> message = try_catch.Message();
CHECK_EQ(1, message->GetLineNumber());
CHECK_EQ(0, message->GetStartColumn());
}
THREADED_TEST(CompilationCache) {
LocalContext context;
v8::HandleScope scope(context->GetIsolate());
v8::Handle<v8::String> source0 =
v8::String::NewFromUtf8(context->GetIsolate(), "1234");
v8::Handle<v8::String> source1 =
v8::String::NewFromUtf8(context->GetIsolate(), "1234");
v8::Handle<v8::Script> script0 = CompileWithOrigin(source0, "test.js");
v8::Handle<v8::Script> script1 = CompileWithOrigin(source1, "test.js");
v8::Handle<v8::Script> script2 =
v8::Script::Compile(source0); // different origin
CHECK_EQ(1234, script0->Run()->Int32Value());
CHECK_EQ(1234, script1->Run()->Int32Value());
CHECK_EQ(1234, script2->Run()->Int32Value());
}
static void FunctionNameCallback(
const v8::FunctionCallbackInfo<v8::Value>& args) {
ApiTestFuzzer::Fuzz();
args.GetReturnValue().Set(v8_num(42));
}
THREADED_TEST(CallbackFunctionName) {
LocalContext context;
v8::Isolate* isolate = context->GetIsolate();
v8::HandleScope scope(isolate);
Local<ObjectTemplate> t = ObjectTemplate::New(isolate);
t->Set(v8_str("asdf"),
v8::FunctionTemplate::New(isolate, FunctionNameCallback));
context->Global()->Set(v8_str("obj"), t->NewInstance());
v8::Handle<v8::Value> value = CompileRun("obj.asdf.name");
CHECK(value->IsString());
v8::String::Utf8Value name(value);
CHECK_EQ(0, strcmp("asdf", *name));
}
THREADED_TEST(DateAccess) {
LocalContext context;
v8::HandleScope scope(context->GetIsolate());
v8::Handle<v8::Value> date =
v8::Date::New(context->GetIsolate(), 1224744689038.0);
CHECK(date->IsDate());
CHECK_EQ(1224744689038.0, date.As<v8::Date>()->ValueOf());
}
void CheckProperties(v8::Isolate* isolate, v8::Handle<v8::Value> val,
unsigned elmc, const char* elmv[]) {
v8::Handle<v8::Object> obj = val.As<v8::Object>();
v8::Handle<v8::Array> props = obj->GetPropertyNames();
CHECK_EQ(elmc, props->Length());
for (unsigned i = 0; i < elmc; i++) {
v8::String::Utf8Value elm(props->Get(v8::Integer::New(isolate, i)));
CHECK_EQ(0, strcmp(elmv[i], *elm));
}
}
void CheckOwnProperties(v8::Isolate* isolate, v8::Handle<v8::Value> val,
unsigned 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 (unsigned i = 0; i < elmc; i++) {
v8::String::Utf8Value elm(props->Get(v8::Integer::New(isolate, i)));
CHECK_EQ(0, strcmp(elmv[i], *elm));
}
}
THREADED_TEST(PropertyEnumeration) {
LocalContext context;
v8::Isolate* isolate = context->GetIsolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::Value> obj = CompileRun(
"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;");
v8::Handle<v8::Array> elms = obj.As<v8::Array>();
CHECK_EQ(4u, elms->Length());
int elmc0 = 0;
const char** elmv0 = NULL;
CheckProperties(
isolate, elms->Get(v8::Integer::New(isolate, 0)), elmc0, elmv0);
CheckOwnProperties(
isolate, elms->Get(v8::Integer::New(isolate, 0)), elmc0, elmv0);
int elmc1 = 2;
const char* elmv1[] = {"a", "b"};
CheckProperties(
isolate, elms->Get(v8::Integer::New(isolate, 1)), elmc1, elmv1);
CheckOwnProperties(
isolate, elms->Get(v8::Integer::New(isolate, 1)), elmc1, elmv1);
int elmc2 = 3;
const char* elmv2[] = {"0", "1", "2"};
CheckProperties(
isolate, elms->Get(v8::Integer::New(isolate, 2)), elmc2, elmv2);
CheckOwnProperties(
isolate, elms->Get(v8::Integer::New(isolate, 2)), elmc2, elmv2);
int elmc3 = 4;
const char* elmv3[] = {"w", "z", "x", "y"};
CheckProperties(
isolate, elms->Get(v8::Integer::New(isolate, 3)), elmc3, elmv3);
int elmc4 = 2;
const char* elmv4[] = {"w", "z"};
CheckOwnProperties(
isolate, elms->Get(v8::Integer::New(isolate, 3)), elmc4, elmv4);
}
THREADED_TEST(PropertyEnumeration2) {
LocalContext context;
v8::Isolate* isolate = context->GetIsolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::Value> obj = CompileRun(
"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;");
v8::Handle<v8::Array> elms = obj.As<v8::Array>();
CHECK_EQ(4u, elms->Length());
int elmc0 = 0;
const char** elmv0 = NULL;
CheckProperties(isolate,
elms->Get(v8::Integer::New(isolate, 0)), elmc0, elmv0);
v8::Handle<v8::Value> val = elms->Get(v8::Integer::New(isolate, 0));
v8::Handle<v8::Array> props = val.As<v8::Object>()->GetPropertyNames();
CHECK_EQ(0u, props->Length());
for (uint32_t i = 0; i < props->Length(); i++) {
printf("p[%u]\n", i);
}
}
THREADED_TEST(AccessChecksReenabledCorrectly) {
LocalContext context;
v8::Isolate* isolate = context->GetIsolate();
v8::HandleScope scope(isolate);
Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
templ->SetAccessCheckCallbacks(AccessAlwaysBlocked, NULL);
templ->Set(v8_str("a"), v8_str("a"));
// Add more than 8 (see kMaxFastProperties) properties
// so that the constructor will force copying map.
// Cannot sprintf, gcc complains unsafety.
char buf[4];
for (char i = '0'; i <= '9' ; i++) {
buf[0] = i;
for (char j = '0'; j <= '9'; j++) {
buf[1] = j;
for (char k = '0'; k <= '9'; k++) {
buf[2] = k;
buf[3] = 0;
templ->Set(v8_str(buf), v8::Number::New(isolate, k));
}
}
}
Local<v8::Object> instance_1 = templ->NewInstance();
context->Global()->Set(v8_str("obj_1"), instance_1);
Local<Value> value_1 = CompileRun("obj_1.a");
CHECK(value_1.IsEmpty());
Local<v8::Object> instance_2 = templ->NewInstance();
context->Global()->Set(v8_str("obj_2"), instance_2);
Local<Value> value_2 = CompileRun("obj_2.a");
CHECK(value_2.IsEmpty());
}
THREADED_TEST(TurnOnAccessCheck) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope handle_scope(isolate);
// Create an environment with access check to the global object disabled by
// default.
v8::Handle<v8::ObjectTemplate> global_template =
v8::ObjectTemplate::New(isolate);
global_template->SetAccessCheckCallbacks(AccessAlwaysBlocked, NULL,
v8::Handle<v8::Value>(), false);
v8::Local<Context> context = Context::New(isolate, NULL, global_template);
Context::Scope context_scope(context);
// Set up a property and a number of functions.
context->Global()->Set(v8_str("a"), v8_num(1));
CompileRun("function f1() {return a;}"
"function f2() {return a;}"
"function g1() {return h();}"
"function g2() {return h();}"
"function h() {return 1;}");
Local<Function> f1 =
Local<Function>::Cast(context->Global()->Get(v8_str("f1")));
Local<Function> f2 =
Local<Function>::Cast(context->Global()->Get(v8_str("f2")));
Local<Function> g1 =
Local<Function>::Cast(context->Global()->Get(v8_str("g1")));
Local<Function> g2 =
Local<Function>::Cast(context->Global()->Get(v8_str("g2")));
Local<Function> h =
Local<Function>::Cast(context->Global()->Get(v8_str("h")));
// Get the global object.
v8::Handle<v8::Object> global = context->Global();
// Call f1 one time and f2 a number of times. This will ensure that f1 still
// uses the runtime system to retreive property a whereas f2 uses global load
// inline cache.
CHECK(f1->Call(global, 0, NULL)->Equals(v8_num(1)));
for (int i = 0; i < 4; i++) {
CHECK(f2->Call(global, 0, NULL)->Equals(v8_num(1)));
}
// Same for g1 and g2.
CHECK(g1->Call(global, 0, NULL)->Equals(v8_num(1)));
for (int i = 0; i < 4; i++) {
CHECK(g2->Call(global, 0, NULL)->Equals(v8_num(1)));
}
// Detach the global and turn on access check.
Local<Object> hidden_global = Local<Object>::Cast(
context->Global()->GetPrototype());
context->DetachGlobal();
hidden_global->TurnOnAccessCheck();
// Failing access check results in exception.
CHECK(f1->Call(global, 0, NULL).IsEmpty());
CHECK(f2->Call(global, 0, NULL).IsEmpty());
CHECK(g1->Call(global, 0, NULL).IsEmpty());
CHECK(g2->Call(global, 0, NULL).IsEmpty());
// No failing access check when just returning a constant.
CHECK(h->Call(global, 0, NULL)->Equals(v8_num(1)));
}
// Tests that ScriptData can be serialized and deserialized.
TEST(PreCompileSerialization) {
v8::V8::Initialize();
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
HandleScope handle_scope(isolate);
i::FLAG_min_preparse_length = 0;
const char* script = "function foo(a) { return a+1; }";
v8::ScriptCompiler::Source source(v8_str(script));
v8::ScriptCompiler::Compile(isolate, &source,
v8::ScriptCompiler::kProduceParserCache);
// Serialize.
const v8::ScriptCompiler::CachedData* cd = source.GetCachedData();
i::byte* serialized_data = i::NewArray<i::byte>(cd->length);
i::MemCopy(serialized_data, cd->data, cd->length);
// Deserialize.
i::ScriptData* deserialized = new i::ScriptData(serialized_data, cd->length);
// Verify that the original is the same as the deserialized.
CHECK_EQ(cd->length, deserialized->length());
CHECK_EQ(0, memcmp(cd->data, deserialized->data(), cd->length));
delete deserialized;
i::DeleteArray(serialized_data);
}
// This tests that we do not allow dictionary load/call inline caches
// to use functions that have not yet been compiled. The potential
// problem of loading a function that has not yet been compiled can
// arise because we share code between contexts via the compilation
// cache.
THREADED_TEST(DictionaryICLoadedFunction) {
v8::HandleScope scope(CcTest::isolate());
// Test LoadIC.
for (int i = 0; i < 2; i++) {
LocalContext context;
context->Global()->Set(v8_str("tmp"), v8::True(CcTest::isolate()));
context->Global()->Delete(v8_str("tmp"));
CompileRun("for (var j = 0; j < 10; j++) new RegExp('');");
}
// Test CallIC.
for (int i = 0; i < 2; i++) {
LocalContext context;
context->Global()->Set(v8_str("tmp"), v8::True(CcTest::isolate()));
context->Global()->Delete(v8_str("tmp"));
CompileRun("for (var j = 0; j < 10; j++) RegExp('')");
}
}
// Test that cross-context new calls use the context of the callee to
// create the new JavaScript object.
THREADED_TEST(CrossContextNew) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Local<Context> context0 = Context::New(isolate);
v8::Local<Context> context1 = Context::New(isolate);
// Allow cross-domain access.
Local<String> token = v8_str("<security token>");
context0->SetSecurityToken(token);
context1->SetSecurityToken(token);
// Set an 'x' property on the Object prototype and define a
// constructor function in context0.
context0->Enter();
CompileRun("Object.prototype.x = 42; function C() {};");
context0->Exit();
// Call the constructor function from context0 and check that the
// result has the 'x' property.
context1->Enter();
context1->Global()->Set(v8_str("other"), context0->Global());
Local<Value> value = CompileRun("var instance = new other.C(); instance.x");
CHECK(value->IsInt32());
CHECK_EQ(42, value->Int32Value());
context1->Exit();
}
// Verify that we can clone an object
TEST(ObjectClone) {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
const char* sample =
"var rv = {};" \
"rv.alpha = 'hello';" \
"rv.beta = 123;" \
"rv;";
// Create an object, verify basics.
Local<Value> val = CompileRun(sample);
CHECK(val->IsObject());
Local<v8::Object> obj = val.As<v8::Object>();
obj->Set(v8_str("gamma"), v8_str("cloneme"));
CHECK(v8_str("hello")->Equals(obj->Get(v8_str("alpha"))));
CHECK(v8::Integer::New(isolate, 123)->Equals(obj->Get(v8_str("beta"))));
CHECK(v8_str("cloneme")->Equals(obj->Get(v8_str("gamma"))));
// Clone it.
Local<v8::Object> clone = obj->Clone();
CHECK(v8_str("hello")->Equals(clone->Get(v8_str("alpha"))));
CHECK(v8::Integer::New(isolate, 123)->Equals(clone->Get(v8_str("beta"))));
CHECK(v8_str("cloneme")->Equals(clone->Get(v8_str("gamma"))));
// Set a property on the clone, verify each object.
clone->Set(v8_str("beta"), v8::Integer::New(isolate, 456));
CHECK(v8::Integer::New(isolate, 123)->Equals(obj->Get(v8_str("beta"))));
CHECK(v8::Integer::New(isolate, 456)->Equals(clone->Get(v8_str("beta"))));
}
class OneByteVectorResource : public v8::String::ExternalOneByteStringResource {
public:
explicit OneByteVectorResource(i::Vector<const char> vector)
: data_(vector) {}
virtual ~OneByteVectorResource() {}
virtual size_t length() const { return data_.length(); }
virtual const char* data() const { return data_.start(); }
private:
i::Vector<const char> data_;
};
class UC16VectorResource : public v8::String::ExternalStringResource {
public:
explicit UC16VectorResource(i::Vector<const i::uc16> vector)
: data_(vector) {}
virtual ~UC16VectorResource() {}
virtual size_t length() const { return data_.length(); }
virtual const i::uc16* data() const { return data_.start(); }
private:
i::Vector<const i::uc16> data_;
};
static void MorphAString(i::String* string,
OneByteVectorResource* one_byte_resource,
UC16VectorResource* uc16_resource) {
CHECK(i::StringShape(string).IsExternal());
if (string->IsOneByteRepresentation()) {
// Check old map is not internalized or long.
CHECK(string->map() == CcTest::heap()->external_one_byte_string_map());
// Morph external string to be TwoByte string.
string->set_map(CcTest::heap()->external_string_map());
i::ExternalTwoByteString* morphed =
i::ExternalTwoByteString::cast(string);
morphed->set_resource(uc16_resource);
} else {
// Check old map is not internalized or long.
CHECK(string->map() == CcTest::heap()->external_string_map());
// Morph external string to be one-byte string.
string->set_map(CcTest::heap()->external_one_byte_string_map());
i::ExternalOneByteString* morphed = i::ExternalOneByteString::cast(string);
morphed->set_resource(one_byte_resource);
}
}
// Test that we can still flatten a string if the components it is built up
// from have been turned into 16 bit strings in the mean time.
THREADED_TEST(MorphCompositeStringTest) {
char utf_buffer[129];
const char* c_string = "Now is the time for all good men"
" to come to the aid of the party";
uint16_t* two_byte_string = AsciiToTwoByteString(c_string);
{
LocalContext env;
i::Factory* factory = CcTest::i_isolate()->factory();
v8::HandleScope scope(env->GetIsolate());
OneByteVectorResource one_byte_resource(
i::Vector<const char>(c_string, i::StrLength(c_string)));
UC16VectorResource uc16_resource(
i::Vector<const uint16_t>(two_byte_string,
i::StrLength(c_string)));
Local<String> lhs(
v8::Utils::ToLocal(factory->NewExternalStringFromOneByte(
&one_byte_resource).ToHandleChecked()));
Local<String> rhs(
v8::Utils::ToLocal(factory->NewExternalStringFromOneByte(
&one_byte_resource).ToHandleChecked()));
env->Global()->Set(v8_str("lhs"), lhs);
env->Global()->Set(v8_str("rhs"), rhs);
CompileRun(
"var cons = lhs + rhs;"
"var slice = lhs.substring(1, lhs.length - 1);"
"var slice_on_cons = (lhs + rhs).substring(1, lhs.length *2 - 1);");
CHECK(lhs->IsOneByte());
CHECK(rhs->IsOneByte());
MorphAString(*v8::Utils::OpenHandle(*lhs), &one_byte_resource,
&uc16_resource);
MorphAString(*v8::Utils::OpenHandle(*rhs), &one_byte_resource,
&uc16_resource);
// This should UTF-8 without flattening, since everything is ASCII.
Handle<String> cons = v8_compile("cons")->Run().As<String>();
CHECK_EQ(128, cons->Utf8Length());
int nchars = -1;
CHECK_EQ(129, cons->WriteUtf8(utf_buffer, -1, &nchars));
CHECK_EQ(128, nchars);
CHECK_EQ(0, strcmp(
utf_buffer,
"Now is the time for all good men to come to the aid of the party"
"Now is the time for all good men to come to the aid of the party"));
// Now do some stuff to make sure the strings are flattened, etc.
CompileRun(
"/[^a-z]/.test(cons);"
"/[^a-z]/.test(slice);"
"/[^a-z]/.test(slice_on_cons);");
const char* expected_cons =
"Now is the time for all good men to come to the aid of the party"
"Now is the time for all good men to come to the aid of the party";
const char* expected_slice =
"ow is the time for all good men to come to the aid of the part";
const char* expected_slice_on_cons =
"ow is the time for all good men to come to the aid of the party"
"Now is the time for all good men to come to the aid of the part";
CHECK(String::NewFromUtf8(env->GetIsolate(), expected_cons)
->Equals(env->Global()->Get(v8_str("cons"))));
CHECK(String::NewFromUtf8(env->GetIsolate(), expected_slice)
->Equals(env->Global()->Get(v8_str("slice"))));
CHECK(String::NewFromUtf8(env->GetIsolate(), expected_slice_on_cons)
->Equals(env->Global()->Get(v8_str("slice_on_cons"))));
}
i::DeleteArray(two_byte_string);
}
TEST(CompileExternalTwoByteSource) {
LocalContext context;
v8::HandleScope scope(context->GetIsolate());
// This is a very short list of sources, which currently is to check for a
// regression caused by r2703.
const char* one_byte_sources[] = {
"0.5",
"-0.5", // This mainly testes PushBack in the Scanner.
"--0.5", // This mainly testes PushBack in the Scanner.
NULL};
// Compile the sources as external two byte strings.
for (int i = 0; one_byte_sources[i] != NULL; i++) {
uint16_t* two_byte_string = AsciiToTwoByteString(one_byte_sources[i]);
TestResource* uc16_resource = new TestResource(two_byte_string);
v8::Local<v8::String> source =
v8::String::NewExternal(context->GetIsolate(), uc16_resource);
v8::Script::Compile(source);
}
}
#ifndef V8_INTERPRETED_REGEXP
struct RegExpInterruptionData {
v8::base::Atomic32 loop_count;
UC16VectorResource* string_resource;
v8::Persistent<v8::String> string;
} regexp_interruption_data;
class RegExpInterruptionThread : public v8::base::Thread {
public:
explicit RegExpInterruptionThread(v8::Isolate* isolate)
: Thread(Options("TimeoutThread")), isolate_(isolate) {}
virtual void Run() {
for (v8::base::NoBarrier_Store(&regexp_interruption_data.loop_count, 0);
v8::base::NoBarrier_Load(&regexp_interruption_data.loop_count) < 7;
v8::base::NoBarrier_AtomicIncrement(
&regexp_interruption_data.loop_count, 1)) {
// Wait a bit before requesting GC.
v8::base::OS::Sleep(v8::base::TimeDelta::FromMilliseconds(50));
reinterpret_cast<i::Isolate*>(isolate_)->stack_guard()->RequestGC();
}
// Wait a bit before terminating.
v8::base::OS::Sleep(v8::base::TimeDelta::FromMilliseconds(50));
v8::V8::TerminateExecution(isolate_);
}
private:
v8::Isolate* isolate_;
};
void RunBeforeGC(v8::GCType type, v8::GCCallbackFlags flags) {
if (v8::base::NoBarrier_Load(&regexp_interruption_data.loop_count) != 2) {
return;
}
v8::HandleScope scope(CcTest::isolate());
v8::Local<v8::String> string = v8::Local<v8::String>::New(
CcTest::isolate(), regexp_interruption_data.string);
string->MakeExternal(regexp_interruption_data.string_resource);
}
// Test that RegExp execution can be interrupted. Specifically, we test
// * interrupting with GC
// * turn the subject string from one-byte internal to two-byte external string
// * force termination
TEST(RegExpInterruption) {
v8::HandleScope scope(CcTest::isolate());
LocalContext env;
RegExpInterruptionThread timeout_thread(CcTest::isolate());
v8::V8::AddGCPrologueCallback(RunBeforeGC);
static const char* one_byte_content = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
i::uc16* uc16_content = AsciiToTwoByteString(one_byte_content);
v8::Local<v8::String> string = v8_str(one_byte_content);
CcTest::global()->Set(v8_str("a"), string);
regexp_interruption_data.string.Reset(CcTest::isolate(), string);
regexp_interruption_data.string_resource = new UC16VectorResource(
i::Vector<const i::uc16>(uc16_content, i::StrLength(one_byte_content)));
v8::TryCatch try_catch(CcTest::isolate());
timeout_thread.Start();
CompileRun("/((a*)*)*b/.exec(a)");
CHECK(try_catch.HasTerminated());
timeout_thread.Join();
regexp_interruption_data.string.Reset();
i::DeleteArray(uc16_content);
}
#endif // V8_INTERPRETED_REGEXP
// Test that we cannot set a property on the global object if there
// is a read-only property in the prototype chain.
TEST(ReadOnlyPropertyInGlobalProto) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(isolate);
LocalContext context(0, templ);
v8::Handle<v8::Object> global = context->Global();
v8::Handle<v8::Object> global_proto =
v8::Handle<v8::Object>::Cast(global->Get(v8_str("__proto__")));
global_proto->ForceSet(v8_str("x"), v8::Integer::New(isolate, 0),
v8::ReadOnly);
global_proto->ForceSet(v8_str("y"), v8::Integer::New(isolate, 0),
v8::ReadOnly);
// Check without 'eval' or 'with'.
v8::Handle<v8::Value> res =
CompileRun("function f() { x = 42; return x; }; f()");
CHECK(v8::Integer::New(isolate, 0)->Equals(res));
// Check with 'eval'.
res = CompileRun("function f() { eval('1'); y = 43; return y; }; f()");
CHECK(v8::Integer::New(isolate, 0)->Equals(res));
// Check with 'with'.
res = CompileRun("function f() { with (this) { y = 44 }; return y; }; f()");
CHECK(v8::Integer::New(isolate, 0)->Equals(res));
}
static int force_set_set_count = 0;
static int force_set_get_count = 0;
bool pass_on_get = false;
static void ForceSetGetter(v8::Local<v8::String> name,
const v8::PropertyCallbackInfo<v8::Value>& info) {
force_set_get_count++;
if (pass_on_get) {
return;
}
info.GetReturnValue().Set(3);
}
static void ForceSetSetter(v8::Local<v8::String> name,
v8::Local<v8::Value> value,
const v8::PropertyCallbackInfo<void>& info) {
force_set_set_count++;
}
static void ForceSetInterceptGetter(
v8::Local<v8::Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
CHECK(name->IsString());
ForceSetGetter(Local<String>::Cast(name), info);
}
static void ForceSetInterceptSetter(
v8::Local<v8::Name> name, v8::Local<v8::Value> value,
const v8::PropertyCallbackInfo<v8::Value>& info) {
force_set_set_count++;
info.GetReturnValue().SetUndefined();
}
TEST(ForceSet) {
force_set_get_count = 0;
force_set_set_count = 0;
pass_on_get = false;
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(isolate);
v8::Handle<v8::String> access_property =
v8::String::NewFromUtf8(isolate, "a");
templ->SetAccessor(access_property, ForceSetGetter, ForceSetSetter);
LocalContext context(NULL, templ);
v8::Handle<v8::Object> global = context->Global();
// Ordinary properties
v8::Handle<v8::String> simple_property =
v8::String::NewFromUtf8(isolate, "p");
global->ForceSet(simple_property, v8::Int32::New(isolate, 4), v8::ReadOnly);
CHECK_EQ(4, global->Get(simple_property)->Int32Value());
// This should fail because the property is read-only
global->Set(simple_property, v8::Int32::New(isolate, 5));
CHECK_EQ(4, global->Get(simple_property)->Int32Value());
// This should succeed even though the property is read-only
global->ForceSet(simple_property, v8::Int32::New(isolate, 6));
CHECK_EQ(6, global->Get(simple_property)->Int32Value());
// Accessors
CHECK_EQ(0, force_set_set_count);
CHECK_EQ(0, force_set_get_count);
CHECK_EQ(3, global->Get(access_property)->Int32Value());
// CHECK_EQ the property shouldn't override it, just call the setter
// which in this case does nothing.
global->Set(access_property, v8::Int32::New(isolate, 7));
CHECK_EQ(3, global->Get(access_property)->Int32Value());
CHECK_EQ(1, force_set_set_count);
CHECK_EQ(2, force_set_get_count);
// ForceSet doesn't call the accessors for now.
// TODO(verwaest): Update once blink doesn't rely on ForceSet to delete api
// accessors.
global->ForceSet(access_property, v8::Int32::New(isolate, 8));
CHECK_EQ(8, global->Get(access_property)->Int32Value());
CHECK_EQ(1, force_set_set_count);
CHECK_EQ(2, force_set_get_count);
}
TEST(ForceSetWithInterceptor) {
force_set_get_count = 0;
force_set_set_count = 0;
pass_on_get = false;
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(isolate);
templ->SetHandler(v8::NamedPropertyHandlerConfiguration(
ForceSetInterceptGetter, ForceSetInterceptSetter));
LocalContext context(NULL, templ);
v8::Handle<v8::Object> global = context->Global();
v8::Handle<v8::String> some_property =
v8::String::NewFromUtf8(isolate, "a");
CHECK_EQ(0, force_set_set_count);
CHECK_EQ(0, force_set_get_count);
CHECK_EQ(3, global->Get(some_property)->Int32Value());
// Setting the property shouldn't override it, just call the setter
// which in this case does nothing.
global->Set(some_property, v8::Int32::New(isolate, 7));
CHECK_EQ(3, global->Get(some_property)->Int32Value());
CHECK_EQ(1, force_set_set_count);
CHECK_EQ(2, force_set_get_count);
// Getting the property when the interceptor returns an empty handle
// should yield undefined, since the property isn't present on the
// object itself yet.
pass_on_get = true;
CHECK(global->Get(some_property)->IsUndefined());
CHECK_EQ(1, force_set_set_count);
CHECK_EQ(3, force_set_get_count);
// Forcing the property to be set should cause the value to be
// set locally without calling the interceptor.
global->ForceSet(some_property, v8::Int32::New(isolate, 8));
CHECK_EQ(8, global->Get(some_property)->Int32Value());
CHECK_EQ(1, force_set_set_count);
CHECK_EQ(4, force_set_get_count);
// Reenabling the interceptor should cause it to take precedence over
// the property
pass_on_get = false;
CHECK_EQ(3, global->Get(some_property)->Int32Value());
CHECK_EQ(1, force_set_set_count);
CHECK_EQ(5, force_set_get_count);
// The interceptor should also work for other properties
CHECK_EQ(3, global->Get(v8::String::NewFromUtf8(isolate, "b"))
->Int32Value());
CHECK_EQ(1, force_set_set_count);
CHECK_EQ(6, force_set_get_count);
}
TEST(CreateDataProperty) {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope handle_scope(isolate);
CompileRun(
"var a = {};"
"var b = [];"
"Object.defineProperty(a, 'foo', {value: 23});"
"Object.defineProperty(a, 'bar', {value: 23, configurable: true});");
v8::Local<v8::Object> obj =
v8::Local<v8::Object>::Cast(env->Global()->Get(v8_str("a")));
v8::Local<v8::Array> arr =
v8::Local<v8::Array>::Cast(env->Global()->Get(v8_str("b")));
{
// Can't change a non-configurable properties.
v8::TryCatch try_catch(isolate);
CHECK(!obj->CreateDataProperty(env.local(), v8_str("foo"),
v8::Integer::New(isolate, 42)).FromJust());
CHECK(!try_catch.HasCaught());
CHECK(obj->CreateDataProperty(env.local(), v8_str("bar"),
v8::Integer::New(isolate, 42)).FromJust());
CHECK(!try_catch.HasCaught());
v8::Local<v8::Value> val =
obj->Get(env.local(), v8_str("bar")).ToLocalChecked();
CHECK(val->IsNumber());
CHECK_EQ(42.0, val->NumberValue(env.local()).FromJust());
}
{
// Set a regular property.
v8::TryCatch try_catch(isolate);
CHECK(obj->CreateDataProperty(env.local(), v8_str("blub"),
v8::Integer::New(isolate, 42)).FromJust());
CHECK(!try_catch.HasCaught());
v8::Local<v8::Value> val =
obj->Get(env.local(), v8_str("blub")).ToLocalChecked();
CHECK(val->IsNumber());
CHECK_EQ(42.0, val->NumberValue(env.local()).FromJust());
}
{
// Set an indexed property.
v8::TryCatch try_catch(isolate);
CHECK(obj->CreateDataProperty(env.local(), v8_str("1"),
v8::Integer::New(isolate, 42)).FromJust());
CHECK(!try_catch.HasCaught());
v8::Local<v8::Value> val = obj->Get(env.local(), 1).ToLocalChecked();
CHECK(val->IsNumber());
CHECK_EQ(42.0, val->NumberValue(env.local()).FromJust());
}
{
// Special cases for arrays.
v8::TryCatch try_catch(isolate);
CHECK(!arr->CreateDataProperty(env.local(), v8_str("length"),
v8::Integer::New(isolate, 1)).FromJust());
CHECK(!try_catch.HasCaught());
}
{
// Special cases for arrays: index exceeds the array's length
v8::TryCatch try_catch(isolate);
CHECK(arr->CreateDataProperty(env.local(), 1, v8::Integer::New(isolate, 23))
.FromJust());
CHECK(!try_catch.HasCaught());
CHECK_EQ(2U, arr->Length());
v8::Local<v8::Value> val = arr->Get(env.local(), 1).ToLocalChecked();
CHECK(val->IsNumber());
CHECK_EQ(23.0, val->NumberValue(env.local()).FromJust());
// Set an existing entry.
CHECK(arr->CreateDataProperty(env.local(), 0, v8::Integer::New(isolate, 42))
.FromJust());
CHECK(!try_catch.HasCaught());
val = arr->Get(env.local(), 0).ToLocalChecked();
CHECK(val->IsNumber());
CHECK_EQ(42.0, val->NumberValue(env.local()).FromJust());
}
CompileRun("Object.freeze(a);");
{
// Can't change non-extensible objects.
v8::TryCatch try_catch(isolate);
CHECK(!obj->CreateDataProperty(env.local(), v8_str("baz"),
v8::Integer::New(isolate, 42)).FromJust());
CHECK(!try_catch.HasCaught());
}
v8::Local<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(isolate);
templ->SetAccessCheckCallbacks(AccessAlwaysBlocked, NULL);
v8::Local<v8::Object> access_checked =
templ->NewInstance(env.local()).ToLocalChecked();
{
v8::TryCatch try_catch(isolate);
CHECK(access_checked->CreateDataProperty(env.local(), v8_str("foo"),
v8::Integer::New(isolate, 42))
.IsNothing());
CHECK(try_catch.HasCaught());
}
}
TEST(DefineOwnProperty) {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope handle_scope(isolate);
CompileRun(