1 // Copyright 2014 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 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6
7 #include "fxjs/xfa/cfxjse_value.h"
8
9 #include <math.h>
10
11 #include "fxjs/cfx_v8.h"
12 #include "fxjs/fxv8.h"
13 #include "fxjs/xfa/cfxjse_class.h"
14 #include "fxjs/xfa/cfxjse_context.h"
15 #include "fxjs/xfa/cfxjse_isolatetracker.h"
16
17 namespace {
18
ftod(float fNumber)19 double ftod(float fNumber) {
20 static_assert(sizeof(float) == 4, "float of incorrect size");
21
22 uint32_t nFloatBits = (uint32_t&)fNumber;
23 uint8_t nExponent = (uint8_t)(nFloatBits >> 23);
24 if (nExponent == 0 || nExponent == 255)
25 return fNumber;
26
27 int8_t nErrExp = nExponent - 150;
28 if (nErrExp >= 0)
29 return fNumber;
30
31 double dwError = pow(2.0, nErrExp), dwErrorHalf = dwError / 2;
32 double dNumber = fNumber, dNumberAbs = fabs(fNumber);
33 double dNumberAbsMin = dNumberAbs - dwErrorHalf,
34 dNumberAbsMax = dNumberAbs + dwErrorHalf;
35 int32_t iErrPos = 0;
36 if (floor(dNumberAbsMin) == floor(dNumberAbsMax)) {
37 dNumberAbsMin = fmod(dNumberAbsMin, 1.0);
38 dNumberAbsMax = fmod(dNumberAbsMax, 1.0);
39 int32_t iErrPosMin = 1, iErrPosMax = 38;
40 do {
41 int32_t iMid = (iErrPosMin + iErrPosMax) / 2;
42 double dPow = pow(10.0, iMid);
43 if (floor(dNumberAbsMin * dPow) == floor(dNumberAbsMax * dPow)) {
44 iErrPosMin = iMid + 1;
45 } else {
46 iErrPosMax = iMid;
47 }
48 } while (iErrPosMin < iErrPosMax);
49 iErrPos = iErrPosMax;
50 }
51 double dPow = pow(10.0, iErrPos);
52 return fNumber < 0 ? ceil(dNumber * dPow - 0.5) / dPow
53 : floor(dNumber * dPow + 0.5) / dPow;
54 }
55
56 } // namespace
57
FXJSE_ThrowMessage(ByteStringView utf8Message)58 void FXJSE_ThrowMessage(ByteStringView utf8Message) {
59 v8::Isolate* pIsolate = v8::Isolate::GetCurrent();
60 ASSERT(pIsolate);
61
62 CFXJSE_ScopeUtil_IsolateHandleRootContext scope(pIsolate);
63 v8::Local<v8::String> hMessage = fxv8::NewStringHelper(pIsolate, utf8Message);
64 v8::Local<v8::Value> hError = v8::Exception::Error(hMessage);
65 pIsolate->ThrowException(hError);
66 }
67
68 CFXJSE_Value::CFXJSE_Value() = default;
69
CFXJSE_Value(v8::Isolate * pIsolate,v8::Local<v8::Value> value)70 CFXJSE_Value::CFXJSE_Value(v8::Isolate* pIsolate, v8::Local<v8::Value> value) {
71 ForceSetValue(pIsolate, value);
72 }
73
74 CFXJSE_Value::~CFXJSE_Value() = default;
75
ToHostObject(v8::Isolate * pIsolate) const76 CFXJSE_HostObject* CFXJSE_Value::ToHostObject(v8::Isolate* pIsolate) const {
77 CFXJSE_ScopeUtil_IsolateHandleRootContext scope(pIsolate);
78 return CFXJSE_HostObject::FromV8(
79 v8::Local<v8::Value>::New(pIsolate, m_hValue));
80 }
81
SetHostObject(v8::Isolate * pIsolate,CFXJSE_HostObject * pObject,CFXJSE_Class * pClass)82 void CFXJSE_Value::SetHostObject(v8::Isolate* pIsolate,
83 CFXJSE_HostObject* pObject,
84 CFXJSE_Class* pClass) {
85 CFXJSE_ScopeUtil_IsolateHandleRootContext scope(pIsolate);
86 m_hValue.Reset(pIsolate, pObject->NewBoundV8Object(
87 pIsolate, pClass->GetTemplate(pIsolate)));
88 }
89
SetArray(v8::Isolate * pIsolate,const std::vector<std::unique_ptr<CFXJSE_Value>> & values)90 void CFXJSE_Value::SetArray(
91 v8::Isolate* pIsolate,
92 const std::vector<std::unique_ptr<CFXJSE_Value>>& values) {
93 CFXJSE_ScopeUtil_IsolateHandleRootContext scope(pIsolate);
94 v8::Local<v8::Array> hArrayObject = v8::Array::New(pIsolate, values.size());
95 uint32_t count = 0;
96 for (auto& v : values) {
97 if (v->IsEmpty())
98 v->SetUndefined(pIsolate);
99 fxv8::ReentrantPutArrayElementHelper(pIsolate, hArrayObject, count++,
100 v->GetValue(pIsolate));
101 }
102 m_hValue.Reset(pIsolate, hArrayObject);
103 }
104
SetFloat(v8::Isolate * pIsolate,float fFloat)105 void CFXJSE_Value::SetFloat(v8::Isolate* pIsolate, float fFloat) {
106 CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate);
107 m_hValue.Reset(pIsolate, fxv8::NewNumberHelper(pIsolate, ftod(fFloat)));
108 }
109
SetObjectProperty(v8::Isolate * pIsolate,ByteStringView szPropName,CFXJSE_Value * lpPropValue)110 bool CFXJSE_Value::SetObjectProperty(v8::Isolate* pIsolate,
111 ByteStringView szPropName,
112 CFXJSE_Value* lpPropValue) {
113 if (lpPropValue->IsEmpty())
114 return false;
115
116 CFXJSE_ScopeUtil_IsolateHandleRootContext scope(pIsolate);
117 v8::Local<v8::Value> hObject = GetValue(pIsolate);
118 if (!hObject->IsObject())
119 return false;
120
121 return fxv8::ReentrantPutObjectPropertyHelper(
122 pIsolate, hObject.As<v8::Object>(), szPropName,
123 lpPropValue->GetValue(pIsolate));
124 }
125
GetObjectProperty(v8::Isolate * pIsolate,ByteStringView szPropName,CFXJSE_Value * lpPropValue)126 bool CFXJSE_Value::GetObjectProperty(v8::Isolate* pIsolate,
127 ByteStringView szPropName,
128 CFXJSE_Value* lpPropValue) {
129 CFXJSE_ScopeUtil_IsolateHandleRootContext scope(pIsolate);
130 v8::Local<v8::Value> hObject = GetValue(pIsolate);
131 if (!hObject->IsObject())
132 return false;
133
134 lpPropValue->ForceSetValue(
135 pIsolate, fxv8::ReentrantGetObjectPropertyHelper(
136 pIsolate, hObject.As<v8::Object>(), szPropName));
137 return true;
138 }
139
GetObjectPropertyByIdx(v8::Isolate * pIsolate,uint32_t uPropIdx,CFXJSE_Value * lpPropValue)140 bool CFXJSE_Value::GetObjectPropertyByIdx(v8::Isolate* pIsolate,
141 uint32_t uPropIdx,
142 CFXJSE_Value* lpPropValue) {
143 CFXJSE_ScopeUtil_IsolateHandleRootContext scope(pIsolate);
144 v8::Local<v8::Value> hObject = GetValue(pIsolate);
145 if (!hObject->IsArray())
146 return false;
147
148 lpPropValue->ForceSetValue(pIsolate,
149 fxv8::ReentrantGetArrayElementHelper(
150 pIsolate, hObject.As<v8::Array>(), uPropIdx));
151 return true;
152 }
153
DeleteObjectProperty(v8::Isolate * pIsolate,ByteStringView szPropName)154 void CFXJSE_Value::DeleteObjectProperty(v8::Isolate* pIsolate,
155 ByteStringView szPropName) {
156 CFXJSE_ScopeUtil_IsolateHandleRootContext scope(pIsolate);
157 v8::Local<v8::Value> hObject = v8::Local<v8::Value>::New(pIsolate, m_hValue);
158 if (hObject->IsObject()) {
159 fxv8::ReentrantDeleteObjectPropertyHelper(
160 pIsolate, hObject.As<v8::Object>(), szPropName);
161 }
162 }
163
HasObjectOwnProperty(v8::Isolate * pIsolate,ByteStringView szPropName,bool bUseTypeGetter)164 bool CFXJSE_Value::HasObjectOwnProperty(v8::Isolate* pIsolate,
165 ByteStringView szPropName,
166 bool bUseTypeGetter) {
167 CFXJSE_ScopeUtil_IsolateHandleRootContext scope(pIsolate);
168 v8::Local<v8::Value> hObject = v8::Local<v8::Value>::New(pIsolate, m_hValue);
169 if (!hObject->IsObject())
170 return false;
171
172 v8::Local<v8::String> hKey = fxv8::NewStringHelper(pIsolate, szPropName);
173 return hObject.As<v8::Object>()
174 ->HasRealNamedProperty(pIsolate->GetCurrentContext(), hKey)
175 .FromJust() ||
176 (bUseTypeGetter &&
177 hObject.As<v8::Object>()
178 ->HasOwnProperty(pIsolate->GetCurrentContext(), hKey)
179 .FromMaybe(false));
180 }
181
SetObjectOwnProperty(v8::Isolate * pIsolate,ByteStringView szPropName,CFXJSE_Value * pPropValue)182 bool CFXJSE_Value::SetObjectOwnProperty(v8::Isolate* pIsolate,
183 ByteStringView szPropName,
184 CFXJSE_Value* pPropValue) {
185 CFXJSE_ScopeUtil_IsolateHandleRootContext scope(pIsolate);
186 v8::Local<v8::Value> hObject = v8::Local<v8::Value>::New(pIsolate, m_hValue);
187 if (!hObject->IsObject())
188 return false;
189
190 v8::Local<v8::Value> pValue =
191 v8::Local<v8::Value>::New(pIsolate, pPropValue->m_hValue);
192 return fxv8::ReentrantSetObjectOwnPropertyHelper(
193 pIsolate, hObject.As<v8::Object>(), szPropName, pValue);
194 }
195
SetBoundFunction(v8::Isolate * pIsolate,v8::Local<v8::Function> hOldFunction,v8::Local<v8::Object> hNewThis)196 bool CFXJSE_Value::SetBoundFunction(v8::Isolate* pIsolate,
197 v8::Local<v8::Function> hOldFunction,
198 v8::Local<v8::Object> hNewThis) {
199 ASSERT(!hOldFunction.IsEmpty());
200 ASSERT(!hNewThis.IsEmpty());
201
202 CFXJSE_ScopeUtil_IsolateHandleRootContext scope(pIsolate);
203 v8::Local<v8::Value> rgArgs[2];
204 rgArgs[0] = hOldFunction;
205 rgArgs[1] = hNewThis;
206 v8::Local<v8::String> hBinderFuncSource =
207 fxv8::NewStringHelper(pIsolate,
208 "(function (oldfunction, newthis) { return "
209 "oldfunction.bind(newthis); })");
210 v8::Local<v8::Context> hContext = pIsolate->GetCurrentContext();
211 v8::Local<v8::Function> hBinderFunc =
212 v8::Script::Compile(hContext, hBinderFuncSource)
213 .ToLocalChecked()
214 ->Run(hContext)
215 .ToLocalChecked()
216 .As<v8::Function>();
217 v8::Local<v8::Value> hBoundFunction =
218 hBinderFunc->Call(hContext, hContext->Global(), 2, rgArgs)
219 .ToLocalChecked();
220 if (!fxv8::IsFunction(hBoundFunction))
221 return false;
222
223 m_hValue.Reset(pIsolate, hBoundFunction);
224 return true;
225 }
226
GetValue(v8::Isolate * pIsolate) const227 v8::Local<v8::Value> CFXJSE_Value::GetValue(v8::Isolate* pIsolate) const {
228 return v8::Local<v8::Value>::New(pIsolate, m_hValue);
229 }
230
IsEmpty() const231 bool CFXJSE_Value::IsEmpty() const {
232 return m_hValue.IsEmpty();
233 }
234
IsUndefined(v8::Isolate * pIsolate) const235 bool CFXJSE_Value::IsUndefined(v8::Isolate* pIsolate) const {
236 if (IsEmpty())
237 return false;
238
239 CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate);
240 v8::Local<v8::Value> hValue = v8::Local<v8::Value>::New(pIsolate, m_hValue);
241 return hValue->IsUndefined();
242 }
243
IsNull(v8::Isolate * pIsolate) const244 bool CFXJSE_Value::IsNull(v8::Isolate* pIsolate) const {
245 if (IsEmpty())
246 return false;
247
248 CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate);
249 v8::Local<v8::Value> hValue = v8::Local<v8::Value>::New(pIsolate, m_hValue);
250 return hValue->IsNull();
251 }
252
IsBoolean(v8::Isolate * pIsolate) const253 bool CFXJSE_Value::IsBoolean(v8::Isolate* pIsolate) const {
254 if (IsEmpty())
255 return false;
256
257 CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate);
258 v8::Local<v8::Value> hValue = v8::Local<v8::Value>::New(pIsolate, m_hValue);
259 return hValue->IsBoolean();
260 }
261
IsString(v8::Isolate * pIsolate) const262 bool CFXJSE_Value::IsString(v8::Isolate* pIsolate) const {
263 if (IsEmpty())
264 return false;
265
266 CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate);
267 v8::Local<v8::Value> hValue = v8::Local<v8::Value>::New(pIsolate, m_hValue);
268 return hValue->IsString();
269 }
270
IsNumber(v8::Isolate * pIsolate) const271 bool CFXJSE_Value::IsNumber(v8::Isolate* pIsolate) const {
272 if (IsEmpty())
273 return false;
274
275 CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate);
276 v8::Local<v8::Value> hValue = v8::Local<v8::Value>::New(pIsolate, m_hValue);
277 return hValue->IsNumber();
278 }
279
IsInteger(v8::Isolate * pIsolate) const280 bool CFXJSE_Value::IsInteger(v8::Isolate* pIsolate) const {
281 if (IsEmpty())
282 return false;
283
284 CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate);
285 v8::Local<v8::Value> hValue = v8::Local<v8::Value>::New(pIsolate, m_hValue);
286 return hValue->IsInt32();
287 }
288
IsObject(v8::Isolate * pIsolate) const289 bool CFXJSE_Value::IsObject(v8::Isolate* pIsolate) const {
290 if (IsEmpty())
291 return false;
292
293 CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate);
294 v8::Local<v8::Value> hValue = v8::Local<v8::Value>::New(pIsolate, m_hValue);
295 return hValue->IsObject();
296 }
297
IsArray(v8::Isolate * pIsolate) const298 bool CFXJSE_Value::IsArray(v8::Isolate* pIsolate) const {
299 if (IsEmpty())
300 return false;
301
302 CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate);
303 v8::Local<v8::Value> hValue = v8::Local<v8::Value>::New(pIsolate, m_hValue);
304 return hValue->IsArray();
305 }
306
IsFunction(v8::Isolate * pIsolate) const307 bool CFXJSE_Value::IsFunction(v8::Isolate* pIsolate) const {
308 if (IsEmpty())
309 return false;
310
311 CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate);
312 v8::Local<v8::Value> hValue = v8::Local<v8::Value>::New(pIsolate, m_hValue);
313 return hValue->IsFunction();
314 }
315
ToBoolean(v8::Isolate * pIsolate) const316 bool CFXJSE_Value::ToBoolean(v8::Isolate* pIsolate) const {
317 ASSERT(!IsEmpty());
318 CFXJSE_ScopeUtil_IsolateHandleRootContext scope(pIsolate);
319 return fxv8::ReentrantToBooleanHelper(
320 pIsolate, v8::Local<v8::Value>::New(pIsolate, m_hValue));
321 }
322
ToFloat(v8::Isolate * pIsolate) const323 float CFXJSE_Value::ToFloat(v8::Isolate* pIsolate) const {
324 return static_cast<float>(ToDouble(pIsolate));
325 }
326
ToDouble(v8::Isolate * pIsolate) const327 double CFXJSE_Value::ToDouble(v8::Isolate* pIsolate) const {
328 ASSERT(!IsEmpty());
329 CFXJSE_ScopeUtil_IsolateHandleRootContext scope(pIsolate);
330 return fxv8::ReentrantToDoubleHelper(
331 pIsolate, v8::Local<v8::Value>::New(pIsolate, m_hValue));
332 }
333
ToInteger(v8::Isolate * pIsolate) const334 int32_t CFXJSE_Value::ToInteger(v8::Isolate* pIsolate) const {
335 ASSERT(!IsEmpty());
336 CFXJSE_ScopeUtil_IsolateHandleRootContext scope(pIsolate);
337 return fxv8::ReentrantToInt32Helper(
338 pIsolate, v8::Local<v8::Value>::New(pIsolate, m_hValue));
339 }
340
ToString(v8::Isolate * pIsolate) const341 ByteString CFXJSE_Value::ToString(v8::Isolate* pIsolate) const {
342 ASSERT(!IsEmpty());
343 CFXJSE_ScopeUtil_IsolateHandleRootContext scope(pIsolate);
344 return fxv8::ReentrantToByteStringHelper(
345 pIsolate, v8::Local<v8::Value>::New(pIsolate, m_hValue));
346 }
347
SetUndefined(v8::Isolate * pIsolate)348 void CFXJSE_Value::SetUndefined(v8::Isolate* pIsolate) {
349 CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate);
350 m_hValue.Reset(pIsolate, fxv8::NewUndefinedHelper(pIsolate));
351 }
352
SetNull(v8::Isolate * pIsolate)353 void CFXJSE_Value::SetNull(v8::Isolate* pIsolate) {
354 CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate);
355 m_hValue.Reset(pIsolate, fxv8::NewNullHelper(pIsolate));
356 }
357
SetBoolean(v8::Isolate * pIsolate,bool bBoolean)358 void CFXJSE_Value::SetBoolean(v8::Isolate* pIsolate, bool bBoolean) {
359 CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate);
360 m_hValue.Reset(pIsolate, fxv8::NewBooleanHelper(pIsolate, bBoolean));
361 }
362
SetInteger(v8::Isolate * pIsolate,int32_t nInteger)363 void CFXJSE_Value::SetInteger(v8::Isolate* pIsolate, int32_t nInteger) {
364 CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate);
365 m_hValue.Reset(pIsolate, fxv8::NewNumberHelper(pIsolate, nInteger));
366 }
367
SetDouble(v8::Isolate * pIsolate,double dDouble)368 void CFXJSE_Value::SetDouble(v8::Isolate* pIsolate, double dDouble) {
369 CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate);
370 m_hValue.Reset(pIsolate, fxv8::NewNumberHelper(pIsolate, dDouble));
371 }
372
SetString(v8::Isolate * pIsolate,ByteStringView szString)373 void CFXJSE_Value::SetString(v8::Isolate* pIsolate, ByteStringView szString) {
374 CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate);
375 m_hValue.Reset(pIsolate, fxv8::NewStringHelper(pIsolate, szString));
376 }
377