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