1 // Copyright 2018 PDFium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "fxjs/cfx_v8_unittest.h"
6 
7 #include <memory>
8 
9 #include "fxjs/cfx_v8.h"
10 #include "testing/gtest/include/gtest/gtest.h"
11 #include "third_party/base/ptr_util.h"
12 
13 namespace {
14 bool getter_sentinel = false;
15 bool setter_sentinel = false;
16 }  // namespace
17 
operator ()(v8::Isolate * ptr) const18 void FXV8UnitTest::V8IsolateDeleter::operator()(v8::Isolate* ptr) const {
19   ptr->Dispose();
20 }
21 
22 FXV8UnitTest::FXV8UnitTest() = default;
23 
24 FXV8UnitTest::~FXV8UnitTest() = default;
25 
SetUp()26 void FXV8UnitTest::SetUp() {
27   array_buffer_allocator_ = pdfium::MakeUnique<CFX_V8ArrayBufferAllocator>();
28 
29   v8::Isolate::CreateParams params;
30   params.array_buffer_allocator = array_buffer_allocator_.get();
31   isolate_.reset(v8::Isolate::New(params));
32 
33   cfx_v8_ = pdfium::MakeUnique<CFX_V8>(isolate_.get());
34 }
35 
TEST_F(FXV8UnitTest,EmptyLocal)36 TEST_F(FXV8UnitTest, EmptyLocal) {
37   v8::Isolate::Scope isolate_scope(isolate());
38   v8::HandleScope handle_scope(isolate());
39   v8::Context::Scope context_scope(v8::Context::New(isolate()));
40 
41   v8::Local<v8::Value> empty;
42   EXPECT_FALSE(cfx_v8()->ToBoolean(empty));
43   EXPECT_EQ(0, cfx_v8()->ToInt32(empty));
44   EXPECT_EQ(0.0, cfx_v8()->ToDouble(empty));
45   EXPECT_EQ("", cfx_v8()->ToByteString(empty));
46   EXPECT_EQ(L"", cfx_v8()->ToWideString(empty));
47   EXPECT_TRUE(cfx_v8()->ToObject(empty).IsEmpty());
48   EXPECT_TRUE(cfx_v8()->ToArray(empty).IsEmpty());
49 
50   // Can't set properties on empty objects, but does not fault.
51   v8::Local<v8::Value> marker = cfx_v8()->NewNumber(2);
52   v8::Local<v8::Object> empty_object;
53   EXPECT_FALSE(cfx_v8()->PutObjectProperty(empty_object, "clams", marker));
54   EXPECT_TRUE(cfx_v8()->GetObjectProperty(empty_object, "clams").IsEmpty());
55   EXPECT_EQ(0u, cfx_v8()->GetObjectPropertyNames(empty_object).size());
56 
57   // Can't set elements in empty arrays, but does not fault.
58   v8::Local<v8::Array> empty_array;
59   EXPECT_FALSE(cfx_v8()->PutArrayElement(empty_array, 0, marker));
60   EXPECT_TRUE(cfx_v8()->GetArrayElement(empty_array, 0).IsEmpty());
61   EXPECT_EQ(0u, cfx_v8()->GetArrayLength(empty_array));
62 }
63 
TEST_F(FXV8UnitTest,NewNull)64 TEST_F(FXV8UnitTest, NewNull) {
65   v8::Isolate::Scope isolate_scope(isolate());
66   v8::HandleScope handle_scope(isolate());
67   v8::Context::Scope context_scope(v8::Context::New(isolate()));
68 
69   auto nullz = cfx_v8()->NewNull();
70   EXPECT_FALSE(cfx_v8()->ToBoolean(nullz));
71   EXPECT_EQ(0, cfx_v8()->ToInt32(nullz));
72   EXPECT_EQ(0.0, cfx_v8()->ToDouble(nullz));
73   EXPECT_EQ("null", cfx_v8()->ToByteString(nullz));
74   EXPECT_EQ(L"null", cfx_v8()->ToWideString(nullz));
75   EXPECT_TRUE(cfx_v8()->ToObject(nullz).IsEmpty());
76   EXPECT_TRUE(cfx_v8()->ToArray(nullz).IsEmpty());
77 }
78 
TEST_F(FXV8UnitTest,NewUndefined)79 TEST_F(FXV8UnitTest, NewUndefined) {
80   v8::Isolate::Scope isolate_scope(isolate());
81   v8::HandleScope handle_scope(isolate());
82   v8::Context::Scope context_scope(v8::Context::New(isolate()));
83 
84   auto undef = cfx_v8()->NewUndefined();
85   EXPECT_FALSE(cfx_v8()->ToBoolean(undef));
86   EXPECT_EQ(0, cfx_v8()->ToInt32(undef));
87   EXPECT_TRUE(std::isnan(cfx_v8()->ToDouble(undef)));
88   EXPECT_EQ("undefined", cfx_v8()->ToByteString(undef));
89   EXPECT_EQ(L"undefined", cfx_v8()->ToWideString(undef));
90   EXPECT_TRUE(cfx_v8()->ToObject(undef).IsEmpty());
91   EXPECT_TRUE(cfx_v8()->ToArray(undef).IsEmpty());
92 }
93 
TEST_F(FXV8UnitTest,NewBoolean)94 TEST_F(FXV8UnitTest, NewBoolean) {
95   v8::Isolate::Scope isolate_scope(isolate());
96   v8::HandleScope handle_scope(isolate());
97   v8::Context::Scope context_scope(v8::Context::New(isolate()));
98 
99   auto boolz = cfx_v8()->NewBoolean(true);
100   EXPECT_TRUE(cfx_v8()->ToBoolean(boolz));
101   EXPECT_EQ(1, cfx_v8()->ToInt32(boolz));
102   EXPECT_EQ(1.0, cfx_v8()->ToDouble(boolz));
103   EXPECT_EQ("true", cfx_v8()->ToByteString(boolz));
104   EXPECT_EQ(L"true", cfx_v8()->ToWideString(boolz));
105   EXPECT_TRUE(cfx_v8()->ToObject(boolz).IsEmpty());
106   EXPECT_TRUE(cfx_v8()->ToArray(boolz).IsEmpty());
107 
108   boolz = cfx_v8()->NewBoolean(false);
109   EXPECT_FALSE(cfx_v8()->ToBoolean(boolz));
110   EXPECT_EQ(0, cfx_v8()->ToInt32(boolz));
111   EXPECT_EQ(0.0, cfx_v8()->ToDouble(boolz));
112   EXPECT_EQ("false", cfx_v8()->ToByteString(boolz));
113   EXPECT_EQ(L"false", cfx_v8()->ToWideString(boolz));
114   EXPECT_TRUE(cfx_v8()->ToObject(boolz).IsEmpty());
115   EXPECT_TRUE(cfx_v8()->ToArray(boolz).IsEmpty());
116 }
117 
TEST_F(FXV8UnitTest,NewNumber)118 TEST_F(FXV8UnitTest, NewNumber) {
119   v8::Isolate::Scope isolate_scope(isolate());
120   v8::HandleScope handle_scope(isolate());
121   v8::Context::Scope context_scope(v8::Context::New(isolate()));
122 
123   auto num = cfx_v8()->NewNumber(42.1);
124   EXPECT_TRUE(cfx_v8()->ToBoolean(num));
125   EXPECT_EQ(42, cfx_v8()->ToInt32(num));
126   EXPECT_EQ(42.1, cfx_v8()->ToDouble(num));
127   EXPECT_EQ("42.1", cfx_v8()->ToByteString(num));
128   EXPECT_EQ(L"42.1", cfx_v8()->ToWideString(num));
129   EXPECT_TRUE(cfx_v8()->ToObject(num).IsEmpty());
130   EXPECT_TRUE(cfx_v8()->ToArray(num).IsEmpty());
131 }
132 
TEST_F(FXV8UnitTest,NewString)133 TEST_F(FXV8UnitTest, NewString) {
134   v8::Isolate::Scope isolate_scope(isolate());
135   v8::HandleScope handle_scope(isolate());
136   v8::Context::Scope context_scope(v8::Context::New(isolate()));
137 
138   auto str = cfx_v8()->NewString("123");
139   EXPECT_TRUE(cfx_v8()->ToBoolean(str));
140   EXPECT_EQ(123, cfx_v8()->ToInt32(str));
141   EXPECT_EQ(123, cfx_v8()->ToDouble(str));
142   EXPECT_EQ("123", cfx_v8()->ToByteString(str));
143   EXPECT_EQ(L"123", cfx_v8()->ToWideString(str));
144   EXPECT_TRUE(cfx_v8()->ToObject(str).IsEmpty());
145   EXPECT_TRUE(cfx_v8()->ToArray(str).IsEmpty());
146 
147   auto str2 = cfx_v8()->NewString(L"123");
148   EXPECT_TRUE(cfx_v8()->ToBoolean(str2));
149   EXPECT_EQ(123, cfx_v8()->ToInt32(str2));
150   EXPECT_EQ(123, cfx_v8()->ToDouble(str2));
151   EXPECT_EQ("123", cfx_v8()->ToByteString(str2));
152   EXPECT_EQ(L"123", cfx_v8()->ToWideString(str2));
153   EXPECT_TRUE(cfx_v8()->ToObject(str2).IsEmpty());
154   EXPECT_TRUE(cfx_v8()->ToArray(str2).IsEmpty());
155 }
156 
TEST_F(FXV8UnitTest,NewDate)157 TEST_F(FXV8UnitTest, NewDate) {
158   v8::Isolate::Scope isolate_scope(isolate());
159   v8::HandleScope handle_scope(isolate());
160   v8::Context::Scope context_scope(v8::Context::New(isolate()));
161 
162   auto date = cfx_v8()->NewDate(1111111111);
163   EXPECT_TRUE(cfx_v8()->ToBoolean(date));
164   EXPECT_EQ(1111111111, cfx_v8()->ToInt32(date));
165   EXPECT_EQ(1111111111.0, cfx_v8()->ToDouble(date));
166   EXPECT_NE("", cfx_v8()->ToByteString(date));   // exact format varies.
167   EXPECT_NE(L"", cfx_v8()->ToWideString(date));  // exact format varies.
168   EXPECT_TRUE(cfx_v8()->ToObject(date)->IsObject());
169   EXPECT_TRUE(cfx_v8()->ToArray(date).IsEmpty());
170 }
171 
TEST_F(FXV8UnitTest,NewArray)172 TEST_F(FXV8UnitTest, NewArray) {
173   v8::Isolate::Scope isolate_scope(isolate());
174   v8::HandleScope handle_scope(isolate());
175   v8::Context::Scope context_scope(v8::Context::New(isolate()));
176 
177   auto array = cfx_v8()->NewArray();
178   EXPECT_EQ(0u, cfx_v8()->GetArrayLength(array));
179   EXPECT_FALSE(cfx_v8()->GetArrayElement(array, 2).IsEmpty());
180   EXPECT_TRUE(cfx_v8()->GetArrayElement(array, 2)->IsUndefined());
181   EXPECT_EQ(0u, cfx_v8()->GetArrayLength(array));
182 
183   EXPECT_TRUE(cfx_v8()->PutArrayElement(array, 3, cfx_v8()->NewNumber(12)));
184   EXPECT_FALSE(cfx_v8()->GetArrayElement(array, 2).IsEmpty());
185   EXPECT_TRUE(cfx_v8()->GetArrayElement(array, 2)->IsUndefined());
186   EXPECT_FALSE(cfx_v8()->GetArrayElement(array, 3).IsEmpty());
187   EXPECT_TRUE(cfx_v8()->GetArrayElement(array, 3)->IsNumber());
188   EXPECT_EQ(4u, cfx_v8()->GetArrayLength(array));
189 
190   EXPECT_TRUE(cfx_v8()->ToBoolean(array));
191   EXPECT_EQ(0, cfx_v8()->ToInt32(array));
192   double d = cfx_v8()->ToDouble(array);
193   EXPECT_NE(d, d);  // i.e. NaN.
194   EXPECT_EQ(L",,,12", cfx_v8()->ToWideString(array));
195   EXPECT_TRUE(cfx_v8()->ToObject(array)->IsObject());
196   EXPECT_TRUE(cfx_v8()->ToArray(array)->IsArray());
197 }
198 
TEST_F(FXV8UnitTest,NewObject)199 TEST_F(FXV8UnitTest, NewObject) {
200   v8::Isolate::Scope isolate_scope(isolate());
201   v8::HandleScope handle_scope(isolate());
202   v8::Context::Scope context_scope(v8::Context::New(isolate()));
203 
204   auto object = cfx_v8()->NewObject();
205   ASSERT_FALSE(object.IsEmpty());
206   EXPECT_EQ(0u, cfx_v8()->GetObjectPropertyNames(object).size());
207   EXPECT_FALSE(cfx_v8()->GetObjectProperty(object, "clams").IsEmpty());
208   EXPECT_TRUE(cfx_v8()->GetObjectProperty(object, "clams")->IsUndefined());
209   EXPECT_EQ(0u, cfx_v8()->GetObjectPropertyNames(object).size());
210 
211   EXPECT_TRUE(
212       cfx_v8()->PutObjectProperty(object, "clams", cfx_v8()->NewNumber(12)));
213   EXPECT_FALSE(cfx_v8()->GetObjectProperty(object, "clams").IsEmpty());
214   EXPECT_TRUE(cfx_v8()->GetObjectProperty(object, "clams")->IsNumber());
215   EXPECT_EQ(1u, cfx_v8()->GetObjectPropertyNames(object).size());
216   EXPECT_EQ(L"clams", cfx_v8()->GetObjectPropertyNames(object)[0]);
217 
218   EXPECT_TRUE(cfx_v8()->ToBoolean(object));
219   EXPECT_EQ(0, cfx_v8()->ToInt32(object));
220   double d = cfx_v8()->ToDouble(object);
221   EXPECT_NE(d, d);  // i.e. NaN.
222   EXPECT_EQ(L"[object Object]", cfx_v8()->ToWideString(object));
223   EXPECT_TRUE(cfx_v8()->ToObject(object)->IsObject());
224   EXPECT_TRUE(cfx_v8()->ToArray(object).IsEmpty());
225 }
226 
TEST_F(FXV8UnitTest,ThrowFromGetter)227 TEST_F(FXV8UnitTest, ThrowFromGetter) {
228   v8::Isolate::Scope isolate_scope(isolate());
229   v8::HandleScope handle_scope(isolate());
230   v8::Local<v8::Context> context = v8::Context::New(isolate());
231   v8::Context::Scope context_scope(context);
232 
233   v8::Local<v8::Object> object = cfx_v8()->NewObject();
234   v8::Local<v8::String> name = cfx_v8()->NewString("clams");
235   EXPECT_TRUE(
236       object
237           ->SetAccessor(context, name,
238                         [](v8::Local<v8::Name> property,
239                            const v8::PropertyCallbackInfo<v8::Value>& info) {
240                           getter_sentinel = true;
241                           info.GetIsolate()->ThrowException(property);
242                         })
243           .FromJust());
244   getter_sentinel = false;
245   EXPECT_TRUE(cfx_v8()->GetObjectProperty(object, "clams").IsEmpty());
246   EXPECT_TRUE(getter_sentinel);
247 }
248 
TEST_F(FXV8UnitTest,ThrowFromSetter)249 TEST_F(FXV8UnitTest, ThrowFromSetter) {
250   v8::Isolate::Scope isolate_scope(isolate());
251   v8::HandleScope handle_scope(isolate());
252   v8::Local<v8::Context> context = v8::Context::New(isolate());
253   v8::Context::Scope context_scope(context);
254 
255   v8::Local<v8::Object> object = cfx_v8()->NewObject();
256   v8::Local<v8::String> name = cfx_v8()->NewString("clams");
257   EXPECT_TRUE(object
258                   ->SetAccessor(context, name, nullptr,
259                                 [](v8::Local<v8::Name> property,
260                                    v8::Local<v8::Value> value,
261                                    const v8::PropertyCallbackInfo<void>& info) {
262                                   setter_sentinel = true;
263                                   info.GetIsolate()->ThrowException(property);
264                                 })
265                   .FromJust());
266   setter_sentinel = false;
267   EXPECT_FALSE(cfx_v8()->PutObjectProperty(object, "clams", name));
268   EXPECT_TRUE(setter_sentinel);
269 }
270