// 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 #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 void TestToV8(V8TestingScope* scope, const char* expected, T value, const char* path, int line_number) { v8::Local 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 { public: GarbageCollectedHolderForToV8Test( GarbageCollectedScriptWrappable* script_wrappable) : script_wrappable_(script_wrappable) {} void Trace(Visitor* visitor) { visitor->Trace(script_wrappable_); } // This should be public in order to access a Member object. Member 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 object. Persistent script_wrappable_; }; TEST(ToV8Test, garbageCollectedScriptWrappable) { V8TestingScope scope; GarbageCollectedScriptWrappable* object = MakeGarbageCollected("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("pointer")); TEST_TOV8("constPointer", static_cast("constPointer")); TEST_TOV8("coreString", String("coreString")); TEST_TOV8("atomicString", AtomicString("atomicString")); // Null strings are converted to empty strings. TEST_TOV8("", static_cast(nullptr)); TEST_TOV8("", static_cast(nullptr)); TEST_TOV8("", String()); TEST_TOV8("", AtomicString()); } TEST(ToV8Test, numeric) { V8TestingScope scope; TEST_TOV8("0", static_cast(0)); TEST_TOV8("1", static_cast(1)); TEST_TOV8("-1", static_cast(-1)); TEST_TOV8("2", static_cast(2)); TEST_TOV8("-2147483648", std::numeric_limits::min()); TEST_TOV8("2147483647", std::numeric_limits::max()); TEST_TOV8("4294967295", std::numeric_limits::max()); // v8::Number can represent exact numbers in [-(2^53-1), 2^53-1]. TEST_TOV8("-9007199254740991", static_cast(-9007199254740991)); // -(2^53-1) TEST_TOV8("9007199254740991", static_cast(9007199254740991)); // 2^53-1 TEST_TOV8("9007199254740991", static_cast(9007199254740991)); // 2^53-1 TEST_TOV8("0.5", static_cast(0.5)); TEST_TOV8("-0.5", static_cast(-0.5)); TEST_TOV8("NaN", std::numeric_limits::quiet_NaN()); TEST_TOV8("Infinity", std::numeric_limits::infinity()); TEST_TOV8("-Infinity", -std::numeric_limits::infinity()); } TEST(ToV8Test, boolean) { V8TestingScope scope; TEST_TOV8("true", true); TEST_TOV8("false", false); } TEST(ToV8Test, v8Value) { V8TestingScope scope; v8::Local local_value(v8::Number::New(scope.GetIsolate(), 1234)); v8::Local 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_vector; string_vector.push_back("foo"); string_vector.push_back("bar"); TEST_TOV8("foo,bar", string_vector); Vector 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_vector; int32_vector.push_back(42); int32_vector.push_back(23); TEST_TOV8("42,23", int32_vector); Vector int64_vector; int64_vector.push_back(31773); int64_vector.push_back(404); TEST_TOV8("31773,404", int64_vector); Vector uint32_vector; uint32_vector.push_back(1); uint32_vector.push_back(2); TEST_TOV8("1,2", uint32_vector); Vector uint64_vector; uint64_vector.push_back(1001); uint64_vector.push_back(2002); TEST_TOV8("1001,2002", uint64_vector); Vector float_vector; float_vector.push_back(0.125); float_vector.push_back(1.); TEST_TOV8("0.125,1", float_vector); Vector double_vector; double_vector.push_back(2.3); double_vector.push_back(4.2); TEST_TOV8("2.3,4.2", double_vector); Vector 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> 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 context = scope.GetScriptState()->GetContext(); v8::Local result = ToV8(pair_vector, context->Global(), scope.GetIsolate()) ->ToObject(context) .ToLocalChecked(); v8::Local one = result->Get(context, V8String(scope.GetIsolate(), "one")) .ToLocalChecked(); EXPECT_EQ(1, one->NumberValue(context).FromJust()); v8::Local two = result->Get(context, V8String(scope.GetIsolate(), "two")) .ToLocalChecked(); EXPECT_EQ(2, two->NumberValue(context).FromJust()); } TEST(ToV8Test, pairHeapVector) { V8TestingScope scope; HeapVector>> pair_heap_vector; pair_heap_vector.push_back(std::make_pair( "one", MakeGarbageCollected("foo"))); pair_heap_vector.push_back(std::make_pair( "two", MakeGarbageCollected("bar"))); TEST_TOV8("[object Object]", pair_heap_vector); v8::Local context = scope.GetScriptState()->GetContext(); v8::Local result = ToV8(pair_heap_vector, context->Global(), scope.GetIsolate()) ->ToObject(context) .ToLocalChecked(); v8::Local one = result->Get(context, V8String(scope.GetIsolate(), "one")) .ToLocalChecked(); EXPECT_TRUE(one->IsObject()); EXPECT_EQ(String("foo"), ToCoreString(one->ToString(context).ToLocalChecked())); v8::Local 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_vector1; string_vector1.push_back("foo"); string_vector1.push_back("bar"); Vector string_vector2; string_vector2.push_back("quux"); Vector> 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 context = scope.GetScriptState()->GetContext(); v8::Local result = ToV8(compound_vector, context->Global(), scope.GetIsolate()) ->ToObject(context) .ToLocalChecked(); v8::Local vector1 = result->Get(context, 0).ToLocalChecked(); EXPECT_TRUE(vector1->IsArray()); EXPECT_EQ(2U, vector1.As()->Length()); v8::Local vector2 = result->Get(context, 1).ToLocalChecked(); EXPECT_TRUE(vector2->IsArray()); EXPECT_EQ(1U, vector2.As()->Length()); } TEST(ToV8Test, heapVector) { V8TestingScope scope; HeapVector> v; v.push_back(MakeGarbageCollected("hoge")); v.push_back(MakeGarbageCollected("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 actual = ToV8(value, scope.GetScriptState()); EXPECT_FALSE(actual.IsEmpty()); double actual_as_number = actual.As()->Value(); EXPECT_EQ(1234.0, actual_as_number); } TEST(ToV8Test, nullableDouble) { V8TestingScope scope; v8::Local global = scope.GetContext()->Global(); v8::Isolate* isolate = scope.GetIsolate(); { v8::Local actual = ToV8(base::Optional(42.0), global, isolate); ASSERT_TRUE(actual->IsNumber()); EXPECT_EQ(42.0, actual.As()->Value()); } { v8::Local actual = ToV8(base::Optional(), global, isolate); EXPECT_TRUE(actual->IsNull()); } } } // namespace } // namespace blink