1 // Copyright 2020 The PDFium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6
7 #include "fxjs/fxv8.h"
8
9 #include "core/fxcrt/compiler_specific.h"
10 #include "core/fxcrt/numerics/safe_conversions.h"
11 #include "v8/include/v8-container.h"
12 #include "v8/include/v8-date.h"
13 #include "v8/include/v8-exception.h"
14 #include "v8/include/v8-isolate.h"
15 #include "v8/include/v8-primitive.h"
16 #include "v8/include/v8-value.h"
17
18 namespace fxv8 {
19
IsUndefined(v8::Local<v8::Value> value)20 bool IsUndefined(v8::Local<v8::Value> value) {
21 return !value.IsEmpty() && value->IsUndefined();
22 }
23
IsNull(v8::Local<v8::Value> value)24 bool IsNull(v8::Local<v8::Value> value) {
25 return !value.IsEmpty() && value->IsNull();
26 }
27
IsBoolean(v8::Local<v8::Value> value)28 bool IsBoolean(v8::Local<v8::Value> value) {
29 return !value.IsEmpty() && value->IsBoolean();
30 }
31
IsString(v8::Local<v8::Value> value)32 bool IsString(v8::Local<v8::Value> value) {
33 return !value.IsEmpty() && value->IsString();
34 }
35
IsNumber(v8::Local<v8::Value> value)36 bool IsNumber(v8::Local<v8::Value> value) {
37 return !value.IsEmpty() && value->IsNumber();
38 }
39
IsInteger(v8::Local<v8::Value> value)40 bool IsInteger(v8::Local<v8::Value> value) {
41 return !value.IsEmpty() && value->IsInt32();
42 }
43
IsObject(v8::Local<v8::Value> value)44 bool IsObject(v8::Local<v8::Value> value) {
45 return !value.IsEmpty() && value->IsObject();
46 }
47
IsArray(v8::Local<v8::Value> value)48 bool IsArray(v8::Local<v8::Value> value) {
49 return !value.IsEmpty() && value->IsArray();
50 }
51
IsDate(v8::Local<v8::Value> value)52 bool IsDate(v8::Local<v8::Value> value) {
53 return !value.IsEmpty() && value->IsDate();
54 }
55
IsFunction(v8::Local<v8::Value> value)56 bool IsFunction(v8::Local<v8::Value> value) {
57 return !value.IsEmpty() && value->IsFunction();
58 }
59
NewNullHelper(v8::Isolate * pIsolate)60 v8::Local<v8::Value> NewNullHelper(v8::Isolate* pIsolate) {
61 return v8::Null(pIsolate);
62 }
63
NewUndefinedHelper(v8::Isolate * pIsolate)64 v8::Local<v8::Value> NewUndefinedHelper(v8::Isolate* pIsolate) {
65 return v8::Undefined(pIsolate);
66 }
67
NewNumberHelper(v8::Isolate * pIsolate,int number)68 v8::Local<v8::Number> NewNumberHelper(v8::Isolate* pIsolate, int number) {
69 return v8::Int32::New(pIsolate, number);
70 }
71
NewNumberHelper(v8::Isolate * pIsolate,double number)72 v8::Local<v8::Number> NewNumberHelper(v8::Isolate* pIsolate, double number) {
73 return v8::Number::New(pIsolate, number);
74 }
75
NewNumberHelper(v8::Isolate * pIsolate,float number)76 v8::Local<v8::Number> NewNumberHelper(v8::Isolate* pIsolate, float number) {
77 return v8::Number::New(pIsolate, number);
78 }
79
NewBooleanHelper(v8::Isolate * pIsolate,bool b)80 v8::Local<v8::Boolean> NewBooleanHelper(v8::Isolate* pIsolate, bool b) {
81 return v8::Boolean::New(pIsolate, b);
82 }
83
NewStringHelper(v8::Isolate * pIsolate,ByteStringView str)84 v8::Local<v8::String> NewStringHelper(v8::Isolate* pIsolate,
85 ByteStringView str) {
86 return v8::String::NewFromUtf8(pIsolate, str.unterminated_c_str(),
87 v8::NewStringType::kNormal,
88 pdfium::checked_cast<int>(str.GetLength()))
89 .ToLocalChecked();
90 }
91
NewStringHelper(v8::Isolate * pIsolate,WideStringView str)92 v8::Local<v8::String> NewStringHelper(v8::Isolate* pIsolate,
93 WideStringView str) {
94 return NewStringHelper(pIsolate, FX_UTF8Encode(str).AsStringView());
95 }
96
NewArrayHelper(v8::Isolate * pIsolate)97 v8::Local<v8::Array> NewArrayHelper(v8::Isolate* pIsolate) {
98 return v8::Array::New(pIsolate);
99 }
100
NewArrayHelper(v8::Isolate * pIsolate,pdfium::span<v8::Local<v8::Value>> values)101 v8::Local<v8::Array> NewArrayHelper(v8::Isolate* pIsolate,
102 pdfium::span<v8::Local<v8::Value>> values) {
103 v8::Local<v8::Array> result = NewArrayHelper(pIsolate);
104 for (size_t i = 0; i < values.size(); ++i) {
105 fxv8::ReentrantPutArrayElementHelper(
106 pIsolate, result, i,
107 values[i].IsEmpty() ? fxv8::NewUndefinedHelper(pIsolate) : values[i]);
108 }
109 return result;
110 }
111
NewObjectHelper(v8::Isolate * pIsolate)112 v8::Local<v8::Object> NewObjectHelper(v8::Isolate* pIsolate) {
113 return v8::Object::New(pIsolate);
114 }
115
NewDateHelper(v8::Isolate * pIsolate,double d)116 v8::Local<v8::Date> NewDateHelper(v8::Isolate* pIsolate, double d) {
117 return v8::Date::New(pIsolate->GetCurrentContext(), d)
118 .ToLocalChecked()
119 .As<v8::Date>();
120 }
121
ToWideString(v8::Isolate * pIsolate,v8::Local<v8::String> pValue)122 WideString ToWideString(v8::Isolate* pIsolate, v8::Local<v8::String> pValue) {
123 v8::String::Utf8Value s(pIsolate, pValue);
124 // SAFETY: required from V8.
125 return WideString::FromUTF8(UNSAFE_BUFFERS(ByteStringView(*s, s.length())));
126 }
127
ToByteString(v8::Isolate * pIsolate,v8::Local<v8::String> pValue)128 ByteString ToByteString(v8::Isolate* pIsolate, v8::Local<v8::String> pValue) {
129 v8::String::Utf8Value s(pIsolate, pValue);
130 // SAFETY: required from V8.
131 return UNSAFE_BUFFERS(ByteString(*s, s.length()));
132 }
133
ReentrantToInt32Helper(v8::Isolate * pIsolate,v8::Local<v8::Value> pValue)134 int ReentrantToInt32Helper(v8::Isolate* pIsolate, v8::Local<v8::Value> pValue) {
135 if (pValue.IsEmpty())
136 return 0;
137 v8::TryCatch squash_exceptions(pIsolate);
138 return pValue->Int32Value(pIsolate->GetCurrentContext()).FromMaybe(0);
139 }
140
ReentrantToBooleanHelper(v8::Isolate * pIsolate,v8::Local<v8::Value> pValue)141 bool ReentrantToBooleanHelper(v8::Isolate* pIsolate,
142 v8::Local<v8::Value> pValue) {
143 if (pValue.IsEmpty())
144 return false;
145 v8::TryCatch squash_exceptions(pIsolate);
146 return pValue->BooleanValue(pIsolate);
147 }
148
ReentrantToFloatHelper(v8::Isolate * pIsolate,v8::Local<v8::Value> pValue)149 float ReentrantToFloatHelper(v8::Isolate* pIsolate,
150 v8::Local<v8::Value> pValue) {
151 return static_cast<float>(ReentrantToDoubleHelper(pIsolate, pValue));
152 }
153
ReentrantToDoubleHelper(v8::Isolate * pIsolate,v8::Local<v8::Value> pValue)154 double ReentrantToDoubleHelper(v8::Isolate* pIsolate,
155 v8::Local<v8::Value> pValue) {
156 if (pValue.IsEmpty())
157 return 0.0;
158 v8::TryCatch squash_exceptions(pIsolate);
159 return pValue->NumberValue(pIsolate->GetCurrentContext()).FromMaybe(0.0);
160 }
161
ReentrantToWideStringHelper(v8::Isolate * pIsolate,v8::Local<v8::Value> pValue)162 WideString ReentrantToWideStringHelper(v8::Isolate* pIsolate,
163 v8::Local<v8::Value> pValue) {
164 if (pValue.IsEmpty())
165 return WideString();
166
167 v8::TryCatch squash_exceptions(pIsolate);
168 v8::MaybeLocal<v8::String> maybe_string =
169 pValue->ToString(pIsolate->GetCurrentContext());
170 if (maybe_string.IsEmpty())
171 return WideString();
172
173 return ToWideString(pIsolate, maybe_string.ToLocalChecked());
174 }
175
ReentrantToByteStringHelper(v8::Isolate * pIsolate,v8::Local<v8::Value> pValue)176 ByteString ReentrantToByteStringHelper(v8::Isolate* pIsolate,
177 v8::Local<v8::Value> pValue) {
178 if (pValue.IsEmpty())
179 return ByteString();
180
181 v8::TryCatch squash_exceptions(pIsolate);
182 v8::MaybeLocal<v8::String> maybe_string =
183 pValue->ToString(pIsolate->GetCurrentContext());
184 if (maybe_string.IsEmpty())
185 return ByteString();
186
187 return ToByteString(pIsolate, maybe_string.ToLocalChecked());
188 }
189
ReentrantToObjectHelper(v8::Isolate * pIsolate,v8::Local<v8::Value> pValue)190 v8::Local<v8::Object> ReentrantToObjectHelper(v8::Isolate* pIsolate,
191 v8::Local<v8::Value> pValue) {
192 if (!fxv8::IsObject(pValue))
193 return v8::Local<v8::Object>();
194
195 v8::TryCatch squash_exceptions(pIsolate);
196 v8::Local<v8::Context> context = pIsolate->GetCurrentContext();
197 return pValue->ToObject(context).ToLocalChecked();
198 }
199
ReentrantToArrayHelper(v8::Isolate * pIsolate,v8::Local<v8::Value> pValue)200 v8::Local<v8::Array> ReentrantToArrayHelper(v8::Isolate* pIsolate,
201 v8::Local<v8::Value> pValue) {
202 if (!fxv8::IsArray(pValue))
203 return v8::Local<v8::Array>();
204
205 v8::TryCatch squash_exceptions(pIsolate);
206 v8::Local<v8::Context> context = pIsolate->GetCurrentContext();
207 return v8::Local<v8::Array>::Cast(pValue->ToObject(context).ToLocalChecked());
208 }
209
ReentrantGetObjectPropertyHelper(v8::Isolate * pIsolate,v8::Local<v8::Object> pObj,ByteStringView bsUTF8PropertyName)210 v8::Local<v8::Value> ReentrantGetObjectPropertyHelper(
211 v8::Isolate* pIsolate,
212 v8::Local<v8::Object> pObj,
213 ByteStringView bsUTF8PropertyName) {
214 if (pObj.IsEmpty())
215 return v8::Local<v8::Value>();
216
217 v8::TryCatch squash_exceptions(pIsolate);
218 v8::Local<v8::Value> val;
219 if (!pObj->Get(pIsolate->GetCurrentContext(),
220 NewStringHelper(pIsolate, bsUTF8PropertyName))
221 .ToLocal(&val)) {
222 return v8::Local<v8::Value>();
223 }
224 return val;
225 }
226
ReentrantGetObjectPropertyNamesHelper(v8::Isolate * pIsolate,v8::Local<v8::Object> pObj)227 std::vector<WideString> ReentrantGetObjectPropertyNamesHelper(
228 v8::Isolate* pIsolate,
229 v8::Local<v8::Object> pObj) {
230 if (pObj.IsEmpty())
231 return std::vector<WideString>();
232
233 v8::TryCatch squash_exceptions(pIsolate);
234 v8::Local<v8::Array> val;
235 v8::Local<v8::Context> context = pIsolate->GetCurrentContext();
236 if (!pObj->GetPropertyNames(context).ToLocal(&val))
237 return std::vector<WideString>();
238
239 std::vector<WideString> result;
240 for (uint32_t i = 0; i < val->Length(); ++i) {
241 result.push_back(ReentrantToWideStringHelper(
242 pIsolate, val->Get(context, i).ToLocalChecked()));
243 }
244 return result;
245 }
246
ReentrantHasObjectOwnPropertyHelper(v8::Isolate * pIsolate,v8::Local<v8::Object> pObj,ByteStringView bsUTF8PropertyName)247 bool ReentrantHasObjectOwnPropertyHelper(v8::Isolate* pIsolate,
248 v8::Local<v8::Object> pObj,
249 ByteStringView bsUTF8PropertyName) {
250 if (pObj.IsEmpty())
251 return false;
252
253 v8::TryCatch squash_exceptions(pIsolate);
254 v8::Local<v8::Context> pContext = pIsolate->GetCurrentContext();
255 v8::Local<v8::String> hKey =
256 fxv8::NewStringHelper(pIsolate, bsUTF8PropertyName);
257 return pObj->HasRealNamedProperty(pContext, hKey).FromJust();
258 }
259
ReentrantSetObjectOwnPropertyHelper(v8::Isolate * pIsolate,v8::Local<v8::Object> pObj,ByteStringView bsUTF8PropertyName,v8::Local<v8::Value> pValue)260 bool ReentrantSetObjectOwnPropertyHelper(v8::Isolate* pIsolate,
261 v8::Local<v8::Object> pObj,
262 ByteStringView bsUTF8PropertyName,
263 v8::Local<v8::Value> pValue) {
264 if (pObj.IsEmpty() || pValue.IsEmpty())
265 return false;
266
267 v8::TryCatch squash_exceptions(pIsolate);
268 v8::Local<v8::String> name = NewStringHelper(pIsolate, bsUTF8PropertyName);
269 return pObj->DefineOwnProperty(pIsolate->GetCurrentContext(), name, pValue)
270 .FromMaybe(false);
271 }
272
ReentrantPutObjectPropertyHelper(v8::Isolate * pIsolate,v8::Local<v8::Object> pObj,ByteStringView bsUTF8PropertyName,v8::Local<v8::Value> pPut)273 bool ReentrantPutObjectPropertyHelper(v8::Isolate* pIsolate,
274 v8::Local<v8::Object> pObj,
275 ByteStringView bsUTF8PropertyName,
276 v8::Local<v8::Value> pPut) {
277 if (pObj.IsEmpty() || pPut.IsEmpty())
278 return false;
279
280 v8::TryCatch squash_exceptions(pIsolate);
281 v8::Local<v8::String> name = NewStringHelper(pIsolate, bsUTF8PropertyName);
282 v8::Maybe<bool> result = pObj->Set(pIsolate->GetCurrentContext(), name, pPut);
283 return result.IsJust() && result.FromJust();
284 }
285
ReentrantDeleteObjectPropertyHelper(v8::Isolate * pIsolate,v8::Local<v8::Object> pObj,ByteStringView bsUTF8PropertyName)286 void ReentrantDeleteObjectPropertyHelper(v8::Isolate* pIsolate,
287 v8::Local<v8::Object> pObj,
288 ByteStringView bsUTF8PropertyName) {
289 v8::TryCatch squash_exceptions(pIsolate);
290 pObj->Delete(pIsolate->GetCurrentContext(),
291 fxv8::NewStringHelper(pIsolate, bsUTF8PropertyName))
292 .FromJust();
293 }
294
ReentrantPutArrayElementHelper(v8::Isolate * pIsolate,v8::Local<v8::Array> pArray,size_t index,v8::Local<v8::Value> pValue)295 bool ReentrantPutArrayElementHelper(v8::Isolate* pIsolate,
296 v8::Local<v8::Array> pArray,
297 size_t index,
298 v8::Local<v8::Value> pValue) {
299 if (pArray.IsEmpty())
300 return false;
301
302 v8::TryCatch squash_exceptions(pIsolate);
303 v8::Maybe<bool> result =
304 pArray->Set(pIsolate->GetCurrentContext(),
305 pdfium::checked_cast<uint32_t>(index), pValue);
306 return result.IsJust() && result.FromJust();
307 }
308
ReentrantGetArrayElementHelper(v8::Isolate * pIsolate,v8::Local<v8::Array> pArray,size_t index)309 v8::Local<v8::Value> ReentrantGetArrayElementHelper(v8::Isolate* pIsolate,
310 v8::Local<v8::Array> pArray,
311 size_t index) {
312 if (pArray.IsEmpty())
313 return v8::Local<v8::Value>();
314
315 v8::TryCatch squash_exceptions(pIsolate);
316 v8::Local<v8::Value> val;
317 if (!pArray
318 ->Get(pIsolate->GetCurrentContext(),
319 pdfium::checked_cast<uint32_t>(index))
320 .ToLocal(&val)) {
321 return v8::Local<v8::Value>();
322 }
323 return val;
324 }
325
GetArrayLengthHelper(v8::Local<v8::Array> pArray)326 size_t GetArrayLengthHelper(v8::Local<v8::Array> pArray) {
327 if (pArray.IsEmpty())
328 return 0;
329 return pArray->Length();
330 }
331
ThrowExceptionHelper(v8::Isolate * pIsolate,ByteStringView str)332 void ThrowExceptionHelper(v8::Isolate* pIsolate, ByteStringView str) {
333 pIsolate->ThrowException(NewStringHelper(pIsolate, str));
334 }
335
ThrowExceptionHelper(v8::Isolate * pIsolate,WideStringView str)336 void ThrowExceptionHelper(v8::Isolate* pIsolate, WideStringView str) {
337 pIsolate->ThrowException(NewStringHelper(pIsolate, str));
338 }
339
340 } // namespace fxv8
341