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