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