blob: 73dc19aa66a359b629b191781b562134704e1353 [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 <memory>
#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/code-stubs.h"
#include "src/compilation-cache.h"
#include "src/debug/debug.h"
#include "src/execution.h"
#include "src/futex-emulation.h"
#include "src/heap/incremental-marking.h"
#include "src/heap/local-allocator.h"
#include "src/lookup.h"
#include "src/objects-inl.h"
#include "src/parsing/preparse-data.h"
#include "src/profiler/cpu-profiler.h"
#include "src/unicode-inl.h"
#include "src/utils.h"
#include "src/vm-state.h"
#include "test/cctest/heap/heap-tester.h"
#include "test/cctest/heap/heap-utils.h"
static const bool kLogThreading = false;
using ::v8::Array;
using ::v8::Boolean;
using ::v8::BooleanObject;
using ::v8::Context;
using ::v8::Extension;
using ::v8::Function;
using ::v8::FunctionTemplate;
using ::v8::HandleScope;
using ::v8::Local;
using ::v8::Maybe;
using ::v8::Message;
using ::v8::MessageCallback;
using ::v8::Module;
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::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_str("my_profile1");
v8::CpuProfiler* cpu_profiler = v8::CpuProfiler::New(env->GetIsolate());
cpu_profiler->StartProfiling(profile_name);
(*test)();
reinterpret_cast<i::CpuProfiler*>(cpu_profiler)->DeleteAllProfiles();
cpu_profiler->Dispose();
}
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.GetIsolate()->GetCurrentContext(),
args.Holder())
.FromJust());
CHECK(signature_expected_receiver->Equals(
args.GetIsolate()->GetCurrentContext(),
args.This())
.FromJust());
v8::Local<v8::Array> result =
v8::Array::New(args.GetIsolate(), args.Length());
for (int i = 0; i < args.Length(); i++) {
CHECK(result->Set(args.GetIsolate()->GetCurrentContext(),
v8::Integer::New(args.GetIsolate(), i), args[i])
.FromJust());
}
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());
}
// Tests that Smi::kZero is set up properly.
UNINITIALIZED_TEST(SmiZero) { CHECK_EQ(i::Smi::kZero, i::Smi::kZero); }
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::Local<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, v8_run_int32value(script));
local_env->Exit();
}
THREADED_TEST(IsolateOfContext) {
v8::HandleScope scope(CcTest::isolate());
v8::Local<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 TestSignatureLooped(const char* operation, Local<Value> receiver,
v8::Isolate* isolate) {
i::ScopedVector<char> source(200);
i::SNPrintF(source,
"for (var i = 0; i < 10; i++) {"
" %s"
"}",
operation);
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(isolate->GetCurrentContext(),
try_catch.Exception()
->ToString(isolate->GetCurrentContext())
.ToLocalChecked())
.FromJust());
}
}
static void TestSignatureOptimized(const char* operation, Local<Value> receiver,
v8::Isolate* isolate) {
i::ScopedVector<char> source(200);
i::SNPrintF(source,
"function test() {"
" %s"
"}"
"try { test() } catch(e) {}"
"try { test() } catch(e) {}"
"%%OptimizeFunctionOnNextCall(test);"
"test()",
operation);
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(3, signature_callback_count);
} else {
CHECK(v8_str("TypeError: Illegal invocation")
->Equals(isolate->GetCurrentContext(),
try_catch.Exception()
->ToString(isolate->GetCurrentContext())
.ToLocalChecked())
.FromJust());
}
}
static void TestSignature(const char* operation, Local<Value> receiver,
v8::Isolate* isolate) {
TestSignatureLooped(operation, receiver, isolate);
TestSignatureOptimized(operation, receiver, isolate);
}
THREADED_TEST(ReceiverSignature) {
i::FLAG_allow_natives_syntax = true;
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
// Setup templates.
v8::Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(isolate);
v8::Local<v8::Signature> sig = v8::Signature::New(isolate, fun);
v8::Local<v8::FunctionTemplate> callback_sig = v8::FunctionTemplate::New(
isolate, IncrementingSignatureCallback, Local<Value>(), sig);
v8::Local<v8::FunctionTemplate> callback =
v8::FunctionTemplate::New(isolate, IncrementingSignatureCallback);
v8::Local<v8::FunctionTemplate> sub_fun = v8::FunctionTemplate::New(isolate);
sub_fun->Inherit(fun);
v8::Local<v8::FunctionTemplate> direct_sub_fun =
v8::FunctionTemplate::New(isolate);
direct_sub_fun->Inherit(fun);
v8::Local<v8::FunctionTemplate> unrel_fun =
v8::FunctionTemplate::New(isolate);
// Install properties.
v8::Local<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(env.local()).ToLocalChecked();
Local<Value> sub_fun_instance =
sub_fun->InstanceTemplate()->NewInstance(env.local()).ToLocalChecked();
// Instance template with properties.
v8::Local<v8::ObjectTemplate> direct_instance_templ =
direct_sub_fun->InstanceTemplate();
direct_instance_templ->Set(v8_str("prop_sig"), callback_sig);
direct_instance_templ->Set(v8_str("prop"), callback);
direct_instance_templ->SetAccessorProperty(v8_str("accessor_sig"),
callback_sig, callback_sig);
direct_instance_templ->SetAccessorProperty(v8_str("accessor"), callback,
callback);
Local<Value> direct_instance =
direct_instance_templ->NewInstance(env.local()).ToLocalChecked();
// Setup global variables.
CHECK(env->Global()
->Set(env.local(), v8_str("Fun"),
fun->GetFunction(env.local()).ToLocalChecked())
.FromJust());
CHECK(env->Global()
->Set(env.local(), v8_str("UnrelFun"),
unrel_fun->GetFunction(env.local()).ToLocalChecked())
.FromJust());
CHECK(env->Global()
->Set(env.local(), v8_str("fun_instance"), fun_instance)
.FromJust());
CHECK(env->Global()
->Set(env.local(), v8_str("sub_fun_instance"), sub_fun_instance)
.FromJust());
CHECK(env->Global()
->Set(env.local(), v8_str("direct_instance"), direct_instance)
.FromJust());
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 plain = {};"
"copy_props(plain);"
"var unrelated = new UnrelFun();"
"copy_props(unrelated);"
"var inherited = { __proto__: fun_instance };"
"var inherited_direct = { __proto__: direct_instance };");
// Test with and without ICs
const char* test_objects[] = {
"fun_instance", "sub_fun_instance", "direct_instance", "plain",
"unrelated", "inherited", "inherited_direct"};
unsigned bad_signature_start_offset = 3;
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::Local<v8::Primitive> undef = v8::Undefined(isolate);
Local<String> undef_str = undef->ToString(env.local()).ToLocalChecked();
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(env.local(), v8_str("foo")).ToLocalChecked();
CHECK(foo_before->IsUndefined());
Local<String> bar_str = v8_str("bar");
CHECK(obj->Set(env.local(), v8_str("foo"), bar_str).FromJust());
Local<Value> foo_after =
obj->Get(env.local(), v8_str("foo")).ToLocalChecked();
CHECK(!foo_after->IsUndefined());
CHECK(foo_after->IsString());
CHECK(bar_str->Equals(env.local(), foo_after).FromJust());
CHECK(obj->Set(env.local(), v8_str("foo"), bar_str).ToChecked());
bool result;
CHECK(obj->Set(env.local(), v8_str("foo"), bar_str).To(&result));
CHECK(result);
}
THREADED_TEST(AccessElement) {
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
Local<v8::Object> obj = v8::Object::New(env->GetIsolate());
Local<Value> before = obj->Get(env.local(), 1).ToLocalChecked();
CHECK(before->IsUndefined());
Local<String> bar_str = v8_str("bar");
CHECK(obj->Set(env.local(), 1, bar_str).FromJust());
Local<Value> after = obj->Get(env.local(), 1).ToLocalChecked();
CHECK(!after->IsUndefined());
CHECK(after->IsString());
CHECK(bar_str->Equals(env.local(), after).FromJust());
Local<v8::Array> value = CompileRun("[\"a\", \"b\"]").As<v8::Array>();
CHECK(v8_str("a")
->Equals(env.local(), value->Get(env.local(), 0).ToLocalChecked())
.FromJust());
CHECK(v8_str("b")
->Equals(env.local(), value->Get(env.local(), 1).ToLocalChecked())
.FromJust());
}
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, v8_run_int32value(script));
}
class TestResource: public String::ExternalStringResource {
public:
explicit TestResource(uint16_t* data, int* counter = nullptr,
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_ != nullptr) ++*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 = nullptr,
size_t offset = 0)
: orig_data_(data),
data_(data + offset),
length_(strlen(data) - offset),
counter_(counter) {}
~TestOneByteResource() {
i::DeleteArray(orig_data_);
if (counter_ != nullptr) ++*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::NewExternalTwoByte(env->GetIsolate(), resource)
.ToLocalChecked();
Local<Script> script = v8_compile(source);
Local<Value> value = script->Run(env.local()).ToLocalChecked();
CHECK(value->IsNumber());
CHECK_EQ(7, value->Int32Value(env.local()).FromJust());
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::CollectAllGarbage();
CHECK_EQ(0, dispose_count);
}
CcTest::i_isolate()->compilation_cache()->Clear();
CcTest::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::NewExternalOneByte(env->GetIsolate(), resource)
.ToLocalChecked();
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(env.local()).ToLocalChecked();
CHECK(value->IsNumber());
CHECK_EQ(7, value->Int32Value(env.local()).FromJust());
CcTest::CollectAllGarbage();
CHECK_EQ(0, dispose_count);
}
CcTest::i_isolate()->compilation_cache()->Clear();
CcTest::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,
v8::NewStringType::kNormal)
.ToLocalChecked();
// Trigger GCs so that the newly allocated string moves to old gen.
CcTest::CollectGarbage(i::NEW_SPACE); // in survivor space now
CcTest::CollectGarbage(i::NEW_SPACE); // in old gen now
CHECK(!source->IsExternal());
CHECK(!source->IsExternalOneByte());
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(env.local()).ToLocalChecked();
CHECK(value->IsNumber());
CHECK_EQ(7, value->Int32Value(env.local()).FromJust());
CcTest::CollectAllGarbage();
CHECK_EQ(0, dispose_count);
}
CcTest::i_isolate()->compilation_cache()->Clear();
CcTest::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::CollectGarbage(i::NEW_SPACE); // in survivor space now
CcTest::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(env.local()).ToLocalChecked();
CHECK(value->IsNumber());
CHECK_EQ(7, value->Int32Value(env.local()).FromJust());
CcTest::CollectAllGarbage();
CHECK_EQ(0, dispose_count);
}
CcTest::i_isolate()->compilation_cache()->Clear();
CcTest::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::CollectGarbage(i::NEW_SPACE);
CcTest::CollectGarbage(i::NEW_SPACE);
uint16_t* two_byte_string = AsciiToTwoByteString("s1");
Local<String> local_string =
String::NewFromTwoByte(env->GetIsolate(), two_byte_string,
v8::NewStringType::kNormal)
.ToLocalChecked();
i::DeleteArray(two_byte_string);
// We should refuse to externalize new space strings.
CHECK(!local_string->CanMakeExternal());
// Trigger GCs so that the newly allocated string moves to old gen.
CcTest::CollectGarbage(i::NEW_SPACE); // in survivor space now
CcTest::CollectGarbage(i::NEW_SPACE); // in old gen now
// Old space strings should be accepted.
CHECK(local_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::CollectGarbage(i::NEW_SPACE);
CcTest::CollectGarbage(i::NEW_SPACE);
Local<String> local_string = v8_str("s1");
// We should refuse to externalize new space strings.
CHECK(!local_string->CanMakeExternal());
// Trigger GCs so that the newly allocated string moves to old gen.
CcTest::CollectGarbage(i::NEW_SPACE); // in survivor space now
CcTest::CollectGarbage(i::NEW_SPACE); // in old gen now
// Old space strings should be accepted.
CHECK(local_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.
i::heap::SimulateFullSpace(CcTest::heap()->old_space());
CcTest::CollectGarbage(i::NEW_SPACE); // in survivor space now
CcTest::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), nullptr, 1));
CHECK(success);
const char* c_slice = "_bcdefghijklmnopqrstuvwxyz";
success = slice->MakeExternal(
new TestOneByteResource(i::StrDup(c_slice), nullptr, 1));
CHECK(success);
// Trigger GCs and force evacuation.
CcTest::CollectAllGarbage();
CcTest::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::NewExternalTwoByte(CcTest::isolate(),
new TestResource(two_byte_string))
.ToLocalChecked();
i::Handle<i::String> istring = v8::Utils::OpenHandle(*string);
// Trigger GCs so that the newly allocated string moves to old gen.
CcTest::CollectGarbage(i::NEW_SPACE); // in survivor space now
CcTest::CollectGarbage(i::NEW_SPACE); // in old gen now
i::Handle<i::String> isymbol =
factory->InternalizeString(istring);
CHECK(isymbol->IsInternalizedString());
}
CcTest::CollectAllGarbage();
CcTest::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::NewExternalOneByte(
CcTest::isolate(),
new TestOneByteResource(i::StrDup(one_byte_string)))
.ToLocalChecked();
i::Handle<i::String> istring = v8::Utils::OpenHandle(*string);
// Trigger GCs so that the newly allocated string moves to old gen.
CcTest::CollectGarbage(i::NEW_SPACE); // in survivor space now
CcTest::CollectGarbage(i::NEW_SPACE); // in old gen now
i::Handle<i::String> isymbol =
factory->InternalizeString(istring);
CHECK(isymbol->IsInternalizedString());
}
CcTest::CollectAllGarbage();
CcTest::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::MaybeLocal<v8::String> maybe_str =
v8::String::NewExternalOneByte(isolate, &r);
CHECK(maybe_str.IsEmpty());
CHECK(!try_catch.HasCaught());
}
{
v8::HandleScope scope(isolate);
v8::TryCatch try_catch(isolate);
RandomLengthResource r(1 << 30);
v8::MaybeLocal<v8::String> maybe_str =
v8::String::NewExternalTwoByte(isolate, &r);
CHECK(maybe_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::NewExternalTwoByte(
CcTest::isolate(),
new TestResource(two_byte_string, &dispose_count))
.ToLocalChecked();
i::Handle<i::String> istring = v8::Utils::OpenHandle(*string);
CcTest::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::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::NewExternalOneByte(
CcTest::isolate(),
new TestOneByteResource(i::StrDup(one_byte_string), &dispose_count))
.ToLocalChecked();
i::Handle<i::String> istring = v8::Utils::OpenHandle(*string);
CcTest::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::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::NewExternalOneByte(env->GetIsolate(), &res_stack)
.ToLocalChecked();
Local<Script> script = v8_compile(source);
Local<Value> value = script->Run(env.local()).ToLocalChecked();
CHECK(value->IsNumber());
CHECK_EQ(7, value->Int32Value(env.local()).FromJust());
CcTest::CollectAllAvailableGarbage();
CHECK_EQ(0, TestOneByteResourceWithDisposeControl::dispose_count);
}
CcTest::i_isolate()->compilation_cache()->Clear();
CcTest::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::NewExternalOneByte(env->GetIsolate(), res_heap)
.ToLocalChecked();
Local<Script> script = v8_compile(source);
Local<Value> value = script->Run(env.local()).ToLocalChecked();
CHECK(value->IsNumber());
CHECK_EQ(7, value->Int32Value(env.local()).FromJust());
CcTest::CollectAllAvailableGarbage();
CHECK_EQ(0, TestOneByteResourceWithDisposeControl::dispose_count);
}
CcTest::i_isolate()->compilation_cache()->Clear();
CcTest::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,
v8::NewStringType::kNormal)
.ToLocalChecked();
i::DeleteArray(two_byte_source);
Local<String> source = String::Concat(left, right);
right = String::NewExternalOneByte(
env->GetIsolate(),
new TestOneByteResource(i::StrDup(one_byte_extern_1)))
.ToLocalChecked();
source = String::Concat(source, right);
right = String::NewExternalTwoByte(
env->GetIsolate(),
new TestResource(AsciiToTwoByteString(two_byte_extern_1)))
.ToLocalChecked();
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,
v8::NewStringType::kNormal)
.ToLocalChecked();
i::DeleteArray(two_byte_source);
source = String::Concat(source, right);
right = String::NewExternalTwoByte(
env->GetIsolate(),
new TestResource(AsciiToTwoByteString(two_byte_extern_2)))
.ToLocalChecked();
source = String::Concat(source, right);
Local<Script> script = v8_compile(source);
Local<Value> value = script->Run(env.local()).ToLocalChecked();
CHECK(value->IsNumber());
CHECK_EQ(68, value->Int32Value(env.local()).FromJust());
}
CcTest::i_isolate()->compilation_cache()->Clear();
CcTest::CollectAllGarbage();
CcTest::CollectAllGarbage();
}
THREADED_TEST(GlobalProperties) {
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
v8::Local<v8::Object> global = env->Global();
CHECK(global->Set(env.local(), v8_str("pi"), v8_num(3.1415926)).FromJust());
Local<Value> pi = global->Get(env.local(), v8_str("pi")).ToLocalChecked();
CHECK_EQ(3.1415926, pi->NumberValue(env.local()).FromJust());
}
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));
CHECK(
info.This()
->Set(info.GetIsolate()->GetCurrentContext(), v8_str("x"), v8_num(1))
.FromJust());
CHECK(
info.This()
->Set(info.GetIsolate()->GetCurrentContext(), v8_str("y"), v8_num(2))
.FromJust());
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.local()).ToLocalChecked();
CHECK(env->Global()->Set(env.local(), v8_str("obj"), fun).FromJust());
Local<Script> script = v8_compile("obj()");
for (int i = 0; i < 30; i++) {
CHECK_EQ(102, v8_run_int32value(script));
}
}
// 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.local()).ToLocalChecked();
CHECK(env->Global()->Set(env.local(), v8_str("obj"), fun).FromJust());
Local<Script> script = v8_compile("obj()");
for (int i = 0; i < 30; i++) {
CHECK_EQ(102, v8_run_int32value(script));
}
}
}
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.local()).ToLocalChecked();
CHECK(env->Global()->Set(env.local(), v8_str("obj"), fun).FromJust());
Local<Value> result =
v8_compile("(new obj()).toString()")->Run(env.local()).ToLocalChecked();
CHECK(v8_str("[object funky]")->Equals(env.local(), result).FromJust());
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, v8_run_int32value(script));
}
script = v8_compile("obj_instance.m");
for (int i = 0; i < 30; i++) {
CHECK_EQ(239, v8_run_int32value(script));
}
}
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::Local<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.local()).ToLocalChecked();
CHECK((*env)
->Global()
->Set(env.local(), v8_str("callback_object"), object)
.FromJust());
v8::Local<v8::Script> script;
script = v8_compile("callback_object.callback(17)");
for (int i = 0; i < 30; i++) {
CHECK_EQ(51424, v8_run_int32value(script));
}
script = v8_compile("callback_object.callback(17, 24)");
for (int i = 0; i < 30; i++) {
CHECK_EQ(51425, v8_run_int32value(script));
}
}
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::Local<v8::Object> object;
if (!fast_return_value_object_is_empty) {
object = Object::New(info.GetIsolate());
}
info.GetReturnValue().Set(object);
}
template <typename T>
Local<Value> TestFastReturnValues() {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::EscapableHandleScope scope(isolate);
v8::Local<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.local()).ToLocalChecked();
CHECK((*env)
->Global()
->Set(env.local(), v8_str("callback_object"), object)
.FromJust());
return scope.Escape(CompileRun("callback_object.callback()"));
}
THREADED_PROFILED_TEST(FastReturnValues) {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
v8::Local<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_EQ(fast_return_value_int32,
value->Int32Value(env.local()).FromJust());
// check uint32_t
fast_return_value_uint32 = static_cast<uint32_t>(int_value);
value = TestFastReturnValues<uint32_t>();
CHECK(value->IsUint32());
CHECK_EQ(fast_return_value_uint32,
value->Uint32Value(env.local()).FromJust());
}
}
// check double
value = TestFastReturnValues<double>();
CHECK(value->IsNumber());
CHECK_EQ(kFastReturnValueDouble,
value->ToNumber(env.local()).ToLocalChecked()->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(env.local()).ToLocalChecked()->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, Local<v8::Value>(),
Local<v8::Signature>(), 23);
Local<Function> fun = fun_templ->GetFunction(env.local()).ToLocalChecked();
CHECK(env->Global()->Set(env.local(), v8_str("obj"), fun).FromJust());
Local<Script> script = v8_compile("obj.length");
CHECK_EQ(23, v8_run_int32value(script));
}
{
Local<v8::FunctionTemplate> fun_templ =
v8::FunctionTemplate::New(isolate, handle_callback);
fun_templ->SetLength(22);
Local<Function> fun = fun_templ->GetFunction(env.local()).ToLocalChecked();
CHECK(env->Global()->Set(env.local(), v8_str("obj"), fun).FromJust());
Local<Script> script = v8_compile("obj.length");
CHECK_EQ(22, v8_run_int32value(script));
}
{
// 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.local()).ToLocalChecked();
CHECK(env->Global()->Set(env.local(), v8_str("obj"), fun).FromJust());
Local<Script> script = v8_compile("obj.length");
CHECK_EQ(0, v8_run_int32value(script));
}
}
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::Local<v8::Value> data = v8::External::New(isolate, expected_ptr);
v8::Local<v8::Object> obj = v8::Object::New(isolate);
CHECK(obj->Set(env.local(), v8_str("func"),
v8::FunctionTemplate::New(isolate, callback, data)
->GetFunction(env.local())
.ToLocalChecked())
.FromJust());
CHECK(env->Global()->Set(env.local(), v8_str("obj"), obj).FromJust());
CHECK(CompileRun("function foo() {\n"
" for (var i = 0; i < 13; i++) obj.func();\n"
"}\n"
"foo(), true")
->BooleanValue(env.local())
.FromJust());
}
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(env.local()).ToLocalChecked();
Local<v8::Function> derived_function =
derived->GetFunction(env.local()).ToLocalChecked();
Local<v8::Function> other_function =
other->GetFunction(env.local()).ToLocalChecked();
Local<v8::Object> base_instance =
base_function->NewInstance(env.local()).ToLocalChecked();
Local<v8::Object> derived_instance =
derived_function->NewInstance(env.local()).ToLocalChecked();
Local<v8::Object> derived_instance2 =
derived_function->NewInstance(env.local()).ToLocalChecked();
Local<v8::Object> other_instance =
other_function->NewInstance(env.local()).ToLocalChecked();
CHECK(
derived_instance2->Set(env.local(), v8_str("__proto__"), derived_instance)
.FromJust());
CHECK(other_instance->Set(env.local(), v8_str("__proto__"), derived_instance2)
.FromJust());
// base_instance is only an instance of base.
CHECK(base_instance->Equals(env.local(),
base_instance->FindInstanceInPrototypeChain(base))
.FromJust());
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(env.local(),
derived_instance->FindInstanceInPrototypeChain(
base))
.FromJust());
CHECK(derived_instance->Equals(env.local(),
derived_instance->FindInstanceInPrototypeChain(
derived))
.FromJust());
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(
env.local(),
other_instance->FindInstanceInPrototypeChain(base))
.FromJust());
CHECK(derived_instance2->Equals(env.local(),
other_instance->FindInstanceInPrototypeChain(
derived))
.FromJust());
CHECK(other_instance->Equals(
env.local(),
other_instance->FindInstanceInPrototypeChain(other))
.FromJust());
}
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_GT(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::Local<Value> syntax_error = CompileRun(
"var out = 0; try { eval(\"#\"); } catch(x) { out = x; } out; ");
CHECK(syntax_error->IsNativeError());
v8::Local<Value> not_error = CompileRun("{a:42}");
CHECK(!not_error->IsNativeError());
v8::Local<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::Local<Value> gen = CompileRun("gen");
v8::Local<Value> genObj = CompileRun("gen()");
v8::Local<Value> object = CompileRun("{a:42}");
v8::Local<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(IsAsyncFunction) {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
CompileRun("async function foo() {}");
v8::Local<Value> foo = CompileRun("foo");
CHECK(foo->IsAsyncFunction());
CHECK(foo->IsFunction());
CHECK(!foo->IsGeneratorFunction());
CHECK(!foo->IsGeneratorObject());
CompileRun("function bar() {}");
v8::Local<Value> bar = CompileRun("bar");
CHECK(!bar->IsAsyncFunction());
CHECK(bar->IsFunction());
}
THREADED_TEST(ArgumentsObject) {
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
v8::Local<Value> arguments_object =
CompileRun("var out = 0; (function(){ out = arguments; })(1,2,3); out;");
CHECK(arguments_object->IsArgumentsObject());
v8::Local<Value> array = CompileRun("[1,2,3]");
CHECK(!array->IsArgumentsObject());
v8::Local<Value> object = CompileRun("{a:42}");
CHECK(!object->IsArgumentsObject());
}
THREADED_TEST(IsMapOrSet) {
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
v8::Local<Value> map = CompileRun("new Map()");
v8::Local<Value> set = CompileRun("new Set()");
v8::Local<Value> weak_map = CompileRun("new WeakMap()");
v8::Local<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::Local<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::Local<Value> boxed_string = CompileRun("new String(\"test\")");
CHECK(boxed_string->IsStringObject());
v8::Local<Value> unboxed_string = CompileRun("\"test\"");
CHECK(!unboxed_string->IsStringObject());
v8::Local<Value> boxed_not_string = CompileRun("new Number(42)");
CHECK(!boxed_not_string->IsStringObject());
v8::Local<Value> not_object = CompileRun("0");
CHECK(!not_object->IsStringObject());
v8::Local<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::Local<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::Local<Value> boxed_string = CompileRun("new String(\"test\")");
CHECK(boxed_string->IsStringObject());
v8::Local<v8::Object> str_obj = boxed_string.As<v8::Object>();
CHECK(!str_obj->Delete(context.local(), 2).FromJust());
CHECK(!str_obj->Delete(context.local(), v8_num(2)).FromJust());
}
THREADED_TEST(NumberObject) {
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
v8::Local<Value> boxed_number = CompileRun("new Number(42)");
CHECK(boxed_number->IsNumberObject());
v8::Local<Value> unboxed_number = CompileRun("42");
CHECK(!unboxed_number->IsNumberObject());
v8::Local<Value> boxed_not_number = CompileRun("new Boolean(false)");
CHECK(!boxed_not_number->IsNumberObject());
v8::Local<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::Local<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::Local<Value> boxed_boolean = CompileRun("new Boolean(true)");
CHECK(boxed_boolean->IsBooleanObject());
v8::Local<Value> unboxed_boolean = CompileRun("true");
CHECK(!unboxed_boolean->IsBooleanObject());
v8::Local<Value> boxed_not_boolean = CompileRun("new Number(42)");
CHECK(!boxed_not_boolean->IsBooleanObject());
v8::Local<v8::BooleanObject> as_boxed = boxed_boolean.As<v8::BooleanObject>();
CHECK(!as_boxed.IsEmpty());
bool the_boolean = as_boxed->ValueOf();
CHECK(the_boolean);
v8::Local<v8::Value> boxed_true =
v8::BooleanObject::New(env->GetIsolate(), true);
v8::Local<v8::Value> boxed_false =
v8::BooleanObject::New(env->GetIsolate(), false);
CHECK(boxed_true->IsBooleanObject());
CHECK(boxed_false->IsBooleanObject());
as_boxed = boxed_true.As<v8::BooleanObject>();
CHECK(as_boxed->ValueOf());
as_boxed = boxed_false.As<v8::BooleanObject>();
CHECK(!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(env.local()).FromJust());
CHECK(!primitive_false->IsTrue());
CHECK(primitive_false->IsFalse());
Local<Value> false_value = BooleanObject::New(env->GetIsolate(), false);
CHECK(!false_value->IsBoolean());
CHECK(false_value->IsBooleanObject());
CHECK(false_value->BooleanValue(env.local()).FromJust());
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());
CHECK(false_boolean_object->BooleanValue(env.local()).FromJust());
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(env.local()).FromJust());
CHECK(primitive_true->IsTrue());
CHECK(!primitive_true->IsFalse());
Local<Value> true_value = BooleanObject::New(env->GetIsolate(), true);
CHECK(!true_value->IsBoolean());
CHECK(true_value->IsBooleanObject());
CHECK(true_value->BooleanValue(env.local()).FromJust());
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());
CHECK(true_boolean_object->BooleanValue(env.local()).FromJust());
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(env.local()).FromJust());
}
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(env.local()).FromJust());
v8::Local<v8::Boolean> t = v8::True(isolate);
CHECK_EQ(1.0, t->NumberValue(env.local()).FromJust());
v8::Local<v8::Boolean> f = v8::False(isolate);
CHECK_EQ(0.0, f->NumberValue(env.local()).FromJust());
}
THREADED_TEST(Date) {
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
double PI = 3.1415926;
Local<Value> date = v8::Date::New(env.local(), PI).ToLocalChecked();
CHECK_EQ(3.0, date->NumberValue(env.local()).FromJust());
CHECK(date.As<v8::Date>()
->Set(env.local(), v8_str("property"),
v8::Integer::New(env->GetIsolate(), 42))
.FromJust());
CHECK_EQ(42, date.As<v8::Date>()
->Get(env.local(), v8_str("property"))
.ToLocalChecked()
->Int32Value(env.local())
.FromJust());
}
THREADED_TEST(Boolean) {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
v8::Local<v8::Boolean> t = v8::True(isolate);
CHECK(t->Value());
v8::Local<v8::Boolean> f = v8::False(isolate);
CHECK(!f->Value());
v8::Local<v8::Primitive> u = v8::Undefined(isolate);
CHECK(!u->BooleanValue(env.local()).FromJust());
v8::Local<v8::Primitive> n = v8::Null(isolate);
CHECK(!n->BooleanValue(env.local()).FromJust());
v8::Local<String> str1 = v8_str("");
CHECK(!str1->BooleanValue(env.local()).FromJust());
v8::Local<String> str2 = v8_str("x");
CHECK(str2->BooleanValue(env.local()).FromJust());
CHECK(!v8::Number::New(isolate, 0)->BooleanValue(env.local()).FromJust());
CHECK(v8::Number::New(isolate, -1)->BooleanValue(env.local()).FromJust());
CHECK(v8::Number::New(isolate, 1)->BooleanValue(env.local()).FromJust());
CHECK(v8::Number::New(isolate, 42)->BooleanValue(env.local()).FromJust());
CHECK(!v8_compile("NaN")
->Run(env.local())
.ToLocalChecked()
->BooleanValue(env.local())
.FromJust());
}
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::Local<v8::FunctionTemplate> func_templ =
v8::FunctionTemplate::New(isolate);
func_templ->PrototypeTemplate()->Set(
isolate, "dummy", v8::FunctionTemplate::New(isolate, DummyCallHandler));
v8::Local<ObjectTemplate> templ = func_templ->InstanceTemplate();
templ->Set(isolate, "x", v8_num(200));
templ->SetAccessor(v8_str("m"), GetM);
LocalContext env(0, templ);
v8::Local<Script> script(v8_compile("dummy()"));
v8::Local<Value> result(script->Run(env.local()).ToLocalChecked());
CHECK_EQ(13.4, result->NumberValue(env.local()).FromJust());
CHECK_EQ(200, v8_run_int32value(v8_compile("x")));
CHECK_EQ(876, v8_run_int32value(v8_compile("m")));
}
THREADED_TEST(ObjectTemplate) {
LocalContext env;
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
Local<v8::FunctionTemplate> acc =
v8::FunctionTemplate::New(isolate, Returns42);
CHECK(env->Global()
->Set(env.local(), v8_str("acc"),
acc->GetFunction(env.local()).ToLocalChecked())
.FromJust());
Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(isolate);
v8::Local<v8::String> class_name = v8_str("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));
templ1->Set(v8_str("foo"), acc);
Local<v8::Object> instance1 =
templ1->NewInstance(env.local()).ToLocalChecked();
CHECK(class_name->StrictEquals(instance1->GetConstructorName()));
CHECK(env->Global()->Set(env.local(), v8_str("p"), instance1).FromJust());
CHECK(CompileRun("(p.x == 10)")->BooleanValue(env.local()).FromJust());
CHECK(CompileRun("(p.y == 13)")->BooleanValue(env.local()).FromJust());
CHECK(CompileRun("(p.foo() == 42)")->BooleanValue(env.local()).FromJust());
CHECK(CompileRun("(p.foo == acc)")->BooleanValue(env.local()).FromJust());
// Ensure that foo become a data field.
CompileRun("p.foo = function() {}");
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);
templ2->Set(v8_str("bar"), acc);
templ2->SetAccessorProperty(v8_str("acc"), acc);
Local<v8::Object> instance2 =
templ2->NewInstance(env.local()).ToLocalChecked();
CHECK(env->Global()->Set(env.local(), v8_str("q"), instance2).FromJust());
CHECK(CompileRun("(q.nirk == 123)")->BooleanValue(env.local()).FromJust());
CHECK(CompileRun("(q.a == 12)")->BooleanValue(env.local()).FromJust());
CHECK(CompileRun("(q.b.x == 10)")->BooleanValue(env.local()).FromJust());
CHECK(CompileRun("(q.b.y == 13)")->BooleanValue(env.local()).FromJust());
CHECK(CompileRun("(q.b.foo() == 42)")->BooleanValue(env.local()).FromJust());
CHECK(CompileRun("(q.b.foo === acc)")->BooleanValue(env.local()).FromJust());
CHECK(CompileRun("(q.b !== p)")->BooleanValue(env.local()).FromJust());
CHECK(CompileRun("(q.acc == 42)")->BooleanValue(env.local()).FromJust());
CHECK(CompileRun("(q.bar() == 42)")->BooleanValue(env.local()).FromJust());
CHECK(CompileRun("(q.bar == acc)")->BooleanValue(env.local()).FromJust());
instance2 = templ2->NewInstance(env.local()).ToLocalChecked();
CHECK(env->Global()->Set(env.local(), v8_str("q2"), instance2).FromJust());
CHECK(CompileRun("(q2.nirk == 123)")->BooleanValue(env.local()).FromJust());
CHECK(CompileRun("(q2.a == 12)")->BooleanValue(env.local()).FromJust());
CHECK(CompileRun("(q2.b.x == 10)")->BooleanValue(env.local()).FromJust());
CHECK(CompileRun("(q2.b.y == 13)")->BooleanValue(env.local()).FromJust());
CHECK(CompileRun("(q2.b.foo() == 42)")->BooleanValue(env.local()).FromJust());
CHECK(CompileRun("(q2.b.foo === acc)")->BooleanValue(env.local()).FromJust());
CHECK(CompileRun("(q2.acc == 42)")->BooleanValue(env.local()).FromJust());
CHECK(CompileRun("(q2.bar() == 42)")->BooleanValue(env.local()).FromJust());
CHECK(CompileRun("(q2.bar === acc)")->BooleanValue(env.local()).FromJust());
CHECK(CompileRun("(q.b !== q2.b)")->BooleanValue(env.local()).FromJust());
CHECK(CompileRun("q.b.x = 17; (q2.b.x == 10)")
->BooleanValue(env.local())
.FromJust());
CHECK(CompileRun("desc1 = Object.getOwnPropertyDescriptor(q, 'acc');"
"(desc1.get === acc)")
->BooleanValue(env.local())
.FromJust());
CHECK(CompileRun("desc2 = Object.getOwnPropertyDescriptor(q2, 'acc');"
"(desc2.get === acc)")
->BooleanValue(env.local())
.FromJust());
}
THREADED_TEST(IntegerValue) {
LocalContext env;
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
CHECK_EQ(0, CompileRun("undefined")->IntegerValue(env.local()).FromJust());
}
static void GetNirk(Local<String> name,
const v8::PropertyCallbackInfo<v8::Value>& info) {
ApiTestFuzzer::Fuzz();
info.GetReturnValue().Set(v8_num(900));
}
static void GetRino(Local<String> name,
const v8::PropertyCallbackInfo<v8::Value>& info) {
ApiTestFuzzer::Fuzz();
info.GetReturnValue().Set(v8_num(560));
}
enum ObjectInstantiationMode {
// Create object using ObjectTemplate::NewInstance.
ObjectTemplate_NewInstance,
// Create object using FunctionTemplate::NewInstance on constructor.
Constructor_GetFunction_NewInstance,
// Create object using new operator on constructor.
Constructor_GetFunction_New
};
// Test object instance creation using a function template with an instance
// template inherited from another function template with accessors and data
// properties in prototype template.
static void TestObjectTemplateInheritedWithPrototype(
ObjectInstantiationMode mode) {
LocalContext env;
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
Local<v8::FunctionTemplate> fun_A = v8::FunctionTemplate::New(isolate);
fun_A->SetClassName(v8_str("A"));
v8::Local<v8::ObjectTemplate> prototype_templ = fun_A->PrototypeTemplate();
prototype_templ->Set(isolate, "a", v8_num(113));
prototype_templ->SetNativeDataProperty(v8_str("nirk"), GetNirk);
prototype_templ->Set(isolate, "b", v8_num(153));
Local<v8::FunctionTemplate> fun_B = v8::FunctionTemplate::New(isolate);
v8::Local<v8::String> class_name = v8_str("B");
fun_B->SetClassName(class_name);
fun_B->Inherit(fun_A);
prototype_templ = fun_B->PrototypeTemplate();
prototype_templ->Set(isolate, "c", v8_num(713));
prototype_templ->SetNativeDataProperty(v8_str("rino"), GetRino);
prototype_templ->Set(isolate, "d", v8_num(753));
Local<ObjectTemplate> templ = fun_B->InstanceTemplate();
templ->Set(isolate, "x", v8_num(10));
templ->Set(isolate, "y", v8_num(13));
// Perform several iterations to trigger creation from cached boilerplate.
for (int i = 0; i < 3; i++) {
Local<v8::Object> instance;
switch (mode) {
case ObjectTemplate_NewInstance:
instance = templ->NewInstance(env.local()).ToLocalChecked();
break;
case Constructor_GetFunction_NewInstance: {
Local<v8::Function> function_B =
fun_B->GetFunction(env.local()).ToLocalChecked();
instance = function_B->NewInstance(env.local()).ToLocalChecked();
break;
}
case Constructor_GetFunction_New: {
Local<v8::Function> function_B =
fun_B->GetFunction(env.local()).ToLocalChecked();
if (i == 0) {
CHECK(env->Global()
->Set(env.local(), class_name, function_B)
.FromJust());
}
instance =
CompileRun("new B()")->ToObject(env.local()).ToLocalChecked();
break;
}
default:
UNREACHABLE();
}
CHECK(class_name->StrictEquals(instance->GetConstructorName()));
CHECK(env->Global()->Set(env.local(), v8_str("o"), instance).FromJust());
CHECK_EQ(10, CompileRun("o.x")->IntegerValue(env.local()).FromJust());
CHECK_EQ(13, CompileRun("o.y")->IntegerValue(env.local()).FromJust());
CHECK_EQ(113, CompileRun("o.a")->IntegerValue(env.local()).FromJust());
CHECK_EQ(900, CompileRun("o.nirk")->IntegerValue(env.local()).FromJust());
CHECK_EQ(153, CompileRun("o.b")->IntegerValue(env.local()).FromJust());
CHECK_EQ(713, CompileRun("o.c")->IntegerValue(env.local()).FromJust());
CHECK_EQ(560, CompileRun("o.rino")->IntegerValue(env.local()).FromJust());
CHECK_EQ(753, CompileRun("o.d")->IntegerValue(env.local()).FromJust());
}
}
THREADED_TEST(TestObjectTemplateInheritedWithAccessorsInPrototype1) {
TestObjectTemplateInheritedWithPrototype(ObjectTemplate_NewInstance);
}
THREADED_TEST(TestObjectTemplateInheritedWithAccessorsInPrototype2) {
TestObjectTemplateInheritedWithPrototype(Constructor_GetFunction_NewInstance);
}
THREADED_TEST(TestObjectTemplateInheritedWithAccessorsInPrototype3) {
TestObjectTemplateInheritedWithPrototype(Constructor_GetFunction_New);
}
// Test object instance creation using a function template without an instance
// template inherited from another function template.
static void TestObjectTemplateInheritedWithoutInstanceTemplate(
ObjectInstantiationMode mode) {
LocalContext env;
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
Local<v8::FunctionTemplate> fun_A = v8::FunctionTemplate::New(isolate);
fun_A->SetClassName(v8_str("A"));
Local<ObjectTemplate> templ_A = fun_A->InstanceTemplate();
templ_A->SetNativeDataProperty(v8_str("nirk"), GetNirk);
templ_A->SetNativeDataProperty(v8_str("rino"), GetRino);
Local<v8::FunctionTemplate> fun_B = v8::FunctionTemplate::New(isolate);
v8::Local<v8::String> class_name = v8_str("B");
fun_B->SetClassName(class_name);
fun_B->Inherit(fun_A);
// Perform several iterations to trigger creation from cached boilerplate.
for (int i = 0; i < 3; i++) {
Local<v8::Object> instance;
switch (mode) {
case Constructor_GetFunction_NewInstance: {
Local<v8::Function> function_B =
fun_B->GetFunction(env.local()).ToLocalChecked();
instance = function_B->NewInstance(env.local()).ToLocalChecked();
break;
}
case Constructor_GetFunction_New: {
Local<v8::Function> function_B =
fun_B->GetFunction(env.local()).ToLocalChecked();
if (i == 0) {
CHECK(env->Global()
->Set(env.local(), class_name, function_B)
.FromJust());
}
instance =
CompileRun("new B()")->ToObject(env.local()).ToLocalChecked();
break;
}
default:
UNREACHABLE();
}
CHECK(class_name->StrictEquals(instance->GetConstructorName()));
CHECK(env->Global()->Set(env.local(), v8_str("o"), instance).FromJust());
CHECK_EQ(900, CompileRun("o.nirk")->IntegerValue(env.local()).FromJust());
CHECK_EQ(560, CompileRun("o.rino")->IntegerValue(env.local()).FromJust());
}
}
THREADED_TEST(TestObjectTemplateInheritedWithPrototype1) {
TestObjectTemplateInheritedWithoutInstanceTemplate(
Constructor_GetFunction_NewInstance);
}
THREADED_TEST(TestObjectTemplateInheritedWithPrototype2) {
TestObjectTemplateInheritedWithoutInstanceTemplate(
Constructor_GetFunction_New);
}
THREADED_TEST(TestObjectTemplateClassInheritance) {
LocalContext env;
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
Local<v8::FunctionTemplate> fun_A = v8::FunctionTemplate::New(isolate);
fun_A->SetClassName(v8_str("A"));
Local<ObjectTemplate> templ_A = fun_A->InstanceTemplate();
templ_A->SetNativeDataProperty(v8_str("nirk"), GetNirk);
templ_A->SetNativeDataProperty(v8_str("rino"), GetRino);
Local<v8::FunctionTemplate> fun_B = v8::FunctionTemplate::New(isolate);
v8::Local<v8::String> class_name = v8_str("B");
fun_B->SetClassName(class_name);
fun_B->Inherit(fun_A);
v8::Local<v8::String> subclass_name = v8_str("C");
v8::Local<v8::Object> b_proto;
v8::Local<v8::Object> c_proto;
// Perform several iterations to make sure the cache doesn't break
// subclassing.
for (int i = 0; i < 3; i++) {
Local<v8::Function> function_B =
fun_B->GetFunction(env.local()).ToLocalChecked();
if (i == 0) {
CHECK(env->Global()->Set(env.local(), class_name, function_B).FromJust());
CompileRun("class C extends B {}");
b_proto =
CompileRun("B.prototype")->ToObject(env.local()).ToLocalChecked();
c_proto =
CompileRun("C.prototype")->ToObject(env.local()).ToLocalChecked();
CHECK(b_proto->Equals(env.local(), c_proto->GetPrototype()).FromJust());
}
Local<v8::Object> instance =
CompileRun("new C()")->ToObject(env.local()).ToLocalChecked();
CHECK(c_proto->Equals(env.local(), instance->GetPrototype()).FromJust());
CHECK(subclass_name->StrictEquals(instance->GetConstructorName()));
CHECK(env->Global()->Set(env.local(), v8_str("o"), instance).FromJust());
CHECK_EQ(900, CompileRun("o.nirk")->IntegerValue(env.local()).FromJust());
CHECK_EQ(560, CompileRun("o.rino")->IntegerValue(env.local()).FromJust());
}
}
static void NamedPropertyGetterWhichReturns42(
Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
info.GetReturnValue().Set(v8_num(42));
}
THREADED_TEST(TestObjectTemplateReflectConstruct) {
LocalContext env;
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
Local<v8::FunctionTemplate> fun_B = v8::FunctionTemplate::New(isolate);
fun_B->InstanceTemplate()->SetHandler(
v8::NamedPropertyHandlerConfiguration(NamedPropertyGetterWhichReturns42));
v8::Local<v8::String> class_name = v8_str("B");
fun_B->SetClassName(class_name);
v8::Local<v8::String> subclass_name = v8_str("C");
v8::Local<v8::Object> b_proto;
v8::Local<v8::Object> c_proto;
// Perform several iterations to make sure the cache doesn't break
// subclassing.
for (int i = 0; i < 3; i++) {
Local<v8::Function> function_B =
fun_B->GetFunction(env.local()).ToLocalChecked();
if (i == 0) {
CHECK(env->Global()->Set(env.local(), class_name, function_B).FromJust());
CompileRun("function C() {}");
c_proto =
CompileRun("C.prototype")->ToObject(env.local()).ToLocalChecked();
}
Local<v8::Object> instance = CompileRun("Reflect.construct(B, [], C)")
->ToObject(env.local())
.ToLocalChecked();
CHECK(c_proto->Equals(env.local(), instance->GetPrototype()).FromJust());
CHECK(subclass_name->StrictEquals(instance->GetConstructorName()));
CHECK(env->Global()->Set(env.local(), v8_str("o"), instance).FromJust());
CHECK_EQ(42, CompileRun("o.nirk")->IntegerValue(env.local()).FromJust());
CHECK_EQ(42, CompileRun("o.rino")->IntegerValue(env.local()).FromJust());
}
}
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::Local<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::Local<v8::FunctionTemplate> base1 = v8::FunctionTemplate::New(isolate);
base1->Inherit(super);
base1->PrototypeTemplate()->Set(isolate, "v1", v8_num(20.1));
v8::Local<v8::FunctionTemplate> base2 = v8::FunctionTemplate::New(isolate);
base2->Inherit(super);
base2->PrototypeTemplate()->Set(isolate, "v2", v8_num(10.1));
LocalContext env;
CHECK(env->Global()
->Set(env.local(), v8_str("s"),
super->GetFunction(env.local()).ToLocalChecked())
.FromJust());
CHECK(env->Global()
->Set(env.local(), v8_str("base1"),
base1->GetFunction(env.local()).ToLocalChecked())
.FromJust());
CHECK(env->Global()
->Set(env.local(), v8_str("base2"),
base2->GetFunction(env.local()).ToLocalChecked())
.FromJust());
// Checks right __proto__ chain.
CHECK(CompileRun("base1.prototype.__proto__ == s.prototype")
->BooleanValue(env.local())
.FromJust());
CHECK(CompileRun("base2.prototype.__proto__ == s.prototype")
->BooleanValue(env.local())
.FromJust());
CHECK(v8_compile("s.prototype.PI == 3.14")
->Run(env.local())
.ToLocalChecked()
->BooleanValue(env.local())
.FromJust());
// Instance accessor should not be visible on function object or its prototype
CHECK(
CompileRun("s.knurd == undefined")->BooleanValue(env.local()).FromJust());
CHECK(CompileRun("s.prototype.knurd == undefined")
->BooleanValue(env.local())
.FromJust());
CHECK(CompileRun("base1.prototype.knurd == undefined")
->BooleanValue(env.local())
.FromJust());
CHECK(env->Global()
->Set(env.local(), v8_str("obj"), base1->GetFunction(env.local())
.ToLocalChecked()
->NewInstance(env.local())
.ToLocalChecked())
.FromJust());
CHECK_EQ(17.2,
CompileRun("obj.flabby()")->NumberValue(env.local()).FromJust());
CHECK(CompileRun("'flabby' in obj")->BooleanValue(env.local()).FromJust());
CHECK_EQ(15.2, CompileRun("obj.knurd")->NumberValue(env.local()).FromJust());
CHECK(CompileRun("'knurd' in obj")->BooleanValue(env.local()).FromJust());
CHECK_EQ(20.1, CompileRun("obj.v1")->NumberValue(env.local()).FromJust());
CHECK(env->Global()
->Set(env.local(), v8_str("obj2"), base2->GetFunction(env.local())
.ToLocalChecked()
->NewInstance(env.local())
.ToLocalChecked())
.FromJust());
CHECK_EQ(17.2,
CompileRun("obj2.flabby()")->NumberValue(env.local()).FromJust());
CHECK(CompileRun("'flabby' in obj2")->BooleanValue(env.local()).FromJust());
CHECK_EQ(15.2, CompileRun("obj2.knurd")->NumberValue(env.local()).FromJust());
CHECK(CompileRun("'knurd' in obj2")->BooleanValue(env.local()).FromJust());
CHECK_EQ(10.1, CompileRun("obj2.v2")->NumberValue(env.local()).FromJust());
// base1 and base2 cannot cross reference to each's prototype
CHECK(CompileRun("obj.v2")->IsUndefined());
CHECK(CompileRun("obj2.v1")->IsUndefined());
}
THREADED_TEST(DescriptorInheritance2) {
LocalContext env;
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Local<v8::FunctionTemplate> fun_A = v8::FunctionTemplate::New(isolate);
fun_A->SetClassName(v8_str("A"));
fun_A->InstanceTemplate()->SetNativeDataProperty(v8_str("knurd1"), GetKnurd);
fun_A->InstanceTemplate()->SetNativeDataProperty(v8_str("nirk1"), GetNirk);
fun_A->InstanceTemplate()->SetNativeDataProperty(v8_str("rino1"), GetRino);
v8::Local<v8::FunctionTemplate> fun_B = v8::FunctionTemplate::New(isolate);
fun_B->SetClassName(v8_str("B"));
fun_B->Inherit(fun_A);
v8::Local<v8::FunctionTemplate> fun_C = v8::FunctionTemplate::New(isolate);
fun_C->SetClassName(v8_str("C"));
fun_C->Inherit(fun_B);
fun_C->InstanceTemplate()->SetNativeDataProperty(v8_str("knurd2"), GetKnurd);
fun_C->InstanceTemplate()->SetNativeDataProperty(v8_str("nirk2"), GetNirk);
fun_C->InstanceTemplate()->SetNativeDataProperty(v8_str("rino2"), GetRino);
v8::Local<v8::FunctionTemplate> fun_D = v8::FunctionTemplate::New(isolate);
fun_D->SetClassName(v8_str("D"));
fun_D->Inherit(fun_C);
v8::Local<v8::FunctionTemplate> fun_E = v8::FunctionTemplate::New(isolate);
fun_E->SetClassName(v8_str("E"));
fun_E->Inherit(fun_D);
fun_E->InstanceTemplate()->SetNativeDataProperty(v8_str("knurd3"), GetKnurd);
fun_E->InstanceTemplate()->SetNativeDataProperty(v8_str("nirk3"), GetNirk);
fun_E->InstanceTemplate()->SetNativeDataProperty(v8_str("rino3"), GetRino);
v8::Local<v8::FunctionTemplate> fun_F = v8::FunctionTemplate::New(isolate);
fun_F->SetClassName(v8_str("F"));
fun_F->Inherit(fun_E);
v8::Local<v8::ObjectTemplate> templ = fun_F->InstanceTemplate();
const int kDataPropertiesNumber = 100;
for (int i = 0; i < kDataPropertiesNumber; i++) {
v8::Local<v8::Value> val = v8_num(i);
v8::Local<v8::String> val_str = val->ToString(env.local()).ToLocalChecked();
v8::Local<v8::String> name = String::Concat(v8_str("p"), val_str);
templ->Set(name, val);
templ->Set(val_str, val);
}
CHECK(env->Global()
->Set(env.local(), v8_str("F"),
fun_F->GetFunction(env.local()).ToLocalChecked())
.FromJust());
v8::Local<v8::Script> script = v8_compile("o = new F()");
for (int i = 0; i < 100; i++) {
v8::HandleScope scope(isolate);
script->Run(env.local()).ToLocalChecked();
}
v8::Local<v8::Object> object = script->Run(env.local())
.ToLocalChecked()
->ToObject(env.local())
.ToLocalChecked();
CHECK_EQ(15.2, CompileRun("o.knurd1")->NumberValue(env.local()).FromJust());
CHECK_EQ(15.2, CompileRun("o.knurd2")->NumberValue(env.local()).FromJust());
CHECK_EQ(15.2, CompileRun("o.knurd3")->NumberValue(env.local()).FromJust());
CHECK_EQ(900, CompileRun("o.nirk1")->IntegerValue(env.local()).FromJust());
CHECK_EQ(900, CompileRun("o.nirk2")->IntegerValue(env.local()).FromJust());
CHECK_EQ(900, CompileRun("o.nirk3")->IntegerValue(env.local()).FromJust());
CHECK_EQ(560, CompileRun("o.rino1")->IntegerValue(env.local()).FromJust());
CHECK_EQ(560, CompileRun("o.rino2")->IntegerValue(env.local()).FromJust());
CHECK_EQ(560, CompileRun("o.rino3")->IntegerValue(env.local()).FromJust());
for (int i = 0; i < kDataPropertiesNumber; i++) {
v8::Local<v8::Value> val = v8_num(i);
v8::Local<v8::String> val_str = val->ToString(env.local()).ToLocalChecked();
v8::Local<v8::String> name = String::Concat(v8_str("p"), val_str);
CHECK_EQ(i, object->Get(env.local(), name)
.ToLocalChecked()
->IntegerValue(env.local())
.FromJust());
CHECK_EQ(i, object->Get(env.local(), val)
.ToLocalChecked()
->IntegerValue(env.local())
.FromJust());
}
}
// Helper functions for Interceptor/Accessor interaction tests
void SimpleAccessorGetter(Local<String> name,
const v8::PropertyCallbackInfo<v8::Value>& info) {
Local<Object> self = Local<Object>::Cast(info.This());
info.GetReturnValue().Set(self->Get(info.GetIsolate()->GetCurrentContext(),
String::Concat(v8_str("accessor_"), name))
.ToLocalChecked());
}
void SimpleAccessorSetter(Local<String> name, Local<Value> value,
const v8::PropertyCallbackInfo<void>& info) {
Local<Object> self = Local<Object>::Cast(info.This());
CHECK(self->Set(info.GetIsolate()->GetCurrentContext(),
String::Concat(v8_str("accessor_"), name), value)
.FromJust());
}
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(AccessorIsPreservedOnAttributeChange) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
LocalContext env;
v8::Local<v8::Value> res = CompileRun("var a = []; a;");
i::Handle<i::JSReceiver> a(v8::Utils::OpenHandle(v8::Object::Cast(*res)));
CHECK_EQ(1, a->map()->instance_descriptors()->number_of_descriptors());
CompileRun("Object.defineProperty(a, 'length', { writable: false });");
CHECK_EQ(0, a->map()->instance_descriptors()->number_of_descriptors());
// But we should still have an AccessorInfo.
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()->IsAccessorInfo());
}
THREADED_TEST(UndefinedIsNotEnumerable) {
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
v8::Local<Value> result = CompileRun("this.propertyIsEnumerable(undefined)");
CHECK(result->IsFalse());
}
v8::Local<Script> call_recursively_script;
static const int kTargetRecursionDepth = 100; // near maximum
static void CallScriptRecursivelyCall(
const v8::FunctionCallbackInfo<v8::Value>& args) {
ApiTestFuzzer::Fuzz();
v8::Local<v8::Context> context = args.GetIsolate()->GetCurrentContext();
int depth = args.This()
->Get(context, v8_str("depth"))
.ToLocalChecked()
->Int32Value(context)
.FromJust();
if (depth == kTargetRecursionDepth) return;
CHECK(args.This()
->Set(context, v8_str("depth"),
v8::Integer::New(args.GetIsolate(), depth + 1))
.FromJust());
args.GetReturnValue().Set(
call_recursively_script->Run(context).ToLocalChecked());
}
static void CallFunctionRecursivelyCall(
const v8::FunctionCallbackInfo<v8::Value>& args) {
ApiTestFuzzer::Fuzz();
v8::Local<v8::Context> context = args.GetIsolate()->GetCurrentContext();
int depth = args.This()
->Get(context, v8_str("depth"))
.ToLocalChecked()
->Int32Value(context)
.FromJust();
if (depth == kTargetRecursionDepth) {
printf("[depth = %d]\n", depth);
return;
}
CHECK(args.This()
->Set(context, v8_str("depth"),
v8::Integer::New(args.GetIsolate(), depth + 1))
.FromJust());
v8::Local<Value> function =
args.This()
->Get(context, v8_str("callFunctionRecursively"))
.ToLocalChecked();
args.GetReturnValue().Set(function.As<Function>()
->Call(context, args.This(), 0, nullptr)
.ToLocalChecked());
}
THREADED_TEST(DeepCrossLanguageRecursion) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Local<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(nullptr, global);
CHECK(env->Global()
->Set(env.local(), v8_str("depth"), v8::Integer::New(isolate, 0))
.FromJust());
call_recursively_script = v8_compile("callScriptRecursively()");
call_recursively_script->Run(env.local()).ToLocalChecked();
call_recursively_script = v8::Local<Script>();
CHECK(env->Global()
->Set(env.local(), v8_str("depth"), v8::Integer::New(isolate, 0))
.FromJust());
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::Local<v8::ObjectTemplate> obj = ObjectTemplate::New(isolate);
obj->SetHandler(v8::NamedPropertyHandlerConfiguration(
ThrowingPropertyHandlerGet, ThrowingPropertyHandlerSet));
LocalContext env;
CHECK(env->Global()
->Set(env.local(), v8_str("obj"),
obj->NewInstance(env.local()).ToLocalChecked())
.FromJust());
v8::Local<Value> otto =
CompileRun("try { with (obj) { otto; } } catch (e) { e; }");
CHECK(v8_str("otto")->Equals(env.local(), otto).FromJust());
v8::Local<Value> netto =
CompileRun("try { with (obj) { netto = 4; } } catch (e) { e; }");
CHECK(v8_str("netto")->Equals(env.local(), netto).FromJust());
}
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;
CHECK(env->Global()
->Set(env.local(), v8_str("Foo"),
Foo->GetFunction(env.local()).ToLocalChecked())
.FromJust());
Local<Script> script = v8_compile("Foo.prototype.plak");
CHECK_EQ(v8_run_int32value(script), 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(env.local())
.ToLocalChecked()
->NewInstance(env.local())
.ToLocalChecked();
CHECK_EQ(1, obj->InternalFieldCount());
CHECK(obj->GetInternalField(0)->IsUndefined());
obj->SetInternalField(0, v8_num(17));
CHECK_EQ(17, obj->GetInternalField(0)->Int32Value(env.local()).FromJust());
}
THREADED_TEST(InternalFieldsOfRegularObjects) {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
const char* sources[] = {"new Object()", "{ a: 'a property' }", "arguments"};
for (size_t i = 0; i < arraysize(sources); ++i) {
i::ScopedVector<char> source(128);
i::SNPrintF(source, "(function() { return %s })()", sources[i]);
v8::Local<v8::Object> obj = CompileRun(source.start()).As<v8::Object>();
CHECK_EQ(0, obj->InternalFieldCount());
}
}
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(nullptr, global_template);
v8::Local<v8::Object> global_proxy = env->Global();
v8::Local<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(env.local()).FromJust());
}
THREADED_TEST(GlobalObjectHasRealIndexedProperty) {
LocalContext env;
v8::HandleScope scope(CcTest::isolate());
v8::Local<v8::Object> global = env->Global();
CHECK(global->Set(env.local(), 0, v8_str("value")).FromJust());
CHECK(global->HasRealIndexedProperty(env.local(), 0).FromJust());
}
static void CheckAlignedPointerInInternalField(Local<v8::Object> obj,
void* value) {
CHECK_EQ(0, static_cast<int>(reinterpret_cast<uintptr_t>(value) & 0x1));
obj->SetAlignedPointerInInternalField(0, value);
CcTest::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(env.local())
.ToLocalChecked()
->NewInstance(env.local())
.ToLocalChecked();
CHECK_EQ(1, obj->InternalFieldCount());
CheckAlignedPointerInInternalField(obj, nullptr);
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));
}
THREADED_TEST(SetAlignedPointerInInternalFields) {
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(2);
Local<v8::Object> obj = templ->GetFunction(env.local())
.ToLocalChecked()
->NewInstance(env.local())
.ToLocalChecked();
CHECK_EQ(2, obj->InternalFieldCount());
int* heap_allocated_1 = new int[100];
int* heap_allocated_2 = new int[100];
int indices[] = {0, 1};
void* values[] = {heap_allocated_1, heap_allocated_2};
obj->SetAlignedPointerInInternalFields(2, indices, values);
CcTest::CollectAllGarbage();
CHECK_EQ(heap_allocated_1, obj->GetAlignedPointerFromInternalField(0));
CHECK_EQ(heap_allocated_2, obj->GetAlignedPointerFromInternalField(1));
indices[0] = 1;
indices[1] = 0;
obj->SetAlignedPointerInInternalFields(2, indices, values);
CcTest::CollectAllGarbage();
CHECK_EQ(heap_allocated_2, obj->GetAlignedPointerFromInternalField(0));
CHECK_EQ(heap_allocated_1, obj->GetAlignedPointerFromInternalField(1));
delete[] heap_allocated_1;
delete[] heap_allocated_2;
}
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::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, nullptr);
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::CollectAllGarbage();
for (int i = 0; i < 100; i++) {
CHECK_EQ(AlignedTestPointer(i), env->GetAlignedPointerFromEmbedderData(i));
}
}
static void CheckEmbedderData(LocalContext* env, int index,
v8::Local<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_str("The quick brown fox jumps"));
CheckEmbedderData(&env, 2, v8_str("over the lazy dog."));
CheckEmbedderData(&env, 1, v8::Number::New(isolate, 1.2345));
CheckEmbedderData(&env, 0, v8::Boolean::New(isolate, true));
}
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::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::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);
Local<Object> global_proxy = env->Global();
i::Handle<i::Object> i_global_proxy = v8::Utils::OpenHandle(*global_proxy);
CHECK(env->Global()
->Set(env.local(), v8_str("global"), global_proxy)
.FromJust());
int32_t hash1;
if (set_in_js) {
CompileRun("var m = new Set(); m.add(global);");
i::Object* original_hash = i_global_proxy->GetHash();
CHECK(original_hash->IsSmi());
hash1 = i::Smi::ToInt(original_hash);
} else {
hash1 = i_global_proxy->GetOrCreateHash(i_isolate)->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(nullptr, Local<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::CollectAllGarbage();
int hash3 = symbol->GetIdentityHash();
CHECK_EQ(hash, hash3);
}
{
v8::Local<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::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_str("str1");
int hash = str->GetIdentityHash();
int hash1 = str->GetIdentityHash();
CHECK_EQ(hash, hash1);
CcTest::CollectAllGarbage();
int hash3 = str->GetIdentityHash();
CHECK_EQ(hash, hash3);
Local<v8::String> str2 = v8_str("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"));
v8::Local<v8::Symbol> sym4 = v8::Symbol::New(isolate, v8_str("native"));
CcTest::CollectAllGarbage();
// Check basic symbol functionality.
CHECK(sym1->IsSymbol());
CHECK(sym2->IsSymbol());
CHECK(!obj->IsSymbol());
CHECK(sym1->Equals(env.local(), sym1).FromJust());
CHECK(sym2->Equals(env.local(), sym2).FromJust());
CHECK(!sym1->Equals(env.local(), sym2).FromJust());
CHECK(!sym2->Equals(env.local(), sym1).FromJust());
CHECK(sym1->StrictEquals(sym1));
CHECK(sym2->StrictEquals(sym2));
CHECK(!sym1->StrictEquals(sym2));
CHECK(!sym2->StrictEquals(sym1));
CHECK(sym2->Name()->Equals(env.local(), v8_str("my-symbol")).FromJust());
v8::Local<v8::Value> sym_val = sym2;
CHECK(sym_val->IsSymbol());
CHECK(sym_val->Equals(env.local(), sym2).FromJust());
CHECK(sym_val->StrictEquals(sym2));
CHECK(v8::Symbol::Cast(*sym_val)->Equals(env.local(), sym2).FromJust());
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(env.local(), sym2).FromJust());
CHECK(!sym_obj->StrictEquals(sym2));
CHECK(v8::SymbolObject::Cast(*sym_obj)
->Equals(env.local(), sym_obj)
.FromJust());
CHECK(v8::SymbolObject::Cast(*sym_obj)
->ValueOf()
->Equals(env.local(), sym2)
.FromJust());
// Make sure delete of a non-existent symbol property works.
CHECK(obj->Delete(env.local(), sym1).FromJust());
CHECK(!obj->Has(env.local(), sym1).FromJust());
CHECK(
obj->Set(env.local(), sym1, v8::Integer::New(isolate, 1503)).FromJust());
CHECK(obj->Has(env.local(), sym1).FromJust());
CHECK_EQ(1503, obj->Get(env.local(), sym1)
.ToLocalChecked()
->Int32Value(env.local())
.FromJust());
CHECK(
obj->Set(env.local(), sym1, v8::Integer::New(isolate, 2002)).FromJust());
CHECK(obj->Has(env.local(), sym1).FromJust());
CHECK_EQ(2002, obj->Get(env.local(), sym1)
.ToLocalChecked()
->Int32Value(env.local())
.FromJust());
CHECK_EQ(v8::None, obj->GetPropertyAttributes(env.local(), sym1).FromJust());
CHECK_EQ(0u,
obj->GetOwnPropertyNames(env.local()).ToLocalChecked()->Length());
unsigned num_props =
obj->GetPropertyNames(env.local()).ToLocalChecked()->Length();
CHECK(obj->Set(env.local(), v8_str("bla"), v8::Integer::New(isolate, 20))
.FromJust());
CHECK_EQ(1u,
obj->GetOwnPropertyNames(env.local()).ToLocalChecked()->Length());
CHECK_EQ(num_props + 1,
obj->GetPropertyNames(env.local()).ToLocalChecked()->Length());
CcTest::CollectAllGarbage();
CHECK(obj->SetAccessor(env.local(), sym3, SymbolAccessorGetter,
SymbolAccessorSetter)
.FromJust());
CHECK(obj->Get(env.local(), sym3).ToLocalChecked()->IsUndefined());
CHECK(obj->Set(env.local(), sym3, v8::Integer::New(isolate, 42)).FromJust());
CHECK(obj->Get(env.local(), sym3)
.ToLocalChecked()
->Equals(env.local(), v8::Integer::New(isolate, 42))
.FromJust());
CHECK(obj->Get(env.local(), v8_str("accessor_sym3"))
.ToLocalChecked()
->Equals(env.local(), v8::Integer::New(isolate, 42))
.FromJust());
CHECK(obj->SetNativeDataProperty(env.local(), sym4, SymbolAccessorGetter)
.FromJust());
CHECK(obj->Get(env.local(), sym4).ToLocalChecked()->IsUndefined());
CHECK(obj->Set(env.local(), v8_str("accessor_native"),
v8::Integer::New(isolate, 123))
.FromJust());
CHECK_EQ(123, obj->Get(env.local(), sym4)
.ToLocalChecked()
->Int32Value(env.local())
.FromJust());
CHECK(obj->Set(env.local(), sym4, v8::Integer::New(isolate, 314)).FromJust());
CHECK(obj->Get(env.local(), sym4)
.ToLocalChecked()
->Equals(env.local(), v8::Integer::New(isolate, 314))
.FromJust());
CHECK(obj->Delete(env.local(), v8_str("accessor_native")).FromJust());
// Add another property and delete it afterwards to force the object in
// slow case.
CHECK(
obj->Set(env.local(), sym2, v8::Integer::New(isolate, 2008)).FromJust());
CHECK_EQ(2002, obj->Get(env.local(), sym1)
.ToLocalChecked()
->Int32Value(env.local())
.FromJust());
CHECK_EQ(2008, obj->Get(env.local(), sym2)
.ToLocalChecked()
->Int32Value(env.local())
.FromJust());
CHECK_EQ(2002, obj->Get(env.local(), sym1)
.ToLocalChecked()
->Int32Value(env.local())
.FromJust());
CHECK_EQ(2u,
obj->GetOwnPropertyNames(env.local()).ToLocalChecked()->Length());
CHECK(obj->Has(env.local(), sym1).FromJust());
CHECK(obj->Has(env.local(), sym2).FromJust());
CHECK(obj->Has(env.local(), sym3).FromJust());
CHECK(obj->Has(env.local(), v8_str("accessor_sym3")).FromJust());
CHECK(obj->Delete(env.local(), sym2).FromJust());
CHECK(obj->Has(env.local(), sym1).FromJust());
CHECK(!obj->Has(env.local(), sym2).FromJust());
CHECK(obj->Has(env.local(), sym3).FromJust());
CHECK(obj->Has(env.local(), v8_str("accessor_sym3")).FromJust());
CHECK_EQ(2002, obj->Get(env.local(), sym1)
.ToLocalChecked()
->Int32Value(env.local())
.FromJust());
CHECK(obj->Get(env.local(), sym3)
.ToLocalChecked()
->Equals(env.local(), v8::Integer::New(isolate, 42))
.FromJust());
CHECK(obj->Get(env.local(), v8_str("accessor_sym3"))
.ToLocalChecked()
->Equals(env.local(), v8::Integer::New(isolate, 42))
.FromJust());
CHECK_EQ(2u,
obj->GetOwnPropertyNames(env.local()).ToLocalChecked()->Length());
// Symbol properties are inherited.
v8::Local<v8::Object> child = v8::Object::New(isolate);
CHECK(child->SetPrototype(env.local(), obj).FromJust());
CHECK(child->Has(env.local(), sym1).FromJust());
CHECK_EQ(2002, child->Get(env.local(), sym1)
.ToLocalChecked()
->Int32Value(env.local())
.FromJust());
CHECK(obj->Get(env.local(), sym3)
.ToLocalChecked()
->Equals(env.local(), v8::Integer::New(isolate, 42))
.FromJust());
CHECK(obj->Get(env.local(), v8_str("accessor_sym3"))
.ToLocalChecked()
->Equals(env.local(), v8::Integer::New(isolate, 42))
.FromJust());
CHECK_EQ(0u,
child->GetOwnPropertyNames(env.local()).ToLocalChecked()->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(env.local()).ToLocalChecked();
CHECK(!new_instance.IsEmpty());
CHECK(new_instance->Has(env.local(), name).FromJust());
}
THREADED_TEST(PrivatePropertiesOnProxies) {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
v8::Local<v8::Object> target = CompileRun("({})").As<v8::Object>();
v8::Local<v8::Object> handler = CompileRun("({})").As<v8::Object>();
v8::Local<v8::Proxy> proxy =
v8::Proxy::New(env.local(), target, handler).ToLocalChecked();
v8::Local<v8::Private> priv1 = v8::Private::New(isolate);
v8::Local<v8::Private> priv2 =
v8::Private::New(isolate, v8_str("my-private"));
CcTest::CollectAllGarbage();
CHECK(priv2->Name()
->Equals(env.local(),
v8::String::NewFromUtf8(isolate, "my-private",
v8::NewStringType::kNormal)
.ToLocalChecked())
.FromJust());
// Make sure delete of a non-existent private symbol property works.
proxy->DeletePrivate(env.local(), priv1).FromJust();
CHECK(!proxy->HasPrivate(env.local(), priv1).FromJust());
CHECK(proxy->SetPrivate(env.local(), priv1, v8::Integer::New(isolate, 1503))
.FromJust());
CHECK(proxy->HasPrivate(env.local(), priv1).FromJust());
CHECK_EQ(1503, proxy->GetPrivate(env.local(), priv1)
.ToLocalChecked()
->Int32Value(env.local())
.FromJust());
CHECK(proxy->SetPrivate(env.local(), priv1, v8::Integer::New(isolate, 2002))
.FromJust());
CHECK(proxy->HasPrivate(env.local(), priv1).FromJust());
CHECK_EQ(2002, proxy->GetPrivate(env.local(), priv1)
.ToLocalChecked()
->Int32Value(env.local())
.FromJust());
CHECK_EQ(0u,
proxy->GetOwnPropertyNames(env.local()).ToLocalChecked()->Length());
unsigned num_props =
proxy->GetPropertyNames(env.local()).ToLocalChecked()->Length();
CHECK(proxy->Set(env.local(), v8::String::NewFromUtf8(
isolate, "bla", v8::NewStringType::kNormal)
.ToLocalChecked(),
v8::Integer::New(isolate, 20))
.FromJust());
CHECK_EQ(1u,
proxy->GetOwnPropertyNames(env.local()).ToLocalChecked()->Length());
CHECK_EQ(num_props + 1,
proxy->GetPropertyNames(env.local()).ToLocalChecked()->Length());
CcTest::CollectAllGarbage();
// Add another property and delete it afterwards to force the object in
// slow case.
CHECK(proxy->SetPrivate(env.local(), priv2, v8::Integer::New(isolate, 2008))
.FromJust());
CHECK_EQ(2002, proxy->GetPrivate(env.local(), priv1)
.ToLocalChecked()
->Int32Value(env.local())
.FromJust());
CHECK_EQ(2008, proxy->GetPrivate(env.local(), priv2)
.ToLocalChecked()
->Int32Value(env.local())
.FromJust());
CHECK_EQ(2002, proxy->GetPrivate(env.local(), priv1)
.ToLocalChecked()
->Int32Value(env.local())
.FromJust());
CHECK_EQ(1u,
proxy->GetOwnPropertyNames(env.local()).ToLocalChecked()->Length());
CHECK(proxy->HasPrivate(env.local(), priv1).FromJust());
CHECK(proxy->HasPrivate(env.local(), priv2).FromJust());
CHECK(proxy->DeletePrivate(env.local(), priv2).FromJust());
CHECK(proxy->HasPrivate(env.local(), priv1).FromJust());
CHECK(!proxy->HasPrivate(env.local(), priv2).FromJust());
CHECK_EQ(2002, proxy->GetPrivate(env.local(), priv1)
.ToLocalChecked()
->Int32Value(env.local())
.FromJust());
CHECK_EQ(1u,
proxy->GetOwnPropertyNames(env.local()).ToLocalChecked()->Length());
// Private properties are not inherited (for the time being).
v8::Local<v8::Object> child = v8::Object::New(isolate);
CHECK(child->SetPrototype(env.local(), proxy).FromJust());
CHECK(!child->HasPrivate(env.local(), priv1).FromJust());
CHECK_EQ(0u,
child->GetOwnPropertyNames(env.local()).ToLocalChecked()->Length());
}
THREADED_TEST(PrivateProperties) {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
v8::Local<v8::Object> obj = v8::Object::New(isolate);
v8::Local<v8::Private> priv1 = v8::Private::New(isolate);
v8::Local<v8::Private> priv2 =
v8::Private::New(isolate, v8_str("my-private"));
CcTest::CollectAllGarbage();
CHECK(priv2->Name()
->Equals(env.local(),
v8::String::NewFromUtf8(isolate, "my-private",
v8::NewStringType::kNormal)
.ToLocalChecked())
.FromJust());
// Make sure delete of a non-existent private symbol property works.
obj->DeletePrivate(env.local(), priv1).FromJust();
CHECK(!obj->HasPrivate(env.local(), priv1).FromJust());
CHECK(obj->SetPrivate(env.local(), priv1, v8::Integer::New(isolate, 1503))
.FromJust());
CHECK(obj->HasPrivate(env.local(), priv1).FromJust());
CHECK_EQ(1503, obj->GetPrivate(env.local(), priv1)
.ToLocalChecked()
->Int32Value(env.local())
.FromJust());
CHECK(obj->SetPrivate(env.local(), priv1, v8::Integer::New(isolate, 2002))
.FromJust());
CHECK(obj->HasPrivate(env.local(), priv1).FromJust());
CHECK_EQ(2002, obj->GetPrivate(env.local(), priv1)
.ToLocalChecked()
->Int32Value(env.local())
.FromJust());
CHECK_EQ(0u,
obj->GetOwnPropertyNames(env.local()).ToLocalChecked()->Length());
unsigned num_props =
obj->GetPropertyNames(env.local()).ToLocalChecked()->Length();
CHECK(obj->Set(env.local(), v8::String::NewFromUtf8(
isolate, "bla", v8::NewStringType::kNormal)
.ToLocalChecked(),
v8::Integer::New(isolate, 20))
.FromJust());
CHECK_EQ(1u,
obj->GetOwnPropertyNames(env.local()).ToLocalChecked()->Length());
CHECK_EQ(num_props + 1,
obj->GetPropertyNames(env.local()).ToLocalChecked()->Length());
CcTest::CollectAllGarbage();
// Add another property and delete it afterwards to force the object in
// slow case.
CHECK(obj->SetPrivate(env.local(), priv2, v8::Integer::New(isolate, 2008))
.FromJust());
CHECK_EQ(2002, obj->GetPrivate(env.local(), priv1)
.ToLocalChecked()
->Int32Value(env.local())
.FromJust());
CHECK_EQ(2008, obj->GetPrivate(env.local(), priv2)
.ToLocalChecked()
->Int32Value(env.local())
.FromJust());
CHECK_EQ(2002, obj->GetPrivate(env.local(), priv1)
.ToLocalChecked()
->Int32Value(env.local())
.FromJust());
CHECK_EQ(1u,
obj->GetOwnPropertyNames(env.local()).ToLocalChecked()->Length());
CHECK(obj->HasPrivate(env.local(), priv1).FromJust());
CHECK(obj->HasPrivate(env.local(), priv2).FromJust());
CHECK(obj->DeletePrivate(env.local(), priv2).FromJust());
CHECK(obj->HasPrivate(env.local(), priv1).FromJust());
CHECK(!obj->HasPrivate(env.local(), priv2).FromJust());
CHECK_EQ(2002, obj->GetPrivate(env.local(), priv1)
.ToLocalChecked()
->Int32Value(env.local())
.FromJust());
CHECK_EQ(1u,
obj->GetOwnPropertyNames(env.local()).ToLocalChecked()->Length());
// Private properties are not inherited (for the time being).
v8::Local<v8::Object> child = v8::Object::New(isolate);
CHECK(child->SetPrototype(env.local(), obj).FromJust());
CHECK(!child->HasPrivate(env.local(), priv1).FromJust());
CHECK_EQ(0u,
child->GetOwnPropertyNames(env.local()).ToLocalChecked()->Length());
}
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(env.local(), v8_str("sym2")).ToLocalChecked();
CHECK(sym2->SameValue(glob));
CHECK(!sym2->SameValue(glob_api));
}
THREADED_TEST(GlobalSymbolsNoContext) {
v8::Isolate* isolate = CcTest::isolate();
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));
}
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(env.local(), v8_str("sym")).ToLocalChecked();
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");
CheckWellKnownSymbol(v8::Symbol::GetHasInstance, "Symbol.hasInstance");
CheckWellKnownSymbol(v8::Symbol::GetIsConcatSpreadable,
"Symbol.isConcatSpreadable");
CheckWellKnownSymbol(v8::Symbol::GetMatch, "Symbol.match");
CheckWellKnownSymbol(v8::Symbol::GetReplace, "Symbol.replace");
CheckWellKnownSymbol(v8::Symbol::GetSearch, "Symbol.search");
CheckWellKnownSymbol(v8::Symbol::GetSplit, "Symbol.split");
CheckWellKnownSymbol(v8::Symbol::GetToPrimitive, "Symbol.toPrimitive");
CheckWellKnownSymbol(v8::Symbol::GetToStringTag, "Symbol.toStringTag");
}
THREADED_TEST(GlobalPrivates) {
i::FLAG_allow_natives_syntax = true;
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
v8::Local<String> name = v8_str("my-private");
v8::Local<v8::Private> glob = v8::Private::ForApi(isolate, name);
v8::Local<v8::Object> obj = v8::Object::New(isolate);
CHECK(obj->SetPrivate(env.local(), glob, v8::Integer::New(isolate, 3))
.FromJust());
v8::Local<v8::Private> glob2 = v8::Private::ForApi(isolate, name);
CHECK(obj->HasPrivate(env.local(), glob2).FromJust());
v8::Local<v8::Private> priv = v8::Private::New(isolate, name);
CHECK(!obj->HasPrivate(env.local(), priv).FromJust());
CompileRun("var intern = %CreatePrivateSymbol('my-private')");
v8::Local<Value> intern =
env->Global()->Get(env.local(), v8_str("intern")).ToLocalChecked();
CHECK(!obj->Has(env.local(), intern).FromJust());
}
class ScopedArrayBufferContents {
public:
explicit ScopedArrayBufferContents(const v8::ArrayBuffer::Contents& contents)
: contents_(contents) {}
~ScopedArrayBufferContents() { free(contents_.AllocationBase()); }
void* Data() const { return contents_.Data(); }
size_t ByteLength() const { return contents_.ByteLength(); }
void* AllocationBase() const { return contents_.AllocationBase(); }
size_t AllocationLength() const { return contents_.AllocationLength(); }
v8::ArrayBuffer::Allocator::AllocationMode AllocationMode() const {
return contents_.AllocationMode();
}
private:
const v8::ArrayBuffer::Contents contents_;
};
template <typename T>
static void CheckInternalFieldsAreZero(v8::Local<T> value) {
CHECK_EQ(T::kInternalFieldCount, value->InternalFieldCount());
for (int i = 0; i < value->InternalFieldCount(); i++) {
CHECK_EQ(0, value->GetInternalField(i)
->Int32Value(CcTest::isolate()->GetCurrentContext())
.FromJust());
}
}
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::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());
CHECK_NOT_NULL(data);
CHECK(env->Global()->Set(env.local(), v8_str("ab"), ab).FromJust());
v8::Local<v8::Value> result = CompileRun("ab.byteLength");
CHECK_EQ(1024, result->Int32Value(env.local()).FromJust());
result = CompileRun(
"var u8 = new Uint8Array(ab);"
"u8[0] = 0xFF;"
"u8[1] = 0xAA;"
"u8.length");
CHECK_EQ(1024, result->Int32Value(env.local()).FromJust());
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(env.local()).FromJust());
}
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(env.local()).FromJust());
result = CompileRun("u8_a[0]");
CHECK_EQ(0xAA, result->Int32Value(env.local()).FromJust());
result = CompileRun("u8_a[1]");
CHECK_EQ(0xFF, result->Int32Value(env.local()).FromJust());
result = CompileRun(
"var u8_b = new Uint8Array(ab1);"
"u8_b[0] = 0xBB;"
"u8_a[0]");
CHECK_EQ(0xBB, result->Int32Value(env.local()).FromJust());
result = CompileRun("u8_b[1]");
CHECK_EQ(0xFF, result->Int32Value(env.local()).FromJust());
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(env.local()).FromJust());
}
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());
CHECK(env->Global()->Set(env.local(), v8_str("ab3"), ab3).FromJust());
v8::Local<v8::Value> result = CompileRun("ab3.byteLength");
CHECK_EQ(100, result->Int32Value(env.local()).FromJust());
result = CompileRun(
"var u8_b = new Uint8Array(ab3);"
"u8_b[0] = 0xBB;"
"u8_b[1] = 0xCC;"
"u8_b.length");
CHECK_EQ(100, result->Int32Value(env.local()).FromJust());
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(env.local()).FromJust());
}
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::Local<v8::DataView> dv) {
CHECK_EQ(0, static_cast<int>(dv->ByteLength()));
CHECK_EQ(0, static_cast<int>(dv->ByteOffset()));
}
static void CheckIsNeutered(v8::Local<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::Local<v8::TypedArray> ta =
v8::Local<v8::TypedArray>::Cast(CompileRun(name));
CheckIsNeutered(ta);
}
template <typename TypedArray, int kElementSize>
static Local<TypedArray> CreateAndCheck(Local<v8::ArrayBuffer> ab,
int byteOffset, int length) {
v8::Local<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::Local<v8::ArrayBuffer> buffer = v8::ArrayBuffer::New(isolate, 1024);
v8::Local<v8::Uint8Array> u8a =
CreateAndCheck<v8::Uint8Array, 1>(buffer, 1, 1023);
v8::Local<v8::Uint8ClampedArray> u8c =
CreateAndCheck<v8::Uint8ClampedArray, 1>(buffer, 1, 1023);
v8::Local<v8::Int8Array> i8a =
CreateAndCheck<v8::Int8Array, 1>(buffer, 1, 1023);
v8::Local<v8::Uint16Array> u16a =
CreateAndCheck<v8::Uint16Array, 2>(buffer, 2, 511);
v8::Local<v8::Int16Array> i16a =
CreateAndCheck<v8::Int16Array, 2>(buffer, 2, 511);
v8::Local<v8::Uint32Array> u32a =
CreateAndCheck<v8::Uint32Array, 4>(buffer, 4, 255);
v8::Local<v8::Int32Array> i32a =
CreateAndCheck<v8::Int32Array, 4>(buffer, 4, 255);
v8::Local<v8::Float32Array> f32a =
CreateAndCheck<v8::Float32Array, 4>(buffer, 4, 255);
v8::Local<v8::Float64Array> f64a =
CreateAndCheck<v8::Float64Array, 8>(buffer, 8, 127);
v8::Local<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::Local<v8::ArrayBuffer> ab =
Local<v8::ArrayBuffer>::Cast(CompileRun("ab"));
v8::Local<v8::DataView> dv = v8::Local<v8::DataView>::Cast(CompileRun("dv"));
ScopedArrayBufferContents contents(ab->Externalize());
ab->Neuter();
CHECK_EQ(0, static_cast<int>(ab->ByteLength()));
CHECK_EQ(0, v8_run_int32value(v8_compile("ab.byteLength")));
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);
}
THREADED_TEST(ArrayBuffer_AllocationInformation) {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope handle_scope(isolate);
const size_t ab_size = 1024;
Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(isolate, ab_size);
ScopedArrayBufferContents contents(ab->Externalize());
// Array buffers should have normal allocation mode.
CHECK_EQ(contents.AllocationMode(),
v8::ArrayBuffer::Allocator::AllocationMode::kNormal);
// The allocation must contain the buffer (normally they will be equal, but
// this is not required by the contract).
CHECK_NOT_NULL(contents.AllocationBase());
const uintptr_t alloc =
reinterpret_cast<uintptr_t>(contents.AllocationBase());
const uintptr_t data = reinterpret_cast<uintptr_t>(contents.Data());
CHECK_LE(alloc, data);
CHECK_LE(data + contents.ByteLength(), alloc + contents.AllocationLength());
}
class ScopedSharedArrayBufferContents {
public:
explicit ScopedSharedArrayBufferContents(
const v8::SharedArrayBuffer::Contents& contents)
: contents_(contents) {}
~ScopedSharedArrayBufferContents() { free(contents_.AllocationBase()); }
void* Data() const { return contents_.Data(); }
size_t ByteLength() const { return contents_.ByteLength(); }
void* AllocationBase() const { return contents_.AllocationBase(); }
size_t AllocationLength() const { return contents_.AllocationLength(); }
v8::ArrayBuffer::Allocator::AllocationMode AllocationMode() const {
return contents_.AllocationMode();
}
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::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());
CHECK_NOT_NULL(data);
CHECK(env->Global()->Set(env.local(), v8_str("ab"), ab).FromJust());
v8::Local<v8::Value> result = CompileRun("ab.byteLength");
CHECK_EQ(1024, result->Int32Value(env.local()).FromJust());
result = CompileRun(
"var u8 = new Uint8Array(ab);"
"u8[0] = 0xFF;"
"u8[1] = 0xAA;"
"u8.length");
CHECK_EQ(1024, result->Int32Value(env.local()).FromJust());
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(env.local()).FromJust());
}
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(env.local()).FromJust());
result = CompileRun("u8_a[0]");
CHECK_EQ(0xAA, result->Int32Value(env.local()).FromJust());
result = CompileRun("u8_a[1]");
CHECK_EQ(0xFF, result->Int32Value(env.local()).FromJust());
result = CompileRun(
"var u8_b = new Uint8Array(ab1);"
"u8_b[0] = 0xBB;"
"u8_a[0]");
CHECK_EQ(0xBB, result->Int32Value(env.local()).FromJust());
result = CompileRun("u8_b[1]");
CHECK_EQ(0xFF, result->Int32Value(env.local()).FromJust());
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(env.local()).FromJust());
}
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());
CHECK(env->Global()->Set(env.local(), v8_str("ab3"), ab3).FromJust());
v8::Local<v8::Value> result = CompileRun("ab3.byteLength");
CHECK_EQ(100, result->Int32Value(env.local()).FromJust());
result = CompileRun(
"var u8_b = new Uint8Array(ab3);"
"u8_b[0] = 0xBB;"
"u8_b[1] = 0xCC;"
"u8_b.length");
CHECK_EQ(100, result->Int32Value(env.local()).FromJust());
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(env.local()).FromJust());
}
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::Private> key =
v8::Private::ForApi(isolate, v8_str("api-test::hidden-key"));
v8::Local<v8::String> empty = v8_str("");
v8::Local<v8::String> prop_name = v8_str("prop_name");
CcTest::CollectAllGarbage();
// Make sure delete of a non-existent hidden value works
obj->DeletePrivate(env.local(), key).FromJust();
CHECK(obj->SetPrivate(env.local(), key, v8::Integer::New(isolate, 1503))
.FromJust());
CHECK_EQ(1503, obj->GetPrivate(env.local(), key)
.ToLocalChecked()
->Int32Value(env.local())
.FromJust());
CHECK(obj->SetPrivate(env.local(), key, v8::Integer::New(isolate, 2002))
.FromJust());
CHECK_EQ(2002, obj->GetPrivate(env.local(), key)
.ToLocalChecked()
->Int32Value(env.local())
.FromJust());
CcTest::CollectAllGarbage();
// Make sure we do not find the hidden property.
CHECK(!obj->Has(env.local(), empty).FromJust());
CHECK_EQ(2002, obj->GetPrivate(env.local(), key)
.ToLocalChecked()
->Int32Value(env.local())
.FromJust());
CHECK(obj->Get(env.local(), empty).ToLocalChecked()->IsUndefined());
CHECK_EQ(2002, obj->GetPrivate(env.local(), key)
.ToLocalChecked()
->Int32Value(env.local())
.FromJust());
CHECK(
obj->Set(env.local(), empty, v8::Integer::New(isolate, 2003)).FromJust());
CHECK_EQ(2002, obj->GetPrivate(env.local(), key)
.ToLocalChecked()
->Int32Value(env.local())
.FromJust());
CHECK_EQ(2003, obj->Get(env.local(), empty)
.ToLocalChecked()
->Int32Value(env.local())
.FromJust());
CcTest::CollectAllGarbage();
// Add another property and delete it afterwards to force the object in
// slow case.
CHECK(obj->Set(env.local(), prop_name, v8::Integer::New(isolate, 2008))
.FromJust());
CHECK_EQ(2002, obj->GetPrivate(env.local(), key)
.ToLocalChecked()
->Int32Value(env.local())
.FromJust());
CHECK_EQ(2008, obj->Get(env.local(), prop_name)
.ToLocalChecked()
->Int32Value(env.local())
.FromJust());
CHECK_EQ(2002, obj->GetPrivate(env.local(), key)
.ToLocalChecked()
->Int32Value(env.local())
.FromJust());
CHECK(obj->Delete(env.local(), prop_name).FromJust());
CHECK_EQ(2002, obj->GetPrivate(env.local(), key)
.ToLocalChecked()
->Int32Value(env.local())
.FromJust());
CcTest::CollectAllGarbage();
CHECK(obj->SetPrivate(env.local(), key, v8::Integer::New(isolate, 2002))
.FromJust());
CHECK(obj->DeletePrivate(env.local(), key).FromJust());
CHECK(!obj->HasPrivate(env.local(), key).FromJust());
}
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::Private> key =
v8::Private::New(env->GetIsolate(), v8_str("hidden"));
CompileRun(
"set_called = false;"
"Object.defineProperty("
" Object.prototype,"
" 'hidden',"
" {get: function() { return 45; },"
" set: function() { set_called = true; }})");
CHECK(!obj->HasPrivate(env.local(), key).FromJust());
// 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->SetPrivate(env.local(), key, v8::Integer::New(env->GetIsolate(), 42))
.FromJust());
ExpectFalse("set_called");
CHECK_EQ(42, obj->GetPrivate(env.local(), key)
.ToLocalChecked()
->Int32Value(env.local())
.FromJust());
}
THREADED_TEST(External) {
v8::HandleScope scope(CcTest::isolate());
int x = 3;
Local<v8::External> ext = v8::External::New(CcTest::isolate(), &x);
LocalContext env;
CHECK(env->Global()->Set(env.local(), v8_str("ext"), ext).FromJust());
Local<Value> reext_obj = CompileRun("this.ext");
v8::Local<v8::External> reext = reext_obj.As<v8::External>();
int* ptr = static_cast<int*>(reext->Value());
CHECK_EQ(3, x);
*ptr = 10;
CHECK_EQ(x, 10);
{
i::Handle<i::Object> obj = v8::Utils::OpenHandle(*ext);
CHECK_EQ(i::HeapObject::cast(*obj)->map(), CcTest::heap()->external_map());
CHECK(ext->IsExternal());
CHECK(!CompileRun("new Set().add(this.ext)").IsEmpty());
CHECK_EQ(i::HeapObject::cast(*obj)->map(), CcTest::heap()->external_map());
CHECK(ext->IsExternal());
}
// 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(3, v8::Local<String>::New(isolate, global)->Length());
}
global.Reset();
{
v8::HandleScope scope(isolate);
global.Reset(isolate, v8_str("str"));
}
{
v8::HandleScope scope(isolate);
CHECK_EQ(3, v8::Local<String>::New(isolate, global)->Length());
}
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(3, v8::Local<String>::New(isolate, global)->Length());
}
{
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(6, v8::Local<String>::New(isolate, global)->Length());
}
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(3, v8::Local<String>::New(isolate, global)->Length());
}
{
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::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::CollectAllGarbage();
EmptyMessageQueues(isolate);
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::CollectAllGarbage();
EmptyMessageQueues(isolate);
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(isolate->GetCurrentContext()).ToLocalChecked();
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 OnWeakCallback(
const v8::WeakCallbackInfo<WeakCallbackDataType>&) {}
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(env.local(), obj).FromJust());
{
typename Map::PersistentValueReference ref = map.GetReference(7);
CHECK(expected->Equals(env.local(), ref.NewLocal(isolate)).FromJust());
}
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(env.local(), ref.NewLocal(isolate)).FromJust());
}
}
CHECK_EQ(initial_handle_count + 1, global_handles->global_handles_count());
if (map.IsWeak()) {
CcTest::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(env.local(), vector.Get(0)).FromJust());
CHECK(obj1->Equals(env.local(), vector.Get(2)).FromJust());
CHECK(obj1->Equals(env.local(), vector.Get(4)).FromJust());
CHECK(obj2->Equals(env.local(), vector.Get(1)).FromJust());
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(global1 == global1);
CHECK(!(global1 != global1));
{
v8::HandleScope scope(isolate);
Local<String> local1 = Local<String>::New(isolate, global1);
Local<String> local2 = Local<String>::New(isolate, global2);
CHECK(global1 == local1);
CHECK(!(global1 != local1));
CHECK(local1 == global1);
CHECK(!(local1 != global1));
CHECK(!(global1 == local2));
CHECK(global1 != local2);
CHECK(!(local2 == global1));
CHECK(local2 != global1);
CHECK(!(local1 == local2));
CHECK(local1 != local2);
Local<String> anotherLocal1 = Local<String>::New(isolate, global1);
CHECK(local1 == anotherLocal1);
CHECK(!(local1 != anotherLocal1));
}
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(3, local->Length());
}
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();
}
THREADED_TEST(ScriptException) {
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
Local<Script> script = v8_compile("throw 'panama!';");
v8::TryCatch try_catch(env->GetIsolate());
v8::MaybeLocal<Value> result = script->Run(env.local());
CHECK(result.IsEmpty());
CHECK(try_catch.HasCaught());
String::Utf8Value exception_value(env->GetIsolate(), 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(env.local())
.ToLocalChecked()
->Get(env.local(), v8_str("a"))
.ToLocalChecked()
->Equals(env.local(), v8_str("b"))
.FromJust());
}
bool message_received;
static void check_message_0(v8::Local<v8::Message> message,
v8::Local<Value> data) {
CHECK_EQ(5.76, data->NumberValue(CcTest::isolate()->GetCurrentContext())
.FromJust());
CHECK_EQ(6.75, message->GetScriptOrigin()
.ResourceName()
->NumberValue(CcTest::isolate()->GetCurrentContext())
.FromJust());
CHECK(!message->IsSharedCrossOrigin());
message_received = true;
}
THREADED_TEST(MessageHandler0) {
message_received = false;
v8::HandleScope scope(CcTest::isolate());
CHECK(!message_received);
LocalContext context;
CcTest::isolate()->AddMessageListener(check_message_0, v8_num(5.76));
v8::Local<v8::Script> script = CompileWithOrigin("throw 'error'", "6.75");
CHECK(script->Run(context.local()).IsEmpty());
CHECK(message_received);
// clear out the message listener
CcTest::isolate()->RemoveMessageListeners(check_message_0);
}
static void check_message_1(v8::Local<v8::Message> message,
v8::Local<Value> data) {
CHECK(data->IsNumber());
CHECK_EQ(1337,
data->Int32Value(CcTest::isolate()->GetCurrentContext()).FromJust());
CHECK(!message->IsSharedCrossOrigin());
message_received = true;
}
TEST(MessageHandler1) {
message_received = false;
v8::HandleScope scope(CcTest::isolate());
CHECK(!message_received);
CcTest::isolate()->AddMessageListener(check_message_1);
LocalContext context;
CompileRun("throw 1337;");
CHECK(message_received);
// clear out the message listener
CcTest::isolate()->RemoveMessageListeners(check_message_1);
}
static void check_message_2(v8::Local<v8::Message> message,
v8::Local<Value> data) {
LocalContext context;
CHECK(data->IsObject());
v8::Local<v8::Value> hidden_property =
v8::Object::Cast(*data)
->GetPrivate(
context.local(),
v8::Private::ForApi(CcTest::isolate(), v8_str("hidden key")))
.ToLocalChecked();
CHECK(v8_str("hidden value")
->Equals(context.local(), hidden_property)
.FromJust());
CHECK(!message->IsSharedCrossOrigin());
message_received = true;
}
TEST(MessageHandler2) {
message_received = false;
v8::HandleScope scope(CcTest::isolate());
CHECK(!message_received);
CcTest::isolate()->AddMessageListener(check_message_2);
LocalContext context;
v8::Local<v8::Value> error = v8::Exception::Error(v8_str("custom error"));
v8::Object::Cast(*error)
->SetPrivate(context.local(),
v8::Private::ForApi(CcTest::isolate(), v8_str("hidden key")),
v8_str("hidden value"))
.FromJust();
CHECK(context->Global()
->Set(context.local(), v8_str("error"), error)
.FromJust());
CompileRun("throw error;");
CHECK(message_received);
// clear out the message listener
CcTest::isolate()->RemoveMessageListeners(check_message_2);
}
static void check_message_3(v8::Local<v8::Message> message,
v8::Local<Value> data) {
CHECK(message->IsSharedCrossOrigin());
CHECK(message->GetScriptOrigin().Options().IsSharedCrossOrigin());
CHECK(message->GetScriptOrigin().Options().IsOpaque());
CHECK_EQ(6.75, message->GetScriptOrigin()
.ResourceName()
->NumberValue(CcTest::isolate()->GetCurrentContext())
.FromJust());
CHECK_EQ(7.40, message->GetScriptOrigin()
.SourceMapUrl()
->NumberValue(CcTest::isolate()->GetCurrentContext())
.FromJust());
message_received = true;
}
TEST(MessageHandler3) {
message_received = false;
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
CHECK(!message_received);
isolate->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),
Local<v8::Integer>(), v8_str("7.40"), v8::True(isolate));
v8::Local<v8::Script> script =
Script::Compile(context.local(), v8_str("throw 'error'"), &origin)
.ToLocalChecked();
CHECK(script->Run(context.local()).IsEmpty());
CHECK(message_received);
// clear out the message listener
isolate->RemoveMessageListeners(check_message_3);
}
static void check_message_4(v8::Local<v8::Message> message,
v8::Local<Value> data) {
CHECK(!message->IsSharedCrossOrigin());
CHECK_EQ(6.75, message->GetScriptOrigin()
.ResourceName()
->NumberValue(CcTest::isolate()->GetCurrentContext())
.FromJust());
message_received = true;
}
TEST(MessageHandler4) {
message_received = false;
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
CHECK(!message_received);
isolate->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::Local<v8::Script> script =
Script::Compile(context.local(), v8_str("throw 'error'"), &origin)
.ToLocalChecked();
CHECK(script->Run(context.local()).IsEmpty());
CHECK(message_received);
// clear out the message listener
isolate->RemoveMessageListeners(check_message_4);
}
static void check_message_5a(v8::Local<v8::Message> message,
v8::Local<Value> data) {
CHECK(message->IsSharedCrossOrigin());
CHECK_EQ(6.75, message->GetScriptOrigin()
.ResourceName()
->NumberValue(CcTest::isolate()->GetCurrentContext())
.FromJust());
message_received = true;
}
static void check_message_5b(v8::Local<v8::Message> message,
v8::Local<Value> data) {
CHECK(!message->IsSharedCrossOrigin());
CHECK_EQ(6.75, message->GetScriptOrigin()
.ResourceName()
->NumberValue(CcTest::isolate()->GetCurrentContext())
.FromJust());
message_received = true;
}
TEST(MessageHandler5) {
message_received = false;
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
CHECK(!message_received);
isolate->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::Local<v8::Script> script =
Script::Compile(context.local(), v8_str("throw 'error'"), &origin1)
.ToLocalChecked();
CHECK(script->Run(context.local()).IsEmpty());
CHECK(message_received);
// clear out the message listener
isolate->RemoveMessageListeners(check_message_5a);
message_received = false;
isolate->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(context.local(), v8_str("throw 'error'"), &origin2)
.ToLocalChecked();
CHECK(script->Run(context.local()).IsEmpty());
CHECK(message_received);
// clear out the message listener
isolate->RemoveMessageListeners(check_message_5b);
}
THREADED_TEST(GetSetProperty) {
LocalContext context;
v8::Isolate* isolate = context->GetIsolate();
v8::HandleScope scope(isolate);
CHECK(context->Global()
->Set(context.local(), v8_str("foo"), v8_num(14))
.FromJust());
CHECK(context->Global()
->Set(context.local(), v8_str("12"), v8_num(92))
.FromJust());
CHECK(context->Global()
->Set(context.local(), v8::Integer::New(isolate, 16), v8_num(32))
.FromJust());
CHECK(context->Global()
->Set(context.local(), v8_num(13), v8_num(56))
.FromJust());
Local<Value> foo = CompileRun("this.foo");
CHECK_EQ(14, foo->Int32Value(context.local()).FromJust());
Local<Value> twelve = CompileRun("this[12]");
CHECK_EQ(92, twelve->Int32Value(context.local()).FromJust());
Local<Value> sixteen = CompileRun("this[16]");
CHECK_EQ(32, sixteen->Int32Value(context.local()).FromJust());
Local<Value> thirteen = CompileRun("this[13]");
CHECK_EQ(56, thirteen->Int32Value(context.local()).FromJust());
CHECK_EQ(92, context->Global()
->Get(context.local(), v8::Integer::New(isolate, 12))
.ToLocalChecked()
->Int32Value(context.local())
.FromJust());
CHECK_EQ(92, context->Global()
->Get(context.local(), v8_str("12"))
.ToLocalChecked()
->Int32Value(context.local())
.FromJust());
CHECK_EQ(92, context->Global()
->Get(context.local(), v8_num(12))
.ToLocalChecked()
->Int32Value(context.local())
.FromJust());
CHECK_EQ(32, context->Global()
->Get(context.local(), v8::Integer::New(isolate, 16))
.ToLocalChecked()
->Int32Value(context.local())
.FromJust());
CHECK_EQ(32, context->Global()
->Get(context.local(), v8_str("16"))
.ToLocalChecked()
->Int32Value(context.local())
.FromJust());
CHECK_EQ(32, context->Global()
->Get(context.local(), v8_num(16))
.ToLocalChecked()
->Int32Value(context.local())
.FromJust());
CHECK_EQ(56, context->Global()
->Get(context.local(), v8::Integer::New(isolate, 13))
.ToLocalChecked()
->Int32Value(context.local())
.FromJust());
CHECK_EQ(56, context->Global()
->Get(context.local(), v8_str("13"))
.ToLocalChecked()
->Int32Value(context.local())
.FromJust());
CHECK_EQ(56, context->Global()
->Get(context.local(), v8_num(13))
.ToLocalChecked()
->Int32Value(context.local())
.FromJust());
}
THREADED_TEST(PropertyAttributes) {
LocalContext context;
v8::HandleScope scope(context->GetIsolate());
// none
Local<String> prop = v8_str("none");
CHECK(context->Global()->Set(context.local(), prop, v8_num(7)).FromJust());
CHECK_EQ(v8::None, context->Global()
->GetPropertyAttributes(context.local(), prop)
.FromJust());
// read-only
prop = v8_str("read_only");
context->Global()
->DefineOwnProperty(context.local(), prop, v8_num(7), v8::ReadOnly)
.FromJust();
CHECK_EQ(7, context->Global()
->Get(context.local(), prop)
.ToLocalChecked()
->Int32Value(context.local())
.FromJust());
CHECK_EQ(v8::ReadOnly, context->Global()
->GetPropertyAttributes(context.local(), prop)
.FromJust());
CompileRun("read_only = 9");
CHECK_EQ(7, context->Global()
->Get(context.local(), prop)
.ToLocalChecked()
->Int32Value(context.local())
.FromJust());
CHECK(context->Global()->Set(context.local(), prop, v8_num(10)).FromJust());
CHECK_EQ(7, context->Global()
->Get(context.local(), prop)
.ToLocalChecked()
->Int32Value(context.local())
.FromJust());
// dont-delete
prop = v8_str("dont_delete");
context->Global()
->DefineOwnProperty(context.local(), prop, v8_num(13), v8::DontDelete)
.FromJust();
CHECK_EQ(13, context->Global()
->Get(context.local(), prop)
.ToLocalChecked()
->Int32Value(context.local())
.FromJust());
CompileRun("delete dont_delete");
CHECK_EQ(13, context->Global()
->Get(context.local(), prop)
.ToLocalChecked()
->Int32Value(context.local())
.FromJust());
CHECK_EQ(v8::DontDelete, context->Global()
->GetPropertyAttributes(context.local(), prop)
.FromJust());
// dont-enum
prop = v8_str("dont_enum");
context->Global()
->DefineOwnProperty(context.local(), prop, v8_num(28), v8::DontEnum)
.FromJust();
CHECK_EQ(v8::DontEnum, context->Global()
->GetPropertyAttributes(context.local(), prop)
.FromJust());
// absent
prop = v8_str("absent");
CHECK_EQ(v8::None, context->Global()
->GetPropertyAttributes(context.local(), prop)
.FromJust());
Local<Value> fake_prop = v8_num(1);
CHECK_EQ(v8::None, context->Global()
->GetPropertyAttributes(context.local(), fake_prop)
.FromJust());
// exception
TryCatch try_catch(context->GetIsolate());
Local<Value> exception =
CompileRun("({ toString: function() { throw 'exception';} })");
CHECK(context->Global()
->GetPropertyAttributes(context.local(), exception)
.IsNothing());
CHECK(try_catch.HasCaught());
String::Utf8Value exception_value(context->GetIsolate(),
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(context.local(), 0).ToLocalChecked()->IsUndefined());
CHECK(!array->Has(context.local(), 0).FromJust());
CHECK(array->Get(context.local(), 100).ToLocalChecked()->IsUndefined());
CHECK(!array->Has(context.local(), 100).FromJust());
CHECK(array->Set(context.local(), 2, v8_num(7)).FromJust());
CHECK_EQ(3u, array->Length());
CHECK(!array->Has(context.local(), 0).FromJust());
CHECK(!array->Has(context.local(), 1).FromJust());
CHECK(array->Has(context.local(), 2).FromJust());
CHECK_EQ(7, array->Get(context.local(), 2)
.ToLocalChecked()
->Int32Value(context.local())
.FromJust());
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(context.local(), 0)
.ToLocalChecked()
->Int32Value(context.local())
.FromJust());
CHECK_EQ(2, arr->Get(context.local(), 1)
.ToLocalChecked()
->Int32Value(context.local())
.FromJust());
CHECK_EQ(3, arr->Get(context.local(), 2)
.ToLocalChecked()
->Int32Value(context.local())
.FromJust());
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++) {
CHECK(result->Set(CcTest::isolate()->GetCurrentContext(), i, args[i])
.FromJust());
}
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(context.local(), 0)
.ToLocalChecked()
->Int32Value(context.local())
.FromJust());
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(context.local(), 0)
.ToLocalChecked()
->Int32Value(context.local())
.FromJust());
CHECK_EQ(13, a2->Get(context.local(), 1)
.ToLocalChecked()
->Int32Value(context.local())
.FromJust());
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(context.local(), 0)
.ToLocalChecked()
->Int32Value(context.local())
.FromJust());
CHECK_EQ(15, a3->Get(context.local(), 1)
.ToLocalChecked()
->Int32Value(context.local())
.FromJust());
CHECK_EQ(16, a3->Get(context.local(), 2)
.ToLocalChecked()
->Int32Value(context.local())
.FromJust());
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(context.local(), 0)
.ToLocalChecked()
->Int32Value(context.local())
.FromJust());
CHECK_EQ(18, a4->Get(context.local(), 1)
.ToLocalChecked()
->Int32Value(context.local())
.FromJust());
CHECK_EQ(19, a4->Get(context.local(), 2)
.ToLocalChecked()
->Int32Value(context.local())
.FromJust());
CHECK_EQ(20, a4->Get(context.local(), 3)
.ToLocalChecked()
->Int32Value(context.local())
.FromJust());
}
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(context.local(), v8_str("Foo")).ToLocalChecked());
Local<Function> ReturnThisSloppy = Local<Function>::Cast(
context->Global()
->Get(context.local(), v8_str("ReturnThisSloppy"))
.ToLocalChecked());
Local<Function> ReturnThisStrict = Local<Function>::Cast(
context->Global()
->Get(context.local(), v8_str("ReturnThisStrict"))
.ToLocalChecked());
v8::Local<Value>* args0 = nullptr;
Local<v8::Array> a0 = Local<v8::Array>::Cast(
Foo->Call(context.local(), Foo, 0, args0).ToLocalChecked());
CHECK_EQ(0u, a0->Length());
v8::Local<Value> args1[] = {v8_num(1.1)};
Local<v8::Array> a1 = Local<v8::Array>::Cast(
Foo->Call(context.local(), Foo, 1, args1).ToLocalChecked());
CHECK_EQ(1u, a1->Length());
CHECK_EQ(1.1, a1->Get(context.local(), v8::Integer::New(isolate, 0))
.ToLocalChecked()
->NumberValue(context.local())
.FromJust());
v8::Local<Value> args2[] = {v8_num(2.2), v8_num(3.3)};
Local<v8::Array> a2 = Local<v8::Array>::Cast(
Foo->Call(context.local(), Foo, 2, args2).ToLocalChecked());
CHECK_EQ(2u, a2->Length());
CHECK_EQ(2.2, a2->Get(context.local(), v8::Integer::New(isolate, 0))
.ToLocalChecked()
->NumberValue(context.local())
.FromJust());
CHECK_EQ(3.3, a2->Get(context.local(), v8::Integer::New(isolate, 1))
.ToLocalChecked()
->NumberValue(context.local())
.FromJust());
v8::Local<Value> args3[] = {v8_num(4.4), v8_num(5.5), v8_num(6.6)};
Local<v8::Array> a3 = Local<v8::Array>::Cast(
Foo->Call(context.local(), Foo, 3, args3).ToLocalChecked());
CHECK_EQ(3u, a3->Length());
CHECK_EQ(4.4, a3->Get(context.local(), v8::Integer::New(isolate, 0))
.ToLocalChecked()
->NumberValue(context.local())
.FromJust());
CHECK_EQ(5.5, a3->Get(context.local(), v8::Integer::New(isolate, 1))
.ToLocalChecked()
->NumberValue(context.local())
.FromJust());
CHECK_EQ(6.6, a3->Get(context.local(), v8::Integer::New(isolate, 2))
.ToLocalChecked()
->NumberValue(context.local())
.FromJust());
v8::Local<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(context.local(), Foo, 4, args4).ToLocalChecked());
CHECK_EQ(4u, a4->Length());
CHECK_EQ(7.7, a4->Get(context.local(), v8::Integer::New(isolate, 0))
.ToLocalChecked()
->NumberValue(context.local())
.FromJust());
CHECK_EQ(8.8, a4->Get(context.local(), v8::Integer::New(isolate, 1))
.ToLocalChecked()
->NumberValue(context.local())
.FromJust());
CHECK_EQ(9.9, a4->Get(context.local(), v8::Integer::New(isolate, 2))
.ToLocalChecked()
->NumberValue(context.local())
.FromJust());
CHECK_EQ(10.11, a4->Get(context.local(), v8::Integer::New(isolate, 3))
.ToLocalChecked()
->NumberValue(context.local())
.FromJust());
Local<v8::Value> r1 =
ReturnThisSloppy
->Call(context.local(), v8::Undefined(isolate), 0, nullptr)
.ToLocalChecked();
CHECK(r1->StrictEquals(context->Global()));
Local<v8::Value> r2 =
ReturnThisSloppy->Call(context.local(), v8::Null(isolate), 0, nullptr)
.ToLocalChecked();
CHECK(r2->StrictEquals(context->Global()));
Local<v8::Value> r3 =
ReturnThisSloppy->Call(context.local(), v8_num(42), 0, nullptr)
.ToLocalChecked();
CHECK(r3->IsNumberObject());
CHECK_EQ(42.0, r3.As<v8::NumberObject>()->ValueOf());
Local<v8::Value> r4 =
ReturnThisSloppy->Call(context.local(), v8_str("hello"), 0, nullptr)
.ToLocalChecked();
CHECK(r4->IsStringObject());
CHECK(r4.As<v8::StringObject>()->ValueOf()->StrictEquals(v8_str("hello")));
Local<v8::Value> r5 =
ReturnThisSloppy->Call(context.local(), v8::True(isolate), 0, nullptr)
.ToLocalChecked();
CHECK(r5->IsBooleanObject());
CHECK(r5.As<v8::BooleanObject>()->ValueOf());
Local<v8::Value> r6 =
ReturnThisStrict
->Call(context.local(), v8::Undefined(isolate), 0, nullptr)
.ToLocalChecked();
CHECK(r6->IsUndefined());
Local<v8::Value> r7 =
ReturnThisStrict->Call(context.local(), v8::Null(isolate), 0, nullptr)
.ToLocalChecked();
CHECK(r7->IsNull());
Local<v8::Value> r8 =
ReturnThisStrict->Call(context.local(), v8_num(42), 0, nullptr)
.ToLocalChecked();
CHECK(r8->StrictEquals(v8_num(42)));
Local<v8::Value> r9 =
ReturnThisStrict->Call(context.local(), v8_str("hello"), 0, nullptr)
.ToLocalChecked();
CHECK(r9->StrictEquals(v8_str("hello")));
Local<v8::Value> r10 =
ReturnThisStrict->Call(context.local(), v8::True(isolate), 0, nullptr)
.ToLocalChecked();
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(context.local(), v8_str("Foo")).ToLocalChecked());
v8::Local<Value>* args0 = nullptr;
Local<v8::Array> a0 = Local<v8::Array>::Cast(
Foo->NewInstance(context.local(), 0, args0).ToLocalChecked());
CHECK_EQ(0u, a0->Length());
v8::Local<Value> args1[] = {v8_num(1.1)};
Local<v8::Array> a1 = Local<v8::Array>::Cast(
Foo->NewInstance(context.local(), 1, args1).ToLocalChecked());
CHECK_EQ(1u, a1->Length());
CHECK_EQ(1.1, a1->Get(context.local(), v8::Integer::New(isolate, 0))
.ToLocalChecked()
->NumberValue(context.local())
.FromJust());
v8::Local<Value> args2[] = {v8_num(2.2), v8_num(3.3)};
Local<v8::Array> a2 = Local<v8::Array>::Cast(
Foo->NewInstance(context.local(), 2, args2).ToLocalChecked());
CHECK_EQ(2u, a2->Length());
CHECK_EQ(2.2, a2->Get(context.local(), v8::Integer::New(isolate, 0))
.ToLocalChecked()
->NumberValue(context.local())
.FromJust());
CHECK_EQ(3.3, a2->Get(context.local(), v8::Integer::New(isolate, 1))
.ToLocalChecked()
->NumberValue(context.local())
.FromJust());
v8::Local<Value> args3[] = {v8_num(4.4), v8_num(5.5), v8_num(6.6)};
Local<v8::Array> a3 = Local<v8::Array>::Cast(
Foo->NewInstance(context.local(), 3, args3).ToLocalChecked());
CHECK_EQ(3u, a3->Length());
CHECK_EQ(4.4, a3->Get(context.local(), v8::Integer::New(isolate, 0))
.ToLocalChecked()
->NumberValue(context.local())
.FromJust());
CHECK_EQ(5.5, a3->Get(context.local(), v8::Integer::New(isolate, 1))
.ToLocalChecked()
->NumberValue(context.local())
.FromJust());
CHECK_EQ(6.6, a3->Get(context.local(), v8::Integer::New(isolate, 2))
.ToLocalChecked()
->NumberValue(context.local())
.FromJust());
v8::Local<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(context.local(), 4, args4).ToLocalChecked());
CHECK_EQ(4u, a4->Length());
CHECK_EQ(7.7, a4->Get(context.local(), v8::Integer::New(isolate, 0))
.ToLocalChecked()
->NumberValue(context.local())
.FromJust());
CHECK_EQ(8.8, a4->Get(context.local(), v8::Integer::New(isolate, 1))
.ToLocalChecked()
->NumberValue(context.local())
.FromJust());
CHECK_EQ(9.9, a4->Get(context.local(), v8::Integer::New(isolate, 2))
.ToLocalChecked()
->NumberValue(context.local())
.FromJust());
CHECK_EQ(10.11, a4->Get(context.local(), v8::Integer::New(isolate, 3))
.ToLocalChecked()
->NumberValue(context.local())
.FromJust());
}
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(env.local(), v8_str("obj")).ToLocalChecked();
CHECK_EQ(5312874545152.0,
obj->ToNumber(env.local()).ToLocalChecked()->Value());
CHECK_EQ(0, obj->ToInt32(env.local()).ToLocalChecked()->Value());
CHECK_EQ(0, obj->ToUint32(env.local()).ToLocalChecked()->Value());
// Large number.
CompileRun("var obj = -1234567890123;");
obj = env->Global()->Get(env.local(), v8_str("obj")).ToLocalChecked();
CHECK_EQ(-1234567890123.0,
obj->ToNumber(env.local()).ToLocalChecked()->Value());
CHECK_EQ(-1912276171, obj->ToInt32(env.local()).ToLocalChecked()->Value());
CHECK_EQ(2382691125, obj->ToUint32(env.local()).ToLocalChecked()->Value());
// Small positive integer.
CompileRun("var obj = 42;");
obj = env->Global()->Get(env.local(), v8_str("obj")).ToLocalChecked();
CHECK_EQ(42.0, obj->ToNumber(env.local()).ToLocalChecked()->Value());
CHECK_EQ(42, obj->ToInt32(env.local()).ToLocalChecked()->Value());
CHECK_EQ(42, obj->ToUint32(env.local()).ToLocalChecked()->Value());
// Negative integer.
CompileRun("var obj = -37;");
obj = env->Global()->Get(env.local(), v8_str("obj")).ToLocalChecked();
CHECK_EQ(-37.0, obj->ToNumber(env.local()).ToLocalChecked()->Value());
CHECK_EQ(-37, obj->ToInt32(env.local()).ToLocalChecked()->Value());
CHECK_EQ(4294967259, obj->ToUint32(env.local()).ToLocalChecked()->Value());
// Positive non-int32 integer.
CompileRun("var obj = 0x81234567;");
obj = env->Global()->Get(env.local(), v8_str("obj")).ToLocalChecked();
CHECK_EQ(2166572391.0, obj->ToNumber(env.local()).ToLocalChecked()->Value());
CHECK_EQ(-2128394905, obj->ToInt32(env.local()).ToLocalChecked()->Value());
CHECK_EQ(2166572391, obj->ToUint32(env.local()).ToLocalChecked()->Value());
// Fraction.
CompileRun("var obj = 42.3;");
obj = env->Global()->Get(env.local(), v8_str("obj")).ToLocalChecked();
CHECK_EQ(42.3, obj->ToNumber(env.local()).ToLocalChecked()->Value());
CHECK_EQ(42, obj->ToInt32(env.local()).ToLocalChecked()->Value());
CHECK_EQ(42, obj->ToUint32(env.local()).ToLocalChecked()->Value());
// Large negative fraction.
CompileRun("var obj = -5726623061.75;");
obj = env->Global()->Get(env.local(), v8_str("obj")).ToLocalChecked();
CHECK_EQ(-5726623061.75,
obj->ToNumber(env.local()).ToLocalChecked()->Value());
CHECK_EQ(-1431655765, obj->ToInt32(env.local()).ToLocalChecked()->Value());
CHECK_EQ(2863311531, obj->ToUint32(env.local()).ToLocalChecked()->Value());
}
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(env.local(), v8_str("obj")).ToLocalChecked();
CHECK(!obj->IsInt32());
CHECK(!obj->IsUint32());
// Large negative number.
CompileRun("var obj = -1234567890123;");
obj = env->Global()->Get(env.local(), v8_str("obj")).ToLocalChecked();
CHECK(!obj->IsInt32());
CHECK(!obj->IsUint32());
// Small positive integer.
CompileRun("var obj = 42;");
obj = env->Global()->Get(env.local(), v8_str("obj")).ToLocalChecked();
CHECK(obj->IsInt32());
CHECK(obj->IsUint32());
// Negative integer.
CompileRun("var obj = -37;");
obj = env->Global()->Get(env.local(), v8_str("obj")).ToLocalChecked();
CHECK(obj->IsInt32());
CHECK(!obj->IsUint32());
// Positive non-int32 integer.
CompileRun("var obj = 0x81234567;");
obj = env->Global()->Get(env.local(), v8_str("obj")).ToLocalChecked();
CHECK(!obj->IsInt32());
CHECK(obj->IsUint32());
// Fraction.
CompileRun("var obj = 42.3;");
obj = env->Global()->Get(env.local(), v8_str("obj")).ToLocalChecked();
CHECK(!obj->IsInt32());
CHECK(!obj->IsUint32());
// Large negative fraction.
CompileRun("var obj = -5726623061.75;");
obj = env->Global()->Get(env.local(), v8_str("obj")).ToLocalChecked();
CHECK(!obj->IsInt32());
CHECK(!obj->IsUint32());
// Positive zero
CompileRun("var obj = 0.0;");
obj = env->Global()->Get(env.local(), v8_str("obj")).ToLocalChecked();
CHECK(obj->IsInt32());
CHECK(obj->IsUint32());
// Positive zero
CompileRun("var obj = -0.0;");
obj = env->Global()->Get(env.local(), v8_str("obj")).ToLocalChecked();
CHECK(!obj->IsInt32());
CHECK(!obj->IsUint32());
}
static void CheckUncle(v8::Isolate* isolate, v8::TryCatch* try_catch) {
CHECK(try_catch->HasCaught());
String::Utf8Value str_value(isolate, try_catch->Exception());
CHECK_EQ(0, strcmp(*str_value, "uncle?"));
try_catch->Reset();
}
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(env.local(), v8_str("obj")).ToLocalChecked();
v8::TryCatch try_catch(isolate);
CHECK(obj->ToString(env.local()).IsEmpty());
CheckUncle(isolate, &try_catch);
CHECK(obj->ToNumber(env.local()).IsEmpty());
CheckUncle(isolate, &try_catch);
CHECK(obj->ToInteger(env.local()).IsEmpty());
CheckUncle(isolate, &try_catch);
CHECK(obj->ToUint32(env.local()).IsEmpty());
CheckUncle(isolate, &try_catch);
CHECK(obj->ToInt32(env.local()).IsEmpty());
CheckUncle(isolate, &try_catch);
CHECK(v8::Undefined(isolate)->ToObject(env.local()).IsEmpty());
CHECK(try_catch.HasCaught());
try_catch.Reset();
CHECK(obj->Int32Value(env.local()).IsNothing());
CheckUncle(isolate, &try_catch);
CHECK(obj->Uint32Value(env.local()).IsNothing());
CheckUncle(isolate, &try_catch);
CHECK(obj->NumberValue(env.local()).IsNothing());
CheckUncle(isolate, &try_catch);
CHECK(obj->IntegerValue(env.local()).IsNothing());
CheckUncle(isolate, &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()->GetCurrentContext())
.ToLocalChecked());
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(context.local(), v8_str("thrown"))
.ToLocalChecked();
CHECK(thrown->BooleanValue(context.local()).FromJust());
}
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_custom_error_tostring(v8::Local<v8::Message> message,
v8::Local<v8::Value> data) {
const char* uncaught_error = "Uncaught MyError toString";
CHECK(message->Get()
->Equals(CcTest::isolate()->GetCurrentContext(),
v8_str(uncaught_error))
.FromJust());
}
TEST(CustomErrorToString) {
LocalContext context;
v8::HandleScope scope(context->GetIsolate());
context->GetIsolate()->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'); ");
context->GetIsolate()->RemoveMessageListeners(check_custom_error_tostring);
}
static void check_custom_error_message(v8::Local<v8::Message> message,
v8::Local<v8::Value> data) {
const char* uncaught_error = "Uncaught MyError: my message";
printf("%s\n", *v8::String::Utf8Value(CcTest::isolate(), message->Get()));
CHECK(message->Get()
->Equals(CcTest::isolate()->GetCurrentContext(),
v8_str(uncaught_error))
.FromJust());
}
TEST(CustomErrorMessage) {
LocalContext context;
v8::HandleScope scope(context->GetIsolate());
context->GetIsolate()->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'); ");
context->GetIsolate()->RemoveMessageListeners(check_custom_error_message);
}
static void check_custom_rethrowing_message(v8::Local<v8::Message> message,
v8::Local<v8::Value> data) {
CHECK(data->IsExternal());
int* callcount = static_cast<int*>(data.As<v8::External>()->Value());
++*callcount;
const char* uncaught_error = "Uncaught exception";
CHECK(message->Get()
->Equals(CcTest::isolate()->GetCurrentContext(),
v8_str(uncaught_error))
.FromJust());
// Test that compiling code inside a message handler works.
CHECK(CompileRunChecked(CcTest::isolate(), "(function(a) { return a; })(42)")
->Equals(CcTest::isolate()->GetCurrentContext(),
v8::Integer::NewFromUnsigned(CcTest::isolate(), 42))
.FromJust());
}
TEST(CustomErrorRethrowsOnToString) {
int callcount = 0;
LocalContext context;
v8::Isolate* isolate = context->GetIsolate();
v8::HandleScope scope(isolate);
context->GetIsolate()->AddMessageListener(
check_custom_rethrowing_message, v8::External::New(isolate, &callcount));
CompileRun(
"var e = { toString: function() { throw e; } };"
"try { throw e; } finally {}");
CHECK_EQ(callcount, 1);
context->GetIsolate()->RemoveMessageListeners(
check_custom_rethrowing_message);
}
TEST(CustomErrorRethrowsOnToStringInsideVerboseTryCatch) {
int callcount = 0;
LocalContext context;
v8::Isolate* isolate = context->GetIsolate();
v8::HandleScope scope(isolate);
v8::TryCatch try_catch(isolate);
try_catch.SetVerbose(true);
context->GetIsolate()->AddMessageListener(
check_custom_rethrowing_message, v8::External::New(isolate, &callcount));
CompileRun(
"var e = { toString: function() { throw e; } };"
"try { throw e; } finally {}");
CHECK_EQ(callcount, 1);
context->GetIsolate()->RemoveMessageListeners(
check_custom_rethrowing_message);
}
static void receive_message(v8::Local<v8::Message> message,
v8::Local<v8::Value> data) {
message->Get();
message_received = true;
}
TEST(APIThrowMessage) {
message_received = false;
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
isolate->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);
isolate->RemoveMessageListeners(receive_message);
}
TEST(APIThrowMessageAndVerboseTryCatch) {
message_received = false;
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
isolate->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);
isolate->RemoveMessageListeners(receive_message);
}
TEST(APIStackOverflowAndVerboseTryCatch) {
message_received = false;
LocalContext context;
v8::HandleScope scope(context->GetIsolate());
context->GetIsolate()->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);
context->GetIsolate()->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(isolate, 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());
v8::Local<v8::Context> context = args.GetIsolate()->GetCurrentContext();
int count = args[0]->Int32Value(context).FromJust();
int cInterval = args[2]->Int32Value(context).FromJust();
if (count == 0) {
args.GetIsolate()->ThrowException(v8_str("FromC"));
return;
} else {
Local<v8::Object> global = context->Global();
Local<Value> fun =
global->Get(context, v8_str("JSThrowCountDown")).ToLocalChecked();
v8::Local<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(context, global, 4, argv)
.FromMaybe(Local<Value>());
int expected = args[3]->Int32Value(context).FromJust();
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(context, global, 4, argv)
.FromMaybe(v8::Local<v8::Value>()));
return;
}
}
}
void JSCheck(const v8::FunctionCallbackInfo<v8::Value>& args) {
ApiTestFuzzer::Fuzz();
CHECK_EQ(3, args.Length());
v8::Local<v8::Context> context = args.GetIsolate()->GetCurrentContext();
bool equality = args[0]->BooleanValue(context).FromJust();
int count = args[1]->Int32Value(context).FromJust();
int expected = args[2]->Int32Value(context).FromJust();
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(context.local(), v8_str("JSThrowCountDown"))
.ToLocalChecked());
const int argc = 4;
// count jsInterval cInterval expected
// *JS[4] *C[3] @JS[2] C[1] JS[0]
v8::Local<Value> a0[argc] = {v8_num(4), v8_num(2), v8_num(3), v8_num(2)};
fun->Call(context.local(), fun, argc, a0).ToLocalChecked();
// JS[5] *C[4] JS[3] @C[2] JS[1] C[0]
v8::Local<Value> a1[argc] = {v8_num(5), v8_num(6), v8_num(1), v8_num(2)};
fun->Call(context.local(), fun, argc, a1).ToLocalChecked();
// JS[6] @C[5] JS[4] C[3] JS[2] C[1] JS[0]
v8::Local<Value> a2[argc] = {v8_num(6), v8_num(7), v8_num(5), v8_num(5)};
fun->Call(context.local(), fun, argc, a2).ToLocalChecked();
// @JS[6] C[5] JS[4] C[3] JS[2] C[1] JS[0]
v8::Local<Value> a3[argc] = {v8_num(6), v8_num(6), v8_num(7), v8_num(6)};
fun->Call(context.local(), fun, argc, a3).ToLocalChecked();
// JS[6] *C[5] @JS[4] C[3] JS[2] C[1] JS[0]
v8::Local<Value> a4[argc] = {v8_num(6), v8_num(4), v8_num(5), v8_num(4)};
fun->Call(context.local(), fun, argc, a4).ToLocalChecked();
// JS[6] C[5] *JS[4] @C[3] JS[2] C[1] JS[0]
v8::Local<Value> a5[argc] = {v8_num(6), v8_num(4), v8_num(3), v8_num(3)};
fun->Call(context.local(), fun, argc, a5).ToLocalChecked();
}
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::Local<v8::Array> result = v8::Local<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(context.local(), v8::Integer::New(isolate, 0))
.ToLocalChecked()
->IsString());
CHECK(result->Get(context.local(), v8::Integer::New(isolate, 1))
.ToLocalChecked()
->IsNumber());
CHECK_EQ(1, result->Get(context.local(), v8::Integer::New(isolate, 1))
.ToLocalChecked()
->Int32Value(context.local())
.FromJust());
CHECK(result->Get(context.local(), v8::Integer::New(isolate, 2))
.ToLocalChecked()
->IsNumber());
CHECK_EQ(0, result->Get(context.local(), v8::Integer::New(isolate, 2))
.ToLocalChecked()
->Int32Value(context.local())
.FromJust());
CHECK(result->Get(context.local(), v8::Integer::New(isolate, 3))
.ToLocalChecked()
->IsNull());
CHECK(result->Get(context.local(), v8::Integer::New(isolate, 4))
.ToLocalChecked()
->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(context.local()).FromJust());
try_catch.Reset();
CHECK(!try_catch.HasCaught());
CompileRun("throw 0");
CHECK(try_catch.HasCaught());
CHECK_EQ(0, try_catch.Exception()->Int32Value(context.local()).FromJust());
}
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);
CHECK(context->Global()
->Set(context.local(), v8_str("native_with_try_catch"),
v8::FunctionTemplate::New(isolate, WithTryCatch)
->GetFunction(context.local())
.ToLocalChecked())
.FromJust());
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(context->GetIsolate(),
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(context->GetIsolate(),
try_catch.Exception()),
"E2"));
}
}
void TryCatchMixedNestingCheck(v8::TryCatch* try_catch) {
CHECK(try_catch->HasCaught());
Local<Message> message = try_catch->Message();
Local<Value> resource = message->GetScriptOrigin().ResourceName();
CHECK_EQ(
0, strcmp(*v8::String::Utf8Value(CcTest::isolate(), resource), "inner"));
CHECK_EQ(0, strcmp(*v8::String::Utf8Value(CcTest::isolate(), message->Get()),
"Uncaught Error: a"));
CHECK_EQ(1, message->GetLineNumber(CcTest::isolate()->GetCurrentContext())
.FromJust());
CHECK_EQ(0, message->GetStartColumn(CcTest::isolate()->GetCurrentContext())
.FromJust());
}
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(context.local(), v8_str("a")).FromJust());
CHECK(!v8_str("a")->Equals(context.local(), v8_str("b")).FromJust());
CHECK(v8_str("a")->Equals(context.local(), v8_str("a")).FromJust());
CHECK(!v8_str("a")->Equals(context.local(), v8_str("b")).FromJust());
CHECK(v8_num(1)->Equals(context.local(), v8_num(1)).FromJust());
CHECK(v8_num(1.00)->Equals(context.local(), v8_num(1)).FromJust());
CHECK(!v8_num(1)->Equals(context.local(), v8_num(2)).FromJust());
// 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::Local<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(TypeOf) {
LocalContext context;
v8::Isolate* isolate = context->GetIsolate();
v8::HandleScope scope(context->GetIsolate());
Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New(isolate);
Local<v8::Function> fun = t1->GetFunction(context.local()).ToLocalChecked();
CHECK(v8::Undefined(isolate)
->TypeOf(isolate)
->Equals(context.local(), v8_str("undefined"))
.FromJust());
CHECK(v8::Null(isolate)
->TypeOf(isolate)
->Equals(context.local(), v8_str("object"))
.FromJust());
CHECK(v8_str("str")
->TypeOf(isolate)
->Equals(context.local(), v8_str("string"))
.FromJust());
CHECK(v8_num(0.0)
->TypeOf(isolate)
->Equals(context.local(), v8_str("number"))
.FromJust());
CHECK(v8_num(1)
->TypeOf(isolate)
->Equals(context.local(), v8_str("number"))
.FromJust());
CHECK(v8::Object::New(isolate)
->TypeOf(isolate)
->Equals(context.local(), v8_str("object"))
.FromJust());
CHECK(v8::Boolean::New(isolate, true)
->TypeOf(isolate)
->Equals(context.local(), v8_str("boolean"))
.FromJust());
CHECK(fun->TypeOf(isolate)
->Equals(context.local(), v8_str("function"))
.FromJust());
}
THREADED_TEST(InstanceOf) {
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
CompileRun(
"var A = {};"
"var B = {};"
"var C = {};"
"B.__proto__ = A;"
"C.__proto__ = B;"
"function F() {}"
"F.prototype = A;"
"var G = { [Symbol.hasInstance] : null};"
"var H = { [Symbol.hasInstance] : () => { throw new Error(); } };"
"var J = { [Symbol.hasInstance] : () => true };"
"class K {}"
"var D = new K;"
"class L extends K {}"
"var E = new L");
v8::Local<v8::Object> f = v8::Local<v8::Object>::Cast(CompileRun("F"));
v8::Local<v8::Object> g = v8::Local<v8::Object>::Cast(CompileRun("G"));
v8::Local<v8::Object> h = v8::Local<v8::Object>::Cast(CompileRun("H"));
v8::Local<v8::Object> j = v8::Local<v8::Object>::Cast(CompileRun("J"));
v8::Local<v8::Object> k = v8::Local<v8::Object>::Cast(CompileRun("K"));
v8::Local<v8::Object> l = v8::Local<v8::Object>::Cast(CompileRun("L"));
v8::Local<v8::Value> a = v8::Local<v8::Value>::Cast(CompileRun("A"));
v8::Local<v8::Value> b = v8::Local<v8::Value>::Cast(CompileRun("B"));
v8::Local<v8::Value> c = v8::Local<v8::Value>::Cast(CompileRun("C"));
v8::Local<v8::Value> d = v8::Local<v8::Value>::Cast(CompileRun("D"));
v8::Local<v8::Value> e = v8::Local<v8::Value>::Cast(CompileRun("E"));
v8::TryCatch try_catch(env->GetIsolate());
CHECK(!a->InstanceOf(env.local(), f).ToChecked());
CHECK(b->InstanceOf(env.local(), f).ToChecked());
CHECK(c->InstanceOf(env.local(), f).ToChecked());
CHECK(!d->InstanceOf(env.local(), f).ToChecked());
CHECK(!e->InstanceOf(env.local(), f).ToChecked());
CHECK(!try_catch.HasCaught());
CHECK(a->InstanceOf(env.local(), g).IsNothing());
CHECK(try_catch.HasCaught());
try_catch.Reset();
CHECK(b->InstanceOf(env.local(), h).IsNothing());
CHECK(try_catch.HasCaught());
try_catch.Reset();
CHECK(v8_num(1)->InstanceOf(env.local(), j).ToChecked());
CHECK(!try_catch.HasCaught());
CHECK(d->InstanceOf(env.local(), k).ToChecked());
CHECK(e->InstanceOf(env.local(), k).ToChecked());
CHECK(!d->InstanceOf(env.local(), l).ToChecked());
CHECK(e->InstanceOf(env.local(), l).ToChecked());
CHECK(!try_catch.HasCaught());
}
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(context.local()).IsEmpty();
}
}
static void GetXValue(Local<Name> name,
const v8::PropertyCallbackInfo<v8::Value>& info) {
ApiTestFuzzer::Fuzz();
CHECK(info.Data()
->Equals(CcTest::isolate()->GetCurrentContext(), v8_str("donut"))
.FromJust());
CHECK(name->Equals(CcTest::isolate()->GetCurrentContext(), v8_str("x"))
.FromJust());
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, nullptr, v8_str("donut"));
CHECK(context->Global()
->Set(context.local(), v8_str("obj"),
templ->NewInstance(context.local()).ToLocalChecked())
.FromJust());
Local<Script> script = v8_compile("obj.x");
for (int i = 0; i < 10; i++) {
Local<Value> result = script->Run(context.local()).ToLocalChecked();
CHECK(result->Equals(context.local(), v8_str("x")).FromJust());
}
}
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, nullptr, v8_str("donut"));
CHECK(context->Global()
->Set(context.local(), v8_str("obj"),
templ->NewInstance(context.local()).ToLocalChecked())
.FromJust());
// 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(context.local()).ToLocalChecked();
CHECK(result->BooleanValue(context.local()).FromJust());
// 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(context.local()).ToLocalChecked();
CHECK(result->Equals(context.local(), v8_num(42)).FromJust());
// Check that the accessor is still configurable
result = script_desc->Run(context.local()).ToLocalChecked();
CHECK(result->BooleanValue(context.local()).FromJust());
// 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(context.local()).ToLocalChecked();
CHECK(result->Equals(context.local(), v8_num(43)).FromJust());
result = script_desc->Run(context.local()).ToLocalChecked();
CHECK(!result->BooleanValue(context.local()).FromJust());
// Make sure that it is not possible to redefine again
v8::TryCatch try_catch(isolate);
CHECK(script_define->Run(context.local()).IsEmpty());
CHECK(try_catch.HasCaught());
String::Utf8Value exception_value(isolate, 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, nullptr, v8_str("donut"));
LocalContext context;
CHECK(context->Global()
->Set(context.local(), v8_str("obj"),
templ->NewInstance(context.local()).ToLocalChecked())
.FromJust());
Local<Script> script_desc = v8_compile(
"var prop ="
"Object.getOwnPropertyDescriptor( "
"obj, 'x');"
"prop.configurable;");
Local<Value> result = script_desc->Run(context.local()).ToLocalChecked();
CHECK(result->BooleanValue(context.local()).FromJust());
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(context.local()).ToLocalChecked();
CHECK(result->Equals(context.local(), v8_num(42)).FromJust());
result = script_desc->Run(context.local()).ToLocalChecked();
CHECK(result->BooleanValue(context.local()).FromJust());
script_define = v8_compile(
"var desc = {get: function(){return 43; },"
" configurable: false };"
"Object.defineProperty(obj, 'x', desc);"
"obj.x");
result = script_define->Run(context.local()).ToLocalChecked();
CHECK(result->Equals(context.local(), v8_num(43)).FromJust());
result = script_desc->Run(context.local()).ToLocalChecked();
CHECK(!result->BooleanValue(context.local()).FromJust());
v8::TryCatch try_catch(isolate);
CHECK(script_define->Run(context.local()).IsEmpty());
CHECK(try_catch.HasCaught());
String::Utf8Value exception_value(isolate, try_catch.Exception());
CHECK_EQ(0,
strcmp(*exception_value, "TypeError: Cannot redefine property: x"));
}
static v8::Local<v8::Object> GetGlobalProperty(LocalContext* context,
char const* name) {
return v8::Local<v8::Object>::Cast(
(*context)
->Global()
->Get(CcTest::isolate()->GetCurrentContext(), v8_str(name))
.ToLocalChecked());
}
THREADED_TEST(DefineAPIAccessorOnObject) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
LocalContext context;
CHECK(context->Global()
->Set(context.local(), v8_str("obj1"),
templ->NewInstance(context.local()).ToLocalChecked())
.FromJust());
CompileRun("var obj2 = {};");
CHECK(CompileRun("obj1.x")->IsUndefined());
CHECK(CompileRun("obj2.x")->IsUndefined());
CHECK(GetGlobalProperty(&context, "obj1")
->SetAccessor(context.local(), v8_str("x"), GetXValue, nullptr,
v8_str("donut"))
.FromJust());
ExpectString("obj1.x", "x");
CHECK(CompileRun("obj2.x")->IsUndefined());
CHECK(GetGlobalProperty(&context, "obj2")
->SetAccessor(context.local(), v8_str("x"), GetXValue, nullptr,
v8_str("donut"))
.FromJust());
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(context.local(), v8_str("x"), GetXValue, nullptr,
v8_str("donut"))
.FromJust());
CHECK(GetGlobalProperty(&context, "obj2")
->SetAccessor(context.local(), v8_str("x"), GetXValue, nullptr,
v8_str("donut"))
.FromJust());
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(context.local(), v8_str("x"), GetXValue, nullptr,
v8_str("donut"))
.FromJust());
CHECK(!GetGlobalProperty(&context, "obj2")
->SetAccessor(context.local(), v8_str("x"), GetXValue, nullptr,
v8_str("donut"))
.FromJust());
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;
CHECK(context->Global()
->Set(context.local(), v8_str("obj1"),
templ->NewInstance(context.local()).ToLocalChecked())
.FromJust());
CompileRun("var obj2 = {};");
CHECK(GetGlobalProperty(&context, "obj1")
->SetAccessor(context.local(), v8_str("x"), GetXValue, nullptr,
v8_str("donut"), v8::DEFAULT, v8::DontDelete)
.FromJust());
CHECK(GetGlobalProperty(&context, "obj2")
->SetAccessor(context.local(), v8_str("x"), GetXValue, nullptr,
v8_str("donut"), v8::DEFAULT, v8::DontDelete)
.FromJust());
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(context.local(), v8_str("x"), GetXValue, nullptr,
v8_str("donut"))
.FromJust());
CHECK(!GetGlobalProperty(&context, "obj2")
->SetAccessor(context.local(), v8_str("x"), GetXValue, nullptr,
v8_str("donut"))
.FromJust());
{
v8::TryCatch try_catch(isolate);
CompileRun(
"Object.defineProperty(obj1, 'x',"
"{get: function() { return 'func'; }})");
CHECK(try_catch.HasCaught());
String::Utf8Value exception_value(isolate, 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(isolate, try_catch.Exception());
CHECK_EQ(
0, strcmp(*exception_value, "TypeError: Cannot redefine property: x"));
}
}
static void Get239Value(Local<Name> name,
const v8::PropertyCallbackInfo<v8::Value>& info) {
ApiTestFuzzer::Fuzz();
CHECK(info.Data()
->Equals(info.GetIsolate()->GetCurrentContext(), v8_str("donut"))
.FromJust());
CHECK(name->Equals(info.GetIsolate()->GetCurrentContext(), v8_str("239"))
.FromJust());
info.GetReturnValue().Set(name);
}
THREADED_TEST(ElementAPIAccessor) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
LocalContext context;
CHECK(context->Global()
->Set(context.local(), v8_str("obj1"),
templ->NewInstance(context.local()).ToLocalChecked())
.FromJust());
CompileRun("var obj2 = {};");
CHECK(GetGlobalProperty(&context, "obj1")
->SetAccessor(context.local(), v8_str("239"), Get239Value, nullptr,
v8_str("donut"))
.FromJust());
CHECK(GetGlobalProperty(&context, "obj2")
->SetAccessor(context.local(), v8_str("239"), Get239Value, nullptr,
v8_str("donut"))
.FromJust());
ExpectString("obj1[239]", "239");
ExpectString("obj2[239]", "239");
ExpectString("obj1['239']", "239");
ExpectString("obj2['239']", "239");
}
v8::Persistent<Value> xValue;
static void SetXValue(Local<Name> name, Local<Value> value,
const v8::PropertyCallbackInfo<void>& info) {
Local<Context> context = info.GetIsolate()->GetCurrentContext();
CHECK(value->Equals(context, v8_num(4)).FromJust());
CHECK(info.Data()->Equals(context, v8_str("donut")).FromJust());
CHECK(name->Equals(context, v8_str("x")).FromJust());
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;
CHECK(context->Global()
->Set(context.local(), v8_str("obj"),
templ->NewInstance(context.local()).ToLocalChecked())
.FromJust());
Local<Script> script = v8_compile("obj.x = 4");
for (int i = 0; i < 10; i++) {
CHECK(xValue.IsEmpty());
script->Run(context.local()).ToLocalChecked();
CHECK(v8_num(4)
->Equals(context.local(),
Local<Value>::New(CcTest::isolate(), xValue))
.FromJust());
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"), nullptr, SetXValue, v8_str("donut"));
LocalContext context;
CHECK(context->Global()
->Set(context.local(), v8_str("obj"),
templ->NewInstance(context.local()).ToLocalChecked())
.FromJust());
Local<Script> script = v8_compile("obj.x = 4; obj.x");
for (int i = 0; i < 10; i++) {
CHECK(xValue.IsEmpty());
script->Run(context.local()).ToLocalChecked();
CHECK(v8_num(4)
->Equals(context.local(),
Local<Value>::New(CcTest::isolate(), xValue))
.FromJust());
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>(nullptr), nullptr,
v8_str("donut"));
LocalContext context;
CHECK(context->Global()
->Set(context.local(), v8_str("obj"),
templ->NewInstance(context.local()).ToLocalChecked())
.FromJust());
Local<Script> script = v8_compile("obj.x = 4; obj.x");
for (int i = 0; i < 10; i++) {
script->Run(context.local()).ToLocalChecked();
}
}
THREADED_TEST(MultiContexts) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Local<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::Local<v8::Object> global0 = context0->Global();
CHECK(global0->Set(context0.local(), v8_str("custom"), v8_num(1234))
.FromJust());
CHECK_EQ(1234, global0->Get(context0.local(), v8_str("custom"))
.ToLocalChecked()
->Int32Value(context0.local())
.FromJust());
// Create an independent environment
LocalContext context1(0, templ);
context1->SetSecurityToken(password);
v8::Local<v8::Object> global1 = context1->Global();
CHECK(global1->Set(context1.local(), v8_str("custom"), v8_num(1234))
.FromJust());
CHECK(!global0->Equals(context1.local(), global1).FromJust());
CHECK_EQ(1234, global0->Get(context1.local(), v8_str("custom"))
.ToLocalChecked()
->Int32Value(context0.local())
.FromJust());
CHECK_EQ(1234, global1->Get(context1.local(), v8_str("custom"))
.ToLocalChecked()
->Int32Value(context1.local())
.FromJust());
// Now create a new context with the old global
LocalContext context2(0, templ, global1);
context2->SetSecurityToken(password);
v8::Local<v8::Object> global2 = context2->Global();
CHECK(global1->Equals(context2.local(), global2).FromJust());
CHECK_EQ(0, global1->Get(context2.local(), v8_str("custom"))
.ToLocalChecked()
->Int32Value(context1.local())
.FromJust());
CHECK_EQ(0, global2->Get(context2.local(), v8_str("custom"))
.ToLocalChecked()
->Int32Value(context2.local())
.FromJust());
}
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::Local<v8::Object> global0 = env0->Global();
v8::Local<v8::Object> object0 = global0->Get(env0.local(), v8_str("Object"))
.ToLocalChecked()
.As<v8::Object>();
v8::Local<v8::Object> tostring0 =
object0->Get(env0.local(), v8_str("toString"))
.ToLocalChecked()
.As<v8::Object>();
v8::Local<v8::Object> proto0 =
tostring0->Get(env0.local(), v8_str("__proto__"))
.ToLocalChecked()
.As<v8::Object>();
CHECK(proto0->Set(env0.local(), v8_str("custom"), v8_num(1234)).FromJust());
LocalContext env1;
v8::Local<v8::Object> global1 = env1->Global();
v8::Local<v8::Object> object1 = global1->Get(env1.local(), v8_str("Object"))
.ToLocalChecked()
.As<v8::Object>();
v8::Local<v8::Object> tostring1 =
object1->Get(env1.local(), v8_str("toString"))
.ToLocalChecked()
.As<v8::Object>();
v8::Local<v8::Object> proto1 =
tostring1->Get(env1.local(), v8_str("__proto__"))
.ToLocalChecked()
.As<v8::Object>();
CHECK(!proto1->Has(env1.local(), v8_str("custom")).FromJust());
}
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(env0.local())
.ToLocalChecked()
->NumberValue(env0.local())
.FromJust());
LocalContext env1;
Local<Script> script1 = v8_compile(source);
CHECK_EQ(8901.0, script1->Run(env1.local())
.ToLocalChecked()
->NumberValue(env1.local())
.FromJust());
}
static void ReturnThis(const v8::FunctionCallbackInfo<v8::Value>& args) {
args.GetReturnValue().Set(args.This());
}
THREADED_TEST(UndetectableObject) {
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
Local<v8::FunctionTemplate> desc =
v8::FunctionTemplate::New(env->GetIsolate());
desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable
desc->InstanceTemplate()->SetCallAsFunctionHandler(ReturnThis); // callable
Local<v8::Object> obj = desc->GetFunction(env.local())
.ToLocalChecked()
->NewInstance(env.local())
.ToLocalChecked();
CHECK(
env->Global()->Set(env.local(), v8_str("undetectable"), obj).FromJust());
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
desc->InstanceTemplate()->SetCallAsFunctionHandler(ReturnThis); // callable
Local<v8::Object> obj = desc->GetFunction(env.local())
.ToLocalChecked()
->NewInstance(env.local())
.ToLocalChecked();
CHECK(
env->Global()->Set(env.local(), v8_str("undetectable"), obj).FromJust());
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
desc->InstanceTemplate()->SetCallAsFunctionHandler(ReturnThis); // callable
Local<v8::Object> obj = desc->GetFunction(env.local())
.ToLocalChecked()
->NewInstance(env.local())
.ToLocalChecked();
CHECK(
env->Global()->Set(env.local(), v8_str("undetectable"), obj).FromJust());
Local<String> source = v8_str(
"undetectable.x = 42;"
"undetectable.x");
Local<Script> script = v8_compile(source);
CHECK(v8::Integer::New(isolate, 42)
->Equals(env.local(), script->Run(env.local()).ToLocalChecked())
.FromJust());
ExpectBoolean("Object.isExtensible(undetectable)", true);
source = v8_str("Object.preventExtensions(undetectable);");
script = v8_compile(source);
script->Run(env.local()).ToLocalChecked();
ExpectBoolean("Object.isExtensible(undetectable)", false);
source = v8_str("undetectable.y = 2000;");
script = v8_compile(source);
script->Run(env.local()).ToLocalChecked();
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::Local<Context> context = Context::New(CcTest::isolate(), &extensions);
Context::Scope lock(context);
v8::Local<Value> result = CompileRun("Foo()");
CHECK(result->Equals(context, v8::Integer::New(CcTest::isolate(), 4))
.FromJust());
}
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::Local<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, v8_run_int32value(v8_compile("error.stack.indexOf('foo')")));
CHECK_EQ(-1, v8_run_int32value(v8_compile("error.stack.indexOf('bar')")));
CHECK_NE(-1, v8_run_int32value(v8_compile("error.stack.indexOf('user')")));
}
TEST(NullExtensions) {
v8::HandleScope handle_scope(CcTest::isolate());
v8::RegisterExtension(new Extension("nulltest", nullptr));
const char* extension_names[] = {"nulltest"};
v8::ExtensionConfiguration extensions(1, extension_names);
v8::Local<Context> context = Context::New(CcTest::isolate(), &extensions);
Context::Scope lock(context);
v8::Local<Value> result = CompileRun("1+3");
CHECK(result->Equals(context, v8::Integer::New(CcTest::isolate(), 4))
.FromJust());
}
static const char* kEmbeddedExtensionSource =
"function Ret54321(){return 54321;}~~@@$"
"$%% THIS IS A SERIES OF NON-nullptr-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::Local<Context> context = Context::New(CcTest::isolate(), &extensions);
CHECK_NULL(*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::Local<Context> context = Context::New(CcTest::isolate(), &extensions);
if (source_len == kEmbeddedExtensionSourceValidLen) {
Context::Scope lock(context);
v8::Local<Value> result = CompileRun("Ret54321()");
CHECK(v8::Integer::New(CcTest::isolate(), 54321)
->Equals(context, result)
.FromJust());
} else {
// Anything but exactly the right length should fail to compile.
CHECK_NULL(*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::Local<Context> context = Context::New(CcTest::isolate(), &extensions);
Context::Scope lock(context);
v8::Local<Value> result = CompileRun("UseEval1()");
CHECK(result->Equals(context, v8::Integer::New(CcTest::isolate(), 42))
.FromJust());
result = CompileRun("UseEval2()");
CHECK(result->Equals(context, v8::Integer::New(CcTest::isolate(), 42))
.FromJust());
}
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::Local<Context> context = Context::New(CcTest::isolate(), &extensions);
Context::Scope lock(context);
v8::Local<Value> result = CompileRun("UseWith1()");
CHECK(result->Equals(context, v8::Integer::New(CcTest::isolate(), 87))
.FromJust());
result = CompileRun("UseWith2()");
CHECK(result->Equals(context, v8::Integer::New(CcTest::isolate(), 87))
.FromJust());
}
TEST(AutoExtensions) {
v8::HandleScope handle_scope(CcTest::isolate());
Extension* extension = new Extension("autotest", kSimpleExtensionSource);
extension->set_auto_enable(true);
v8::RegisterExtension(extension);
v8::Local<Context> context = Context::New(CcTest::isolate());
Context::Scope lock(context);
v8::Local<Value> result = CompileRun("Foo()");
CHECK(result->Equals(context, v8::Integer::New(CcTest::isolate(), 4))
.FromJust());
}
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::Local<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::Local<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');"
"}";
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::Local<Context> context = Context::New(CcTest::isolate(), &extensions);
Context::Scope lock(context);
v8::Local<Value> result = CompileRun(kNativeCallTest);
CHECK(result->Equals(context, v8::Integer::New(CcTest::isolate(), 24))
.FromJust());
}
class NativeFunctionExtension : public Extension {
public:
NativeFunctionExtension(const char* name, const char* source,
v8::FunctionCallback fun = &Echo)
: Extension(name, source), function_(fun) {}
virtual v8::Local<v8::FunctionTemplate> GetNativeFunctionTemplate(
v8::Isolate* isolate, v8::Local<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::Local<Context> context = Context::New(CcTest::isolate(), &extensions);
Context::Scope lock(context);
v8::Local<Value> result = CompileRun("foo(42);");
CHECK(result->Equals(context, v8::Integer::New(CcTest::isolate(), 42))
.FromJust());
}
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::Local<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::Local<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(
v8_str(expected)
->Equals(context.local(), context->Global()
->Get(context.local(), v8_str("loaded"))
.ToLocalChecked())
.FromJust());
}
/*
* 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.local(), context->Global()
->Get(context.local(), v8_str("loaded"))
.ToLocalChecked())
.FromJust());
}
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()) {
CHECK(args.This()
->Set(args.GetIsolate()->GetCurrentContext(), v8_str("data"),
args.Data())
.FromJust());
args.GetReturnValue().SetNull();
return;
}
args.GetReturnValue().Set(args.Data());
}
class FunctionExtension : public Extension {
public:
FunctionExtension() : Extension("functiontest", kExtensionTestScript) {}
virtual v8::Local<v8::FunctionTemplate> GetNativeFunctionTemplate(
v8::Isolate* isolate, v8::Local<String> name);
};
static int lookup_count = 0;
v8::Local<v8::FunctionTemplate> FunctionExtension::GetNativeFunctionTemplate(
v8::Isolate* isolate, v8::Local<String> name) {
lookup_count++;
if (name->Equals(isolate->GetCurrentContext(), v8_str("A")).FromJust()) {
return v8::FunctionTemplate::New(isolate, CallFun,
v8::Integer::New(isolate, 8));
} else if (name->Equals(isolate->GetCurrentContext(), v8_str("B"))
.FromJust()) {
return v8::FunctionTemplate::New(isolate, CallFun,
v8::Integer::New(isolate, 7));
} else if (name->Equals(isolate->GetCurrentContext(), v8_str("C"))
.FromJust()) {
return v8::FunctionTemplate::New(isolate, CallFun,
v8::Integer::New(isolate, 6));
} else {
return v8::Local<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(context.local(), CompileRun("Foo(0)"))
.FromJust());
CHECK(v8::Integer::New(CcTest::isolate(), 7)
->Equals(context.local(), CompileRun("Foo(1)"))
.FromJust());
CHECK(v8::Integer::New(CcTest::isolate(), 6)
->Equals(context.local(), CompileRun("Foo(2)"))
.FromJust());
}
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(context.local(), CompileRun("(new A()).data"))
.FromJust());
CHECK(v8::Integer::New(CcTest::isolate(), 7)
->Equals(context.local(), CompileRun("(new B()).data"))
.FromJust());
CHECK(v8::Integer::New(CcTest::isolate(), 6)
->Equals(context.local(), CompileRun("(new C()).data"))
.FromJust());
}
}
static const char* last_location;
static const char* last_message;
void StoringErrorCallback(const char* location, const char* message) {
if (last_location == nullptr) {
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) {
CcTest::isolate()->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 = nullptr;
v8::ExtensionConfiguration config(1, bDeps);
v8::Local<Context> context = Context::New(CcTest::isolate(), &config);
CHECK(context.IsEmpty());
CHECK(last_location);
}
static size_t dcheck_count;
void DcheckErrorCallback(const char* file, int line, const char* message) {
last_message = message;
++dcheck_count;
}
TEST(DcheckErrorHandler) {
V8::SetDcheckErrorHandler(DcheckErrorCallback);
last_message = nullptr;
dcheck_count = 0;
DCHECK(false && "w00t");
#ifdef DEBUG
CHECK_EQ(dcheck_count, 1);
CHECK(last_message);
CHECK(std::string(last_message).find("w00t") != std::string::npos);
#else
// The DCHECK should be a noop in non-DEBUG builds.
CHECK_EQ(dcheck_count, 0);
#endif
}
static void MissingScriptInfoMessageListener(v8::Local<v8::Message> message,
v8::Local<Value> data) {
v8::Isolate* isolate = CcTest::isolate();
Local<Context> context = isolate->GetCurrentContext();
CHECK(message->GetScriptOrigin().ResourceName()->IsUndefined());
CHECK(v8::Undefined(isolate)
->Equals(context, message->GetScriptOrigin().ResourceName())
.FromJust());
message->GetLineNumber(context).FromJust();
message->GetSourceLine(context).ToLocalChecked();
}
THREADED_TEST(ErrorWithMissingScriptInfo) {
LocalContext context;
v8::HandleScope scope(context->GetIsolate());
context->GetIsolate()->AddMessageListener(MissingScriptInfoMessageListener);
CompileRun("throw Error()");
context->GetIsolate()->RemoveMessageListeners(
MissingScriptInfoMessageListener);
}
struct FlagAndPersistent {
bool flag;
v8::Global<v8::Object> handle;
};
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.GetInternalField(0));
Trivial2* t2 = reinterpret_cast<Trivial2*>(data.GetInternalField(1));
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::Persistent<v8::Object> handle;
{
v8::HandleScope scope(isolate);
Local<v8::Object> obj = templ->GetFunction(env.local())
.ToLocalChecked()
->NewInstance(env.local())
.ToLocalChecked();
handle.Reset(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) {
CcTest::CollectAllGarbage();
} else {
CcTest::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);
}
v8::Local<Function> args_fun;
static void ArgumentsTestCallback(
const v8::FunctionCallbackInfo<v8::Value>& args) {
ApiTestFuzzer::Fuzz();
v8::Isolate* isolate = args.GetIsolate();
Local<Context> context = isolate->GetCurrentContext();
CHECK_EQ(3, args.Length());
CHECK(v8::Integer::New(isolate, 1)->Equals(context, args[0]).FromJust());
CHECK(v8::Integer::New(isolate, 2)->Equals(context, args[1]).FromJust());
CHECK(v8::Integer::New(isolate, 3)->Equals(context, args[2]).FromJust());
CHECK(v8::Undefined(isolate)->Equals(context, args[3]).FromJust());
v8::HandleScope scope(args.GetIsolate());
CcTest::CollectAllGarbage();
}
THREADED_TEST(Arguments) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Local<v8::ObjectTemplate> global = ObjectTemplate::New(isolate);
global->Set(v8_str("f"),
v8::FunctionTemplate::New(isolate, ArgumentsTestCallback));
LocalContext context(nullptr, global);
args_fun = context->Global()
->Get(context.local(), v8_str("f"))
.ToLocalChecked()
.As<Function>();
v8_compile("f(1, 2, 3)")->Run(context.local()).ToLocalChecked();
}
static int p_getter_count;
static int p_getter_count2;
static void PGetter(Local<Name> name,
const v8::PropertyCallbackInfo<v8::Value>& info) {
ApiTestFuzzer::Fuzz();
p_getter_count++;
v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();
v8::Local<v8::Object> global = context->Global();
CHECK(
info.Holder()
->Equals(context, global->Get(context, v8_str("o1")).ToLocalChecked())
.FromJust());
if (name->Equals(context, v8_str("p1")).FromJust()) {
CHECK(info.This()
->Equals(context,
global->Get(context, v8_str("o1")).ToLocalChecked())
.FromJust());
} else if (name->Equals(context, v8_str("p2")).FromJust()) {
CHECK(info.This()
->Equals(context,
global->Get(context, v8_str("o2")).ToLocalChecked())
.FromJust());
} else if (name->Equals(context, v8_str("p3")).FromJust()) {
CHECK(info.This()
->Equals(context,
global->Get(context, v8_str("o3")).ToLocalChecked())
.FromJust());
} else if (name->Equals(context, v8_str("p4")).FromJust()) {
CHECK(info.This()
->Equals(context,
global->Get(context, v8_str("o4")).ToLocalChecked())
.FromJust());
}
}
static void RunHolderTest(v8::Local<v8::ObjectTemplate> obj) {
ApiTestFuzzer::Fuzz();
LocalContext context;
CHECK(context->Global()
->Set(context.local(), v8_str("o1"),
obj->NewInstance(context.local()).ToLocalChecked())
.FromJust());
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::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();
v8::Local<v8::Object> global = context->Global();
CHECK(
info.Holder()
->Equals(context, global->Get(context, v8_str("o1")).ToLocalChecked())
.FromJust());
if (name->Equals(context, v8_str("p1")).FromJust()) {
CHECK(info.This()
->Equals(context,
global->Get(context, v8_str("o1")).ToLocalChecked())
.FromJust());
} else if (name->Equals(context, v8_str("p2")).FromJust()) {
CHECK(info.This()
->Equals(context,
global->Get(context, v8_str("o2")).ToLocalChecked())
.FromJust());
} else if (name->Equals(context, v8_str("p3")).FromJust()) {
CHECK(info.This()
->Equals(context,
global->Get(context, v8_str("o3")).ToLocalChecked())
.FromJust());
} else if (name->Equals(context, v8_str("p4")).FromJust()) {
CHECK(info.This()
->Equals(context,
global->Get(context, v8_str("o4")).ToLocalChecked())
.FromJust());
}
}
THREADED_TEST(GetterHolders) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Local<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::Local<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::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
templ->SetAccessor(v8_str("t"), PGetter2);
LocalContext context;
CHECK(context->Global()
->Set(context.local(), v8_str("o"),
templ->NewInstance(context.local()).ToLocalChecked())
.FromJust());
for (int i = 0; i < 100; i++) {
v8::HandleScope inner_scope(CcTest::isolate());
v8::Local<v8::Object> obj =
templ->NewInstance(context.local()).ToLocalChecked();
CHECK(!obj->Equals(context.local(), context->Global()
->Get(context.local(), v8_str("o"))
.ToLocalChecked())
.FromJust());
CHECK(
context->Global()->Set(context.local(), v8_str("o2"), obj).FromJust());
v8::Local<Value> value = CompileRun("o.__proto__ === o2.__proto__");
CHECK(v8::True(isolate)->Equals(context.local(), value).FromJust());
CHECK(context->Global()->Set(context.local(), v8_str("o"), obj).FromJust());
}
}
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(Local<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::Local<String> str = v8_str("abcde");
// abc<Icelandic eth><Unicode snowman>.
v8::Local<String> str2 = v8_str("abc\xC3\xB0\xE2\x98\x83");
v8::Local<String> str3 =
v8::String::NewFromUtf8(context->GetIsolate(), "abc\0def",
v8::NewStringType::kNormal, 7)
.ToLocalChecked();
// "ab" + lead surrogate + "wx" + trail surrogate + "yz"
uint16_t orphans[8] = {0x61, 0x62, 0xD800, 0x77, 0x78, 0xDC00, 0x79, 0x7A};
v8::Local<String> orphans_str =
v8::String::NewFromTwoByte(context->GetIsolate(), orphans,
v8::NewStringType::kNormal, 8)
.ToLocalChecked();
// single lead surrogate
uint16_t lead[1] = {0xD800};
v8::Local<String> lead_str =
v8::String::NewFromTwoByte(context->GetIsolate(), lead,
v8::NewStringType::kNormal, 1)
.ToLocalChecked();
// single trail surrogate
uint16_t trail[1] = {0xDC00};
v8::Local<String> trail_str =
v8::String::NewFromTwoByte(context->GetIsolate(), trail,
v8::NewStringType::kNormal, 1)
.ToLocalChecked();
// surrogate pair
uint16_t pair[2] = {0xD800, 0xDC00};
v8::Local<String> pair_str =
v8::String::NewFromTwoByte(context->GetIsolate(), pair,
v8::NewStringType::kNormal, 2)
.ToLocalChecked();
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::Local<v8::Object> global = context->Global();
Local<String> left_tree = global->Get(context.local(), v8_str("left"))
.ToLocalChecked()
.As<String>();
Local<String> right_tree = global->Get(context.local(), v8_str("right"))
.ToLocalChecked()
.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\xC3\xB0\xE2\x98\x83"));
memset(utf8buf, 0x1, 1000);
len = str2->WriteUtf8(utf8buf, 8, &charlen);
CHECK_EQ(8, len);
CHECK_EQ(5, charlen);
CHECK_EQ(0, strncmp(utf8buf, "abc\xC3\xB0\xE2\x98\x83\x01", 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\xC3\xB0\x01", 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\xC3\xB0\x01", 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\xC3\xB0\x01", 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\x01", 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\x01", 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\x01", 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\xED\xA0\x80wx\xED\xB0\x80yz"));
// 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\xEF\xBF\xBDwx\xEF\xBF\xBDyz"));
// 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, "\xEF\xBF\xBD"));
// 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, "\xEF\xBF\xBD"));
// 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\x01", 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\x01", 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\x01", 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\x01", 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\xC3\xB0\xE2\x98\x83", 8));
CHECK_NE(0, strcmp(utf8buf, "abc\xC3\xB0\xE2\x98\x83"));
utf8buf[8] = '\0';
CHECK_EQ(0, strcmp(utf8buf, "abc\xC3\xB0\xE2\x98\x83"));
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(nullptr, 0, 0, String::NO_NULL_TERMINATION));
CHECK_EQ(0, str->WriteUtf8(nullptr, 0, 0, String::NO_NULL_TERMINATION));
CHECK_EQ(0, str->Write(nullptr, 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(context.local(), v8_str(name)).ToLocalChecked());
Local<v8::Array> alens =
Local<v8::Array>::Cast(context->Global()
->Get(context.local(), v8_str(lengths_name))
.ToLocalChecked());
for (int i = 0; i < len; i++) {
Local<v8::String> string =
Local<v8::String>::Cast(a->Get(context.local(), i).ToLocalChecked());
Local<v8::Number> expected_len = Local<v8::Number>::Cast(
alens->Get(context.local(), i).ToLocalChecked());
int length = GetUtf8Length(string);
CHECK_EQ(static_cast<int>(expected_len->Value()), length);
}
}
void TestUtf8DecodingAgainstReference(
const char* cases[],
const std::vector<std::vector<uint16_t>>& unicode_expected) {
for (size_t test_ix = 0; test_ix < unicode_expected.size(); ++test_ix) {
v8::Local<String> str = v8_str(cases[test_ix]);
CHECK_EQ(unicode_expected[test_ix].size(), str->Length());
std::unique_ptr<uint16_t[]> buffer(new uint16_t[str->Length()]);
str->Write(buffer.get(), 0, -1, String::NO_NULL_TERMINATION);
for (size_t i = 0; i < unicode_expected[test_ix].size(); ++i) {
CHECK_EQ(unicode_expected[test_ix][i], buffer[i]);
}
}
}
THREADED_TEST(OverlongSequencesAndSurrogates) {
LocalContext context;
v8::HandleScope scope(context->GetIsolate());
const char* cases[] = {
// Overlong 2-byte sequence.
"X\xc0\xbfY\0",
// Another overlong 2-byte sequence.
"X\xc1\xbfY\0",
// Overlong 3-byte sequence.
"X\xe0\x9f\xbfY\0",
// Overlong 4-byte sequence.
"X\xf0\x89\xbf\xbfY\0",
// Invalid 3-byte sequence (reserved for surrogates).
"X\xed\xa0\x80Y\0",
// Invalid 4-bytes sequence (value out of range).
"X\xf4\x90\x80\x80Y\0",
// Start of an overlong 3-byte sequence but not enough continuation bytes.
"X\xe0\x9fY\0",
// Start of an overlong 4-byte sequence but not enough continuation bytes.
"X\xf0\x89\xbfY\0",
// Start of an invalid 3-byte sequence (reserved for surrogates) but not
// enough continuation bytes.
"X\xed\xa0Y\0",
// Start of an invalid 4-bytes sequence (value out of range) but not
// enough continuation bytes.
"X\xf4\x90\x80Y\0",
};
const std::vector<std::vector<uint16_t>> unicode_expected = {
{0x58, 0xFFFD, 0xFFFD, 0x59},
{0x58, 0xFFFD, 0xFFFD, 0x59},
{0x58, 0xFFFD, 0xFFFD, 0xFFFD, 0x59},
{0x58, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0x59},
{0x58, 0xFFFD, 0xFFFD, 0xFFFD, 0x59},
{0x58, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0x59},
{0x58, 0xFFFD, 0xFFFD, 0x59},
{0x58, 0xFFFD, 0xFFFD, 0xFFFD, 0x59},
{0x58, 0xFFFD, 0xFFFD, 0x59},
{0x58, 0xFFFD, 0xFFFD, 0xFFFD, 0x59},
};
CHECK_EQ(unicode_expected.size(), arraysize(cases));
TestUtf8DecodingAgainstReference(cases, unicode_expected);
}
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(Local<String> s1, Local<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());
Local<String> symbol1 =
v8::String::NewFromUtf8(context->GetIsolate(), "abc",
v8::NewStringType::kInternalized)
.ToLocalChecked();
Local<String> symbol2 =
v8::String::NewFromUtf8(context->GetIsolate(), "abc",
v8::NewStringType::kInternalized)
.ToLocalChecked();
CHECK(SameSymbol(symbol1, symbol2));
CompileRun(
"var sym0 = 'benedictus';"
"var sym0b = 'S\xC3\xB8ren';"
"var sym1 = '\xED\xA0\x81\xED\xB0\x87';"
"var sym2 = '\xF0\x90\x90\x88';"
"var sym3 = 'x\xED\xA0\x81\xED\xB0\x87';"
"var sym4 = 'x\xF0\x90\x90\x88';"
"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);");
Local<String> sym0 =
v8::String::NewFromUtf8(context->GetIsolate(), "benedictus",
v8::NewStringType::kInternalized)
.ToLocalChecked();
Local<String> sym0b =
v8::String::NewFromUtf8(context->GetIsolate(), "S\xC3\xB8ren",
v8::NewStringType::kInternalized)
.ToLocalChecked();
Local<String> sym1 =
v8::String::NewFromUtf8(context->GetIsolate(), "\xED\xA0\x81\xED\xB0\x87",
v8::NewStringType::kInternalized)
.ToLocalChecked();
Local<String> sym2 =
v8::String::NewFromUtf8(context->GetIsolate(), "\xF0\x90\x90\x88",
v8::NewStringType::kInternalized)
.ToLocalChecked();
Local<String> sym3 = v8::String::NewFromUtf8(context->GetIsolate(),
"x\xED\xA0\x81\xED\xB0\x87",
v8::NewStringType::kInternalized)
.ToLocalChecked();
Local<String> sym4 =
v8::String::NewFromUtf8(context->GetIsolate(), "x\xF0\x90\x90\x88",
v8::NewStringType::kInternalized)
.ToLocalChecked();
v8::Local<v8::Object> global = context->Global();
Local<Value> s0 =
global->Get(context.local(), v8_str("sym0")).ToLocalChecked();
Local<Value> s0b =
global->Get(context.local(), v8_str("sym0b")).ToLocalChecked();
Local<Value> s1 =
global->Get(context.local(), v8_str("sym1")).ToLocalChecked();
Local<Value> s2 =
global->Get(context.local(), v8_str("sym2")).ToLocalChecked();
Local<Value> s3 =
global->Get(context.local(), v8_str("sym3")).ToLocalChecked();
Local<Value> s4 =
global->Get(context.local(), v8_str("sym4")).ToLocalChecked();
CHECK(SameSymbol(sym0, Local<String>::Cast(s0)));
CHECK(SameSymbol(sym0b, Local<String>::Cast(s0b)));
CHECK(SameSymbol(sym1, Local<String>::Cast(s1)));
CHECK(SameSymbol(sym2, Local<String>::Cast(s2)));
CHECK(SameSymbol(sym3, Local<String>::Cast(s3)));
CHECK(SameSymbol(sym4, Local<String>::Cast(s4)));
}
THREADED_TEST(Utf16MissingTrailing) {
LocalContext context;
v8::HandleScope scope(context->GetIsolate());
// Make sure it will go past the buffer, so it will call `WriteUtf16Slow`
int size = 1024 * 64;
uint8_t* buffer = new uint8_t[size];
for (int i = 0; i < size; i += 4) {
buffer[i] = 0xF0;
buffer[i + 1] = 0x9D;
buffer[i + 2] = 0x80;
buffer[i + 3] = 0x9E;
}
// Now invoke the decoder without last 3 bytes
v8::Local<v8::String> str =
v8::String::NewFromUtf8(
context->GetIsolate(), reinterpret_cast<char*>(buffer),
v8::NewStringType::kNormal, size - 3).ToLocalChecked();
USE(str);
delete[] buffer;
}
THREADED_TEST(Utf16Trailing3Byte) {
LocalContext context;
v8::Isolate* isolate = context->GetIsolate();
v8::HandleScope scope(isolate);
// Make sure it will go past the buffer, so it will call `WriteUtf16Slow`
int size = 1024 * 63;
uint8_t* buffer = new uint8_t[size];
for (int i = 0; i < size; i += 3) {
buffer[i] = 0xE2;
buffer[i + 1] = 0x80;
buffer[i + 2] = 0xA6;
}
// Now invoke the decoder without last 3 bytes
v8::Local<v8::String> str =
v8::String::NewFromUtf8(isolate, reinterpret_cast<char*>(buffer),
v8::NewStringType::kNormal, size)
.ToLocalChecked();
v8::String::Value value(isolate, str);
CHECK_EQ(value.length(), size / 3);
CHECK_EQ((*value)[value.length() - 1], 0x2026);
delete[] buffer;
}
THREADED_TEST(ToArrayIndex) {
LocalContext context;
v8::Isolate* isolate = context->GetIsolate();
v8::HandleScope scope(isolate);
v8::Local<String> str = v8_str("42");
v8::MaybeLocal<v8::Uint32> index = str->ToArrayIndex(context.local());
CHECK(!index.IsEmpty());
CHECK_EQ(42.0,
index.ToLocalChecked()->Uint32Value(context.local()).FromJust());
str = v8_str("42asdf");
index = str->ToArrayIndex(context.local());
CHECK(index.IsEmpty());
str = v8_str("-42");
index = str->ToArrayIndex(context.local());
CHECK(index.IsEmpty());
str = v8_str("4294967294");
index = str->ToArrayIndex(context.local());
CHECK(!index.IsEmpty());
CHECK_EQ(4294967294.0,
index.ToLocalChecked()->Uint32Value(context.local()).FromJust());
v8::Local<v8::Number> num = v8::Number::New(isolate, 1);
index = num->ToArrayIndex(context.local());
CHECK(!index.IsEmpty());
CHECK_EQ(1.0,
index.ToLocalChecked()->Uint32Value(context.local()).FromJust());
num = v8::Number::New(isolate, -1);
index = num->ToArrayIndex(context.local());
CHECK(index.IsEmpty());
v8::Local<v8::Object> obj = v8::Object::New(isolate);
index = obj->ToArrayIndex(context.local());
CHECK(index.IsEmpty());
}
THREADED_TEST(ErrorConstruction) {
LocalContext context;
v8::HandleScope scope(context->GetIsolate());
v8::Local<String> foo = v8_str("foo");
v8::Local<String> message = v8_str("message");
v8::Local<Value> range_error = v8::Exception::RangeError(foo);
CHECK(range_error->IsObject());
CHECK(range_error.As<v8::Object>()
->Get(context.local(), message)
.ToLocalChecked()
->Equals(context.local(), foo)
.FromJust());
v8::Local<Value> reference_error = v8::Exception::ReferenceError(foo);
CHECK(reference_error->IsObject());
CHECK(reference_error.As<v8::Object>()
->Get(context.local(), message)
.ToLocalChecked()
->Equals(context.local(), foo)
.FromJust());
v8::Local<Value> syntax_error = v8::Exception::SyntaxError(foo);
CHECK(syntax_error->IsObject());
CHECK(syntax_error.As<v8::Object>()
->Get(context.local(), message)
.ToLocalChecked()
->Equals(context.local(), foo)
.FromJust());
v8::Local<Value> type_error = v8::Exception::TypeError(foo);
CHECK(type_error->IsObject());
CHECK(type_error.As<v8::Object>()
->Get(context.local(), message)
.ToLocalChecked()
->Equals(context.local(), foo)
.FromJust());
v8::Local<Value> error = v8::Exception::Error(foo);
CHECK(error->IsObject());
CHECK(error.As<v8::Object>()
->Get(context.local(), message)
.ToLocalChecked()
->Equals(context.local(), foo)
.FromJust());
}
static void ThrowV8Exception(const v8::FunctionCallbackInfo<v8::Value>& info) {
ApiTestFuzzer::Fuzz();
v8::Local<String> foo = v8_str("foo");
v8::Local<String> message = v8_str("message");
v8::Local<Value> error = v8::Exception::Error(foo);
CHECK(error->IsObject());
v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();
CHECK(error.As<v8::Object>()
->Get(context, message)
.ToLocalChecked()
->Equals(context, foo)
.FromJust());
info.GetIsolate()->ThrowException(error);
info.GetReturnValue().SetUndefined();
}
THREADED_TEST(ExceptionCreateMessage) {
LocalContext context;
v8::HandleScope scope(context->GetIsolate());
v8::Local<String> foo_str = v8_str("foo");
v8::Local<String> message_str = v8_str("message");
context->GetIsolate()->SetCaptureStackTraceForUncaughtExceptions(true);
Local<v8::FunctionTemplate> fun =
v8::FunctionTemplate::New(context->GetIsolate(), ThrowV8Exception);
v8::Local<v8::Object> global = context->Global();
CHECK(global->Set(context.local(), v8_str("throwV8Exception"),
fun->GetFunction(context.local()).ToLocalChecked())
.FromJust());
TryCatch try_catch(context->GetIsolate());
CompileRun(
"function f1() {\n"
" throwV8Exception();\n"
"};\n"
"f1();");
CHECK(try_catch.HasCaught());
v8::Local<v8::Value> error = try_catch.Exception();
CHECK(error->IsObject());
CHECK(error.As<v8::Object>()
->Get(context.local(), message_str)
.ToLocalChecked()
->Equals(context.local(), foo_str)
.FromJust());
v8::Local<v8::Message> message =
v8::Exception::CreateMessage(context->GetIsolate(), error);
CHECK(!message.IsEmpty());
CHECK_EQ(2, message->GetLineNumber(context.local()).FromJust());
CHECK_EQ(2, message->GetStartColumn(context.local()).FromJust());
v8::Local<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());
context->GetIsolate()->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(context.local(), message_str)
.ToLocalChecked()
->Equals(context.local(), foo_str)
.FromJust());
message = v8::Exception::CreateMessage(context->GetIsolate(), error);
CHECK(!message.IsEmpty());
CHECK_EQ(2, message->GetLineNumber(context.local()).FromJust());
CHECK_EQ(9, message->GetStartColumn(context.local()).FromJust());
// Should be empty stack trace.
stackTrace = message->GetStackTrace();
CHECK(stackTrace.IsEmpty());
CHECK(v8::Exception::GetStackTrace(error).IsEmpty());
}
THREADED_TEST(ExceptionCreateMessageLength) {
LocalContext context;
v8::HandleScope scope(context->GetIsolate());
// Test that the message is not truncated.
TryCatch try_catch(context->GetIsolate());
CompileRun(
"var message = 'm';"
"while (message.length < 1000) message += message;"
"throw message;");
CHECK(try_catch.HasCaught());
CHECK_LT(1000, try_catch.Message()->Get()->Length());
}
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());
v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();
if (this_obj->Has(context, name).FromJust())
this_obj->Delete(context, name).FromJust();
CHECK(this_obj->Set(context, name, value).FromJust());
}
THREADED_TEST(DeleteAccessor) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Local<v8::ObjectTemplate> obj = ObjectTemplate::New(isolate);
obj->SetAccessor(v8_str("y"), YGetter, YSetter);
LocalContext context;
v8::Local<v8::Object> holder =
obj->NewInstance(context.local()).ToLocalChecked();
CHECK(context->Global()
->Set(context.local(), v8_str("holder"), holder)
.FromJust());
v8::Local<Value> result =
CompileRun("holder.y = 11; holder.y = 12; holder.y");
CHECK_EQ(12u, result->Uint32Value(context.local()).FromJust());
}
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::Context> context = args.GetIsolate()->GetCurrentContext();
Local<v8::Object> arg_this = context->Global();
Local<Value> trouble_callee =
(trouble_nesting == 3)
? arg_this->Get(context, v8_str("trouble_callee")).ToLocalChecked()
: arg_this->Get(context, v8_str("trouble_caller")).ToLocalChecked();
CHECK(trouble_callee->IsFunction());
args.GetReturnValue().Set(Function::Cast(*trouble_callee)
->Call(context, arg_this, 0, nullptr)
.FromMaybe(v8::Local<v8::Value>()));
}
static int report_count = 0;
static void ApiUncaughtExceptionTestListener(v8::Local<v8::Message>,
v8::Local<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);
isolate->AddMessageListener(ApiUncaughtExceptionTestListener);
Local<v8::FunctionTemplate> fun =
v8::FunctionTemplate::New(isolate, TroubleCallback);
v8::Local<v8::Object> global = env->Global();
CHECK(global->Set(env.local(), v8_str("trouble"),
fun->GetFunction(env.local()).ToLocalChecked())
.FromJust());
CompileRun(
"function trouble_callee() {"
" var x = null;"
" return x.foo;"
"};"
"function trouble_caller() {"
" trouble();"
"};");
Local<Value> trouble =
global->Get(env.local(), v8_str("trouble")).ToLocalChecked();
CHECK(trouble->IsFunction());
Local<Value> trouble_callee =
global->Get(env.local(), v8_str("trouble_callee")).ToLocalChecked();
CHECK(trouble_callee->IsFunction());
Local<Value> trouble_caller =
global->Get(env.local(), v8_str("trouble_caller")).ToLocalChecked();
CHECK(trouble_caller->IsFunction());
Function::Cast(*trouble_caller)
->Call(env.local(), global, 0, nullptr)
.FromMaybe(v8::Local<v8::Value>());
CHECK_EQ(1, report_count);
isolate->RemoveMessageListeners(ApiUncaughtExceptionTestListener);
}
static const char* script_resource_name = "ExceptionInNativeScript.js";
static void ExceptionInNativeScriptTestListener(v8::Local<v8::Message> message,
v8::Local<Value>) {
v8::Local<v8::Value> name_val = message->GetScriptOrigin().ResourceName();
CHECK(!name_val.IsEmpty() && name_val->IsString());
v8::String::Utf8Value name(v8::Isolate::GetCurrent(),
message->GetScriptOrigin().ResourceName());
CHECK_EQ(0, strcmp(script_resource_name, *name));
v8::Local<v8::Context> context =
v8::Isolate::GetCurrent()->GetCurrentContext();
CHECK_EQ(3, message->GetLineNumber(context).FromJust());
v8::String::Utf8Value source_line(
v8::Isolate::GetCurrent(),
message->GetSourceLine(context).ToLocalChecked());
CHECK_EQ(0, strcmp(" new o.foo();", *source_line));
}
TEST(ExceptionInNativeScript) {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
isolate->AddMessageListener(ExceptionInNativeScriptTestListener);
Local<v8::FunctionTemplate> fun =
v8::FunctionTemplate::New(isolate, TroubleCallback);
v8::Local<v8::Object> global = env->Global();
CHECK(global->Set(env.local(), v8_str("trouble"),
fun->GetFunction(env.local()).ToLocalChecked())
.FromJust());
CompileRunWithOrigin(
"function trouble() {\n"
" var o = {};\n"
" new o.foo();\n"
"};",
script_resource_name);
Local<Value> trouble =
global->Get(env.local(), v8_str("trouble")).ToLocalChecked();
CHECK(trouble->IsFunction());
CHECK(Function::Cast(*trouble)
->Call(env.local(), global, 0, nullptr)
.IsEmpty());
isolate->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()->GetCurrentContext())
.ToLocalChecked());
}
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(isolate, 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(isolate, 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::Context> accessing_context,
Local<v8::Object> accessed_object,
Local<v8::Value> data) {
printf("a\n");
CHECK(!data.IsEmpty() && data->IsInt32());
CHECK_EQ(42, data->Int32Value(accessing_context).FromJust());
return g_security_callback_result;
}
// SecurityHandler can't be run twice
TEST(SecurityHandler) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope0(isolate);
v8::Local<v8::ObjectTemplate> global_template =
v8::ObjectTemplate::New(isolate);
global_template->SetAccessCheckCallback(SecurityTestCallback, v8_num(42));
// Create an environment
v8::Local<Context> context0 = Context::New(isolate, nullptr, global_template);
context0->Enter();
v8::Local<v8::Object> global0 = context0->Global();
v8::Local<Script> script0 = v8_compile("foo = 111");
script0->Run(context0).ToLocalChecked();
CHECK(global0->Set(context0, v8_str("0"), v8_num(999)).FromJust());
v8::Local<Value> foo0 =
global0->Get(context0, v8_str("foo")).ToLocalChecked();
CHECK_EQ(111, foo0->Int32Value(context0).FromJust());
v8::Local<Value> z0 = global0->Get(context0, v8_str("0")).ToLocalChecked();
CHECK_EQ(999, z0->Int32Value(context0).FromJust());
// Create another environment, should fail security checks.
v8::HandleScope scope1(isolate);
v8::Local<Context> context1 = Context::New(isolate, nullptr, global_template);
context1->Enter();
v8::Local<v8::Object> global1 = context1->Global();
global1->Set(context1, v8_str("othercontext"), global0).FromJust();
// This set will fail the security check.
v8::Local<Script> script1 =
v8_compile("othercontext.foo = 222; othercontext[0] = 888;");
CHECK(script1->Run(context1).IsEmpty());
g_security_callback_result = true;
// This read will pass the security check.
v8::Local<Value> foo1 =
global0->Get(context1, v8_str("foo")).ToLocalChecked();
CHECK_EQ(111, foo1->Int32Value(context0).FromJust());
// This read will pass the security check.
v8::Local<Value> z1 = global0->Get(context1, v8_str("0")).ToLocalChecked();
CHECK_EQ(999, z1->Int32Value(context1).FromJust());
// Create another environment, should pass security checks.
{
v8::HandleScope scope2(isolate);
LocalContext context2;
v8::Local<v8::Object> global2 = context2->Global();
CHECK(global2->Set(context2.local(), v8_str("othercontext"), global0)
.FromJust());
v8::Local<Script> script2 =
v8_compile("othercontext.foo = 333; othercontext[0] = 888;");
script2->Run(context2.local()).ToLocalChecked();
v8::Local<Value> foo2 =
global0->Get(context2.local(), v8_str("foo")).ToLocalChecked();
CHECK_EQ(333, foo2->Int32Value(context2.local()).FromJust());
v8::Local<Value> z2 =
global0->Get(context2.local(), v8_str("0")).ToLocalChecked();
CHECK_EQ(888, z2->Int32Value(context2.local()).FromJust());
}
context1->Exit();
context0->Exit();
}
THREADED_TEST(SecurityChecks) {
LocalContext env1;
v8::HandleScope handle_scope(env1->GetIsolate());
v8::Local<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(env1.local(), v8_str("spy")).ToLocalChecked();
CHECK(spy->IsFunction());
// Create another function accessing global objects.
CompileRun("spy2=function(){return new this.Array();}");
Local<Value> spy2 =
env1->Global()->Get(env1.local(), v8_str("spy2")).ToLocalChecked();
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, env2->Global(), 0, nullptr)
.ToLocalChecked();
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());
CHECK(Function::Cast(*spy2)
->Call(env2, env2->Global(), 0, nullptr)
.IsEmpty());
CHECK(try_catch.HasCaught());
}
}
// Regression test case for issue 1183439.
THREADED_TEST(SecurityChecksForPrototypeChain) {
LocalContext current;
v8::HandleScope scope(current->GetIsolate());
v8::Local<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(other, v8_str("Object")).ToLocalChecked();
CHECK(other->Global()->Set(other, v8_num(42), v8_num(87)).FromJust());
}
CHECK(current->Global()
->Set(current.local(), v8_str("other"), other->Global())
.FromJust());
CHECK(v8_compile("other")
->Run(current.local())
.ToLocalChecked()
->Equals(current.local(), other->Global())
.FromJust());
// 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(current.local()).IsEmpty());
CHECK(access_other1->Run(current.local()).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(current.local())
.ToLocalChecked();
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(current.local()).IsEmpty());
CHECK(access_f1->Run(current.local()).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);
CHECK(other->Global()
->Set(other, v8_str("__proto__"), current->Global())
.FromJust());
}
// 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.
CHECK(current->Global()
->Set(current.local(), v8_str("foo"), v8_num(100))
.FromJust());
CHECK(current->Global()
->Set(current.local(), v8_num(99), v8_num(101))
.FromJust());
// 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(current.local()).IsEmpty());
CHECK(access_f3->Run(current.local()).IsEmpty());
}
}
static bool security_check_with_gc_called;
static bool SecurityTestCallbackWithGC(Local<v8::Context> accessing_context,
Local<v8::Object> accessed_object,
Local<v8::Value> data) {
CcTest::CollectAllGarbage();
security_check_with_gc_called = true;
return true;
}
TEST(SecurityTestGCAllowed) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope handle_scope(isolate);
v8::Local<v8::ObjectTemplate> object_template =
v8::ObjectTemplate::New(isolate);
object_template->SetAccessCheckCallback(SecurityTestCallbackWithGC);
v8::Local<Context> context = Context::New(isolate);
v8::Context::Scope context_scope(context);
CHECK(context->Global()
->Set(context, v8_str("obj"),
object_template->NewInstance(context).ToLocalChecked())
.FromJust());
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(context)
.ToLocalChecked()
->Equals(context, v8_str("1002"))
.FromJust());
CHECK(security_check_with_gc_called);
}
THREADED_TEST(CrossDomainDelete) {
LocalContext env1;
v8::HandleScope handle_scope(env1->GetIsolate());
v8::Local<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);
CHECK(
env1->Global()->Set(env1.local(), v8_str("prop"), v8_num(3)).FromJust());
CHECK(env2->Global()->Set(env2, v8_str("env1"), env1->Global()).FromJust());
// 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(env1.local(), v8_str("prop")).ToLocalChecked();
CHECK(v->IsNumber());
CHECK_EQ(3, v->Int32Value(env1.local()).FromJust());
}
THREADED_TEST(CrossDomainPropertyIsEnumerable) {
LocalContext env1;
v8::HandleScope handle_scope(env1->GetIsolate());
v8::Local<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);
CHECK(
env1->Global()->Set(env1.local(), v8_str("prop"), v8_num(3)).FromJust());
CHECK(env2->Global()->Set(env2, v8_str("env1"), env1->Global()).FromJust());
// 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(CrossDomainFor) {
LocalContext env1;
v8::HandleScope handle_scope(env1->GetIsolate());
v8::Local<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);
CHECK(
env1->Global()->Set(env1.local(), v8_str("prop"), v8_num(3)).FromJust());
CHECK(env2->Global()->Set(env2, v8_str("env1"), env1->Global()).FromJust());
// 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. It shouldn't throw either, just silently ignore them.
env2->SetSecurityToken(bar);
{
Context::Scope scope_env2(env2);
Local<Value> result = CompileRun(
"(function() {"
" try {"
" for (var p in env1) {"
" if (p == 'prop') return false;"
" }"
" return true;"
" } catch (e) {"
" return false;"
" }"
"})()");
CHECK(result->IsTrue());
}
}
THREADED_TEST(CrossDomainForInOnPrototype) {
LocalContext env1;
v8::HandleScope handle_scope(env1->GetIsolate());
v8::Local<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);
CHECK(
env1->Global()->Set(env1.local(), v8_str("prop"), v8_num(3)).FromJust());
CHECK(env2->Global()->Set(env2, v8_str("env1"), env1->Global()).FromJust());
// 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 true;"
" } catch (e) {"
" return false;"
" }"
"})()");
CHECK(result->IsTrue());
}
}
TEST(ContextDetachGlobal) {
LocalContext env1;
v8::HandleScope handle_scope(env1->GetIsolate());
v8::Local<Context> env2 = Context::New(env1->GetIsolate());
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();
CHECK(global2->Set(env2, v8_str("prop"),
v8::Integer::New(env2->GetIsolate(), 1))
.FromJust());
CompileRun("function getProp() {return prop;}");
CHECK(env1->Global()
->Set(env1.local(), v8_str("getProp"),
global2->Get(env2, v8_str("getProp")).ToLocalChecked())
.FromJust());
// Detach env2's global, and reuse the global object of env2
env2->Exit();
env2->DetachGlobal();
v8::Local<Context> env3 = Context::New(
env1->GetIsolate(), 0, v8::Local<v8::ObjectTemplate>(), global2);
env3->SetSecurityToken(v8_str("bar"));
env3->Enter();
Local<v8::Object> global3 = env3->Global();
CHECK(global2->Equals(env3, global3).FromJust());
CHECK(global3->Get(env3, v8_str("prop")).ToLocalChecked()->IsUndefined());
CHECK(global3->Get(env3, v8_str("getProp")).ToLocalChecked()->IsUndefined());
CHECK(global3->Set(env3, v8_str("prop"),
v8::Integer::New(env3->GetIsolate(), -1))
.FromJust());
CHECK(global3->Set(env3, v8_str("prop2"),
v8::Integer::New(env3->GetIsolate(), 2))
.FromJust());
env3->Exit();
// Call getProp in env1, and it should return the value 1
{
Local<v8::Object> global1 = env1->Global();
Local<Value> get_prop =
global1->Get(env1.local(), v8_str("getProp")).ToLocalChecked();
CHECK(get_prop->IsFunction());
v8::TryCatch try_catch(env1->GetIsolate());
Local<Value> r = Function::Cast(*get_prop)
->Call(env1.local(), global1, 0, nullptr)
.ToLocalChecked();
CHECK(!try_catch.HasCaught());
CHECK_EQ(1, r->Int32Value(env1.local()).FromJust());
}
// Check that env3 is not accessible from env1
{
v8::MaybeLocal<Value> r = global3->Get(env1.local(), v8_str("prop2"));
CHECK(r.IsEmpty());
}
}
TEST(DetachGlobal) {
LocalContext env1;
v8::HandleScope scope(env1->GetIsolate());
// Create second environment.
v8::Local<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);
CHECK(env2->Global()
->Set(env2, v8_str("p"), v8::Integer::New(env2->GetIsolate(), 42))
.FromJust());
}
// Create a reference to env2 global from env1 global.
CHECK(env1->Global()
->Set(env1.local(), v8_str("other"), env2->Global())
.FromJust());
// 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(env1.local()).FromJust());
// 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::Local<Context> env3 = Context::New(
env1->GetIsolate(), 0, v8::Local<v8::ObjectTemplate>(), global2);
CHECK(global2->Equals(env1.local(), env3->Global()).FromJust());
// 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);
CHECK(env3->Global()
->Set(env3, v8_str("p"), v8::Integer::New(env3->GetIsolate(), 24))
.FromJust());
}
// 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(env3).FromJust());
}
void GetThisX(const v8::FunctionCallbackInfo<v8::Value>& info) {
v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();
info.GetReturnValue().Set(
context->Global()->Get(context, v8_str("x")).ToLocalChecked());
}
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(), nullptr, inner_global_template);
Local<Value> foo = v8_str("foo");
// Set same security token for env1 and env2.
env1->SetSecurityToken(foo);
env2->SetSecurityToken(foo);
CHECK(env1->Global()
->Set(env1.local(), v8_str("x"), v8_str("env1_x"))
.FromJust());
{
v8::Context::Scope scope(env2);
CHECK(env2->Global()->Set(env2, v8_str("x"), v8_str("env2_x")).FromJust());
CompileRun(
"function bound_x() { return x; }"
"function get_x() { return this.x; }"
"function get_x_w() { return (function() {return this.x;})(); }");
CHECK(env1->Global()
->Set(env1.local(), v8_str("bound_x"), CompileRun("bound_x"))
.FromJust());
CHECK(env1->Global()
->Set(env1.local(), v8_str("get_x"), CompileRun("get_x"))
.FromJust());
CHECK(env1->Global()
->Set(env1.local(), v8_str("get_x_w"), CompileRun("get_x_w"))
.FromJust());
env1->Global()
->Set(env1.local(), v8_str("this_x"),
CompileRun("Object.getOwnPropertyDescriptor(this, 'this_x').get"))
.FromJust();
}
Local<Object> env2_global = env2->Global();
env2->DetachGlobal();
Local<Value> result;
result = CompileRun("bound_x()");
CHECK(v8_str("env2_x")->Equals(env1.local(), result).FromJust());
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(env1.local(), result).FromJust());
// Reattach env2's proxy
env2 = Context::New(env1->GetIsolate(), 0, v8::Local<v8::ObjectTemplate>(),
env2_global);
env2->SetSecurityToken(foo);
{
v8::Context::Scope scope(env2);
CHECK(env2->Global()->Set(env2, v8_str("x"), v8_str("env3_x")).FromJust());
CHECK(env2->Global()->Set(env2, v8_str("env1"), env1->Global()).FromJust());
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(env2, results->Get(env2, i + 0).ToLocalChecked())
.FromJust());
CHECK(v8_str("env1_x")
->Equals(env2, results->Get(env2, i + 1).ToLocalChecked())
.FromJust());
CHECK(v8_str("env3_x")
->Equals(env2, results->Get(env2, i + 2).ToLocalChecked())
.FromJust());
CHECK(v8_str("env2_x")
->Equals(env2, results->Get(env2, i + 3).ToLocalChecked())
.FromJust());
}
}
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(env1.local(),
results->Get(env1.local(), i + 0).ToLocalChecked())
.FromJust());
CHECK(v8_str("env3_x")
->Equals(env1.local(),
results->Get(env1.local(), i + 1).ToLocalChecked())
.FromJust());
CHECK(v8_str("env3_x")
->Equals(env1.local(),
results->Get(env1.local(), i + 2).ToLocalChecked())
.FromJust());
CHECK(v8_str("env2_x")
->Equals(env1.local(),
results->Get(env1.local(), i + 3).ToLocalChecked())
.FromJust());
}
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(env1.local(),
results->Get(env1.local(), i + 0).ToLocalChecked())
.FromJust());
CHECK(v8_str("env1_x")
->Equals(env1.local(),
results->Get(env1.local(), i + 1).ToLocalChecked())
.FromJust());
CHECK(v8_str("env3_x")
->Equals(env1.local(),
results->Get(env1.local(), i + 2).ToLocalChecked())
.FromJust());
CHECK(v8_str("env2_x")
->Equals(env1.local(),
results->Get(env1.local(), i + 3).ToLocalChecked())
.FromJust());
}
}
static bool allowed_access = false;
static bool AccessBlocker(Local<v8::Context> accessing_context,
Local<v8::Object> accessed_object,
Local<v8::Value> data) {
v8::Local<v8::Context> context = CcTest::isolate()->GetCurrentContext();
return context->Global()->Equals(context, accessed_object).FromJust() ||
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>& args) {
if (value->IsNumber())
g_echo_value =
value->Int32Value(args.GetIsolate()->GetCurrentContext()).FromJust();
}
static void UnreachableGetter(
Local<String> name,
const v8::PropertyCallbackInfo<v8::Value>& info) {
UNREACHABLE(); // This function should not be called..
}
static void UnreachableSetter(Local<String>,
Local<Value>,
const v8::PropertyCallbackInfo<void>&) {
UNREACHABLE(); // This function should not be called.
}
static void UnreachableFunction(
const v8::FunctionCallbackInfo<v8::Value>& info) {
UNREACHABLE(); // This function should not be called..
}
TEST(AccessControl) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope handle_scope(isolate);
v8::Local<v8::ObjectTemplate> global_template =
v8::ObjectTemplate::New(isolate);
global_template->SetAccessCheckCallback(AccessBlocker);
// Add an accessor accessible by cross-domain JS code.
global_template->SetAccessor(
v8_str("accessible_prop"), EchoGetter, EchoSetter, v8::Local<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::Local<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, nullptr, global_template);
context0->Enter();
v8::Local<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(context0, v8_str("getter")).ToLocalChecked();
Local<Value> setter =
global0->Get(context0, v8_str("setter")).ToLocalChecked();
// And define normal element.
CHECK(global0->Set(context0, 239, v8_str("239")).FromJust());
// 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(context0, v8_str("el_getter")).ToLocalChecked();
Local<Value> el_setter =
global0->Get(context0, v8_str("el_setter")).ToLocalChecked();
v8::HandleScope scope1(isolate);
v8::Local<Context> context1 = Context::New(isolate);
context1->Enter();
v8::Local<v8::Object> global1 = context1->Global();
CHECK(global1->Set(context1, v8_str("other"), global0).FromJust());
// 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::Local<Value> value;
// Access accessible property
value = CompileRun("other.accessible_prop = 3");
CHECK(value->IsNumber());
CHECK_EQ(3, value->Int32Value(context1).FromJust());
CHECK_EQ(3, g_echo_value);
value = CompileRun("other.accessible_prop");
CHECK(value->IsNumber());
CHECK_EQ(3, value->Int32Value(context1).FromJust());
value = CompileRun(
"Object.getOwnPropertyDescriptor(other, 'accessible_prop').value");
CHECK(value->IsNumber());
CHECK_EQ(3, value->Int32Value(context1).FromJust());
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.
// Enumeration doesn't throw, it silently ignores what it can't access.
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 true;"
" } catch (e) {"
" return false;"
" }"
"})()");
CHECK(value->IsTrue());
// Test that preventExtensions fails on a non-accessible object even if that
// object is already non-extensible.
CHECK(global1->Set(context1, v8_str("checked_object"),
global_template->NewInstance(context1).ToLocalChecked())
.FromJust());
allowed_access = true;
CompileRun("Object.preventExtensions(checked_object)");
ExpectFalse("Object.isExtensible(checked_object)");
allowed_access = false;
CHECK(CompileRun("Object.preventExtensions(checked_object)").IsEmpty());
context1->Exit();
context0->Exit();
}
TEST(AccessControlES5) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope handle_scope(isolate);
v8::Local<v8::ObjectTemplate> global_template =
v8::ObjectTemplate::New(isolate);
global_template->SetAccessCheckCallback(AccessBlocker);
// Add accessible accessor.
global_template->SetAccessor(
v8_str("accessible_prop"), EchoGetter, EchoSetter, v8::Local<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::Local<Value>(),
v8::DEFAULT);
// Create an environment
v8::Local<Context> context0 = Context::New(isolate, nullptr, global_template);
context0->Enter();
v8::Local<v8::Object> global0 = context0->Global();
v8::Local<Context> context1 = Context::New(isolate);
context1->Enter();
v8::Local<v8::Object> global1 = context1->Global();
CHECK(global1->Set(context1, v8_str("other"), global0).FromJust());
// Regression test for issue 1154.
CHECK(CompileRun("Object.keys(other).length == 1")
->BooleanValue(context1)
.FromJust());
CHECK(CompileRun("Object.keys(other)[0] == 'accessible_prop'")
->BooleanValue(context1)
.FromJust());
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);
// [[DefineOwnProperty]] always throws for access-checked objects.
CHECK(
CompileRun("Object.defineProperty(other, 'accessible_prop', {value: 43})")
.IsEmpty());
CHECK(CompileRun("other.accessible_prop == 42")->IsTrue());
CHECK_EQ(42, g_echo_value); // Make sure we didn't call the setter.
}
static bool AccessAlwaysBlocked(Local<v8::Context> accessing_context,
Local<v8::Object> global,
Local<v8::Value> data) {
i::PrintF("Access blocked.\n");
return false;
}
static bool AccessAlwaysAllowed(Local<v8::Context> accessing_context,
Local<v8::Object> global,
Local<v8::Value> data) {
i::PrintF("Access allowed.\n");
return true;
}
THREADED_TEST(AccessControlGetOwnPropertyNames) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope handle_scope(isolate);
v8::Local<v8::ObjectTemplate> obj_template = v8::ObjectTemplate::New(isolate);
obj_template->Set(v8_str("x"), v8::Integer::New(isolate, 42));
obj_template->SetAccessCheckCallback(AccessAlwaysBlocked);
// Add an accessor accessible by cross-domain JS code.
obj_template->SetAccessor(
v8_str("accessible_prop"), EchoGetter, EchoSetter, v8::Local<Value>(),
v8::AccessControl(v8::ALL_CAN_READ | v8::ALL_CAN_WRITE));
// Create an environment
v8::Local<Context> context0 = Context::New(isolate, nullptr, obj_template);
context0->Enter();
v8::Local<v8::Object> global0 = context0->Global();
v8::HandleScope scope1(CcTest::isolate());
v8::Local<Context> context1 = Context::New(isolate);
context1->Enter();
v8::Local<v8::Object> global1 = context1->Global();
CHECK(global1->Set(context1, v8_str("other"), global0).FromJust());
CHECK(global1->Set(context1, v8_str("object"),
obj_template->NewInstance(context1).ToLocalChecked())
.FromJust());
v8::Local<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(
"var names = Object.getOwnPropertyNames(other);"
"names.length == 1 && names[0] == 'accessible_prop';");
CHECK(value->BooleanValue(context1).FromJust());
value = CompileRun(
"var names = Object.getOwnPropertyNames(object);"
"names.length == 1 && names[0] == 'accessible_prop';");
CHECK(value->BooleanValue(context1).FromJust());
context1->Exit();
context0->Exit();
}
TEST(Regress470113) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope handle_scope(isolate);
v8::Local<v8::ObjectTemplate> obj_template = v8::ObjectTemplate::New(isolate);
obj_template->SetAccessCheckCallback(AccessAlwaysBlocked);
LocalContext env;
CHECK(env->Global()
->Set(env.local(), v8_str("prohibited"),
obj_template->NewInstance(env.local()).ToLocalChecked())
.FromJust());
{
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::Local<v8::FunctionTemplate> func_template =
v8::FunctionTemplate::New(isolate);
v8::Local<v8::ObjectTemplate> global_template =
func_template->InstanceTemplate();
v8::Local<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::Local<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::Local<Value>(), v8::DEFAULT);
v8::Local<Context> context0 = Context::New(isolate, nullptr, global_template);
context0->Enter();
Local<v8::Object> global = context0->Global();
// Add a normal property that shadows 'accessible'
CHECK(global->Set(context0, v8_str("accessible"), v8_num(11)).FromJust());
// Enter a new context.
v8::HandleScope scope1(CcTest::isolate());
v8::Local<Context> context1 = Context::New(isolate);
context1->Enter();
v8::Local<v8::Object> global1 = context1->Global();
CHECK(global1->Set(context1, v8_str("other"), global).FromJust());
// Should return 10, instead of 11
v8::Local<Value> value =
v8_compile("other.accessible")->Run(context1).ToLocalChecked();
CHECK(value->IsNumber());
CHECK_EQ(10, value->Int32Value(context1).FromJust());
v8::MaybeLocal<v8::Value> maybe_value =
v8_compile("other.unreachable")->Run(context1);
CHECK(maybe_value.IsEmpty());
context1->Exit();
context0->Exit();
}
static int access_count = 0;
static bool AccessCounter(Local<v8::Context> accessing_context,
Local<v8::Object> accessed_object,
Local<v8::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::Local<v8::ObjectTemplate> object_template =
v8::ObjectTemplate::New(isolate);
object_template->SetAccessCheckCallback(AccessCounter);
Local<v8::Object> object =
object_template->NewInstance(context0).ToLocalChecked();
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::Local<v8::Object> global1 = context1->Global();
CHECK(global1->Set(context1, v8_str("obj"), object).FromJust());
v8::Local<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(context1).FromJust());
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(context1).FromJust());
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(context1).FromJust());
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(context1).FromJust());
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(context1).FromJust());
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(context1).FromJust());
// 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(context1).FromJust());
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(context.local())
.ToLocalChecked()
->NewInstance(context.local())
.ToLocalChecked();
CHECK(context->Global()->Set(context.local(), v8_str("i"), o).FromJust());
Local<Value> value = CompileRun("i.x");
CHECK_EQ(42, value->Int32Value(context.local()).FromJust());
value = CompileRun("i.f()");
CHECK_EQ(12, value->Int32Value(context.local()).FromJust());
}
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(nullptr, 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(env.local()).FromJust());
value = CompileRun("f()");
CHECK_EQ(12, value->Int32Value(env.local()).FromJust());
value = CompileRun(script);
CHECK_EQ(1, value->Int32Value(env.local()).FromJust());
}
{
// Create new environment reusing the global object.
LocalContext env(nullptr, instance_template, global_object);
Local<Value> value = CompileRun("x");
CHECK_EQ(42, value->Int32Value(env.local()).FromJust());
value = CompileRun("f()");
CHECK_EQ(12, value->Int32Value(env.local()).FromJust());
value = CompileRun(script);
CHECK_EQ(1, value->Int32Value(env.local()).FromJust());
}
}
THREADED_TEST(ObjectGetOwnPropertyNames) {
LocalContext context;
v8::Isolate* isolate = context->GetIsolate();
v8::HandleScope handle_scope(isolate);
v8::Local<v8::Object> value =
v8::Local<v8::Object>::Cast(v8::StringObject::New(v8_str("test")));
v8::Local<v8::Array> properties;
CHECK(value
->GetOwnPropertyNames(context.local(),
static_cast<v8::PropertyFilter>(
v8::PropertyFilter::ALL_PROPERTIES |
v8::PropertyFilter::SKIP_SYMBOLS))
.ToLocal(&properties));
CHECK_EQ(5u, properties->Length());
v8::Local<v8::Value> property;
CHECK(properties->Get(context.local(), 4).ToLocal(&property) &&
property->IsString());
CHECK(property.As<v8::String>()
->Equals(context.local(), v8_str("length"))
.FromMaybe(false));
for (int i = 0; i < 4; ++i) {
v8::Local<v8::Value> property;
CHECK(properties->Get(context.local(), i).ToLocal(&property) &&
property->IsInt32());
CHECK_EQ(property.As<v8::Int32>()->Value(), i);
}
CHECK(value->GetOwnPropertyNames(context.local(), v8::ONLY_ENUMERABLE)
.ToLocal(&properties));
CHECK_EQ(4u, properties->Length());
for (int i = 0; i < 4; ++i) {
v8::Local<v8::Value> property;
CHECK(properties->Get(context.local(), i).ToLocal(&property) &&
property->IsInt32());
CHECK_EQ(property.As<v8::Int32>()->Value(), i);
}
value = value->GetPrototype().As<v8::Object>();
CHECK(value
->GetOwnPropertyNames(context.local(),
static_cast<v8::PropertyFilter>(
v8::PropertyFilter::ALL_PROPERTIES |
v8::PropertyFilter::SKIP_SYMBOLS))
.ToLocal(&properties));
bool concat_found = false;
bool starts_with_found = false;
for (uint32_t i = 0; i < properties->Length(); ++i) {
v8::Local<v8::Value> property;
CHECK(properties->Get(context.local(), i).ToLocal(&property));
if (!property->IsString()) continue;
if (!concat_found)
concat_found = property.As<v8::String>()
->Equals(context.local(), v8_str("concat"))
.FromMaybe(false);
if (!starts_with_found)
starts_with_found = property.As<v8::String>()
->Equals(context.local(), v8_str("startsWith"))
.FromMaybe(false);
}
CHECK(concat_found && starts_with_found);
}
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(nullptr, 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(nullptr, instance_template, global_object);
CHECK(env->Global()->Set(env.local(), v8_str("foo"), foo).FromJust());
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(nullptr, 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(context.local())
.ToLocalChecked()
->NewInstance(context.local())
.ToLocalChecked();
CHECK(context->Global()
->Set(context.local(), v8_str("__proto__"), o)
.FromJust());
Local<Value> value =
CompileRun("this.propertyIsEnumerable(0)");
CHECK(value->IsBoolean());
CHECK(!value->BooleanValue(context.local()).FromJust());
value = CompileRun("x");
CHECK_EQ(12, value->Int32Value(context.local()).FromJust());
value = CompileRun("f()");
CHECK_EQ(42, value->Int32Value(context.local()).FromJust());
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(context.local()).FromJust());
}
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(context.local())
.ToLocalChecked()
->NewInstance(context.local())
.ToLocalChecked();
Local<v8::Object> o1 = t1->GetFunction(context.local())
.ToLocalChecked()
->NewInstance(context.local())
.ToLocalChecked();
Local<v8::Object> o2 = t2->GetFunction(context.local())
.ToLocalChecked()
->NewInstance(context.local())
.ToLocalChecked();
Local<v8::Object> o3 = t3->GetFunction(context.local())
.ToLocalChecked()
->NewInstance(context.local())
.ToLocalChecked();
// Setting the prototype on an object skips hidden prototypes.
CHECK_EQ(0, o0->Get(context.local(), v8_str("x"))
.ToLocalChecked()
->Int32Value(context.local())
.FromJust());
CHECK(o0->Set(context.local(), v8_str("__proto__"), o1).FromJust());
CHECK_EQ(0, o0->Get(context.local(), v8_str("x"))
.ToLocalChecked()
->Int32Value(context.local())
.FromJust());
CHECK_EQ(1, o0->Get(context.local(), v8_str("y"))
.ToLocalChecked()
->Int32Value(context.local())
.FromJust());
CHECK(o0->Set(context.local(), v8_str("__proto__"), o2).FromJust());
CHECK_EQ(0, o0->Get(context.local(), v8_str("x"))
.ToLocalChecked()
->Int32Value(context.local())
.FromJust());
CHECK_EQ(1, o0->Get(context.local(), v8_str("y"))
.ToLocalChecked()
->Int32Value(context.local())
.FromJust());
CHECK_EQ(2, o0->Get(context.local(), v8_str("z"))
.ToLocalChecked()
->Int32Value(context.local())
.FromJust());
CHECK(o0->Set(context.local(), v8_str("__proto__"), o3).FromJust());
CHECK_EQ(0, o0->Get(context.local(), v8_str("x"))
.ToLocalChecked()
->Int32Value(context.local())
.FromJust());
CHECK_EQ(1, o0->Get(context.local(), v8_str("y"))
.ToLocalChecked()
->Int32Value(context.local())
.FromJust());
CHECK_EQ(2, o0->Get(context.local(), v8_str("z"))
.ToLocalChecked()
->Int32Value(context.local())
.FromJust());
CHECK_EQ(3, o0->Get(context.local(), v8_str("u"))
.ToLocalChecked()
->Int32Value(context.local())
.FromJust());
// 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(context.local(), v8_str("__proto__")).ToLocalChecked();
CHECK(proto->IsObject());
CHECK(proto.As<v8::Object>()
->Get(context.local(), v8_str("z"))
.ToLocalChecked()
->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(context.local())
.ToLocalChecked()
->NewInstance(context.local())
.ToLocalChecked();
Local<v8::Object> h = ht->GetFunction(context.local())
.ToLocalChecked()
->NewInstance(context.local())
.ToLocalChecked();
Local<v8::Object> p = pt->GetFunction(context.local())
.ToLocalChecked()
->NewInstance(context.local())
.ToLocalChecked();
CHECK(o->Set(context.local(), v8_str("__proto__"), h).FromJust());
CHECK(h->Set(context.local(), v8_str("__proto__"), p).FromJust());
// Setting a property that exists on the hidden prototype goes there.
CHECK(o->Set(context.local(), v8_str("x"), v8_num(7)).FromJust());
CHECK_EQ(7, o->Get(context.local(), v8_str("x"))
.ToLocalChecked()
->Int32Value(context.local())
.FromJust());
CHECK_EQ(7, h->Get(context.local(), v8_str("x"))
.ToLocalChecked()
->Int32Value(context.local())
.FromJust());
CHECK(p->Get(context.local(), v8_str("x")).ToLocalChecked()->IsUndefined());
// Setting a new property should not be forwarded to the hidden prototype.
CHECK(o->Set(context.local(), v8_str("y"), v8_num(6)).FromJust());
CHECK_EQ(6, o->Get(context.local(), v8_str("y"))
.ToLocalChecked()
->Int32Value(context.local())
.FromJust());
CHECK(h->Get(context.local(), v8_str("y")).ToLocalChecked()->IsUndefined());
CHECK(p->Get(context.local(), v8_str("y")).ToLocalChecked()->IsUndefined());
// Setting a property that only exists on a prototype of the hidden prototype
// is treated normally again.
CHECK(p->Set(context.local(), v8_str("z"), v8_num(8)).FromJust());
CHECK_EQ(8, o->Get(context.local(), v8_str("z"))
.ToLocalChecked()
->Int32Value(context.local())
.FromJust());
CHECK_EQ(8, h->Get(context.local(), v8_str("z"))
.ToLocalChecked()
->Int32Value(context.local())
.FromJust());
CHECK_EQ(8, p->Get(context.local(), v8_str("z"))
.ToLocalChecked()
->Int32Value(context.local())
.FromJust());
CHECK(o->Set(context.local(), v8_str("z"), v8_num(9)).FromJust());
CHECK_EQ(9, o->Get(context.local(), v8_str("z"))
.ToLocalChecked()
->Int32Value(context.local())
.FromJust());
CHECK_EQ(8, h->Get(context.local(), v8_str("z"))
.ToLocalChecked()
->Int32Value(context.local())
.FromJust());
CHECK_EQ(8, p->Get(context.local(), v8_str("z"))
.ToLocalChecked()
->Int32Value(context.local())
.FromJust());
}
// Regression test for issue 2457.
THREADED_TEST(HiddenPrototypeIdentityHash) {
LocalContext context;
v8::HandleScope handle_scope(context->GetIsolate());
Local<FunctionTemplate> t = FunctionTemplate::New(context->GetIsolate());
t->SetHiddenPrototype(true);
t->InstanceTemplate()->Set(v8_str("foo"), v8_num(75));
Local<Object> p = t->GetFunction(context.local())
.ToLocalChecked()
->NewInstance(context.local())
.ToLocalChecked();
Local<Object> o = Object::New(context->GetIsolate());
CHECK(o->SetPrototype(context.local(), p).FromJust());
int hash = o->GetIdentityHash();
USE(hash);
CHECK(o->Set(context.local(), v8_str("foo"), v8_num(42)).FromJust());
CHECK_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(context.local())
.ToLocalChecked()
->NewInstance(context.local())
.ToLocalChecked();
Local<v8::Object> o1 = t1->GetFunction(context.local())
.ToLocalChecked()
->NewInstance(context.local())
.ToLocalChecked();
Local<v8::Object> o2 = t2->GetFunction(context.local())
.ToLocalChecked()
->NewInstance(context.local())
.ToLocalChecked();
Local<v8::Object> o3 = t3->GetFunction(context.local())
.ToLocalChecked()
->NewInstance(context.local())
.ToLocalChecked();
// Setting the prototype on an object does not skip hidden prototypes.
CHECK_EQ(0, o0->Get(context.local(), v8_str("x"))
.ToLocalChecked()
->Int32Value(context.local())
.FromJust());
CHECK(o0->SetPrototype(context.local(), o1).FromJust());
CHECK_EQ(0, o0->Get(context.local(), v8_str("x"))
.ToLocalChecked()
->Int32Value(context.local())
.FromJust());
CHECK_EQ(1, o0->Get(context.local(), v8_str("y"))
.ToLocalChecked()
->Int32Value(context.local())
.FromJust());
CHECK(o1->SetPrototype(context.local(), o2).FromJust());
CHECK_EQ(0, o0->Get(context.local(), v8_str("x"))
.ToLocalChecked()
->Int32Value(context.local())
.FromJust());
CHECK_EQ(1, o0->Get(context.local(), v8_str("y"))
.ToLocalChecked()
->Int32Value(context.local())
.FromJust());
CHECK_EQ(2, o0->Get(context.local(), v8_str("z"))
.ToLocalChecked()
->Int32Value(context.local())
.FromJust());
CHECK(o2->SetPrototype(context.local(), o3).FromJust());
CHECK_EQ(0, o0->Get(context.local(), v8_str("x"))
.ToLocalChecked()
->Int32Value(context.local())
.FromJust());
CHECK_EQ(1, o0->Get(context.local(), v8_str("y"))
.ToLocalChecked()
->Int32Value(context.local())
.FromJust());
CHECK_EQ(2, o0->Get(context.local(), v8_str("z"))
.ToLocalChecked()
->Int32Value(context.local())
.FromJust());
CHECK_EQ(3, o0->Get(context.local(), v8_str("u"))
.ToLocalChecked()
->Int32Value(context.local())
.FromJust());
// 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(context.local(), v8_str("__proto__")).ToLocalChecked();
CHECK(proto->IsObject());
CHECK(proto.As<v8::Object>()->Equals(context.local(), o3).FromJust());
// However, Object::GetPrototype ignores hidden prototype.
Local<Value> proto0 = o0->GetPrototype();
CHECK(proto0->IsObject());
CHECK(proto0.As<v8::Object>()->Equals(context.local(), o1).FromJust());
Local<Value> proto1 = o1->GetPrototype();
CHECK(proto1->IsObject());
CHECK(proto1.As<v8::Object>()->Equals(context.local(), o2).FromJust());
Local<Value> proto2 = o2->GetPrototype();
CHECK(proto2->IsObject());
CHECK(proto2.As<v8::Object>()->Equals(context.local(), o3).FromJust());
}
// 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::ObjectTemplate::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(context.local())
.ToLocalChecked()
->NewInstance(context.local())
.ToLocalChecked();
Local<v8::Object> o2 = t2->GetFunction(context.local())
.ToLocalChecked()
->NewInstance(context.local())
.ToLocalChecked();
Local<v8::Object> o3 = t3->GetFunction(context.local())
.ToLocalChecked()
->NewInstance(context.local())
.ToLocalChecked();
Local<v8::Object> o4 = t4->GetFunction(context.local())
.ToLocalChecked()
->NewInstance(context.local())
.ToLocalChecked();
// Create prototype chain of hidden prototypes.
CHECK(o4->SetPrototype(context.local(), o3).FromJust());
CHECK(o3->SetPrototype(context.local(), o2).FromJust());
CHECK(o2->SetPrototype(context.local(), o1).FromJust());
// Call the runtime version of GetOwnPropertyNames() on the natively
// created object through JavaScript.
CHECK(context->Global()->Set(context.local(), v8_str("obj"), o4).FromJust());
// PROPERTY_FILTER_NONE = 0
CompileRun("var names = %GetOwnPropertyKeys(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(\"objects\") >= 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(context.local())
.ToLocalChecked()
->NewInstance(context.local())
.ToLocalChecked();
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(context.local())
.ToLocalChecked()
->NewInstance(context.local())
.ToLocalChecked();
CHECK(o2->SetPrototype(context.local(), o1).FromJust());
v8::Local<v8::Symbol> sym =
v8::Symbol::New(context->GetIsolate(), v8_str("s1"));
CHECK(o1->Set(context.local(), sym, v8_num(3)).FromJust());
o1->SetPrivate(context.local(),
v8::Private::New(context->GetIsolate(), v8_str("h1")),
v8::Integer::New(context->GetIsolate(), 2013))
.FromJust();
// Call the runtime version of GetOwnPropertyNames() on
// the natively created object through JavaScript.
CHECK(context->Global()->Set(context.local(), v8_str("obj"), o2).FromJust());
CHECK(context->Global()->Set(context.local(), v8_str("sym"), sym).FromJust());
// PROPERTY_FILTER_NONE = 0
CompileRun("var names = %GetOwnPropertyKeys(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();
CHECK(context->Global()
->Set(context.local(), v8_str("func1"),
t1->GetFunction(context.local()).ToLocalChecked())
.FromJust());
// Configured value of ReadOnly flag.
CHECK(
CompileRun(
"(function() {"
" descriptor = Object.getOwnPropertyDescriptor(func1, 'prototype');"
" return (descriptor['writable'] == false);"
"})()")
->BooleanValue(context.local())
.FromJust());
CHECK_EQ(
42,
CompileRun("func1.prototype.x")->Int32Value(context.local()).FromJust());
CHECK_EQ(42, CompileRun("func1.prototype = {}; func1.prototype.x")
->Int32Value(context.local())
.FromJust());
Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New(isolate);
t2->PrototypeTemplate()->Set(v8_str("x"), v8::Integer::New(isolate, 42));
CHECK(context->Global()
->Set(context.local(), v8_str("func2"),
t2->GetFunction(context.local()).ToLocalChecked())
.FromJust());
// Default value of ReadOnly flag.
CHECK(
CompileRun(
"(function() {"
" descriptor = Object.getOwnPropertyDescriptor(func2, 'prototype');"
" return (descriptor['writable'] == true);"
"})()")
->BooleanValue(context.local())
.FromJust());
CHECK_EQ(
42,
CompileRun("func2.prototype.x")->Int32Value(context.local()).FromJust());
}
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(context.local())
.ToLocalChecked()
->NewInstance(context.local())
.ToLocalChecked();
Local<v8::Object> o1 = t->GetFunction(context.local())
.ToLocalChecked()
->NewInstance(context.local())
.ToLocalChecked();
CHECK(o0->SetPrototype(context.local(), o1).FromJust());
// 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(context.local(), o0).IsNothing());
CHECK(!try_catch.HasCaught());
CHECK(!CcTest::i_isolate()->has_pending_exception());
CHECK_EQ(42, CompileRun("function f() { return 42; }; f()")
->Int32Value(context.local())
.FromJust());
}
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.local()).ToLocalChecked();
CHECK(!fun->IsConstructor());
CHECK(context->Global()->Set(context.local(), v8_str("fun"), fun).FromJust());
CHECK(!CompileRun("'prototype' in fun")
->BooleanValue(context.local())
.FromJust());
v8::TryCatch try_catch(isolate);
CompileRun("new fun()");
CHECK(try_catch.HasCaught());
try_catch.Reset();
CHECK(fun->NewInstance(context.local()).IsEmpty());
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(context.local(), v8_str("x")).ToLocalChecked());
v8::TryCatch try_catch(isolate);
CHECK(x->Set(context.local(), v8_str("set"), v8::Integer::New(isolate, 8))
.IsNothing());
CHECK(x->Get(context.local(), v8_str("get")).IsEmpty());
CHECK(x->Set(context.local(), v8_str("set"), v8::Integer::New(isolate, 8))
.IsNothing());
CHECK(x->Get(context.local(), v8_str("get")).IsEmpty());
CHECK(x->Set(context.local(), v8_str("set"), v8::Integer::New(isolate, 8))
.IsNothing());
CHECK(x->Get(context.local(), v8_str("get")).IsEmpty());
CHECK(x->Set(context.local(), v8_str("set"), v8::Integer::New(isolate, 8))
.IsNothing());
CHECK(x->Get(context.local(), v8_str("get")).IsEmpty());
}
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.local()).ToLocalChecked();
CHECK(
context->Global()->Set(context.local(), v8_str("Fun"), cons).FromJust());
Local<v8::Object> inst = cons->NewInstance(context.local()).ToLocalChecked();
i::Handle<i::JSReceiver> obj(v8::Utils::OpenHandle(*inst));
CHECK(obj->IsJSObject());
Local<Value> value = CompileRun("(new Fun()).constructor === Fun");
CHECK(value->BooleanValue(context.local()).FromJust());
}
static void ConstructorCallback(
const v8::FunctionCallbackInfo<v8::Value>& args) {
ApiTestFuzzer::Fuzz();
Local<Object> This;
v8::Local<v8::Context> context = args.GetIsolate()->GetCurrentContext();
if (args.IsConstructCall()) {
Local<Object> Holder = args.Holder();
This = Object::New(args.GetIsolate());
Local<Value> proto = Holder->GetPrototype();
if (proto->IsObject()) {
This->SetPrototype(context, proto).FromJust();
}
} else {
This = args.This();
}
This->Set(context, v8_str("a"), args[0]).FromJust();
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.local()).ToLocalChecked();
CHECK(context->Global()
->Set(context.local(), v8_str("obj"), instance)
.FromJust());
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(context.local()).FromJust());
Local<Value> args1[] = {v8_num(28)};
Local<Value> value_obj1 =
instance->CallAsConstructor(context.local(), 1, args1).ToLocalChecked();
CHECK(value_obj1->IsObject());
Local<Object> object1 = Local<Object>::Cast(value_obj1);
value = object1->Get(context.local(), v8_str("a")).ToLocalChecked();
CHECK(value->IsInt32());
CHECK(!try_catch.HasCaught());
CHECK_EQ(28, value->Int32Value(context.local()).FromJust());
// 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(
isolate, value->ToString(context.local()).ToLocalChecked());
CHECK_EQ(0, strcmp("tipli", *string_value1));
Local<Value> args2[] = {v8_str("tipli")};
Local<Value> value_obj2 =
instance->CallAsConstructor(context.local(), 1, args2).ToLocalChecked();
CHECK(value_obj2->IsObject());
Local<Object> object2 = Local<Object>::Cast(value_obj2);
value = object2->Get(context.local(), v8_str("a")).ToLocalChecked();
CHECK(!try_catch.HasCaught());
CHECK(value->IsString());
String::Utf8Value string_value2(
isolate, value->ToString(context.local()).ToLocalChecked());
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(value->BooleanValue(context.local()).FromJust());
Local<Value> args3[] = {v8::True(isolate)};
Local<Value> value_obj3 =
instance->CallAsConstructor(context.local(), 1, args3).ToLocalChecked();
CHECK(value_obj3->IsObject());
Local<Object> object3 = Local<Object>::Cast(value_obj3);
value = object3->Get(context.local(), v8_str("a")).ToLocalChecked();
CHECK(!try_catch.HasCaught());
CHECK(value->IsBoolean());
CHECK(value->BooleanValue(context.local()).FromJust());
// Call the Object's constructor with undefined.
Local<Value> args4[] = {v8::Undefined(isolate)};
Local<Value> value_obj4 =
instance->CallAsConstructor(context.local(), 1, args4).ToLocalChecked();
CHECK(value_obj4->IsObject());
Local<Object> object4 = Local<Object>::Cast(value_obj4);
value = object4->Get(context.local(), v8_str("a")).ToLocalChecked();
CHECK(!try_catch.HasCaught());
CHECK(value->IsUndefined());
// Call the Object's constructor with null.
Local<Value> args5[] = {v8::Null(isolate)};
Local<Value> value_obj5 =
instance->CallAsConstructor(context.local(), 1, args5).ToLocalChecked();
CHECK(value_obj5->IsObject());
Local<Object> object5 = Local<Object>::Cast(value_obj5);
value = object5->Get(context.local(), v8_str("a")).ToLocalChecked();
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.local()).ToLocalChecked();
CHECK(context->Global()
->Set(context.local(), v8_str("obj2"), instance)
.FromJust());
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(isolate, try_catch.Exception());
CHECK_EQ(0,
strcmp("TypeError: obj2 is not a constructor", *exception_value1));
try_catch.Reset();
Local<Value> args[] = {v8_num(29)};
CHECK(instance->CallAsConstructor(context.local(), 1, args).IsEmpty());
CHECK(try_catch.HasCaught());
String::Utf8Value exception_value2(isolate, try_catch.Exception());
CHECK_EQ(
0, strcmp("TypeError: object is not a constructor", *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.local()).ToLocalChecked();
CHECK(context->Global()
->Set(context.local(), v8_str("obj3"), instance)
.FromJust());
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(isolate, try_catch.Exception());
CHECK_EQ(0, strcmp("22", *exception_value1));
try_catch.Reset();
Local<Value> args[] = {v8_num(23)};
CHECK(instance->CallAsConstructor(context.local(), 1, args).IsEmpty());
CHECK(try_catch.HasCaught());
String::Utf8Value exception_value2(isolate, 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(context.local()).ToLocalChecked();
Local<Object> instance1 = function;
CHECK(instance1->IsObject());
CHECK(instance1->IsFunction());
CHECK(context->Global()
->Set(context.local(), v8_str("obj4"), instance1)
.FromJust());
v8::TryCatch try_catch(isolate);
CHECK(!try_catch.HasCaught());
{
Local<Value> value = CompileRun("new obj4(28)");
CHECK(!try_catch.HasCaught());
CHECK(value->IsObject());
Local<Value> args[] = {v8_num(28)};
value = instance1->CallAsConstructor(context.local(), 1, args)
.ToLocalChecked();
CHECK(!try_catch.HasCaught());
CHECK(value->IsObject());
}
Local<Value> proxy = CompileRun("proxy = new Proxy({},{})");
CHECK(!try_catch.HasCaught());
CHECK(proxy->IsProxy());
{
Local<Value> value = CompileRun("new obj4(proxy)");
CHECK(!try_catch.HasCaught());
CHECK(value->IsProxy());
CHECK(value->SameValue(proxy));
Local<Value> args[] = {proxy};
value = instance1->CallAsConstructor(context.local(), 1, args)
.ToLocalChecked();
CHECK(!try_catch.HasCaught());
CHECK(value->SameValue(proxy));
}
Local<ObjectTemplate> instance_template = ObjectTemplate::New(isolate);
instance_template->SetCallAsFunctionHandler(FakeConstructorCallback);
Local<Object> instance2 =
instance_template->NewInstance(context.local()).ToLocalChecked();
CHECK(instance2->IsObject());
CHECK(instance2->IsFunction());
CHECK(context->Global()
->Set(context.local(), v8_str("obj5"), instance2)
.FromJust());
CHECK(!try_catch.HasCaught());
{
Local<Value> value = CompileRun("new obj5(28)");
CHECK(!try_catch.HasCaught());
CHECK(!value->IsObject());
Local<Value> args[] = {v8_num(28)};
value = instance2->CallAsConstructor(context.local(), 1, args)
.ToLocalChecked();
CHECK(!try_catch.HasCaught());
CHECK(!value->IsObject());
}
{
Local<Value> value = CompileRun("new obj5(proxy)");
CHECK(!try_catch.HasCaught());
CHECK(value->IsProxy());
CHECK(value->SameValue(proxy));
Local<Value> args[] = {proxy};
value = instance2->CallAsConstructor(context.local(), 1, args)
.ToLocalChecked();
CHECK(!try_catch.HasCaught());
CHECK(value->SameValue(proxy));
}
}
}
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.local()).ToLocalChecked();
CHECK(
context->Global()->Set(context.local(), v8_str("Fun"), cons).FromJust());
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(context.local()).FromJust());
}
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(current.local()).ToLocalChecked();
CHECK_EQ(2, current->Global()
->Get(current.local(), v8_str("result1"))
.ToLocalChecked()
->Int32Value(current.local())
.FromJust());
CHECK_EQ(0, current->Global()
->Get(current.local(), v8_str("result2"))
.ToLocalChecked()
->Int32Value(current.local())
.FromJust());
CHECK_EQ(1, current->Global()
->Get(current.local(), v8_str("result3"))
.ToLocalChecked()
->Int32Value(current.local())
.FromJust());
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(current.local()).ToLocalChecked();
CHECK(!try_catch.HasCaught());
CHECK_EQ(2, current->Global()
->Get(current.local(), v8_str("result4"))
.ToLocalChecked()
->Int32Value(current.local())
.FromJust());
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.
CHECK(current->Global()
->Set(current.local(), v8_str("other"), other->Global())
.FromJust());
// Check that new variables are introduced in other context.
Local<Script> script = v8_compile("other.eval('var foo = 1234')");
script->Run(current.local()).ToLocalChecked();
Local<Value> foo =
other->Global()->Get(current.local(), v8_str("foo")).ToLocalChecked();
CHECK_EQ(1234, foo->Int32Value(other.local()).FromJust());
CHECK(!current->Global()->Has(current.local(), v8_str("foo")).FromJust());
// Check that writing to non-existing properties introduces them in
// the other context.
script = v8_compile("other.eval('na = 1234')");
script->Run(current.local()).ToLocalChecked();
CHECK_EQ(1234, other->Global()
->Get(current.local(), v8_str("na"))
.ToLocalChecked()
->Int32Value(other.local())
.FromJust());
CHECK(!current->Global()->Has(current.local(), v8_str("na")).FromJust());
// 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');");
CHECK(script->Run(current.local()).IsEmpty());
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');"
"})();");
CHECK(script->Run(current.local()).IsEmpty());
CHECK(try_catch.HasCaught());
try_catch.Reset();
// Check that global variables in the other environment are visible
// when evaluting code.
CHECK(other->Global()
->Set(other.local(), v8_str("bis"), v8_num(1234))
.FromJust());
script = v8_compile("other.eval('bis')");
CHECK_EQ(1234, script->Run(current.local())
.ToLocalChecked()
->Int32Value(current.local())
.FromJust());
CHECK(!try_catch.HasCaught());
// Check that the 'this' pointer points to the global object evaluating
// code.
CHECK(other->Global()
->Set(current.local(), v8_str("t"), other->Global())
.FromJust());
script = v8_compile("other.eval('this == t')");
Local<Value> result = script->Run(current.local()).ToLocalChecked();
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')}");
CHECK(script->Run(current.local()).IsEmpty());
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')");
CHECK(script->Run(current.local()).IsEmpty());
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);
Local<String> token = v8_str("<security token>");
context0->SetSecurityToken(token);
context1->SetSecurityToken(token);
// Set up function in context0 that uses eval from context0.
context0->Enter();
v8::Local<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 undefined is returned.
context1->Enter();
CHECK(context1->Global()->Set(context1, v8_str("fun"), fun).FromJust());
v8::Local<v8::Value> x_value = CompileRun("fun('x')");
CHECK_EQ(42, x_value->Int32Value(context1).FromJust());
context0->DetachGlobal();
x_value = CompileRun("fun('x')");
CHECK(x_value->IsUndefined());
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.
CHECK(current->Global()
->Set(current.local(), v8_str("other"), other->Global())
.FromJust());
// Trigger lazy loading in other context.
Local<Script> script = v8_compile("other.eval('new Date(42)')");
Local<Value> value = script->Run(current.local()).ToLocalChecked();
CHECK_EQ(42.0, value->NumberValue(current.local()).FromJust());
}
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(args.GetIsolate()->GetCurrentContext())
.FromJust()));
return;
}
}
args.GetReturnValue().Set(args[0]);
}
// Test that a call handler can be set for objects which will allow
// non-function objects created through the API to be called as
// functions.
THREADED_TEST(CallAsFunction) {
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(context.local())
.ToLocalChecked()
->NewInstance(context.local())
.ToLocalChecked();
CHECK(context->Global()
->Set(context.local(), v8_str("obj"), instance)
.FromJust());
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(context.local()).FromJust());
value = CompileRun("(function(o){return o(49)})(obj)");
CHECK(!try_catch.HasCaught());
CHECK_EQ(49, value->Int32Value(context.local()).FromJust());
// test special case of call as function
value = CompileRun("[obj]['0'](45)");
CHECK(!try_catch.HasCaught());
CHECK_EQ(45, value->Int32Value(context.local()).FromJust());
value = CompileRun(
"obj.call = Function.prototype.call;"
"obj.call(null, 87)");
CHECK(!try_catch.HasCaught());
CHECK_EQ(87, value->Int32Value(context.local()).FromJust());
// 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(context.local()).FromJust());
const char* call_17 = "Function.prototype.call.call(obj, this, 17)";
value = CompileRun(call_17);
CHECK(!try_catch.HasCaught());
CHECK_EQ(17, value->Int32Value(context.local()).FromJust());
// 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(context.local()).FromJust());
// Check that the call-as-function handler can be called through
// the API.
v8::Local<Value> args[] = {v8_num(28)};
value = instance->CallAsFunction(context.local(), instance, 1, args)
.ToLocalChecked();
CHECK(!try_catch.HasCaught());
CHECK_EQ(28, value->Int32Value(context.local()).FromJust());
}
{
Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(isolate);
Local<ObjectTemplate> instance_template(t->InstanceTemplate());
USE(instance_template);
Local<v8::Object> instance = t->GetFunction(context.local())
.ToLocalChecked()
->NewInstance(context.local())
.ToLocalChecked();
CHECK(context->Global()
->Set(context.local(), v8_str("obj2"), instance)
.FromJust());
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(isolate, 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::Local<Value> args[] = {v8_num(28)};
CHECK(
instance->CallAsFunction(context.local(), instance, 1, args).IsEmpty());
CHECK(try_catch.HasCaught());
String::Utf8Value exception_value2(isolate, try_catch.Exception());
CHECK_EQ(0,
strcmp("TypeError: 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(context.local())
.ToLocalChecked()
->NewInstance(context.local())
.ToLocalChecked();
CHECK(context->Global()
->Set(context.local(), v8_str("obj3"), instance)
.FromJust());
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(isolate, try_catch.Exception());
CHECK_EQ(0, strcmp("22", *exception_value1));
try_catch.Reset();
v8::Local<Value> args[] = {v8_num(23)};
CHECK(
instance->CallAsFunction(context.local(), instance, 1, args).IsEmpty());
CHECK(try_catch.HasCaught());
String::Utf8Value exception_value2(isolate, 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(context.local())
.ToLocalChecked()
->NewInstance(context.local())
.ToLocalChecked();
Local<v8::Value> a1 =
instance
->CallAsFunction(context.local(), v8::Undefined(isolate), 0,
nullptr)
.ToLocalChecked();
CHECK(a1->StrictEquals(instance));
Local<v8::Value> a2 =
instance->CallAsFunction(context.local(), v8::Null(isolate), 0, nullptr)
.ToLocalChecked();
CHECK(a2->StrictEquals(instance));
Local<v8::Value> a3 =
instance->CallAsFunction(context.local(), v8_num(42), 0, nullptr)
.ToLocalChecked();
CHECK(a3->StrictEquals(instance));
Local<v8::Value> a4 =
instance->CallAsFunction(context.local(), v8_str("hello"), 0, nullptr)
.ToLocalChecked();
CHECK(a4->StrictEquals(instance));
Local<v8::Value> a5 =
instance->CallAsFunction(context.local(), v8::True(isolate), 0, nullptr)
.ToLocalChecked();
CHECK(a5->StrictEquals(instance));
}
{
CompileRun(
"function ReturnThisSloppy() {"
" return this;"
"}"
"function ReturnThisStrict() {"
" 'use strict';"
" return this;"
"}");
Local<Function> ReturnThisSloppy = Local<Function>::Cast(
context->Global()
->Get(context.local(), v8_str("ReturnThisSloppy"))
.ToLocalChecked());
Local<Function> ReturnThisStrict = Local<Function>::Cast(
context->Global()
->Get(context.local(), v8_str("ReturnThisStrict"))
.ToLocalChecked());
Local<v8::Value> a1 =
ReturnThisSloppy
->CallAsFunction(context.local(), v8::Undefined(isolate), 0,
nullptr)
.ToLocalChecked();
CHECK(a1->StrictEquals(context->Global()));
Local<v8::Value> a2 =
ReturnThisSloppy
->CallAsFunction(context.local(), v8::Null(isolate), 0, nullptr)
.ToLocalChecked();
CHECK(a2->StrictEquals(context->Global()));
Local<v8::Value> a3 =
ReturnThisSloppy
->CallAsFunction(context.local(), v8_num(42), 0, nullptr)
.ToLocalChecked();
CHECK(a3->IsNumberObject());
CHECK_EQ(42.0, a3.As<v8::NumberObject>()->ValueOf());
Local<v8::Value> a4 =
ReturnThisSloppy
->CallAsFunction(context.local(), v8_str("hello"), 0, nullptr)
.ToLocalChecked();
CHECK(a4->IsStringObject());
CHECK(a4.As<v8::StringObject>()->ValueOf()->StrictEquals(v8_str("hello")));
Local<v8::Value> a5 =
ReturnThisSloppy
->CallAsFunction(context.local(), v8::True(isolate), 0, nullptr)
.ToLocalChecked();
CHECK(a5->IsBooleanObject());
CHECK(a5.As<v8::BooleanObject>()->ValueOf());
Local<v8::Value> a6 =
ReturnThisStrict
->CallAsFunction(context.local(), v8::Undefined(isolate), 0,
nullptr)
.ToLocalChecked();
CHECK(a6->IsUndefined());
Local<v8::Value> a7 =
ReturnThisStrict
->CallAsFunction(context.local(), v8::Null(isolate), 0, nullptr)
.ToLocalChecked();
CHECK(a7->IsNull());
Local<v8::Value> a8 =
ReturnThisStrict
->CallAsFunction(context.local(), v8_num(42), 0, nullptr)
.ToLocalChecked();
CHECK(a8->StrictEquals(v8_num(42)));
Local<v8::Value> a9 =
ReturnThisStrict
->CallAsFunction(context.local(), v8_str("hello"), 0, nullptr)
.ToLocalChecked();
CHECK(a9->StrictEquals(v8_str("hello")));
Local<v8::Value> a10 =
ReturnThisStrict
->CallAsFunction(context.local(), v8::True(isolate), 0, nullptr)
.ToLocalChecked();
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(context.local()).ToLocalChecked();
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(context.local()).ToLocalChecked();
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(context.local()).ToLocalChecked();
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(context.local()).ToLocalChecked();
Local<Object> instance = function;
v8::TryCatch try_catch(isolate);
CHECK(instance->IsCallable());
CHECK(!try_catch.HasCaught());
}
}
THREADED_TEST(Regress567998) {
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
Local<v8::FunctionTemplate> desc =
v8::FunctionTemplate::New(env->GetIsolate());
desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable
desc->InstanceTemplate()->SetCallAsFunctionHandler(ReturnThis); // callable
Local<v8::Object> obj = desc->GetFunction(env.local())
.ToLocalChecked()
->NewInstance(env.local())
.ToLocalChecked();
CHECK(
env->Global()->Set(env.local(), v8_str("undetectable"), obj).FromJust());
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);
}
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::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(isolate->GetCurrentContext(), args.Holder())
.FromJust());
CHECK(args.Data()
->Equals(isolate->GetCurrentContext(), v8_str("method_data"))
.FromJust());
args.GetReturnValue().Set(
args[0]->Int32Value(isolate->GetCurrentContext()).FromJust() + 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(isolate->GetCurrentContext(), args.Holder())
.FromJust());
CHECK(args.Data()
->Equals(isolate->GetCurrentContext(), v8_str("method_data"))
.FromJust());
// Note, we're using HasRealNamedProperty instead of Has to avoid
// invoking the interceptor again.
CHECK(args.Holder()
->HasRealNamedProperty(isolate->GetCurrentContext(), v8_str("foo"))
.FromJust());
args.GetReturnValue().Set(
args[0]->Int32Value(isolate->GetCurrentContext()).FromJust() + 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::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::Local<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.local()).ToLocalChecked();
CHECK(context->Global()
->Set(context.local(), v8_str("nativeobject"), nativeobject_obj)
.FromJust());
// 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::Local<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.local()).ToLocalChecked();
CHECK(context->Global()
->Set(context.local(), v8_str("nativeobject"), nativeobject_obj)
.FromJust());
// call the api function multiple times to ensure direct call stub creation.
v8::Local<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(context.local(), result).FromJust());
}
static int p_getter_count_3;
static Local<Value> DoDirectGetter() {
if (++p_getter_count_3 % 3 == 0) {
CcTest::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::Local<v8::ObjectTemplate> obj = v8::ObjectTemplate::New(isolate);
obj->SetAccessor(v8_str("p1"), accessor);
CHECK(context->Global()
->Set(context.local(), v8_str("o1"),
obj->NewInstance(context.local()).ToLocalChecked())
.FromJust());
p_getter_count_3 = 0;
v8::Local<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(context.local(), result)
.FromJust());
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::Local<v8::ObjectTemplate> obj = v8::ObjectTemplate::New(isolate);
obj->SetAccessor(v8_str("p1"), ThrowingDirectGetterCallback);
CHECK(context->Global()
->Set(context.local(), v8_str("o1"),
obj->NewInstance(context.local()).ToLocalChecked())
.FromJust());
v8::Local<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(context.local(), result).FromJust());
}
THREADED_PROFILED_TEST(InterceptorCallICFastApi_TrivialSignature) {
int interceptor_call_count = 0;
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Local<v8::FunctionTemplate> fun_templ =
v8::FunctionTemplate::New(isolate);
v8::Local<v8::FunctionTemplate> method_templ = v8::FunctionTemplate::New(
isolate, FastApiCallback_TrivialSignature, v8_str("method_data"),
v8::Local<v8::Signature>());
v8::Local<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
proto_templ->Set(v8_str("method"), method_templ);
v8::Local<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
templ->SetHandler(v8::NamedPropertyHandlerConfiguration(
InterceptorCallICFastApi, nullptr, nullptr, nullptr, nullptr,
v8::External::New(isolate, &interceptor_call_count)));
LocalContext context;
v8::Local<v8::Function> fun =
fun_templ->GetFunction(context.local()).ToLocalChecked();
GenerateSomeGarbage();
CHECK(context->Global()
->Set(context.local(), v8_str("o"),
fun->NewInstance(context.local()).ToLocalChecked())
.FromJust());
CompileRun(
"var result = 0;"
"for (var i = 0; i < 100; i++) {"
" result = o.method(41);"
"}");
CHECK_EQ(42, context->Global()
->Get(context.local(), v8_str("result"))
.ToLocalChecked()
->Int32Value(context.local())
.FromJust());
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::Local<v8::FunctionTemplate> fun_templ =
v8::FunctionTemplate::New(isolate);
v8::Local<v8::FunctionTemplate> method_templ = v8::FunctionTemplate::New(
isolate, FastApiCallback_SimpleSignature, v8_str("method_data"),
v8::Signature::New(isolate, fun_templ));
v8::Local<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
proto_templ->Set(v8_str("method"), method_templ);
fun_templ->SetHiddenPrototype(true);
v8::Local<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
templ->SetHandler(v8::NamedPropertyHandlerConfiguration(
InterceptorCallICFastApi, nullptr, nullptr, nullptr, nullptr,
v8::External::New(isolate, &interceptor_call_count)));
LocalContext context;
v8::Local<v8::Function> fun =
fun_templ->GetFunction(context.local()).ToLocalChecked();
GenerateSomeGarbage();
CHECK(context->Global()
->Set(context.local(), v8_str("o"),
fun->NewInstance(context.local()).ToLocalChecked())
.FromJust());
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(context.local(), v8_str("result"))
.ToLocalChecked()
->Int32Value(context.local())
.FromJust());
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::Local<v8::FunctionTemplate> fun_templ =
v8::FunctionTemplate::New(isolate);
v8::Local<v8::FunctionTemplate> method_templ = v8::FunctionTemplate::New(
isolate, FastApiCallback_SimpleSignature, v8_str("method_data"),
v8::Signature::New(isolate, fun_templ));
v8::Local<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
proto_templ->Set(v8_str("method"), method_templ);
fun_templ->SetHiddenPrototype(true);
v8::Local<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
templ->SetHandler(v8::NamedPropertyHandlerConfiguration(
InterceptorCallICFastApi, nullptr, nullptr, nullptr, nullptr,
v8::External::New(isolate, &interceptor_call_count)));
LocalContext context;
v8::Local<v8::Function> fun =
fun_templ->GetFunction(context.local()).ToLocalChecked();
GenerateSomeGarbage();
CHECK(context->Global()
->Set(context.local(), v8_str("o"),
fun->NewInstance(context.local()).ToLocalChecked())
.FromJust());
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(context.local(), v8_str("result"))
.ToLocalChecked()
->Int32Value(context.local())
.FromJust());
CHECK_EQ(42, context->Global()
->Get(context.local(), v8_str("saved_result"))
.ToLocalChecked()
->Int32Value(context.local())
.FromJust());
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::Local<v8::FunctionTemplate> fun_templ =
v8::FunctionTemplate::New(isolate);
v8::Local<v8::FunctionTemplate> method_templ = v8::FunctionTemplate::New(
isolate, FastApiCallback_SimpleSignature, v8_str("method_data"),
v8::Signature::New(isolate, fun_templ));
v8::Local<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
proto_templ->Set(v8_str("method"), method_templ);
fun_templ->SetHiddenPrototype(true);
v8::Local<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
templ->SetHandler(v8::NamedPropertyHandlerConfiguration(
InterceptorCallICFastApi, nullptr, nullptr, nullptr, nullptr,
v8::External::New(isolate, &interceptor_call_count)));
LocalContext context;
v8::Local<v8::Function> fun =
fun_templ->GetFunction(context.local()).ToLocalChecked();
GenerateSomeGarbage();
CHECK(context->Global()
->Set(context.local(), v8_str("o"),
fun->NewInstance(context.local()).ToLocalChecked())
.FromJust());
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(context.local(), v8_str("result"))
.ToLocalChecked()
->Int32Value(context.local())
.FromJust());
CHECK_EQ(42, context->Global()
->Get(context.local(), v8_str("saved_result"))
.ToLocalChecked()
->Int32Value(context.local())
.FromJust());
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::Local<v8::FunctionTemplate> fun_templ =
v8::FunctionTemplate::New(isolate);
v8::Local<v8::FunctionTemplate> method_templ = v8::FunctionTemplate::New(
isolate, FastApiCallback_SimpleSignature, v8_str("method_data"),
v8::Signature::New(isolate, fun_templ));
v8::Local<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
proto_templ->Set(v8_str("method"), method_templ);
fun_templ->SetHiddenPrototype(true);
v8::Local<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
templ->SetHandler(v8::NamedPropertyHandlerConfiguration(
InterceptorCallICFastApi, nullptr, nullptr, nullptr, nullptr,
v8::External::New(isolate, &interceptor_call_count)));
LocalContext context;
v8::Local<v8::Function> fun =
fun_templ->GetFunction(context.local()).ToLocalChecked();
GenerateSomeGarbage();
CHECK(context->Global()
->Set(context.local(), v8_str("o"),
fun->NewInstance(context.local()).ToLocalChecked())
.FromJust());
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(
context.local(),
try_catch.Exception()->ToString(context.local()).ToLocalChecked())
.FromJust());
CHECK_EQ(42, context->Global()
->Get(context.local(), v8_str("saved_result"))
.ToLocalChecked()
->Int32Value(context.local())
.FromJust());
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::Local<v8::FunctionTemplate> fun_templ =
v8::FunctionTemplate::New(isolate);
v8::Local<v8::FunctionTemplate> method_templ = v8::FunctionTemplate::New(
isolate, FastApiCallback_SimpleSignature, v8_str("method_data"),
v8::Signature::New(isolate, fun_templ));
v8::Local<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
proto_templ->Set(v8_str("method"), method_templ);
fun_templ->SetHiddenPrototype(true);
v8::Local<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
templ->SetHandler(v8::NamedPropertyHandlerConfiguration(
InterceptorCallICFastApi, nullptr, nullptr, nullptr, nullptr,
v8::External::New(isolate, &interceptor_call_count)));
LocalContext context;
v8::Local<v8::Function> fun =
fun_templ->GetFunction(context.local()).ToLocalChecked();
GenerateSomeGarbage();
CHECK(context->Global()
->Set(context.local(), v8_str("o"),
fun->NewInstance(context.local()).ToLocalChecked())
.FromJust());
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(
context.local(),
try_catch.Exception()->ToString(context.local()).ToLocalChecked())
.FromJust());
CHECK_EQ(42, context->Global()
->Get(context.local(), v8_str("saved_result"))
.ToLocalChecked()
->Int32Value(context.local())
.FromJust());
CHECK_GE(interceptor_call_count, 50);
}
THREADED_PROFILED_TEST(CallICFastApi_TrivialSignature) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Local<v8::FunctionTemplate> fun_templ =
v8::FunctionTemplate::New(isolate);
v8::Local<v8::FunctionTemplate> method_templ = v8::FunctionTemplate::New(
isolate, FastApiCallback_TrivialSignature, v8_str("method_data"),
v8::Local<v8::Signature>());
v8::Local<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
proto_templ->Set(v8_str("method"), method_templ);
v8::Local<v8::ObjectTemplate> templ(fun_templ->InstanceTemplate());
USE(templ);
LocalContext context;
v8::Local<v8::Function> fun =
fun_templ->GetFunction(context.local()).ToLocalChecked();
GenerateSomeGarbage();
CHECK(context->Global()
->Set(context.local(), v8_str("o"),
fun->NewInstance(context.local()).ToLocalChecked())
.FromJust());
CompileRun(
"var result = 0;"
"for (var i = 0; i < 100; i++) {"
" result = o.method(41);"
"}");
CHECK_EQ(42, context->Global()
->Get(context.local(), v8_str("result"))
.ToLocalChecked()
->Int32Value(context.local())
.FromJust());
}
THREADED_PROFILED_TEST(CallICFastApi_SimpleSignature) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Local<v8::FunctionTemplate> fun_templ =
v8::FunctionTemplate::New(isolate);
v8::Local<v8::FunctionTemplate> method_templ = v8::FunctionTemplate::New(
isolate, FastApiCallback_SimpleSignature, v8_str("method_data"),
v8::Signature::New(isolate, fun_templ));
v8::Local<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
proto_templ->Set(v8_str("method"), method_templ);
fun_templ->SetHiddenPrototype(true);
v8::Local<v8::ObjectTemplate> templ(fun_templ->InstanceTemplate());
CHECK(!templ.IsEmpty());
LocalContext context;
v8::Local<v8::Function> fun =
fun_templ->GetFunction(context.local()).ToLocalChecked();
GenerateSomeGarbage();
CHECK(context->Global()
->Set(context.local(), v8_str("o"),
fun->NewInstance(context.local()).ToLocalChecked())
.FromJust());
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(context.local(), v8_str("result"))
.ToLocalChecked()
->Int32Value(context.local())
.FromJust());
}
THREADED_PROFILED_TEST(CallICFastApi_SimpleSignature_Miss1) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Local<v8::FunctionTemplate> fun_templ =
v8::FunctionTemplate::New(isolate);
v8::Local<v8::FunctionTemplate> method_templ = v8::FunctionTemplate::New(
isolate, FastApiCallback_SimpleSignature, v8_str("method_data"),
v8::Signature::New(isolate, fun_templ));
v8::Local<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
proto_templ->Set(v8_str("method"), method_templ);
fun_templ->SetHiddenPrototype(true);
v8::Local<v8::ObjectTemplate> templ(fun_templ->InstanceTemplate());
CHECK(!templ.IsEmpty());
LocalContext context;
v8::Local<v8::Function> fun =
fun_templ->GetFunction(context.local()).ToLocalChecked();
GenerateSomeGarbage();
CHECK(context->Global()
->Set(context.local(), v8_str("o"),
fun->NewInstance(context.local()).ToLocalChecked())
.FromJust());
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(context.local(), v8_str("result"))
.ToLocalChecked()
->Int32Value(context.local())
.FromJust());
CHECK_EQ(42, context->Global()
->Get(context.local(), v8_str("saved_result"))
.ToLocalChecked()
->Int32Value(context.local())
.FromJust());
}
THREADED_PROFILED_TEST(CallICFastApi_SimpleSignature_Miss2) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Local<v8::FunctionTemplate> fun_templ =
v8::FunctionTemplate::New(isolate);
v8::Local<v8::FunctionTemplate> method_templ = v8::FunctionTemplate::New(
isolate, FastApiCallback_SimpleSignature, v8_str("method_data"),
v8::Signature::New(isolate, fun_templ));
v8::Local<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
proto_templ->Set(v8_str("method"), method_templ);
fun_templ->SetHiddenPrototype(true);
v8::Local<v8::ObjectTemplate> templ(fun_templ->InstanceTemplate());
CHECK(!templ.IsEmpty());
LocalContext context;
v8::Local<v8::Function> fun =
fun_templ->GetFunction(context.local()).ToLocalChecked();
GenerateSomeGarbage();
CHECK(context->Global()
->Set(context.local(), v8_str("o"),
fun->NewInstance(context.local()).ToLocalChecked())
.FromJust());
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(
context.local(),
try_catch.Exception()->ToString(context.local()).ToLocalChecked())
.FromJust());
CHECK_EQ(42, context->Global()
->Get(context.local(), v8_str("saved_result"))
.ToLocalChecked()
->Int32Value(context.local())
.FromJust());
}
THREADED_PROFILED_TEST(CallICFastApi_SimpleSignature_TypeError) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Local<v8::FunctionTemplate> fun_templ =
v8::FunctionTemplate::New(isolate);
v8::Local<v8::FunctionTemplate> method_templ = v8::FunctionTemplate::New(
isolate, FastApiCallback_SimpleSignature, v8_str("method_data"),
v8::Signature::New(isolate, fun_templ));
v8::Local<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
proto_templ->Set(v8_str("method"), method_templ);
fun_templ->SetHiddenPrototype(true);
v8::Local<v8::ObjectTemplate> templ(fun_templ->InstanceTemplate());
CHECK(!templ.IsEmpty());
LocalContext context;
v8::Local<v8::Function> fun =
fun_templ->GetFunction(context.local()).ToLocalChecked();
GenerateSomeGarbage();
CHECK(context->Global()
->Set(context.local(), v8_str("o"),
fun->NewInstance(context.local()).ToLocalChecked())
.FromJust());
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(
context.local(),
try_catch.Exception()->ToString(context.local()).ToLocalChecked())
.FromJust());
CHECK_EQ(42, context->Global()
->Get(context.local(), v8_str("saved_result"))
.ToLocalChecked()
->Int32Value(context.local())
.FromJust());
}
static void ThrowingGetter(Local<String> name,
const v8::PropertyCallbackInfo<v8::Value>& info) {
ApiTestFuzzer::Fuzz();
info.GetIsolate()->ThrowException(Local<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(context.local())
.ToLocalChecked()
->NewInstance(context.local())
.ToLocalChecked();
Local<Object> another = Object::New(context->GetIsolate());
CHECK(another->SetPrototype(context.local(), instance).FromJust());
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());
v8::MaybeLocal<Value> result =
instance->GetRealNamedProperty(context.local(), v8_str("f"));
CHECK(try_catch.HasCaught());
try_catch.Reset();
CHECK(result.IsEmpty());
Maybe<PropertyAttribute> attr =
instance->GetRealNamedPropertyAttributes(context.local(), v8_str("f"));
CHECK(!try_catch.HasCaught());
CHECK(Just(None) == attr);
result = another->GetRealNamedProperty(context.local(), v8_str("f"));
CHECK(try_catch.HasCaught());
try_catch.Reset();
CHECK(result.IsEmpty());
attr = another->GetRealNamedPropertyAttributes(context.local(), v8_str("f"));
CHECK(!try_catch.HasCaught());
CHECK(Just(None) == attr);
result = another->GetRealNamedPropertyInPrototypeChain(context.local(),
v8_str("f"));
CHECK(try_catch.HasCaught());
try_catch.Reset();
CHECK(result.IsEmpty());
attr = another->GetRealNamedPropertyAttributesInPrototypeChain(
context.local(), v8_str("f"));
CHECK(!try_catch.HasCaught());
CHECK(Just(None) == attr);
result = another->Get(context.local(), v8_str("f"));
CHECK(try_catch.HasCaught());
try_catch.Reset();
CHECK(result.IsEmpty());
result = with_js_getter->GetRealNamedProperty(context.local(), v8_str("f"));
CHECK(try_catch.HasCaught());
try_catch.Reset();
CHECK(result.IsEmpty());
attr = with_js_getter->GetRealNamedPropertyAttributes(context.local(),
v8_str("f"));
CHECK(!try_catch.HasCaught());
CHECK(Just(None) == attr);
result = with_js_getter->Get(context.local(), v8_str("f"));
CHECK(try_catch.HasCaught());
try_catch.Reset();
CHECK(result.IsEmpty());
Local<Object> target = CompileRun("({})").As<Object>();
Local<Object> handler = CompileRun("({})").As<Object>();
Local<v8::Proxy> proxy =
v8::Proxy::New(context.local(), target, handler).ToLocalChecked();
result = target->GetRealNamedProperty(context.local(), v8_str("f"));
CHECK(!try_catch.HasCaught());
CHECK(result.IsEmpty());
result = proxy->GetRealNamedProperty(context.local(), v8_str("f"));
CHECK(!try_catch.HasCaught());
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(Local<Message> message, Local<Value> data) {
TryCatch try_catch(CcTest::isolate());
}
static void ThrowFromJS(Local<Message> message, Local<Value> data) {
if (--call_depth) CompileRun("throw 'ThrowInJS';");
}
static void ThrowViaApi(Local<Message> message, Local<Value> data) {
if (--call_depth) CcTest::isolate()->ThrowException(v8_str("ThrowViaApi"));
}
static void WebKitLike(Local<Message> message, Local<Value> data) {
Local<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.local())
.ToLocalChecked();
CHECK(
context->Global()->Set(context.local(), v8_str("func"), func).FromJust());
MessageCallback callbacks[] = {nullptr, WebKitLike, ThrowViaApi, ThrowFromJS,
WithTryCatch};
for (unsigned i = 0; i < sizeof(callbacks)/sizeof(callbacks[0]); i++) {
MessageCallback callback = callbacks[i];
if (callback != nullptr) {
isolate->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 != nullptr) {
isolate->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::Local<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
// nullptr setter.
child_instance_templ->SetAccessor(v8_str("i"), ChildGetter, 0,
v8::Local<Value>(), v8::DEFAULT,
v8::ReadOnly);
// Instantiate the child template.
Local<v8::Object> instance = child_templ->GetFunction(context.local())
.ToLocalChecked()
->NewInstance(context.local())
.ToLocalChecked();
// Check that the child function overrides the parent one.
CHECK(context->Global()
->Set(context.local(), v8_str("o"), instance)
.FromJust());
Local<Value> value = v8_compile("o.f")->Run(context.local()).ToLocalChecked();
// Check that the 'g' that was added last is hit.
CHECK_EQ(42, value->Int32Value(context.local()).FromJust());
value = v8_compile("o.g")->Run(context.local()).ToLocalChecked();
CHECK_EQ(42, value->Int32Value(context.local()).FromJust());
// Check that 'h' cannot be shadowed.
value = v8_compile("o.h = 3; o.h")->Run(context.local()).ToLocalChecked();
CHECK_EQ(1, value->Int32Value(context.local()).FromJust());
// Check that 'i' cannot be shadowed or changed.
value = v8_compile("o.i = 3; o.i")->Run(context.local()).ToLocalChecked();
CHECK_EQ(42, value->Int32Value(context.local()).FromJust());
}
static void ShouldThrowOnErrorGetter(
Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
ApiTestFuzzer::Fuzz();
v8::Isolate* isolate = info.GetIsolate();
Local<Boolean> should_throw_on_error =
Boolean::New(isolate, info.ShouldThrowOnError());
info.GetReturnValue().Set(should_throw_on_error);
}
template <typename T>
static void ShouldThrowOnErrorSetter(Local<Name> name, Local<v8::Value> value,
const v8::PropertyCallbackInfo<T>& info) {
ApiTestFuzzer::Fuzz();
v8::Isolate* isolate = info.GetIsolate();
auto context = isolate->GetCurrentContext();
Local<Boolean> should_throw_on_error_value =
Boolean::New(isolate, info.ShouldThrowOnError());
CHECK(context->Global()
->Set(isolate->GetCurrentContext(), v8_str("should_throw_setter"),
should_throw_on_error_value)
.FromJust());
}
THREADED_TEST(AccessorShouldThrowOnError) {
LocalContext context;
v8::Isolate* isolate = context->GetIsolate();
v8::HandleScope scope(isolate);
Local<Object> global = context->Global();
Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate);
Local<ObjectTemplate> instance_templ = templ->InstanceTemplate();
instance_templ->SetAccessor(v8_str("f"), ShouldThrowOnErrorGetter,
ShouldThrowOnErrorSetter<void>);
Local<v8::Object> instance = templ->GetFunction(context.local())
.ToLocalChecked()
->NewInstance(context.local())
.ToLocalChecked();
CHECK(global->Set(context.local(), v8_str("o"), instance).FromJust());
// SLOPPY mode
Local<Value> value = v8_compile("o.f")->Run(context.local()).ToLocalChecked();
CHECK(value->IsFalse());
v8_compile("o.f = 153")->Run(context.local()).ToLocalChecked();
value = global->Get(context.local(), v8_str("should_throw_setter"))
.ToLocalChecked();
CHECK(value->IsFalse());
// STRICT mode
value = v8_compile("'use strict';o.f")->Run(context.local()).ToLocalChecked();
CHECK(value->IsFalse());
v8_compile("'use strict'; o.f = 153")->Run(context.local()).ToLocalChecked();
value = global->Get(context.local(), v8_str("should_throw_setter"))
.ToLocalChecked();
CHECK(value->IsTrue());
}
static void ShouldThrowOnErrorQuery(
Local<Name> name, const v8::PropertyCallbackInfo<v8::Integer>& info) {
ApiTestFuzzer::Fuzz();
v8::Isolate* isolate = info.GetIsolate();
info.GetReturnValue().Set(v8::None);
auto context = isolate->GetCurrentContext();
Local<Boolean> should_throw_on_error_value =
Boolean::New(isolate, info.ShouldThrowOnError());
CHECK(context->Global()
->Set(isolate->GetCurrentContext(), v8_str("should_throw_query"),
should_throw_on_error_value)
.FromJust());
}
static void ShouldThrowOnErrorDeleter(
Local<Name> name, const v8::PropertyCallbackInfo<v8::Boolean>& info) {
ApiTestFuzzer::Fuzz();
v8::Isolate* isolate = info.GetIsolate();
info.GetReturnValue().Set(v8::True(isolate));
auto context = isolate->GetCurrentContext();
Local<Boolean> should_throw_on_error_value =
Boolean::New(isolate, info.ShouldThrowOnError());
CHECK(context->Global()
->Set(isolate->GetCurrentContext(), v8_str("should_throw_deleter"),
should_throw_on_error_value)
.FromJust());
}
static void ShouldThrowOnErrorPropertyEnumerator(
const v8::PropertyCallbackInfo<v8::Array>& info) {
ApiTestFuzzer::Fuzz();
v8::Isolate* isolate = info.GetIsolate();
Local<v8::Array> names = v8::Array::New(isolate, 1);
CHECK(names->Set(isolate->GetCurrentContext(), names, v8_num(1)).FromJust());
info.GetReturnValue().Set(names);
auto context = isolate->GetCurrentContext();
Local<Boolean> should_throw_on_error_value =
Boolean::New(isolate, info.ShouldThrowOnError());
CHECK(context->Global()
->Set(isolate->GetCurrentContext(),
v8_str("should_throw_enumerator"),
should_throw_on_error_value)
.FromJust());
}
THREADED_TEST(InterceptorShouldThrowOnError) {
LocalContext context;
v8::Isolate* isolate = context->GetIsolate();
v8::HandleScope scope(isolate);
Local<Object> global = context->Global();
auto interceptor_templ = v8::ObjectTemplate::New(isolate);
v8::NamedPropertyHandlerConfiguration handler(
ShouldThrowOnErrorGetter, ShouldThrowOnErrorSetter<Value>,
ShouldThrowOnErrorQuery, ShouldThrowOnErrorDeleter,
ShouldThrowOnErrorPropertyEnumerator);
interceptor_templ->SetHandler(handler);
Local<v8::Object> instance =
interceptor_templ->NewInstance(context.local()).ToLocalChecked();
CHECK(global->Set(context.local(), v8_str("o"), instance).FromJust());
// SLOPPY mode
Local<Value> value = v8_compile("o.f")->Run(context.local()).ToLocalChecked();
CHECK(value->IsFalse());
v8_compile("o.f = 153")->Run(context.local()).ToLocalChecked();
value = global->Get(context.local(), v8_str("should_throw_setter"))
.ToLocalChecked();
CHECK(value->IsFalse());
v8_compile("delete o.f")->Run(context.local()).ToLocalChecked();
value = global->Get(context.local(), v8_str("should_throw_deleter"))
.ToLocalChecked();
CHECK(value->IsFalse());
v8_compile("Object.getOwnPropertyNames(o)")
->Run(context.local())
.ToLocalChecked();
value = global->Get(context.local(), v8_str("should_throw_enumerator"))
.ToLocalChecked();
CHECK(value->IsFalse());
// STRICT mode
value = v8_compile("'use strict';o.f")->Run(context.local()).ToLocalChecked();
CHECK(value->IsFalse());
v8_compile("'use strict'; o.f = 153")->Run(context.local()).ToLocalChecked();
value = global->Get(context.local(), v8_str("should_throw_setter"))
.ToLocalChecked();
CHECK(value->IsTrue());
v8_compile("'use strict'; delete o.f")->Run(context.local()).ToLocalChecked();
value = global->Get(context.local(), v8_str("should_throw_deleter"))
.ToLocalChecked();
CHECK(value->IsTrue());
v8_compile("'use strict'; Object.getOwnPropertyNames(o)")
->Run(context.local())
.ToLocalChecked();
value = global->Get(context.local(), v8_str("should_throw_enumerator"))
.ToLocalChecked();
CHECK(value->IsFalse());
}
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;
CHECK(context->Global()
->Set(context.local(), v8_str("f"),
templ->GetFunction(context.local()).ToLocalChecked())
.FromJust());
Local<Value> value = v8_compile("f()")->Run(context.local()).ToLocalChecked();
CHECK(!value->BooleanValue(context.local()).FromJust());
value = v8_compile("new f()")->Run(context.local()).ToLocalChecked();
CHECK(value->BooleanValue(context.local()).FromJust());
}
static void NewTargetHandler(const v8::FunctionCallbackInfo<v8::Value>& args) {
ApiTestFuzzer::Fuzz();
args.GetReturnValue().Set(args.NewTarget());
}
THREADED_TEST(NewTargetHandler) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
// Function template with call handler.
Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate);
templ->SetCallHandler(NewTargetHandler);
LocalContext context;
Local<Function> function =
templ->GetFunction(context.local()).ToLocalChecked();
CHECK(context->Global()
->Set(context.local(), v8_str("f"), function)
.FromJust());
Local<Value> value = CompileRun("f()");
CHECK(value->IsUndefined());
value = CompileRun("new f()");
CHECK(value->IsFunction());
CHECK(value == function);
Local<Value> subclass = CompileRun("var g = class extends f { }; g");
CHECK(subclass->IsFunction());
value = CompileRun("new g()");
CHECK(value->IsFunction());
CHECK(value == subclass);
value = CompileRun("Reflect.construct(f, [], Array)");
CHECK(value->IsFunction());
CHECK(value ==
context->Global()
->Get(context.local(), v8_str("Array"))
.ToLocalChecked());
}
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(context.local())
.ToLocalChecked();
// Normal ToString call should call replaced Object.prototype.toString
Local<v8::Object> instance = templ->GetFunction(context.local())
.ToLocalChecked()
->NewInstance(context.local())
.ToLocalChecked();
Local<String> value = instance->ToString(context.local()).ToLocalChecked();
CHECK(value->IsString() &&
value->Equals(context.local(), customized_tostring).FromJust());
// ObjectProtoToString should not call replace toString function.
value = instance->ObjectProtoToString(context.local()).ToLocalChecked();
CHECK(value->IsString() &&
value->Equals(context.local(), v8_str("[object MyClass]")).FromJust());
// Check global
value =
context->Global()->ObjectProtoToString(context.local()).ToLocalChecked();
CHECK(value->IsString() &&
value->Equals(context.local(), v8_str("[object Object]")).FromJust());
// Check ordinary object
Local<Value> object =
v8_compile("new Object()")->Run(context.local()).ToLocalChecked();
value = object.As<v8::Object>()
->ObjectProtoToString(context.local())
.ToLocalChecked();
CHECK(value->IsString() &&
value->Equals(context.local(), v8_str("[object Object]")).FromJust());
}