blob: 219ca42cf6f8fc09273bdd654b8363b15e7bff8d [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/bindings/core/v8/to_v8_for_core.h"
#include <limits>
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h"
#include "third_party/blink/renderer/core/testing/garbage_collected_script_wrappable.h"
#include "third_party/blink/renderer/platform/heap/heap.h"
#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
#include "third_party/blink/renderer/platform/wtf/vector.h"
#define TEST_TOV8(expected, value) \
TestToV8(&scope, expected, value, __FILE__, __LINE__)
namespace blink {
namespace {
template <typename T>
void TestToV8(V8TestingScope* scope,
const char* expected,
T value,
const char* path,
int line_number) {
v8::Local<v8::Value> actual =
ToV8(value, scope->GetContext()->Global(), scope->GetIsolate());
if (actual.IsEmpty()) {
ADD_FAILURE_AT(path, line_number) << "toV8 returns an empty value.";
return;
}
String actual_string =
ToCoreString(actual->ToString(scope->GetContext()).ToLocalChecked());
if (String(expected) != actual_string) {
ADD_FAILURE_AT(path, line_number)
<< "toV8 returns an incorrect value.\n Actual: "
<< actual_string.Utf8() << "\nExpected: " << expected;
return;
}
}
class GarbageCollectedHolderForToV8Test
: public GarbageCollected<GarbageCollectedHolderForToV8Test> {
public:
GarbageCollectedHolderForToV8Test(
GarbageCollectedScriptWrappable* script_wrappable)
: script_wrappable_(script_wrappable) {}
void Trace(Visitor* visitor) const { visitor->Trace(script_wrappable_); }
// This should be public in order to access a Member<X> object.
Member<GarbageCollectedScriptWrappable> script_wrappable_;
};
class OffHeapGarbageCollectedHolder {
STACK_ALLOCATED();
public:
OffHeapGarbageCollectedHolder(
GarbageCollectedScriptWrappable* script_wrappable)
: script_wrappable_(script_wrappable) {}
// This should be public in order to access a Persistent<X> object.
Persistent<GarbageCollectedScriptWrappable> script_wrappable_;
};
TEST(ToV8Test, garbageCollectedScriptWrappable) {
V8TestingScope scope;
GarbageCollectedScriptWrappable* object =
MakeGarbageCollected<GarbageCollectedScriptWrappable>("world");
GarbageCollectedHolderForToV8Test holder(object);
OffHeapGarbageCollectedHolder off_heap_holder(object);
TEST_TOV8("world", object);
TEST_TOV8("world", holder.script_wrappable_);
TEST_TOV8("world", off_heap_holder.script_wrappable_);
object = nullptr;
holder.script_wrappable_ = nullptr;
off_heap_holder.script_wrappable_ = nullptr;
TEST_TOV8("null", object);
TEST_TOV8("null", holder.script_wrappable_);
TEST_TOV8("null", off_heap_holder.script_wrappable_);
}
TEST(ToV8Test, string) {
V8TestingScope scope;
char array_string[] = "arrayString";
const char kConstArrayString[] = "constArrayString";
TEST_TOV8("arrayString", array_string);
TEST_TOV8("constArrayString", kConstArrayString);
TEST_TOV8("pointer", const_cast<char*>("pointer"));
TEST_TOV8("constPointer", static_cast<const char*>("constPointer"));
TEST_TOV8("coreString", String("coreString"));
TEST_TOV8("atomicString", AtomicString("atomicString"));
// Null strings are converted to empty strings.
TEST_TOV8("", static_cast<char*>(nullptr));
TEST_TOV8("", static_cast<const char*>(nullptr));
TEST_TOV8("", String());
TEST_TOV8("", AtomicString());
}
TEST(ToV8Test, numeric) {
V8TestingScope scope;
TEST_TOV8("0", static_cast<int32_t>(0));
TEST_TOV8("1", static_cast<int32_t>(1));
TEST_TOV8("-1", static_cast<int32_t>(-1));
TEST_TOV8("2", static_cast<uint32_t>(2));
TEST_TOV8("-2147483648", std::numeric_limits<int32_t>::min());
TEST_TOV8("2147483647", std::numeric_limits<int32_t>::max());
TEST_TOV8("4294967295", std::numeric_limits<uint32_t>::max());
// v8::Number can represent exact numbers in [-(2^53-1), 2^53-1].
TEST_TOV8("-9007199254740991",
static_cast<int64_t>(-9007199254740991)); // -(2^53-1)
TEST_TOV8("9007199254740991",
static_cast<int64_t>(9007199254740991)); // 2^53-1
TEST_TOV8("9007199254740991",
static_cast<uint64_t>(9007199254740991)); // 2^53-1
TEST_TOV8("0.5", static_cast<double>(0.5));
TEST_TOV8("-0.5", static_cast<float>(-0.5));
TEST_TOV8("NaN", std::numeric_limits<double>::quiet_NaN());
TEST_TOV8("Infinity", std::numeric_limits<double>::infinity());
TEST_TOV8("-Infinity", -std::numeric_limits<double>::infinity());
}
TEST(ToV8Test, boolean) {
V8TestingScope scope;
TEST_TOV8("true", true);
TEST_TOV8("false", false);
}
TEST(ToV8Test, v8Value) {
V8TestingScope scope;
v8::Local<v8::Value> local_value(v8::Number::New(scope.GetIsolate(), 1234));
v8::Local<v8::Value> handle_value(v8::Number::New(scope.GetIsolate(), 5678));
TEST_TOV8("1234", local_value);
TEST_TOV8("5678", handle_value);
}
TEST(ToV8Test, undefinedType) {
V8TestingScope scope;
TEST_TOV8("undefined", ToV8UndefinedGenerator());
}
TEST(ToV8Test, scriptValue) {
V8TestingScope scope;
ScriptValue value(scope.GetIsolate(),
v8::Number::New(scope.GetIsolate(), 1234));
TEST_TOV8("1234", value);
}
TEST(ToV8Test, stringVectors) {
V8TestingScope scope;
Vector<String> string_vector;
string_vector.push_back("foo");
string_vector.push_back("bar");
TEST_TOV8("foo,bar", string_vector);
Vector<AtomicString> atomic_string_vector;
atomic_string_vector.push_back("quux");
atomic_string_vector.push_back("bar");
TEST_TOV8("quux,bar", atomic_string_vector);
}
TEST(ToV8Test, basicTypeVectors) {
V8TestingScope scope;
Vector<int32_t> int32_vector;
int32_vector.push_back(42);
int32_vector.push_back(23);
TEST_TOV8("42,23", int32_vector);
Vector<int64_t> int64_vector;
int64_vector.push_back(31773);
int64_vector.push_back(404);
TEST_TOV8("31773,404", int64_vector);
Vector<uint32_t> uint32_vector;
uint32_vector.push_back(1);
uint32_vector.push_back(2);
TEST_TOV8("1,2", uint32_vector);
Vector<uint64_t> uint64_vector;
uint64_vector.push_back(1001);
uint64_vector.push_back(2002);
TEST_TOV8("1001,2002", uint64_vector);
Vector<float> float_vector;
float_vector.push_back(0.125);
float_vector.push_back(1.);
TEST_TOV8("0.125,1", float_vector);
Vector<double> double_vector;
double_vector.push_back(2.3);
double_vector.push_back(4.2);
TEST_TOV8("2.3,4.2", double_vector);
Vector<bool> bool_vector;
bool_vector.push_back(true);
bool_vector.push_back(true);
bool_vector.push_back(false);
TEST_TOV8("true,true,false", bool_vector);
}
TEST(ToV8Test, pairVector) {
V8TestingScope scope;
Vector<std::pair<String, int>> pair_vector;
pair_vector.push_back(std::make_pair("one", 1));
pair_vector.push_back(std::make_pair("two", 2));
TEST_TOV8("[object Object]", pair_vector);
v8::Local<v8::Context> context = scope.GetScriptState()->GetContext();
v8::Local<v8::Object> result =
ToV8(pair_vector, context->Global(), scope.GetIsolate())
->ToObject(context)
.ToLocalChecked();
v8::Local<v8::Value> one =
result->Get(context, V8String(scope.GetIsolate(), "one"))
.ToLocalChecked();
EXPECT_EQ(1, one->NumberValue(context).FromJust());
v8::Local<v8::Value> two =
result->Get(context, V8String(scope.GetIsolate(), "two"))
.ToLocalChecked();
EXPECT_EQ(2, two->NumberValue(context).FromJust());
}
TEST(ToV8Test, pairHeapVector) {
V8TestingScope scope;
HeapVector<std::pair<String, Member<GarbageCollectedScriptWrappable>>>
pair_heap_vector;
pair_heap_vector.push_back(std::make_pair(
"one", MakeGarbageCollected<GarbageCollectedScriptWrappable>("foo")));
pair_heap_vector.push_back(std::make_pair(
"two", MakeGarbageCollected<GarbageCollectedScriptWrappable>("bar")));
TEST_TOV8("[object Object]", pair_heap_vector);
v8::Local<v8::Context> context = scope.GetScriptState()->GetContext();
v8::Local<v8::Object> result =
ToV8(pair_heap_vector, context->Global(), scope.GetIsolate())
->ToObject(context)
.ToLocalChecked();
v8::Local<v8::Value> one =
result->Get(context, V8String(scope.GetIsolate(), "one"))
.ToLocalChecked();
EXPECT_TRUE(one->IsObject());
EXPECT_EQ(String("foo"),
ToCoreString(one->ToString(context).ToLocalChecked()));
v8::Local<v8::Value> two =
result->Get(context, V8String(scope.GetIsolate(), "two"))
.ToLocalChecked();
EXPECT_TRUE(two->IsObject());
EXPECT_EQ(String("bar"),
ToCoreString(two->ToString(context).ToLocalChecked()));
}
TEST(ToV8Test, stringVectorVector) {
V8TestingScope scope;
Vector<String> string_vector1;
string_vector1.push_back("foo");
string_vector1.push_back("bar");
Vector<String> string_vector2;
string_vector2.push_back("quux");
Vector<Vector<String>> compound_vector;
compound_vector.push_back(string_vector1);
compound_vector.push_back(string_vector2);
EXPECT_EQ(2U, compound_vector.size());
TEST_TOV8("foo,bar,quux", compound_vector);
v8::Local<v8::Context> context = scope.GetScriptState()->GetContext();
v8::Local<v8::Object> result =
ToV8(compound_vector, context->Global(), scope.GetIsolate())
->ToObject(context)
.ToLocalChecked();
v8::Local<v8::Value> vector1 = result->Get(context, 0).ToLocalChecked();
EXPECT_TRUE(vector1->IsArray());
EXPECT_EQ(2U, vector1.As<v8::Array>()->Length());
v8::Local<v8::Value> vector2 = result->Get(context, 1).ToLocalChecked();
EXPECT_TRUE(vector2->IsArray());
EXPECT_EQ(1U, vector2.As<v8::Array>()->Length());
}
TEST(ToV8Test, heapVector) {
V8TestingScope scope;
HeapVector<Member<GarbageCollectedScriptWrappable>> v;
v.push_back(MakeGarbageCollected<GarbageCollectedScriptWrappable>("hoge"));
v.push_back(MakeGarbageCollected<GarbageCollectedScriptWrappable>("fuga"));
v.push_back(nullptr);
TEST_TOV8("hoge,fuga,", v);
}
TEST(ToV8Test, withScriptState) {
V8TestingScope scope;
ScriptValue value(scope.GetIsolate(),
v8::Number::New(scope.GetIsolate(), 1234.0));
v8::Local<v8::Value> actual = ToV8(value, scope.GetScriptState());
EXPECT_FALSE(actual.IsEmpty());
double actual_as_number = actual.As<v8::Number>()->Value();
EXPECT_EQ(1234.0, actual_as_number);
}
TEST(ToV8Test, nullableDouble) {
V8TestingScope scope;
v8::Local<v8::Object> global = scope.GetContext()->Global();
v8::Isolate* isolate = scope.GetIsolate();
{
v8::Local<v8::Value> actual =
ToV8(base::Optional<double>(42.0), global, isolate);
ASSERT_TRUE(actual->IsNumber());
EXPECT_EQ(42.0, actual.As<v8::Number>()->Value());
}
{
v8::Local<v8::Value> actual =
ToV8(base::Optional<double>(), global, isolate);
EXPECT_TRUE(actual->IsNull());
}
}
} // namespace
} // namespace blink