blob: 345018aaa41e69e65260284f8dcb3a77490224f1 [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-inl.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/global-handles.h"
#include "src/heap/incremental-marking.h"
#include "src/heap/local-allocator.h"
#include "src/lookup.h"
#include "src/objects-inl.h"
#include "src/objects/hash-table-inl.h"
#include "src/objects/js-array-buffer-inl.h"
#include "src/objects/js-array-inl.h"
#include "src/objects/js-promise-inl.h"
#include "src/profiler/cpu-profiler.h"
#include "src/unicode-inl.h"
#include "src/utils.h"
#include "src/vm-state.h"
#include "src/wasm/wasm-js.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(isolate) + 1);
undef_str->WriteUtf8(isolate, 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() override {
if (owning_data_) i::DeleteArray(data_);
if (counter_ != nullptr) ++*counter_;
}
const uint16_t* data() const override { return data_; }
size_t length() const override { 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() override {
i::DeleteArray(orig_data_);
if (counter_ != nullptr) ++*counter_;
}
const char* data() const override { return data_; }
size_t length() const override { 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::heap()->CollectAllGarbage(i::Heap::kReduceMemoryFootprintMask,
i::GarbageCollectionReason::kTesting);
}
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) {}
const uint16_t* data() const override { return string_; }
size_t length() const override { return length_; }
private:
uint16_t string_[10];
int length_;
};
class RandomLengthOneByteResource
: public v8::String::ExternalOneByteStringResource {
public:
explicit RandomLengthOneByteResource(int length) : length_(length) {}
const char* data() const override { return string_; }
size_t length() const override { 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());
}
}
TEST(ScavengeExternalString) {
ManualGCScope manual_gc_scope;
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 = i::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);
}
TEST(ScavengeExternalOneByteString) {
ManualGCScope manual_gc_scope;
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 = i::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() override {
++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::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
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(isolate, left, right);
right = String::NewExternalOneByte(
env->GetIsolate(),
new TestOneByteResource(i::StrDup(one_byte_extern_1)))
.ToLocalChecked();
source = String::Concat(isolate, source, right);
right = String::NewExternalTwoByte(
env->GetIsolate(),
new TestResource(AsciiToTwoByteString(two_byte_extern_1)))
.ToLocalChecked();
source = String::Concat(isolate, source, right);
right = v8_str(one_byte_string_2);
source = String::Concat(isolate, 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(isolate, source, right);
right = String::NewExternalTwoByte(
env->GetIsolate(),
new TestResource(AsciiToTwoByteString(two_byte_extern_2)))
.ToLocalChecked();
source = String::Concat(isolate, 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 FunctionCallbackForProxyTest(
const v8::FunctionCallbackInfo<Value>& info) {
info.GetReturnValue().Set(info.This());
}
THREADED_TEST(FunctionTemplateWithProxy) {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
v8::Local<v8::FunctionTemplate> function_template =
v8::FunctionTemplate::New(isolate, FunctionCallbackForProxyTest);
v8::Local<v8::Function> function =
function_template->GetFunction(env.local()).ToLocalChecked();
CHECK((*env)->Global()->Set(env.local(), v8_str("f"), function).FromJust());
v8::Local<v8::Value> proxy =
CompileRun("var proxy = new Proxy({}, {}); proxy");
CHECK(proxy->IsProxy());
v8::Local<v8::Value> result = CompileRun("f(proxy)");
CHECK(result->Equals(env.local(), (*env)->Global()).FromJust());
result = CompileRun("f.call(proxy)");
CHECK(result->Equals(env.local(), proxy).FromJust());
result = CompileRun("Reflect.apply(f, proxy, [1])");
CHECK(result->Equals(env.local(), proxy).FromJust());
}
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->BooleanValue(isolate));
}
// 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(isolate));
}
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());
}
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());
}
}
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());
}
}
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());
}
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());
}
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());
}
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());
}
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(CcTest::isolate(), 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(BigIntObject) {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
v8::Local<v8::Context> context(env.local());
v8::Local<Value> boxed_bigint = CompileRun("new Object(42n)");
CHECK(!boxed_bigint->IsBigInt());
CHECK(boxed_bigint->IsBigIntObject());
v8::Local<Value> unboxed_bigint = CompileRun("42n");
CHECK(unboxed_bigint->IsBigInt());
CHECK(!unboxed_bigint->IsBigIntObject());
v8::Local<v8::BigIntObject> as_boxed = boxed_bigint.As<v8::BigIntObject>();
CHECK(!as_boxed.IsEmpty());
v8::Local<v8::BigInt> unpacked = as_boxed->ValueOf();
CHECK(!unpacked.IsEmpty());
v8::Local<v8::Value> new_boxed_bigint = v8::BigIntObject::New(isolate, 43);
CHECK(new_boxed_bigint->IsBigIntObject());
v8::Local<v8::Value> new_unboxed_bigint = v8::BigInt::New(isolate, 44);
CHECK(new_unboxed_bigint->IsBigInt());
// Test functionality inherited from v8::Value.
CHECK(unboxed_bigint->BooleanValue(isolate));
v8::Local<v8::String> string =
unboxed_bigint->ToString(context).ToLocalChecked();
CHECK_EQ(0, strcmp("42", *v8::String::Utf8Value(isolate, string)));
// IntegerValue throws.
CHECK(unboxed_bigint->IntegerValue(context).IsNothing());
}
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::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
Local<Value> primitive_false = Boolean::New(isolate, false);
CHECK(primitive_false->IsBoolean());
CHECK(!primitive_false->IsBooleanObject());
CHECK(!primitive_false->BooleanValue(isolate));
CHECK(!primitive_false->IsTrue());
CHECK(primitive_false->IsFalse());
Local<Value> false_value = BooleanObject::New(isolate, false);
CHECK(!false_value->IsBoolean());
CHECK(false_value->IsBooleanObject());
CHECK(false_value->BooleanValue(isolate));
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(isolate));
CHECK(!false_boolean_object->ValueOf());
CHECK(!false_boolean_object->IsTrue());
CHECK(!false_boolean_object->IsFalse());
Local<Value> primitive_true = Boolean::New(isolate, true);
CHECK(primitive_true->IsBoolean());
CHECK(!primitive_true->IsBooleanObject());
CHECK(primitive_true->BooleanValue(isolate));
CHECK(primitive_true->IsTrue());
CHECK(!primitive_true->IsFalse());
Local<Value> true_value = BooleanObject::New(isolate, true);
CHECK(!true_value->IsBoolean());
CHECK(true_value->IsBooleanObject());
CHECK(true_value->BooleanValue(isolate));
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(isolate));
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(isolate));
v8::Local<v8::Primitive> n = v8::Null(isolate);
CHECK(!n->BooleanValue(isolate));
v8::Local<String> str1 = v8_str("");
CHECK(!str1->BooleanValue(isolate));
v8::Local<String> str2 = v8_str("x");
CHECK(str2->BooleanValue(isolate));
CHECK(!v8::Number::New(isolate, 0)->BooleanValue(isolate));
CHECK(v8::Number::New(isolate, -1)->BooleanValue(isolate));
CHECK(v8::Number::New(isolate, 1)->BooleanValue(isolate));
CHECK(v8::Number::New(isolate, 42)->BooleanValue(isolate));
CHECK(!v8_compile("NaN")
->Run(env.local())
.ToLocalChecked()
->BooleanValue(isolate));
}
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(nullptr, 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(isolate));
CHECK(CompileRun("(p.y == 13)")->BooleanValue(isolate));
CHECK(CompileRun("(p.foo() == 42)")->BooleanValue(isolate));
CHECK(CompileRun("(p.foo == acc)")->BooleanValue(isolate));
// 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(isolate));
CHECK(CompileRun("(q.a == 12)")->BooleanValue(isolate));
CHECK(CompileRun("(q.b.x == 10)")->BooleanValue(isolate));
CHECK(CompileRun("(q.b.y == 13)")->BooleanValue(isolate));
CHECK(CompileRun("(q.b.foo() == 42)")->BooleanValue(isolate));
CHECK(CompileRun("(q.b.foo === acc)")->BooleanValue(isolate));
CHECK(CompileRun("(q.b !== p)")->BooleanValue(isolate));
CHECK(CompileRun("(q.acc == 42)")->BooleanValue(isolate));
CHECK(CompileRun("(q.bar() == 42)")->BooleanValue(isolate));
CHECK(CompileRun("(q.bar == acc)")->BooleanValue(isolate));
instance2 = templ2->NewInstance(env.local()).ToLocalChecked();
CHECK(env->Global()->Set(env.local(), v8_str("q2"), instance2).FromJust());
CHECK(CompileRun("(q2.nirk == 123)")->BooleanValue(isolate));
CHECK(CompileRun("(q2.a == 12)")->BooleanValue(isolate));
CHECK(CompileRun("(q2.b.x == 10)")->BooleanValue(isolate));
CHECK(CompileRun("(q2.b.y == 13)")->BooleanValue(isolate));
CHECK(CompileRun("(q2.b.foo() == 42)")->BooleanValue(isolate));
CHECK(CompileRun("(q2.b.foo === acc)")->BooleanValue(isolate));
CHECK(CompileRun("(q2.acc == 42)")->BooleanValue(isolate));
CHECK(CompileRun("(q2.bar() == 42)")->BooleanValue(isolate));
CHECK(CompileRun("(q2.bar === acc)")->BooleanValue(isolate));
CHECK(CompileRun("(q.b !== q2.b)")->BooleanValue(isolate));
CHECK(CompileRun("q.b.x = 17; (q2.b.x == 10)")->BooleanValue(isolate));
CHECK(CompileRun("desc1 = Object.getOwnPropertyDescriptor(q, 'acc');"
"(desc1.get === acc)")
->BooleanValue(isolate));
CHECK(CompileRun("desc2 = Object.getOwnPropertyDescriptor(q2, 'acc');"
"(desc2.get === acc)")
->BooleanValue(isolate));
}
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(isolate));
CHECK(CompileRun("base2.prototype.__proto__ == s.prototype")
->BooleanValue(isolate));
CHECK(v8_compile("s.prototype.PI == 3.14")
->Run(env.local())
.ToLocalChecked()
->BooleanValue(isolate));
// Instance accessor should not be visible on function object or its prototype
CHECK(CompileRun("s.knurd == undefined")->BooleanValue(isolate));
CHECK(CompileRun("s.prototype.knurd == undefined")->BooleanValue(isolate));
CHECK(
CompileRun("base1.prototype.knurd == undefined")->BooleanValue(isolate));
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(isolate));
CHECK_EQ(15.2, CompileRun("obj.knurd")->NumberValue(env.local()).FromJust());
CHECK(CompileRun("'knurd' in obj")->BooleanValue(isolate));
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(isolate));
CHECK_EQ(15.2, CompileRun("obj2.knurd")->NumberValue(env.local()).FromJust());
CHECK(CompileRun("'knurd' in obj2")->BooleanValue(isolate));
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