blob: 9ddc9db71df6475affa0beae4f2d4ab9a7cb6da4 [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 "src/v8.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/cpu-profiler.h"
#include "src/execution.h"
#include "src/isolate.h"
#include "src/objects.h"
#include "src/parser.h"
#include "src/snapshot.h"
#include "src/unicode-inl.h"
#include "src/utils.h"
#include "src/vm-state.h"
#include "test/cctest/cctest.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::Message;
using ::v8::MessageCallback;
using ::v8::Object;
using ::v8::ObjectTemplate;
using ::v8::Persistent;
using ::v8::Script;
using ::v8::StackTrace;
using ::v8::String;
using ::v8::TryCatch;
using ::v8::Undefined;
using ::v8::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_EQ(signature_expected_receiver, args.Holder());
CHECK_EQ(signature_expected_receiver, 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 SignatureCallback(
const v8::FunctionCallbackInfo<v8::Value>& args) {
ApiTestFuzzer::Fuzz();
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);
}
// Tests that call v8::V8::Dispose() cannot be threaded.
TEST(InitializeAndDisposeOnce) {
CHECK(v8::V8::Initialize());
CHECK(v8::V8::Dispose());
}
// Tests that call v8::V8::Dispose() cannot be threaded.
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());
// TODO(mstarzinger): This should fail gracefully instead of asserting.
// 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) {
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;
CompileRun(source.start());
CHECK_EQ(expected_to_throw, try_catch.HasCaught());
if (!expected_to_throw) {
CHECK_EQ(10, signature_callback_count);
} else {
CHECK_EQ(v8_str("TypeError: Illegal invocation"),
try_catch.Exception()->ToString());
}
}
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 < ARRAY_SIZE(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);
TestSignature("test_object.accessor;", test_object);
TestSignature("test_object[accessor_key];", test_object);
TestSignature("test_object.accessor = 1;", test_object);
TestSignature("test_object[accessor_key] = 1;", test_object);
if (i >= bad_signature_start_offset) test_object = Local<Value>();
TestSignature("test_object.prop_sig();", test_object);
TestSignature("test_object.accessor_sig;", test_object);
TestSignature("test_object[accessor_sig_key];", test_object);
TestSignature("test_object.accessor_sig = 1;", test_object);
TestSignature("test_object[accessor_sig_key] = 1;", test_object);
}
}
THREADED_TEST(ArgumentSignature) {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::FunctionTemplate> cons = v8::FunctionTemplate::New(isolate);
cons->SetClassName(v8_str("Cons"));
v8::Handle<v8::Signature> sig = v8::Signature::New(
isolate, v8::Handle<v8::FunctionTemplate>(), 1, &cons);
v8::Handle<v8::FunctionTemplate> fun =
v8::FunctionTemplate::New(isolate,
SignatureCallback,
v8::Handle<Value>(),
sig);
env->Global()->Set(v8_str("Cons"), cons->GetFunction());
env->Global()->Set(v8_str("Fun1"), fun->GetFunction());
v8::Handle<Value> value1 = CompileRun("Fun1(4) == '';");
CHECK(value1->IsTrue());
v8::Handle<Value> value2 = CompileRun("Fun1(new Cons()) == '[object Cons]';");
CHECK(value2->IsTrue());
v8::Handle<Value> value3 = CompileRun("Fun1() == '';");
CHECK(value3->IsTrue());
v8::Handle<v8::FunctionTemplate> cons1 = v8::FunctionTemplate::New(isolate);
cons1->SetClassName(v8_str("Cons1"));
v8::Handle<v8::FunctionTemplate> cons2 = v8::FunctionTemplate::New(isolate);
cons2->SetClassName(v8_str("Cons2"));
v8::Handle<v8::FunctionTemplate> cons3 = v8::FunctionTemplate::New(isolate);
cons3->SetClassName(v8_str("Cons3"));
v8::Handle<v8::FunctionTemplate> args[3] = { cons1, cons2, cons3 };
v8::Handle<v8::Signature> wsig = v8::Signature::New(
isolate, v8::Handle<v8::FunctionTemplate>(), 3, args);
v8::Handle<v8::FunctionTemplate> fun2 =
v8::FunctionTemplate::New(isolate,
SignatureCallback,
v8::Handle<Value>(),
wsig);
env->Global()->Set(v8_str("Cons1"), cons1->GetFunction());
env->Global()->Set(v8_str("Cons2"), cons2->GetFunction());
env->Global()->Set(v8_str("Cons3"), cons3->GetFunction());
env->Global()->Set(v8_str("Fun2"), fun2->GetFunction());
v8::Handle<Value> value4 = CompileRun(
"Fun2(new Cons1(), new Cons2(), new Cons3()) =="
"'[object Cons1],[object Cons2],[object Cons3]'");
CHECK(value4->IsTrue());
v8::Handle<Value> value5 = CompileRun(
"Fun2(new Cons1(), new Cons2(), 5) == '[object Cons1],[object Cons2],'");
CHECK(value5->IsTrue());
v8::Handle<Value> value6 = CompileRun(
"Fun2(new Cons3(), new Cons2(), new Cons1()) == ',[object Cons2],'");
CHECK(value6->IsTrue());
v8::Handle<Value> value7 = CompileRun(
"Fun2(new Cons1(), new Cons2(), new Cons3(), 'd') == "
"'[object Cons1],[object Cons2],[object Cons3],d';");
CHECK(value7->IsTrue());
v8::Handle<Value> value8 = CompileRun(
"Fun2(new Cons1(), new Cons2()) == '[object Cons1],[object Cons2]'");
CHECK(value8->IsTrue());
}
THREADED_TEST(HulIgennem) {
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();
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_EQ(bar_str, 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_EQ(bar_str, after);
Local<v8::Array> value = CompileRun("[\"a\", \"b\"]").As<v8::Array>();
CHECK_EQ(v8_str("a"), value->Get(0));
CHECK_EQ(v8_str("b"), value->Get(1));
}
THREADED_TEST(Script) {
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 TestAsciiResource: public String::ExternalAsciiStringResource {
public:
explicit TestAsciiResource(const char* data, int* counter = NULL,
size_t offset = 0)
: orig_data_(data),
data_(data + offset),
length_(strlen(data) - offset),
counter_(counter) {}
~TestAsciiResource() {
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(i::Heap::kNoGCFlags);
CHECK_EQ(0, dispose_count);
}
CcTest::i_isolate()->compilation_cache()->Clear();
CcTest::heap()->CollectAllAvailableGarbage();
CHECK_EQ(1, dispose_count);
}
THREADED_TEST(ScriptUsingAsciiStringResource) {
int dispose_count = 0;
const char* c_source = "1 + 2 * 3";
{
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
TestAsciiResource* resource = new TestAsciiResource(i::StrDup(c_source),
&dispose_count);
Local<String> source = String::NewExternal(env->GetIsolate(), resource);
CHECK(source->IsExternalAscii());
CHECK_EQ(static_cast<const String::ExternalStringResourceBase*>(resource),
source->GetExternalAsciiStringResource());
String::Encoding encoding = String::UNKNOWN_ENCODING;
CHECK_EQ(static_cast<const String::ExternalStringResourceBase*>(resource),
source->GetExternalStringResourceBase(&encoding));
CHECK_EQ(String::ASCII_ENCODING, encoding);
Local<Script> script = v8_compile(source);
Local<Value> value = script->Run();
CHECK(value->IsNumber());
CHECK_EQ(7, value->Int32Value());
CcTest::heap()->CollectAllGarbage(i::Heap::kNoGCFlags);
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->IsExternalAscii(), false);
String::Encoding encoding = String::UNKNOWN_ENCODING;
CHECK_EQ(NULL, source->GetExternalStringResourceBase(&encoding));
CHECK_EQ(String::ASCII_ENCODING, encoding);
bool success = source->MakeExternal(new TestResource(two_byte_source,
&dispose_count));
CHECK(success);
Local<Script> script = v8_compile(source);
Local<Value> value = script->Run();
CHECK(value->IsNumber());
CHECK_EQ(7, value->Int32Value());
CcTest::heap()->CollectAllGarbage(i::Heap::kNoGCFlags);
CHECK_EQ(0, dispose_count);
}
CcTest::i_isolate()->compilation_cache()->Clear();
CcTest::heap()->CollectAllGarbage(i::Heap::kAbortIncrementalMarkingMask);
CHECK_EQ(1, dispose_count);
}
THREADED_TEST(ScriptMakingExternalAsciiString) {
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 TestAsciiResource(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(i::Heap::kNoGCFlags);
CHECK_EQ(0, dispose_count);
}
CcTest::i_isolate()->compilation_cache()->Clear();
CcTest::heap()->CollectAllGarbage(i::Heap::kAbortIncrementalMarkingMask);
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(MakingExternalAsciiStringConditions) {
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(MakingExternalUnalignedAsciiString) {
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_pointer_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 TestAsciiResource(i::StrDup(c_cons), NULL, 1));
CHECK(success);
const char* c_slice = "_bcdefghijklmnopqrstuvwxyz";
success = slice->MakeExternal(
new TestAsciiResource(i::StrDup(c_slice), NULL, 1));
CHECK(success);
// Trigger GCs and force evacuation.
CcTest::heap()->CollectAllGarbage(i::Heap::kNoGCFlags);
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(i::Heap::kNoGCFlags);
CcTest::heap()->CollectAllGarbage(i::Heap::kNoGCFlags);
}
THREADED_TEST(UsingExternalAsciiString) {
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 TestAsciiResource(i::StrDup(one_byte_string)));
i::Handle<i::String> istring = v8::Utils::OpenHandle(*string);
// Trigger GCs so that the newly allocated string moves to old gen.
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(i::Heap::kNoGCFlags);
CcTest::heap()->CollectAllGarbage(i::Heap::kNoGCFlags);
}
THREADED_TEST(ScavengeExternalString) {
i::FLAG_stress_compaction = false;
i::FLAG_gc_global = false;
int dispose_count = 0;
bool in_new_space = false;
{
v8::HandleScope scope(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_data_space()->Contains(*istring));
CHECK_EQ(0, dispose_count);
}
CcTest::heap()->CollectGarbage(
in_new_space ? i::NEW_SPACE : i::OLD_DATA_SPACE);
CHECK_EQ(1, dispose_count);
}
THREADED_TEST(ScavengeExternalAsciiString) {
i::FLAG_stress_compaction = false;
i::FLAG_gc_global = false;
int dispose_count = 0;
bool in_new_space = false;
{
v8::HandleScope scope(CcTest::isolate());
const char* one_byte_string = "test string";
Local<String> string = String::NewExternal(
CcTest::isolate(),
new TestAsciiResource(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_data_space()->Contains(*istring));
CHECK_EQ(0, dispose_count);
}
CcTest::heap()->CollectGarbage(
in_new_space ? i::NEW_SPACE : i::OLD_DATA_SPACE);
CHECK_EQ(1, dispose_count);
}
class TestAsciiResourceWithDisposeControl: public TestAsciiResource {
public:
// Only used by non-threaded tests, so it can use static fields.
static int dispose_calls;
static int dispose_count;
TestAsciiResourceWithDisposeControl(const char* data, bool dispose)
: TestAsciiResource(data, &dispose_count),
dispose_(dispose) { }
void Dispose() {
++dispose_calls;
if (dispose_) delete this;
}
private:
bool dispose_;
};
int TestAsciiResourceWithDisposeControl::dispose_count = 0;
int TestAsciiResourceWithDisposeControl::dispose_calls = 0;
TEST(ExternalStringWithDisposeHandling) {
const char* c_source = "1 + 2 * 3";
// Use a stack allocated external string resource allocated object.
TestAsciiResourceWithDisposeControl::dispose_count = 0;
TestAsciiResourceWithDisposeControl::dispose_calls = 0;
TestAsciiResourceWithDisposeControl res_stack(i::StrDup(c_source), false);
{
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, TestAsciiResourceWithDisposeControl::dispose_count);
}
CcTest::i_isolate()->compilation_cache()->Clear();
CcTest::heap()->CollectAllAvailableGarbage();
CHECK_EQ(1, TestAsciiResourceWithDisposeControl::dispose_calls);
CHECK_EQ(0, TestAsciiResourceWithDisposeControl::dispose_count);
// Use a heap allocated external string resource allocated object.
TestAsciiResourceWithDisposeControl::dispose_count = 0;
TestAsciiResourceWithDisposeControl::dispose_calls = 0;
TestAsciiResource* res_heap =
new TestAsciiResourceWithDisposeControl(i::StrDup(c_source), true);
{
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, TestAsciiResourceWithDisposeControl::dispose_count);
}
CcTest::i_isolate()->compilation_cache()->Clear();
CcTest::heap()->CollectAllAvailableGarbage();
CHECK_EQ(1, TestAsciiResourceWithDisposeControl::dispose_calls);
CHECK_EQ(1, TestAsciiResourceWithDisposeControl::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 TestAsciiResource(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(i::Heap::kNoGCFlags);
CcTest::heap()->CollectAllGarbage(i::Heap::kNoGCFlags);
}
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());
}
template<typename T>
static void CheckReturnValue(const T& t, i::Address callback) {
v8::ReturnValue<v8::Value> rv = t.GetReturnValue();
i::Object** o = *reinterpret_cast<i::Object***>(&rv);
CHECK_EQ(CcTest::isolate(), t.GetIsolate());
CHECK_EQ(t.GetIsolate(), rv.GetIsolate());
CHECK((*o)->IsTheHole() || (*o)->IsUndefined());
// Verify reset
bool is_runtime = (*o)->IsTheHole();
rv.Set(true);
CHECK(!(*o)->IsTheHole() && !(*o)->IsUndefined());
rv.Set(v8::Handle<v8::Object>());
CHECK((*o)->IsTheHole() || (*o)->IsUndefined());
CHECK_EQ(is_runtime, (*o)->IsTheHole());
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(t.GetIsolate());
// If CPU profiler is active check that when API callback is invoked
// VMState is set to EXTERNAL.
if (isolate->cpu_profiler()->is_profiling()) {
CHECK_EQ(i::EXTERNAL, isolate->current_vm_state());
CHECK(isolate->external_callback_scope());
CHECK_EQ(callback, isolate->external_callback_scope()->callback());
}
}
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_EQ(v8_str("[object funky]"), 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::HandleScope scope(CcTest::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 < ARRAY_SIZE(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()->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()->Value());
}
// check oddballs
ReturnValueOddball oddballs[] = {
kNullReturnValue,
kUndefinedReturnValue,
kEmptyStringReturnValue
};
for (size_t i = 0; i < ARRAY_SIZE(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_EQ(base_instance,
base_instance->FindInstanceInPrototypeChain(base));
CHECK(base_instance->FindInstanceInPrototypeChain(derived).IsEmpty());
CHECK(base_instance->FindInstanceInPrototypeChain(other).IsEmpty());
// derived_instance is an instance of base and derived.
CHECK_EQ(derived_instance,
derived_instance->FindInstanceInPrototypeChain(base));
CHECK_EQ(derived_instance,
derived_instance->FindInstanceInPrototypeChain(derived));
CHECK(derived_instance->FindInstanceInPrototypeChain(other).IsEmpty());
// other_instance is an instance of other and its immediate
// prototype derived_instance2 is an instance of base and derived.
// Note, derived_instance is an instance of base and derived too,
// but it comes after derived_instance2 in the prototype chain of
// other_instance.
CHECK_EQ(derived_instance2,
other_instance->FindInstanceInPrototypeChain(base));
CHECK_EQ(derived_instance2,
other_instance->FindInstanceInPrototypeChain(derived));
CHECK_EQ(other_instance,
other_instance->FindInstanceInPrototypeChain(other));
}
THREADED_TEST(TinyInteger) {
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(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);
}
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<ObjectTemplate> templ1 = ObjectTemplate::New(isolate);
templ1->Set(isolate, "x", v8_num(10));
templ1->Set(isolate, "y", v8_num(13));
LocalContext env;
Local<v8::Object> instance1 = templ1->NewInstance();
env->Global()->Set(v8_str("p"), instance1);
CHECK(v8_compile("(p.x == 10)")->Run()->BooleanValue());
CHECK(v8_compile("(p.y == 13)")->Run()->BooleanValue());
Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(isolate);
fun->PrototypeTemplate()->Set(isolate, "nirk", v8_num(123));
Local<ObjectTemplate> templ2 = fun->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());
}
int echo_named_call_count;
static void EchoNamedProperty(Local<String> name,
const v8::PropertyCallbackInfo<v8::Value>& info) {
ApiTestFuzzer::Fuzz();
CHECK_EQ(v8_str("data"), info.Data());
echo_named_call_count++;
info.GetReturnValue().Set(name);
}
// 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 EmptyInterceptorGetter(Local<String> name,
const v8::PropertyCallbackInfo<v8::Value>& info) {
}
void EmptyInterceptorSetter(Local<String> name,
Local<Value> value,
const v8::PropertyCallbackInfo<v8::Value>& info) {
}
void InterceptorGetter(Local<String> name,
const v8::PropertyCallbackInfo<v8::Value>& info) {
// Intercept names that start with 'interceptor_'.
String::Utf8Value utf8(name);
char* name_str = *utf8;
char prefix[] = "interceptor_";
int i;
for (i = 0; name_str[i] && prefix[i]; ++i) {
if (name_str[i] != prefix[i]) return;
}
Handle<Object> self = Handle<Object>::Cast(info.This());
info.GetReturnValue().Set(self->GetHiddenValue(v8_str(name_str + i)));
}
void InterceptorSetter(Local<String> name,
Local<Value> value,
const v8::PropertyCallbackInfo<v8::Value>& info) {
// Intercept accesses that set certain integer values, for which the name does
// not start with 'accessor_'.
String::Utf8Value utf8(name);
char* name_str = *utf8;
char prefix[] = "accessor_";
int i;
for (i = 0; name_str[i] && prefix[i]; ++i) {
if (name_str[i] != prefix[i]) break;
}
if (!prefix[i]) return;
if (value->IsInt32() && value->Int32Value() < 10000) {
Handle<Object> self = Handle<Object>::Cast(info.This());
self->SetHiddenValue(name, value);
info.GetReturnValue().Set(value);
}
}
void AddAccessor(Handle<FunctionTemplate> templ,
Handle<String> name,
v8::AccessorGetterCallback getter,
v8::AccessorSetterCallback setter) {
templ->PrototypeTemplate()->SetAccessor(name, getter, setter);
}
void AddInterceptor(Handle<FunctionTemplate> templ,
v8::NamedPropertyGetterCallback getter,
v8::NamedPropertySetterCallback setter) {
templ->InstanceTemplate()->SetNamedPropertyHandler(getter, setter);
}
THREADED_TEST(EmptyInterceptorDoesNotShadowAccessors) {
v8::HandleScope scope(CcTest::isolate());
Handle<FunctionTemplate> parent = FunctionTemplate::New(CcTest::isolate());
Handle<FunctionTemplate> child = FunctionTemplate::New(CcTest::isolate());
child->Inherit(parent);
AddAccessor(parent, v8_str("age"),
SimpleAccessorGetter, SimpleAccessorSetter);
AddInterceptor(child, EmptyInterceptorGetter, EmptyInterceptorSetter);
LocalContext env;
env->Global()->Set(v8_str("Child"), child->GetFunction());
CompileRun("var child = new Child;"
"child.age = 10;");
ExpectBoolean("child.hasOwnProperty('age')", false);
ExpectInt32("child.age", 10);
ExpectInt32("child.accessor_age", 10);
}
THREADED_TEST(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::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
i::LookupResult lookup(i_isolate);
i::Handle<i::String> name(v8::Utils::OpenHandle(*v8_str("length")));
a->LookupOwnRealNamedProperty(name, &lookup);
CHECK(lookup.IsPropertyCallbacks());
i::Handle<i::Object> callback(lookup.GetCallbackObject(), i_isolate);
CHECK(callback->IsExecutableAccessorInfo());
}
THREADED_TEST(EmptyInterceptorBreakTransitions) {
v8::HandleScope scope(CcTest::isolate());
Handle<FunctionTemplate> templ = FunctionTemplate::New(CcTest::isolate());
AddInterceptor(templ, EmptyInterceptorGetter, EmptyInterceptorSetter);
LocalContext env;
env->Global()->Set(v8_str("Constructor"), templ->GetFunction());
CompileRun("var o1 = new Constructor;"
"o1.a = 1;" // Ensure a and x share the descriptor array.
"Object.defineProperty(o1, 'x', {value: 10});");
CompileRun("var o2 = new Constructor;"
"o2.a = 1;"
"Object.defineProperty(o2, 'x', {value: 10});");
}
THREADED_TEST(EmptyInterceptorDoesNotShadowJSAccessors) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
Handle<FunctionTemplate> parent = FunctionTemplate::New(isolate);
Handle<FunctionTemplate> child = FunctionTemplate::New(isolate);
child->Inherit(parent);
AddInterceptor(child, EmptyInterceptorGetter, EmptyInterceptorSetter);
LocalContext env;
env->Global()->Set(v8_str("Child"), child->GetFunction());
CompileRun("var child = new Child;"
"var parent = child.__proto__;"
"Object.defineProperty(parent, 'age', "
" {get: function(){ return this.accessor_age; }, "
" set: function(v){ this.accessor_age = v; }, "
" enumerable: true, configurable: true});"
"child.age = 10;");
ExpectBoolean("child.hasOwnProperty('age')", false);
ExpectInt32("child.age", 10);
ExpectInt32("child.accessor_age", 10);
}
THREADED_TEST(EmptyInterceptorDoesNotAffectJSProperties) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
Handle<FunctionTemplate> parent = FunctionTemplate::New(isolate);
Handle<FunctionTemplate> child = FunctionTemplate::New(isolate);
child->Inherit(parent);
AddInterceptor(child, EmptyInterceptorGetter, EmptyInterceptorSetter);
LocalContext env;
env->Global()->Set(v8_str("Child"), child->GetFunction());
CompileRun("var child = new Child;"
"var parent = child.__proto__;"
"parent.name = 'Alice';");
ExpectBoolean("child.hasOwnProperty('name')", false);
ExpectString("child.name", "Alice");
CompileRun("child.name = 'Bob';");
ExpectString("child.name", "Bob");
ExpectBoolean("child.hasOwnProperty('name')", true);
ExpectString("parent.name", "Alice");
}
THREADED_TEST(SwitchFromInterceptorToAccessor) {
v8::HandleScope scope(CcTest::isolate());
Handle<FunctionTemplate> templ = FunctionTemplate::New(CcTest::isolate());
AddAccessor(templ, v8_str("age"),
SimpleAccessorGetter, SimpleAccessorSetter);
AddInterceptor(templ, InterceptorGetter, InterceptorSetter);
LocalContext env;
env->Global()->Set(v8_str("Obj"), templ->GetFunction());
CompileRun("var obj = new Obj;"
"function setAge(i){ obj.age = i; };"
"for(var i = 0; i <= 10000; i++) setAge(i);");
// All i < 10000 go to the interceptor.
ExpectInt32("obj.interceptor_age", 9999);
// The last i goes to the accessor.
ExpectInt32("obj.accessor_age", 10000);
}
THREADED_TEST(SwitchFromAccessorToInterceptor) {
v8::HandleScope scope(CcTest::isolate());
Handle<FunctionTemplate> templ = FunctionTemplate::New(CcTest::isolate());
AddAccessor(templ, v8_str("age"),
SimpleAccessorGetter, SimpleAccessorSetter);
AddInterceptor(templ, InterceptorGetter, InterceptorSetter);
LocalContext env;
env->Global()->Set(v8_str("Obj"), templ->GetFunction());
CompileRun("var obj = new Obj;"
"function setAge(i){ obj.age = i; };"
"for(var i = 20000; i >= 9999; i--) setAge(i);");
// All i >= 10000 go to the accessor.
ExpectInt32("obj.accessor_age", 10000);
// The last i goes to the interceptor.
ExpectInt32("obj.interceptor_age", 9999);
}
THREADED_TEST(SwitchFromInterceptorToAccessorWithInheritance) {
v8::HandleScope scope(CcTest::isolate());
Handle<FunctionTemplate> parent = FunctionTemplate::New(CcTest::isolate());
Handle<FunctionTemplate> child = FunctionTemplate::New(CcTest::isolate());
child->Inherit(parent);
AddAccessor(parent, v8_str("age"),
SimpleAccessorGetter, SimpleAccessorSetter);
AddInterceptor(child, InterceptorGetter, InterceptorSetter);
LocalContext env;
env->Global()->Set(v8_str("Child"), child->GetFunction());
CompileRun("var child = new Child;"
"function setAge(i){ child.age = i; };"
"for(var i = 0; i <= 10000; i++) setAge(i);");
// All i < 10000 go to the interceptor.
ExpectInt32("child.interceptor_age", 9999);
// The last i goes to the accessor.
ExpectInt32("child.accessor_age", 10000);
}
THREADED_TEST(SwitchFromAccessorToInterceptorWithInheritance) {
v8::HandleScope scope(CcTest::isolate());
Handle<FunctionTemplate> parent = FunctionTemplate::New(CcTest::isolate());
Handle<FunctionTemplate> child = FunctionTemplate::New(CcTest::isolate());
child->Inherit(parent);
AddAccessor(parent, v8_str("age"),
SimpleAccessorGetter, SimpleAccessorSetter);
AddInterceptor(child, InterceptorGetter, InterceptorSetter);
LocalContext env;
env->Global()->Set(v8_str("Child"), child->GetFunction());
CompileRun("var child = new Child;"
"function setAge(i){ child.age = i; };"
"for(var i = 20000; i >= 9999; i--) setAge(i);");
// All i >= 10000 go to the accessor.
ExpectInt32("child.accessor_age", 10000);
// The last i goes to the interceptor.
ExpectInt32("child.interceptor_age", 9999);
}
THREADED_TEST(SwitchFromInterceptorToJSAccessor) {
v8::HandleScope scope(CcTest::isolate());
Handle<FunctionTemplate> templ = FunctionTemplate::New(CcTest::isolate());
AddInterceptor(templ, InterceptorGetter, InterceptorSetter);
LocalContext env;
env->Global()->Set(v8_str("Obj"), templ->GetFunction());
CompileRun("var obj = new Obj;"
"function setter(i) { this.accessor_age = i; };"
"function getter() { return this.accessor_age; };"
"function setAge(i) { obj.age = i; };"
"Object.defineProperty(obj, 'age', { get:getter, set:setter });"
"for(var i = 0; i <= 10000; i++) setAge(i);");
// All i < 10000 go to the interceptor.
ExpectInt32("obj.interceptor_age", 9999);
// The last i goes to the JavaScript accessor.
ExpectInt32("obj.accessor_age", 10000);
// The installed JavaScript getter is still intact.
// This last part is a regression test for issue 1651 and relies on the fact
// that both interceptor and accessor are being installed on the same object.
ExpectInt32("obj.age", 10000);
ExpectBoolean("obj.hasOwnProperty('age')", true);
ExpectUndefined("Object.getOwnPropertyDescriptor(obj, 'age').value");
}
THREADED_TEST(SwitchFromJSAccessorToInterceptor) {
v8::HandleScope scope(CcTest::isolate());
Handle<FunctionTemplate> templ = FunctionTemplate::New(CcTest::isolate());
AddInterceptor(templ, InterceptorGetter, InterceptorSetter);
LocalContext env;
env->Global()->Set(v8_str("Obj"), templ->GetFunction());
CompileRun("var obj = new Obj;"
"function setter(i) { this.accessor_age = i; };"
"function getter() { return this.accessor_age; };"
"function setAge(i) { obj.age = i; };"
"Object.defineProperty(obj, 'age', { get:getter, set:setter });"
"for(var i = 20000; i >= 9999; i--) setAge(i);");
// All i >= 10000 go to the accessor.
ExpectInt32("obj.accessor_age", 10000);
// The last i goes to the interceptor.
ExpectInt32("obj.interceptor_age", 9999);
// The installed JavaScript getter is still intact.
// This last part is a regression test for issue 1651 and relies on the fact
// that both interceptor and accessor are being installed on the same object.
ExpectInt32("obj.age", 10000);
ExpectBoolean("obj.hasOwnProperty('age')", true);
ExpectUndefined("Object.getOwnPropertyDescriptor(obj, 'age').value");
}
THREADED_TEST(SwitchFromInterceptorToProperty) {
v8::HandleScope scope(CcTest::isolate());
Handle<FunctionTemplate> parent = FunctionTemplate::New(CcTest::isolate());
Handle<FunctionTemplate> child = FunctionTemplate::New(CcTest::isolate());
child->Inherit(parent);
AddInterceptor(child, InterceptorGetter, InterceptorSetter);
LocalContext env;
env->Global()->Set(v8_str("Child"), child->GetFunction());
CompileRun("var child = new Child;"
"function setAge(i){ child.age = i; };"
"for(var i = 0; i <= 10000; i++) setAge(i);");
// All i < 10000 go to the interceptor.
ExpectInt32("child.interceptor_age", 9999);
// The last i goes to child's own property.
ExpectInt32("child.age", 10000);
}
THREADED_TEST(SwitchFromPropertyToInterceptor) {
v8::HandleScope scope(CcTest::isolate());
Handle<FunctionTemplate> parent = FunctionTemplate::New(CcTest::isolate());
Handle<FunctionTemplate> child = FunctionTemplate::New(CcTest::isolate());
child->Inherit(parent);
AddInterceptor(child, InterceptorGetter, InterceptorSetter);
LocalContext env;
env->Global()->Set(v8_str("Child"), child->GetFunction());
CompileRun("var child = new Child;"
"function setAge(i){ child.age = i; };"
"for(var i = 20000; i >= 9999; i--) setAge(i);");
// All i >= 10000 go to child's own property.
ExpectInt32("child.age", 10000);
// The last i goes to the interceptor.
ExpectInt32("child.interceptor_age", 9999);
}
THREADED_TEST(NamedPropertyHandlerGetter) {
echo_named_call_count = 0;
v8::HandleScope scope(CcTest::isolate());
v8::Handle<v8::FunctionTemplate> templ =
v8::FunctionTemplate::New(CcTest::isolate());
templ->InstanceTemplate()->SetNamedPropertyHandler(EchoNamedProperty,
0, 0, 0, 0,
v8_str("data"));
LocalContext env;
env->Global()->Set(v8_str("obj"),
templ->GetFunction()->NewInstance());
CHECK_EQ(echo_named_call_count, 0);
v8_compile("obj.x")->Run();
CHECK_EQ(echo_named_call_count, 1);
const char* code = "var str = 'oddle'; obj[str] + obj.poddle;";
v8::Handle<Value> str = CompileRun(code);
String::Utf8Value value(str);
CHECK_EQ(*value, "oddlepoddle");
// Check default behavior
CHECK_EQ(v8_compile("obj.flob = 10;")->Run()->Int32Value(), 10);
CHECK(v8_compile("'myProperty' in obj")->Run()->BooleanValue());
CHECK(v8_compile("delete obj.myProperty")->Run()->BooleanValue());
}
int echo_indexed_call_count = 0;
static void EchoIndexedProperty(
uint32_t index,
const v8::PropertyCallbackInfo<v8::Value>& info) {
ApiTestFuzzer::Fuzz();
CHECK_EQ(v8_num(637), info.Data());
echo_indexed_call_count++;
info.GetReturnValue().Set(v8_num(index));
}
THREADED_TEST(IndexedPropertyHandlerGetter) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate);
templ->InstanceTemplate()->SetIndexedPropertyHandler(EchoIndexedProperty,
0, 0, 0, 0,
v8_num(637));
LocalContext env;
env->Global()->Set(v8_str("obj"),
templ->GetFunction()->NewInstance());
Local<Script> script = v8_compile("obj[900]");
CHECK_EQ(script->Run()->Int32Value(), 900);
}
v8::Handle<v8::Object> bottom;
static void CheckThisIndexedPropertyHandler(
uint32_t index,
const v8::PropertyCallbackInfo<v8::Value>& info) {
CheckReturnValue(info, FUNCTION_ADDR(CheckThisIndexedPropertyHandler));
ApiTestFuzzer::Fuzz();
CHECK(info.This()->Equals(bottom));
}
static void CheckThisNamedPropertyHandler(
Local<String> name,
const v8::PropertyCallbackInfo<v8::Value>& info) {
CheckReturnValue(info, FUNCTION_ADDR(CheckThisNamedPropertyHandler));
ApiTestFuzzer::Fuzz();
CHECK(info.This()->Equals(bottom));
}
void CheckThisIndexedPropertySetter(
uint32_t index,
Local<Value> value,
const v8::PropertyCallbackInfo<v8::Value>& info) {
CheckReturnValue(info, FUNCTION_ADDR(CheckThisIndexedPropertySetter));
ApiTestFuzzer::Fuzz();
CHECK(info.This()->Equals(bottom));
}
void CheckThisNamedPropertySetter(
Local<String> property,
Local<Value> value,
const v8::PropertyCallbackInfo<v8::Value>& info) {
CheckReturnValue(info, FUNCTION_ADDR(CheckThisNamedPropertySetter));
ApiTestFuzzer::Fuzz();
CHECK(info.This()->Equals(bottom));
}
void CheckThisIndexedPropertyQuery(
uint32_t index,
const v8::PropertyCallbackInfo<v8::Integer>& info) {
CheckReturnValue(info, FUNCTION_ADDR(CheckThisIndexedPropertyQuery));
ApiTestFuzzer::Fuzz();
CHECK(info.This()->Equals(bottom));
}
void CheckThisNamedPropertyQuery(
Local<String> property,
const v8::PropertyCallbackInfo<v8::Integer>& info) {
CheckReturnValue(info, FUNCTION_ADDR(CheckThisNamedPropertyQuery));
ApiTestFuzzer::Fuzz();
CHECK(info.This()->Equals(bottom));
}
void CheckThisIndexedPropertyDeleter(
uint32_t index,
const v8::PropertyCallbackInfo<v8::Boolean>& info) {
CheckReturnValue(info, FUNCTION_ADDR(CheckThisIndexedPropertyDeleter));
ApiTestFuzzer::Fuzz();
CHECK(info.This()->Equals(bottom));
}
void CheckThisNamedPropertyDeleter(
Local<String> property,
const v8::PropertyCallbackInfo<v8::Boolean>& info) {
CheckReturnValue(info, FUNCTION_ADDR(CheckThisNamedPropertyDeleter));
ApiTestFuzzer::Fuzz();
CHECK(info.This()->Equals(bottom));
}
void CheckThisIndexedPropertyEnumerator(
const v8::PropertyCallbackInfo<v8::Array>& info) {
CheckReturnValue(info, FUNCTION_ADDR(CheckThisIndexedPropertyEnumerator));
ApiTestFuzzer::Fuzz();
CHECK(info.This()->Equals(bottom));
}
void CheckThisNamedPropertyEnumerator(
const v8::PropertyCallbackInfo<v8::Array>& info) {
CheckReturnValue(info, FUNCTION_ADDR(CheckThisNamedPropertyEnumerator));
ApiTestFuzzer::Fuzz();
CHECK(info.This()->Equals(bottom));
}
THREADED_PROFILED_TEST(PropertyHandlerInPrototype) {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
// Set up a prototype chain with three interceptors.
v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate);
templ->InstanceTemplate()->SetIndexedPropertyHandler(
CheckThisIndexedPropertyHandler,
CheckThisIndexedPropertySetter,
CheckThisIndexedPropertyQuery,
CheckThisIndexedPropertyDeleter,
CheckThisIndexedPropertyEnumerator);
templ->InstanceTemplate()->SetNamedPropertyHandler(
CheckThisNamedPropertyHandler,
CheckThisNamedPropertySetter,
CheckThisNamedPropertyQuery,
CheckThisNamedPropertyDeleter,
CheckThisNamedPropertyEnumerator);
bottom = templ->GetFunction()->NewInstance();
Local<v8::Object> top = templ->GetFunction()->NewInstance();
Local<v8::Object> middle = templ->GetFunction()->NewInstance();
bottom->SetPrototype(middle);
middle->SetPrototype(top);
env->Global()->Set(v8_str("obj"), bottom);
// Indexed and named get.
CompileRun("obj[0]");
CompileRun("obj.x");
// Indexed and named set.
CompileRun("obj[1] = 42");
CompileRun("obj.y = 42");
// Indexed and named query.
CompileRun("0 in obj");
CompileRun("'x' in obj");
// Indexed and named deleter.
CompileRun("delete obj[0]");
CompileRun("delete obj.x");
// Enumerators.
CompileRun("for (var p in obj) ;");
}
static void PrePropertyHandlerGet(
Local<String> key,
const v8::PropertyCallbackInfo<v8::Value>& info) {
ApiTestFuzzer::Fuzz();
if (v8_str("pre")->Equals(key)) {
info.GetReturnValue().Set(v8_str("PrePropertyHandler: pre"));
}
}
static void PrePropertyHandlerQuery(
Local<String> key,
const v8::PropertyCallbackInfo<v8::Integer>& info) {
if (v8_str("pre")->Equals(key)) {
info.GetReturnValue().Set(static_cast<int32_t>(v8::None));
}
}
THREADED_TEST(PrePropertyHandler) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::FunctionTemplate> desc = v8::FunctionTemplate::New(isolate);
desc->InstanceTemplate()->SetNamedPropertyHandler(PrePropertyHandlerGet,
0,
PrePropertyHandlerQuery);
LocalContext env(NULL, desc->InstanceTemplate());
CompileRun("var pre = 'Object: pre'; var on = 'Object: on';");
v8::Handle<Value> result_pre = CompileRun("pre");
CHECK_EQ(v8_str("PrePropertyHandler: pre"), result_pre);
v8::Handle<Value> result_on = CompileRun("on");
CHECK_EQ(v8_str("Object: on"), result_on);
v8::Handle<Value> result_post = CompileRun("post");
CHECK(result_post.IsEmpty());
}
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();