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/xfa/cfxjse_class.h"
12 #include "fxjs/xfa/cfxjse_context.h"
13 #include "fxjs/xfa/cfxjse_isolatetracker.h"
14 
15 namespace {
16 
ftod(float fNumber)17 double ftod(float fNumber) {
18   static_assert(sizeof(float) == 4, "float of incorrect size");
19 
20   uint32_t nFloatBits = (uint32_t&)fNumber;
21   uint8_t nExponent = (uint8_t)(nFloatBits >> 23);
22   if (nExponent == 0 || nExponent == 255)
23     return fNumber;
24 
25   int8_t nErrExp = nExponent - 150;
26   if (nErrExp >= 0)
27     return fNumber;
28 
29   double dwError = pow(2.0, nErrExp), dwErrorHalf = dwError / 2;
30   double dNumber = fNumber, dNumberAbs = fabs(fNumber);
31   double dNumberAbsMin = dNumberAbs - dwErrorHalf,
32          dNumberAbsMax = dNumberAbs + dwErrorHalf;
33   int32_t iErrPos = 0;
34   if (floor(dNumberAbsMin) == floor(dNumberAbsMax)) {
35     dNumberAbsMin = fmod(dNumberAbsMin, 1.0);
36     dNumberAbsMax = fmod(dNumberAbsMax, 1.0);
37     int32_t iErrPosMin = 1, iErrPosMax = 38;
38     do {
39       int32_t iMid = (iErrPosMin + iErrPosMax) / 2;
40       double dPow = pow(10.0, iMid);
41       if (floor(dNumberAbsMin * dPow) == floor(dNumberAbsMax * dPow)) {
42         iErrPosMin = iMid + 1;
43       } else {
44         iErrPosMax = iMid;
45       }
46     } while (iErrPosMin < iErrPosMax);
47     iErrPos = iErrPosMax;
48   }
49   double dPow = pow(10.0, iErrPos);
50   return fNumber < 0 ? ceil(dNumber * dPow - 0.5) / dPow
51                      : floor(dNumber * dPow + 0.5) / dPow;
52 }
53 
54 }  // namespace
55 
FXJSE_ThrowMessage(ByteStringView utf8Message)56 void FXJSE_ThrowMessage(ByteStringView utf8Message) {
57   v8::Isolate* pIsolate = v8::Isolate::GetCurrent();
58   ASSERT(pIsolate);
59 
60   CFXJSE_ScopeUtil_IsolateHandleRootContext scope(pIsolate);
61   v8::Local<v8::String> hMessage =
62       v8::String::NewFromUtf8(pIsolate, utf8Message.unterminated_c_str(),
63                               v8::NewStringType::kNormal,
64                               utf8Message.GetLength())
65           .ToLocalChecked();
66   v8::Local<v8::Value> hError = v8::Exception::Error(hMessage);
67   pIsolate->ThrowException(hError);
68 }
69 
CFXJSE_Value(v8::Isolate * pIsolate)70 CFXJSE_Value::CFXJSE_Value(v8::Isolate* pIsolate) : m_pIsolate(pIsolate) {}
71 
~CFXJSE_Value()72 CFXJSE_Value::~CFXJSE_Value() {}
73 
ToHostObject() const74 CFXJSE_HostObject* CFXJSE_Value::ToHostObject() const {
75   CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate());
76   v8::Local<v8::Value> pValue =
77       v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
78   ASSERT(!pValue.IsEmpty());
79   if (!pValue->IsObject())
80     return nullptr;
81 
82   return FXJSE_RetrieveObjectBinding(pValue.As<v8::Object>());
83 }
84 
SetHostObject(CFXJSE_HostObject * lpObject,CFXJSE_Class * pClass)85 void CFXJSE_Value::SetHostObject(CFXJSE_HostObject* lpObject,
86                                  CFXJSE_Class* pClass) {
87   CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate());
88   v8::Local<v8::FunctionTemplate> hClass =
89       v8::Local<v8::FunctionTemplate>::New(GetIsolate(), pClass->m_hTemplate);
90   v8::Local<v8::Object> hObject =
91       hClass->InstanceTemplate()
92           ->NewInstance(GetIsolate()->GetCurrentContext())
93           .ToLocalChecked();
94   FXJSE_UpdateObjectBinding(hObject, lpObject);
95   m_hValue.Reset(GetIsolate(), hObject);
96 }
97 
ClearHostObject()98 void CFXJSE_Value::ClearHostObject() {
99   CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate());
100   FXJSE_ClearObjectBinding(m_hValue.Get(GetIsolate()).As<v8::Object>());
101   v8::Local<v8::Value> hValue = v8::Null(GetIsolate());
102   m_hValue.Reset(GetIsolate(), hValue);
103 }
104 
SetArray(const std::vector<std::unique_ptr<CFXJSE_Value>> & values)105 void CFXJSE_Value::SetArray(
106     const std::vector<std::unique_ptr<CFXJSE_Value>>& values) {
107   CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate());
108   v8::Local<v8::Array> hArrayObject =
109       v8::Array::New(GetIsolate(), values.size());
110   v8::Local<v8::Context> context = GetIsolate()->GetCurrentContext();
111   uint32_t count = 0;
112   for (auto& v : values) {
113     if (v->IsEmpty())
114       v->SetUndefined();
115     hArrayObject
116         ->Set(
117             context, count++,
118             v8::Local<v8::Value>::New(GetIsolate(), v.get()->DirectGetValue()))
119         .FromJust();
120   }
121   m_hValue.Reset(GetIsolate(), hArrayObject);
122 }
123 
SetFloat(float fFloat)124 void CFXJSE_Value::SetFloat(float fFloat) {
125   CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate());
126   v8::Local<v8::Value> pValue = v8::Number::New(GetIsolate(), ftod(fFloat));
127   m_hValue.Reset(GetIsolate(), pValue);
128 }
129 
SetObjectProperty(ByteStringView szPropName,CFXJSE_Value * lpPropValue)130 bool CFXJSE_Value::SetObjectProperty(ByteStringView szPropName,
131                                      CFXJSE_Value* lpPropValue) {
132   ASSERT(lpPropValue);
133   if (lpPropValue->IsEmpty())
134     return false;
135 
136   CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate());
137   v8::Local<v8::Value> hObject =
138       v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
139   if (!hObject->IsObject())
140     return false;
141 
142   v8::Local<v8::String> hPropName =
143       v8::String::NewFromUtf8(GetIsolate(), szPropName.unterminated_c_str(),
144                               v8::NewStringType::kNormal,
145                               szPropName.GetLength())
146           .ToLocalChecked();
147   v8::Local<v8::Value> hPropValue =
148       v8::Local<v8::Value>::New(GetIsolate(), lpPropValue->DirectGetValue());
149   v8::Maybe<bool> result = hObject.As<v8::Object>()->Set(
150       GetIsolate()->GetCurrentContext(), hPropName, hPropValue);
151   return result.IsJust() && result.FromJust();
152 }
153 
GetObjectProperty(ByteStringView szPropName,CFXJSE_Value * lpPropValue)154 bool CFXJSE_Value::GetObjectProperty(ByteStringView szPropName,
155                                      CFXJSE_Value* lpPropValue) {
156   ASSERT(lpPropValue);
157   CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate());
158   v8::Local<v8::Value> hObject =
159       v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
160   if (!hObject->IsObject())
161     return false;
162 
163   v8::Local<v8::String> hPropName =
164       v8::String::NewFromUtf8(GetIsolate(), szPropName.unterminated_c_str(),
165                               v8::NewStringType::kNormal,
166                               szPropName.GetLength())
167           .ToLocalChecked();
168   v8::Local<v8::Value> hPropValue =
169       hObject.As<v8::Object>()
170           ->Get(GetIsolate()->GetCurrentContext(), hPropName)
171           .ToLocalChecked();
172   lpPropValue->ForceSetValue(hPropValue);
173   return true;
174 }
175 
GetObjectPropertyByIdx(uint32_t uPropIdx,CFXJSE_Value * lpPropValue)176 bool CFXJSE_Value::GetObjectPropertyByIdx(uint32_t uPropIdx,
177                                           CFXJSE_Value* lpPropValue) {
178   CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate());
179   v8::Local<v8::Value> hObject =
180       v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
181   if (!hObject->IsObject())
182     return false;
183 
184   v8::Local<v8::Value> hPropValue =
185       hObject.As<v8::Object>()
186           ->Get(GetIsolate()->GetCurrentContext(), uPropIdx)
187           .ToLocalChecked();
188   lpPropValue->ForceSetValue(hPropValue);
189   return true;
190 }
191 
DeleteObjectProperty(ByteStringView szPropName)192 bool CFXJSE_Value::DeleteObjectProperty(ByteStringView szPropName) {
193   CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate());
194   v8::Local<v8::Value> hObject =
195       v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
196   if (!hObject->IsObject())
197     return false;
198 
199   v8::Local<v8::String> hPropName =
200       v8::String::NewFromUtf8(GetIsolate(), szPropName.unterminated_c_str(),
201                               v8::NewStringType::kNormal,
202                               szPropName.GetLength())
203           .ToLocalChecked();
204   return hObject.As<v8::Object>()
205       ->Delete(GetIsolate()->GetCurrentContext(), hPropName)
206       .FromJust();
207 }
208 
HasObjectOwnProperty(ByteStringView szPropName,bool bUseTypeGetter)209 bool CFXJSE_Value::HasObjectOwnProperty(ByteStringView szPropName,
210                                         bool bUseTypeGetter) {
211   CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate());
212   v8::Local<v8::Value> hObject =
213       v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
214   if (!hObject->IsObject())
215     return false;
216 
217   v8::Local<v8::String> hKey =
218       v8::String::NewFromUtf8(GetIsolate(), szPropName.unterminated_c_str(),
219                               v8::NewStringType::kNormal,
220                               szPropName.GetLength())
221           .ToLocalChecked();
222   return hObject.As<v8::Object>()
223              ->HasRealNamedProperty(GetIsolate()->GetCurrentContext(), hKey)
224              .FromJust() ||
225          (bUseTypeGetter &&
226           hObject.As<v8::Object>()
227               ->HasOwnProperty(GetIsolate()->GetCurrentContext(), hKey)
228               .FromMaybe(false));
229 }
230 
SetObjectOwnProperty(ByteStringView szPropName,CFXJSE_Value * lpPropValue)231 bool CFXJSE_Value::SetObjectOwnProperty(ByteStringView szPropName,
232                                         CFXJSE_Value* lpPropValue) {
233   ASSERT(lpPropValue);
234   CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate());
235   v8::Local<v8::Value> hObject =
236       v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
237   if (!hObject->IsObject())
238     return false;
239 
240   v8::Local<v8::String> hPropName =
241       v8::String::NewFromUtf8(GetIsolate(), szPropName.unterminated_c_str(),
242                               v8::NewStringType::kNormal,
243                               szPropName.GetLength())
244           .ToLocalChecked();
245   v8::Local<v8::Value> pValue =
246       v8::Local<v8::Value>::New(GetIsolate(), lpPropValue->m_hValue);
247   return hObject.As<v8::Object>()
248       ->DefineOwnProperty(GetIsolate()->GetCurrentContext(), hPropName, pValue)
249       .FromMaybe(false);
250 }
251 
SetFunctionBind(CFXJSE_Value * lpOldFunction,CFXJSE_Value * lpNewThis)252 bool CFXJSE_Value::SetFunctionBind(CFXJSE_Value* lpOldFunction,
253                                    CFXJSE_Value* lpNewThis) {
254   ASSERT(lpOldFunction);
255   ASSERT(lpNewThis);
256 
257   CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate());
258   v8::Local<v8::Value> rgArgs[2];
259   v8::Local<v8::Value> hOldFunction =
260       v8::Local<v8::Value>::New(GetIsolate(), lpOldFunction->DirectGetValue());
261   if (hOldFunction.IsEmpty() || !hOldFunction->IsFunction())
262     return false;
263 
264   rgArgs[0] = hOldFunction;
265   v8::Local<v8::Value> hNewThis =
266       v8::Local<v8::Value>::New(GetIsolate(), lpNewThis->DirectGetValue());
267   if (hNewThis.IsEmpty())
268     return false;
269 
270   rgArgs[1] = hNewThis;
271   v8::Local<v8::String> hBinderFuncSource =
272       v8::String::NewFromUtf8(GetIsolate(),
273                               "(function (oldfunction, newthis) { return "
274                               "oldfunction.bind(newthis); })",
275                               v8::NewStringType::kNormal)
276           .ToLocalChecked();
277   v8::Local<v8::Context> hContext = GetIsolate()->GetCurrentContext();
278   v8::Local<v8::Function> hBinderFunc =
279       v8::Script::Compile(hContext, hBinderFuncSource)
280           .ToLocalChecked()
281           ->Run(hContext)
282           .ToLocalChecked()
283           .As<v8::Function>();
284   v8::Local<v8::Value> hBoundFunction =
285       hBinderFunc->Call(hContext, hContext->Global(), 2, rgArgs)
286           .ToLocalChecked();
287   if (hBoundFunction.IsEmpty() || !hBoundFunction->IsFunction())
288     return false;
289 
290   m_hValue.Reset(GetIsolate(), hBoundFunction);
291   return true;
292 }
293 
IsEmpty() const294 bool CFXJSE_Value::IsEmpty() const {
295   return m_hValue.IsEmpty();
296 }
297 
IsUndefined() const298 bool CFXJSE_Value::IsUndefined() const {
299   if (IsEmpty())
300     return false;
301 
302   CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate());
303   v8::Local<v8::Value> hValue =
304       v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
305   return hValue->IsUndefined();
306 }
307 
IsNull() const308 bool CFXJSE_Value::IsNull() const {
309   if (IsEmpty())
310     return false;
311 
312   CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate());
313   v8::Local<v8::Value> hValue =
314       v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
315   return hValue->IsNull();
316 }
317 
IsBoolean() const318 bool CFXJSE_Value::IsBoolean() const {
319   if (IsEmpty())
320     return false;
321 
322   CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate());
323   v8::Local<v8::Value> hValue =
324       v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
325   return hValue->IsBoolean();
326 }
327 
IsString() const328 bool CFXJSE_Value::IsString() const {
329   if (IsEmpty())
330     return false;
331 
332   CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate());
333   v8::Local<v8::Value> hValue =
334       v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
335   return hValue->IsString();
336 }
337 
IsNumber() const338 bool CFXJSE_Value::IsNumber() const {
339   if (IsEmpty())
340     return false;
341 
342   CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate());
343   v8::Local<v8::Value> hValue =
344       v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
345   return hValue->IsNumber();
346 }
347 
IsInteger() const348 bool CFXJSE_Value::IsInteger() const {
349   if (IsEmpty())
350     return false;
351 
352   CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate());
353   v8::Local<v8::Value> hValue =
354       v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
355   return hValue->IsInt32();
356 }
357 
IsObject() const358 bool CFXJSE_Value::IsObject() const {
359   if (IsEmpty())
360     return false;
361 
362   CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate());
363   v8::Local<v8::Value> hValue =
364       v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
365   return hValue->IsObject();
366 }
367 
IsArray() const368 bool CFXJSE_Value::IsArray() const {
369   if (IsEmpty())
370     return false;
371 
372   CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate());
373   v8::Local<v8::Value> hValue =
374       v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
375   return hValue->IsArray();
376 }
377 
IsFunction() const378 bool CFXJSE_Value::IsFunction() const {
379   if (IsEmpty())
380     return false;
381 
382   CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate());
383   v8::Local<v8::Value> hValue =
384       v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
385   return hValue->IsFunction();
386 }
387 
ToBoolean() const388 bool CFXJSE_Value::ToBoolean() const {
389   ASSERT(!IsEmpty());
390   CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate());
391   v8::Local<v8::Value> hValue =
392       v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
393   return hValue->BooleanValue(GetIsolate());
394 }
395 
ToFloat() const396 float CFXJSE_Value::ToFloat() const {
397   ASSERT(!IsEmpty());
398   CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate());
399   v8::Local<v8::Value> hValue =
400       v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
401   return static_cast<float>(
402       hValue->NumberValue(GetIsolate()->GetCurrentContext()).FromMaybe(0.0));
403 }
404 
ToDouble() const405 double CFXJSE_Value::ToDouble() const {
406   ASSERT(!IsEmpty());
407   CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate());
408   v8::Local<v8::Value> hValue =
409       v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
410   return hValue->NumberValue(GetIsolate()->GetCurrentContext()).FromMaybe(0.0);
411 }
412 
ToInteger() const413 int32_t CFXJSE_Value::ToInteger() const {
414   ASSERT(!IsEmpty());
415   CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate());
416   v8::Local<v8::Value> hValue =
417       v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
418   return static_cast<int32_t>(
419       hValue->NumberValue(GetIsolate()->GetCurrentContext()).FromMaybe(0.0));
420 }
421 
ToString() const422 ByteString CFXJSE_Value::ToString() const {
423   ASSERT(!IsEmpty());
424   CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate());
425   v8::Local<v8::Value> hValue =
426       v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
427   v8::Local<v8::String> hString =
428       hValue->ToString(GetIsolate()->GetCurrentContext()).ToLocalChecked();
429   v8::String::Utf8Value hStringVal(GetIsolate(), hString);
430   return ByteString(*hStringVal);
431 }
432 
SetUndefined()433 void CFXJSE_Value::SetUndefined() {
434   CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate());
435   v8::Local<v8::Value> hValue = v8::Undefined(GetIsolate());
436   m_hValue.Reset(GetIsolate(), hValue);
437 }
438 
SetNull()439 void CFXJSE_Value::SetNull() {
440   CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate());
441   v8::Local<v8::Value> hValue = v8::Null(GetIsolate());
442   m_hValue.Reset(GetIsolate(), hValue);
443 }
444 
SetBoolean(bool bBoolean)445 void CFXJSE_Value::SetBoolean(bool bBoolean) {
446   CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate());
447   v8::Local<v8::Value> hValue = v8::Boolean::New(GetIsolate(), !!bBoolean);
448   m_hValue.Reset(GetIsolate(), hValue);
449 }
450 
SetInteger(int32_t nInteger)451 void CFXJSE_Value::SetInteger(int32_t nInteger) {
452   CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate());
453   v8::Local<v8::Value> hValue = v8::Integer::New(GetIsolate(), nInteger);
454   m_hValue.Reset(GetIsolate(), hValue);
455 }
456 
SetDouble(double dDouble)457 void CFXJSE_Value::SetDouble(double dDouble) {
458   CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate());
459   v8::Local<v8::Value> hValue = v8::Number::New(GetIsolate(), dDouble);
460   m_hValue.Reset(GetIsolate(), hValue);
461 }
462 
SetString(ByteStringView szString)463 void CFXJSE_Value::SetString(ByteStringView szString) {
464   CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate());
465   v8::Local<v8::Value> hValue =
466       v8::String::NewFromUtf8(GetIsolate(), szString.unterminated_c_str(),
467                               v8::NewStringType::kNormal, szString.GetLength())
468           .ToLocalChecked();
469   m_hValue.Reset(GetIsolate(), hValue);
470 }
471