Fix JS_GetArrayElement to not return an empty handle on success
[pdfium.git] / fpdfsdk / src / jsapi / fxjs_v8.cpp
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 "../../../core/include/fxcrt/fx_basic.h"
8 #include "../../../core/include/fxcrt/fx_ext.h"
9 #include "../../include/jsapi/fxjs_v8.h"
10 #include "../../include/fsdk_define.h"
11 #include "time.h"
12 #include <cmath>
13 #include <limits>
14
15 #define VALUE_NAME_STRING L"string"
16 #define VALUE_NAME_NUMBER L"number"
17 #define VALUE_NAME_BOOLEAN L"boolean"
18 #define VALUE_NAME_DATE L"date"
19 #define VALUE_NAME_OBJECT L"object"
20 #define VALUE_NAME_FXOBJ L"fxobj"
21 #define VALUE_NAME_NULL L"null"
22 #define VALUE_NAME_UNDEFINED L"undefined"
23
24 const static FX_DWORD g_nan[2] = {0, 0x7FF80000};
25 static double GetNan() {
26   return *(double*)g_nan;
27 }
28 static unsigned int g_embedderDataSlot = 0u;
29
30 class CJS_PrivateData {
31  public:
32   CJS_PrivateData() : ObjDefID(-1), pPrivate(NULL) {}
33   int ObjDefID;
34   void* pPrivate;
35 };
36
37 class CJS_ObjDefintion {
38  public:
39   CJS_ObjDefintion(v8::Isolate* isolate,
40                    const wchar_t* sObjName,
41                    FXJSOBJTYPE eObjType,
42                    LP_CONSTRUCTOR pConstructor,
43                    LP_DESTRUCTOR pDestructor)
44       : objName(sObjName),
45         objType(eObjType),
46         m_pConstructor(pConstructor),
47         m_pDestructor(pDestructor),
48         m_bSetAsGlobalObject(FALSE) {
49     v8::Isolate::Scope isolate_scope(isolate);
50     v8::HandleScope handle_scope(isolate);
51
52     v8::Local<v8::ObjectTemplate> objTemplate =
53         v8::ObjectTemplate::New(isolate);
54     objTemplate->SetInternalFieldCount(2);
55     m_objTemplate.Reset(isolate, objTemplate);
56
57     // Document as the global object.
58     if (FXSYS_wcscmp(sObjName, L"Document") == 0) {
59       m_bSetAsGlobalObject = TRUE;
60     }
61   }
62   ~CJS_ObjDefintion() {
63     m_objTemplate.Reset();
64     m_StaticObj.Reset();
65   }
66
67  public:
68   const wchar_t* objName;
69   FXJSOBJTYPE objType;
70   LP_CONSTRUCTOR m_pConstructor;
71   LP_DESTRUCTOR m_pDestructor;
72   FX_BOOL m_bSetAsGlobalObject;
73
74   v8::Global<v8::ObjectTemplate> m_objTemplate;
75   v8::Global<v8::Object> m_StaticObj;
76 };
77
78 int JS_DefineObj(IJS_Runtime* pJSRuntime,
79                  const wchar_t* sObjName,
80                  FXJSOBJTYPE eObjType,
81                  LP_CONSTRUCTOR pConstructor,
82                  LP_DESTRUCTOR pDestructor) {
83   v8::Isolate* isolate = (v8::Isolate*)pJSRuntime;
84   v8::Isolate::Scope isolate_scope(isolate);
85   v8::HandleScope handle_scope(isolate);
86   CFX_PtrArray* pArray = (CFX_PtrArray*)isolate->GetData(g_embedderDataSlot);
87   if (!pArray) {
88     pArray = new CFX_PtrArray();
89     isolate->SetData(g_embedderDataSlot, pArray);
90   }
91   CJS_ObjDefintion* pObjDef = new CJS_ObjDefintion(isolate, sObjName, eObjType,
92                                                    pConstructor, pDestructor);
93   pArray->Add(pObjDef);
94   return pArray->GetSize() - 1;
95 }
96
97 int JS_DefineObjMethod(IJS_Runtime* pJSRuntime,
98                        int nObjDefnID,
99                        const wchar_t* sMethodName,
100                        v8::FunctionCallback pMethodCall) {
101   v8::Isolate* isolate = (v8::Isolate*)pJSRuntime;
102   v8::Isolate::Scope isolate_scope(isolate);
103   v8::HandleScope handle_scope(isolate);
104
105   CFX_WideString ws = CFX_WideString(sMethodName);
106   CFX_ByteString bsMethodName = ws.UTF8Encode();
107
108   CFX_PtrArray* pArray = (CFX_PtrArray*)isolate->GetData(g_embedderDataSlot);
109   if (!pArray)
110     return 0;
111
112   if (nObjDefnID < 0 || nObjDefnID >= pArray->GetSize())
113     return 0;
114   CJS_ObjDefintion* pObjDef = (CJS_ObjDefintion*)pArray->GetAt(nObjDefnID);
115   v8::Local<v8::ObjectTemplate> objTemp =
116       v8::Local<v8::ObjectTemplate>::New(isolate, pObjDef->m_objTemplate);
117   objTemp->Set(v8::String::NewFromUtf8(isolate, bsMethodName.c_str(),
118                                        v8::NewStringType::kNormal)
119                    .ToLocalChecked(),
120                v8::FunctionTemplate::New(isolate, pMethodCall), v8::ReadOnly);
121   pObjDef->m_objTemplate.Reset(isolate, objTemp);
122   return 0;
123 }
124
125 int JS_DefineObjProperty(IJS_Runtime* pJSRuntime,
126                          int nObjDefnID,
127                          const wchar_t* sPropName,
128                          v8::AccessorGetterCallback pPropGet,
129                          v8::AccessorSetterCallback pPropPut) {
130   v8::Isolate* isolate = (v8::Isolate*)pJSRuntime;
131   v8::Isolate::Scope isolate_scope(isolate);
132   v8::HandleScope handle_scope(isolate);
133
134   CFX_WideString ws = CFX_WideString(sPropName);
135   CFX_ByteString bsPropertyName = ws.UTF8Encode();
136
137   CFX_PtrArray* pArray = (CFX_PtrArray*)isolate->GetData(g_embedderDataSlot);
138   if (!pArray)
139     return 0;
140
141   if (nObjDefnID < 0 || nObjDefnID >= pArray->GetSize())
142     return 0;
143   CJS_ObjDefintion* pObjDef = (CJS_ObjDefintion*)pArray->GetAt(nObjDefnID);
144   v8::Local<v8::ObjectTemplate> objTemp =
145       v8::Local<v8::ObjectTemplate>::New(isolate, pObjDef->m_objTemplate);
146   objTemp->SetAccessor(v8::String::NewFromUtf8(isolate, bsPropertyName.c_str(),
147                                                v8::NewStringType::kNormal)
148                            .ToLocalChecked(),
149                        pPropGet, pPropPut);
150   pObjDef->m_objTemplate.Reset(isolate, objTemp);
151   return 0;
152 }
153
154 int JS_DefineObjAllProperties(IJS_Runtime* pJSRuntime,
155                               int nObjDefnID,
156                               v8::NamedPropertyQueryCallback pPropQurey,
157                               v8::NamedPropertyGetterCallback pPropGet,
158                               v8::NamedPropertySetterCallback pPropPut,
159                               v8::NamedPropertyDeleterCallback pPropDel) {
160   v8::Isolate* isolate = (v8::Isolate*)pJSRuntime;
161   v8::Isolate::Scope isolate_scope(isolate);
162   v8::HandleScope handle_scope(isolate);
163
164   CFX_PtrArray* pArray = (CFX_PtrArray*)isolate->GetData(g_embedderDataSlot);
165   if (!pArray)
166     return 0;
167
168   if (nObjDefnID < 0 || nObjDefnID >= pArray->GetSize())
169     return 0;
170   CJS_ObjDefintion* pObjDef = (CJS_ObjDefintion*)pArray->GetAt(nObjDefnID);
171   v8::Local<v8::ObjectTemplate> objTemp =
172       v8::Local<v8::ObjectTemplate>::New(isolate, pObjDef->m_objTemplate);
173   objTemp->SetNamedPropertyHandler(pPropGet, pPropPut, pPropQurey, pPropDel);
174   pObjDef->m_objTemplate.Reset(isolate, objTemp);
175   return 0;
176 }
177
178 int JS_DefineObjConst(IJS_Runtime* pJSRuntime,
179                       int nObjDefnID,
180                       const wchar_t* sConstName,
181                       v8::Local<v8::Value> pDefault) {
182   v8::Isolate* isolate = (v8::Isolate*)pJSRuntime;
183   v8::Isolate::Scope isolate_scope(isolate);
184   v8::HandleScope handle_scope(isolate);
185
186   CFX_PtrArray* pArray = (CFX_PtrArray*)isolate->GetData(g_embedderDataSlot);
187   if (!pArray)
188     return 0;
189
190   CFX_WideString ws = CFX_WideString(sConstName);
191   CFX_ByteString bsConstName = ws.UTF8Encode();
192
193   if (nObjDefnID < 0 || nObjDefnID >= pArray->GetSize())
194     return 0;
195   CJS_ObjDefintion* pObjDef = (CJS_ObjDefintion*)pArray->GetAt(nObjDefnID);
196   v8::Local<v8::ObjectTemplate> objTemp =
197       v8::Local<v8::ObjectTemplate>::New(isolate, pObjDef->m_objTemplate);
198   objTemp->Set(isolate, bsConstName.c_str(), pDefault);
199   pObjDef->m_objTemplate.Reset(isolate, objTemp);
200   return 0;
201 }
202
203 static v8::Global<v8::ObjectTemplate>& _getGlobalObjectTemplate(
204     IJS_Runtime* pJSRuntime) {
205   v8::Isolate* isolate = (v8::Isolate*)pJSRuntime;
206   v8::Isolate::Scope isolate_scope(isolate);
207   v8::HandleScope handle_scope(isolate);
208
209   CFX_PtrArray* pArray = (CFX_PtrArray*)isolate->GetData(g_embedderDataSlot);
210   ASSERT(pArray != NULL);
211   for (int i = 0; i < pArray->GetSize(); i++) {
212     CJS_ObjDefintion* pObjDef = (CJS_ObjDefintion*)pArray->GetAt(i);
213     if (pObjDef->m_bSetAsGlobalObject)
214       return pObjDef->m_objTemplate;
215   }
216   static v8::Global<v8::ObjectTemplate> gloabalObjectTemplate;
217   return gloabalObjectTemplate;
218 }
219
220 int JS_DefineGlobalMethod(IJS_Runtime* pJSRuntime,
221                           const wchar_t* sMethodName,
222                           v8::FunctionCallback pMethodCall) {
223   v8::Isolate* isolate = (v8::Isolate*)pJSRuntime;
224   v8::Isolate::Scope isolate_scope(isolate);
225   v8::HandleScope handle_scope(isolate);
226
227   CFX_WideString ws = CFX_WideString(sMethodName);
228   CFX_ByteString bsMethodName = ws.UTF8Encode();
229
230   v8::Local<v8::FunctionTemplate> funTempl =
231       v8::FunctionTemplate::New(isolate, pMethodCall);
232   v8::Local<v8::ObjectTemplate> objTemp;
233
234   v8::Global<v8::ObjectTemplate>& globalObjTemp =
235       _getGlobalObjectTemplate(pJSRuntime);
236   if (globalObjTemp.IsEmpty())
237     objTemp = v8::ObjectTemplate::New(isolate);
238   else
239     objTemp = v8::Local<v8::ObjectTemplate>::New(isolate, globalObjTemp);
240   objTemp->Set(v8::String::NewFromUtf8(isolate, bsMethodName.c_str(),
241                                        v8::NewStringType::kNormal)
242                    .ToLocalChecked(),
243                funTempl, v8::ReadOnly);
244
245   globalObjTemp.Reset(isolate, objTemp);
246
247   return 0;
248 }
249
250 int JS_DefineGlobalConst(IJS_Runtime* pJSRuntime,
251                          const wchar_t* sConstName,
252                          v8::Local<v8::Value> pDefault) {
253   v8::Isolate* isolate = (v8::Isolate*)pJSRuntime;
254   v8::Isolate::Scope isolate_scope(isolate);
255   v8::HandleScope handle_scope(isolate);
256
257   CFX_WideString ws = CFX_WideString(sConstName);
258   CFX_ByteString bsConst = ws.UTF8Encode();
259
260   v8::Local<v8::ObjectTemplate> objTemp;
261
262   v8::Global<v8::ObjectTemplate>& globalObjTemp =
263       _getGlobalObjectTemplate(pJSRuntime);
264   if (globalObjTemp.IsEmpty())
265     objTemp = v8::ObjectTemplate::New(isolate);
266   else
267     objTemp = v8::Local<v8::ObjectTemplate>::New(isolate, globalObjTemp);
268   objTemp->Set(v8::String::NewFromUtf8(isolate, bsConst.c_str(),
269                                        v8::NewStringType::kNormal)
270                    .ToLocalChecked(),
271                pDefault, v8::ReadOnly);
272
273   globalObjTemp.Reset(isolate, objTemp);
274
275   return 0;
276 }
277
278 void JS_InitialRuntime(IJS_Runtime* pJSRuntime,
279                        IFXJS_Runtime* pFXRuntime,
280                        IFXJS_Context* context,
281                        v8::Global<v8::Context>& v8PersistentContext) {
282   v8::Isolate* isolate = (v8::Isolate*)pJSRuntime;
283   v8::Isolate::Scope isolate_scope(isolate);
284   v8::HandleScope handle_scope(isolate);
285
286   v8::Global<v8::ObjectTemplate>& globalObjTemp =
287       _getGlobalObjectTemplate(pJSRuntime);
288   v8::Local<v8::Context> v8Context = v8::Context::New(
289       isolate, NULL,
290       v8::Local<v8::ObjectTemplate>::New(isolate, globalObjTemp));
291   v8::Context::Scope context_scope(v8Context);
292
293   v8::Local<v8::External> ptr = v8::External::New(isolate, pFXRuntime);
294   v8Context->SetEmbedderData(1, ptr);
295
296   CFX_PtrArray* pArray = (CFX_PtrArray*)isolate->GetData(g_embedderDataSlot);
297   if (!pArray)
298     return;
299
300   for (int i = 0; i < pArray->GetSize(); i++) {
301     CJS_ObjDefintion* pObjDef = (CJS_ObjDefintion*)pArray->GetAt(i);
302     CFX_WideString ws = CFX_WideString(pObjDef->objName);
303     CFX_ByteString bs = ws.UTF8Encode();
304     v8::Local<v8::String> objName =
305         v8::String::NewFromUtf8(isolate, bs.c_str(), v8::NewStringType::kNormal,
306                                 bs.GetLength())
307             .ToLocalChecked();
308
309     if (pObjDef->objType == JS_DYNAMIC) {
310       // Document is set as global object, need to construct it first.
311       if (ws.Equal(L"Document")) {
312         CJS_PrivateData* pPrivateData = new CJS_PrivateData;
313         pPrivateData->ObjDefID = i;
314
315         v8Context->Global()
316             ->GetPrototype()
317             ->ToObject(v8Context)
318             .ToLocalChecked()
319             ->SetAlignedPointerInInternalField(0, pPrivateData);
320
321         if (pObjDef->m_pConstructor)
322           pObjDef->m_pConstructor(context, v8Context->Global()
323                                                ->GetPrototype()
324                                                ->ToObject(v8Context)
325                                                .ToLocalChecked(),
326                                   v8Context->Global()
327                                       ->GetPrototype()
328                                       ->ToObject(v8Context)
329                                       .ToLocalChecked());
330       }
331     } else {
332       v8::Local<v8::Object> obj = JS_NewFxDynamicObj(pJSRuntime, context, i);
333       v8Context->Global()->Set(v8Context, objName, obj).FromJust();
334       pObjDef->m_StaticObj.Reset(isolate, obj);
335     }
336   }
337   v8PersistentContext.Reset(isolate, v8Context);
338 }
339
340 void JS_ReleaseRuntime(IJS_Runtime* pJSRuntime,
341                        v8::Global<v8::Context>& v8PersistentContext) {
342   v8::Isolate* isolate = (v8::Isolate*)pJSRuntime;
343   v8::Isolate::Scope isolate_scope(isolate);
344   v8::HandleScope handle_scope(isolate);
345   v8::Local<v8::Context> context =
346       v8::Local<v8::Context>::New(isolate, v8PersistentContext);
347   v8::Context::Scope context_scope(context);
348
349   CFX_PtrArray* pArray = (CFX_PtrArray*)isolate->GetData(g_embedderDataSlot);
350   if (!pArray)
351     return;
352
353   for (int i = 0; i < pArray->GetSize(); i++) {
354     CJS_ObjDefintion* pObjDef = (CJS_ObjDefintion*)pArray->GetAt(i);
355     if (!pObjDef->m_StaticObj.IsEmpty()) {
356       v8::Local<v8::Object> pObj =
357           v8::Local<v8::Object>::New(isolate, pObjDef->m_StaticObj);
358       if (pObjDef->m_pDestructor)
359         pObjDef->m_pDestructor(pObj);
360       JS_FreePrivate(pObj);
361     }
362     delete pObjDef;
363   }
364   delete pArray;
365   isolate->SetData(g_embedderDataSlot, NULL);
366 }
367
368 void JS_Initial(unsigned int embedderDataSlot) {
369   g_embedderDataSlot = embedderDataSlot;
370 }
371
372 void JS_Release() {
373 }
374
375 int JS_Execute(IJS_Runtime* pJSRuntime,
376                IFXJS_Context* pJSContext,
377                const wchar_t* script,
378                long length,
379                FXJSErr* perror) {
380   v8::Isolate* isolate = (v8::Isolate*)pJSRuntime;
381   v8::Isolate::Scope isolate_scope(isolate);
382   v8::TryCatch try_catch(isolate);
383
384   CFX_WideString wsScript(script);
385   CFX_ByteString bsScript = wsScript.UTF8Encode();
386
387   v8::Local<v8::Context> context = isolate->GetCurrentContext();
388   v8::Local<v8::Script> compiled_script;
389   if (!v8::Script::Compile(context,
390                            v8::String::NewFromUtf8(isolate, bsScript.c_str(),
391                                                    v8::NewStringType::kNormal,
392                                                    bsScript.GetLength())
393                                .ToLocalChecked())
394            .ToLocal(&compiled_script)) {
395     v8::String::Utf8Value error(try_catch.Exception());
396     return -1;
397   }
398
399   v8::Local<v8::Value> result;
400   if (!compiled_script->Run(context).ToLocal(&result)) {
401     v8::String::Utf8Value error(try_catch.Exception());
402     return -1;
403   }
404   return 0;
405 }
406
407 v8::Local<v8::Object> JS_NewFxDynamicObj(IJS_Runtime* pJSRuntime,
408                                          IFXJS_Context* pJSContext,
409                                          int nObjDefnID) {
410   v8::Isolate* isolate = (v8::Isolate*)pJSRuntime;
411   v8::Isolate::Scope isolate_scope(isolate);
412   v8::Local<v8::Context> context = isolate->GetCurrentContext();
413   if (-1 == nObjDefnID) {
414     v8::Local<v8::ObjectTemplate> objTempl = v8::ObjectTemplate::New(isolate);
415     v8::Local<v8::Object> obj;
416     if (objTempl->NewInstance(context).ToLocal(&obj))
417       return obj;
418     return v8::Local<v8::Object>();
419   }
420
421   CFX_PtrArray* pArray = (CFX_PtrArray*)isolate->GetData(g_embedderDataSlot);
422   if (!pArray)
423     return v8::Local<v8::Object>();
424
425   if (nObjDefnID < 0 || nObjDefnID >= pArray->GetSize())
426     return v8::Local<v8::Object>();
427   CJS_ObjDefintion* pObjDef = (CJS_ObjDefintion*)pArray->GetAt(nObjDefnID);
428
429   v8::Local<v8::ObjectTemplate> objTemp =
430       v8::Local<v8::ObjectTemplate>::New(isolate, pObjDef->m_objTemplate);
431   v8::Local<v8::Object> obj;
432   if (!objTemp->NewInstance(context).ToLocal(&obj))
433     return v8::Local<v8::Object>();
434
435   CJS_PrivateData* pPrivateData = new CJS_PrivateData;
436   pPrivateData->ObjDefID = nObjDefnID;
437
438   obj->SetAlignedPointerInInternalField(0, pPrivateData);
439   if (pObjDef->m_pConstructor)
440     pObjDef->m_pConstructor(
441         pJSContext, obj,
442         context->Global()->GetPrototype()->ToObject(context).ToLocalChecked());
443
444   return obj;
445 }
446
447 v8::Local<v8::Object> JS_GetStaticObj(IJS_Runtime* pJSRuntime, int nObjDefnID) {
448   v8::Isolate* isolate = (v8::Isolate*)pJSRuntime;
449   v8::Isolate::Scope isolate_scope(isolate);
450
451   CFX_PtrArray* pArray = (CFX_PtrArray*)isolate->GetData(g_embedderDataSlot);
452   if (!pArray)
453     return v8::Local<v8::Object>();
454
455   if (nObjDefnID < 0 || nObjDefnID >= pArray->GetSize())
456     return v8::Local<v8::Object>();
457   CJS_ObjDefintion* pObjDef = (CJS_ObjDefintion*)pArray->GetAt(nObjDefnID);
458   v8::Local<v8::Object> obj =
459       v8::Local<v8::Object>::New(isolate, pObjDef->m_StaticObj);
460   return obj;
461 }
462
463 void JS_SetThisObj(IJS_Runtime* pJSRuntime, int nThisObjID) {
464   // Do nothing.
465 }
466 v8::Local<v8::Object> JS_GetThisObj(IJS_Runtime* pJSRuntime) {
467   // Return the global object.
468   v8::Isolate* isolate = (v8::Isolate*)pJSRuntime;
469   v8::Isolate::Scope isolate_scope(isolate);
470
471   CFX_PtrArray* pArray = (CFX_PtrArray*)isolate->GetData(g_embedderDataSlot);
472   if (!pArray)
473     return v8::Local<v8::Object>();
474
475   v8::Local<v8::Context> context = isolate->GetCurrentContext();
476   return context->Global()->GetPrototype()->ToObject(context).ToLocalChecked();
477 }
478
479 int JS_GetObjDefnID(v8::Local<v8::Object> pObj) {
480   if (pObj.IsEmpty() || !pObj->InternalFieldCount())
481     return -1;
482   CJS_PrivateData* pPrivateData =
483       (CJS_PrivateData*)pObj->GetAlignedPointerFromInternalField(0);
484   if (pPrivateData)
485     return pPrivateData->ObjDefID;
486   return -1;
487 }
488
489 IJS_Runtime* JS_GetRuntime(v8::Local<v8::Object> pObj) {
490   if (pObj.IsEmpty())
491     return NULL;
492   v8::Local<v8::Context> context = pObj->CreationContext();
493   if (context.IsEmpty())
494     return NULL;
495   return context->GetIsolate();
496 }
497
498 int JS_GetObjDefnID(IJS_Runtime* pJSRuntime, const wchar_t* pObjName) {
499   v8::Isolate* isolate = (v8::Isolate*)pJSRuntime;
500   v8::Isolate::Scope isolate_scope(isolate);
501
502   CFX_PtrArray* pArray = (CFX_PtrArray*)isolate->GetData(g_embedderDataSlot);
503   if (!pArray)
504     return -1;
505
506   for (int i = 0; i < pArray->GetSize(); i++) {
507     CJS_ObjDefintion* pObjDef = (CJS_ObjDefintion*)pArray->GetAt(i);
508     if (FXSYS_wcscmp(pObjDef->objName, pObjName) == 0)
509       return i;
510   }
511   return -1;
512 }
513
514 void JS_Error(v8::Isolate* isolate, const CFX_WideString& message) {
515   // Conversion from pdfium's wchar_t wide-strings to v8's uint16_t
516   // wide-strings isn't handled by v8, so use UTF8 as a common
517   // intermediate format.
518   CFX_ByteString utf8_message = message.UTF8Encode();
519   isolate->ThrowException(v8::String::NewFromUtf8(isolate, utf8_message.c_str(),
520                                                   v8::NewStringType::kNormal)
521                               .ToLocalChecked());
522 }
523
524 unsigned JS_CalcHash(const wchar_t* main, unsigned nLen) {
525   return (unsigned)FX_HashCode_String_GetW(main, nLen);
526 }
527
528 unsigned JS_CalcHash(const wchar_t* main) {
529   return (unsigned)FX_HashCode_String_GetW(main, FXSYS_wcslen(main));
530 }
531 const wchar_t* JS_GetTypeof(v8::Local<v8::Value> pObj) {
532   if (pObj.IsEmpty())
533     return NULL;
534   if (pObj->IsString())
535     return VALUE_NAME_STRING;
536   if (pObj->IsNumber())
537     return VALUE_NAME_NUMBER;
538   if (pObj->IsBoolean())
539     return VALUE_NAME_BOOLEAN;
540   if (pObj->IsDate())
541     return VALUE_NAME_DATE;
542   if (pObj->IsObject())
543     return VALUE_NAME_OBJECT;
544   if (pObj->IsNull())
545     return VALUE_NAME_NULL;
546   if (pObj->IsUndefined())
547     return VALUE_NAME_UNDEFINED;
548   return NULL;
549 }
550 void JS_SetPrivate(v8::Local<v8::Object> pObj, void* p) {
551   JS_SetPrivate(NULL, pObj, p);
552 }
553
554 void* JS_GetPrivate(v8::Local<v8::Object> pObj) {
555   return JS_GetPrivate(NULL, pObj);
556 }
557
558 void JS_SetPrivate(IJS_Runtime* pJSRuntime,
559                    v8::Local<v8::Object> pObj,
560                    void* p) {
561   if (pObj.IsEmpty() || !pObj->InternalFieldCount())
562     return;
563   CJS_PrivateData* pPrivateData =
564       (CJS_PrivateData*)pObj->GetAlignedPointerFromInternalField(0);
565   if (!pPrivateData)
566     return;
567   pPrivateData->pPrivate = p;
568 }
569
570 void* JS_GetPrivate(IJS_Runtime* pJSRuntime, v8::Local<v8::Object> pObj) {
571   if (pObj.IsEmpty())
572     return NULL;
573   CJS_PrivateData* pPrivateData = NULL;
574   if (pObj->InternalFieldCount())
575     pPrivateData =
576         (CJS_PrivateData*)pObj->GetAlignedPointerFromInternalField(0);
577   else {
578     // It could be a global proxy object.
579     v8::Local<v8::Value> v = pObj->GetPrototype();
580     v8::Isolate* isolate = (v8::Isolate*)pJSRuntime;
581     v8::Local<v8::Context> context = isolate->GetCurrentContext();
582     if (v->IsObject())
583       pPrivateData = (CJS_PrivateData*)v->ToObject(context)
584                          .ToLocalChecked()
585                          ->GetAlignedPointerFromInternalField(0);
586   }
587   if (!pPrivateData)
588     return NULL;
589   return pPrivateData->pPrivate;
590 }
591
592 void JS_FreePrivate(void* pPrivateData) {
593   delete (CJS_PrivateData*)pPrivateData;
594 }
595
596 void JS_FreePrivate(v8::Local<v8::Object> pObj) {
597   if (pObj.IsEmpty() || !pObj->InternalFieldCount())
598     return;
599   JS_FreePrivate(pObj->GetAlignedPointerFromInternalField(0));
600   pObj->SetAlignedPointerInInternalField(0, NULL);
601 }
602
603 v8::Local<v8::Value> JS_GetObjectValue(v8::Local<v8::Object> pObj) {
604   return pObj;
605 }
606
607 v8::Local<v8::String> WSToJSString(IJS_Runtime* pJSRuntime,
608                                    const wchar_t* PropertyName,
609                                    int Len = -1) {
610   CFX_WideString ws = CFX_WideString(PropertyName, Len);
611   CFX_ByteString bs = ws.UTF8Encode();
612   if (!pJSRuntime)
613     pJSRuntime = v8::Isolate::GetCurrent();
614   return v8::String::NewFromUtf8(pJSRuntime, bs.c_str(),
615                                  v8::NewStringType::kNormal)
616       .ToLocalChecked();
617 }
618
619 v8::Local<v8::Value> JS_GetObjectElement(IJS_Runtime* pJSRuntime,
620                                          v8::Local<v8::Object> pObj,
621                                          const wchar_t* PropertyName) {
622   if (pObj.IsEmpty())
623     return v8::Local<v8::Value>();
624   v8::Local<v8::Value> val;
625   if (!pObj->Get(pJSRuntime->GetCurrentContext(),
626                  WSToJSString(pJSRuntime, PropertyName))
627            .ToLocal(&val))
628     return v8::Local<v8::Value>();
629   return val;
630 }
631
632 v8::Local<v8::Array> JS_GetObjectElementNames(IJS_Runtime* pJSRuntime,
633                                               v8::Local<v8::Object> pObj) {
634   if (pObj.IsEmpty())
635     return v8::Local<v8::Array>();
636   v8::Local<v8::Array> val;
637   if (!pObj->GetPropertyNames(pJSRuntime->GetCurrentContext()).ToLocal(&val))
638     return v8::Local<v8::Array>();
639   return val;
640 }
641
642 void JS_PutObjectString(IJS_Runtime* pJSRuntime,
643                         v8::Local<v8::Object> pObj,
644                         const wchar_t* PropertyName,
645                         const wchar_t* sValue)  // VT_string
646 {
647   if (pObj.IsEmpty())
648     return;
649   pObj->Set(pJSRuntime->GetCurrentContext(),
650             WSToJSString(pJSRuntime, PropertyName),
651             WSToJSString(pJSRuntime, sValue))
652       .FromJust();
653 }
654
655 void JS_PutObjectNumber(IJS_Runtime* pJSRuntime,
656                         v8::Local<v8::Object> pObj,
657                         const wchar_t* PropertyName,
658                         int nValue) {
659   if (pObj.IsEmpty())
660     return;
661   pObj->Set(pJSRuntime->GetCurrentContext(),
662             WSToJSString(pJSRuntime, PropertyName),
663             v8::Int32::New(pJSRuntime, nValue))
664       .FromJust();
665 }
666
667 void JS_PutObjectNumber(IJS_Runtime* pJSRuntime,
668                         v8::Local<v8::Object> pObj,
669                         const wchar_t* PropertyName,
670                         float fValue) {
671   if (pObj.IsEmpty())
672     return;
673   pObj->Set(pJSRuntime->GetCurrentContext(),
674             WSToJSString(pJSRuntime, PropertyName),
675             v8::Number::New(pJSRuntime, (double)fValue))
676       .FromJust();
677 }
678
679 void JS_PutObjectNumber(IJS_Runtime* pJSRuntime,
680                         v8::Local<v8::Object> pObj,
681                         const wchar_t* PropertyName,
682                         double dValue) {
683   if (pObj.IsEmpty())
684     return;
685   pObj->Set(pJSRuntime->GetCurrentContext(),
686             WSToJSString(pJSRuntime, PropertyName),
687             v8::Number::New(pJSRuntime, (double)dValue))
688       .FromJust();
689 }
690
691 void JS_PutObjectBoolean(IJS_Runtime* pJSRuntime,
692                          v8::Local<v8::Object> pObj,
693                          const wchar_t* PropertyName,
694                          bool bValue) {
695   if (pObj.IsEmpty())
696     return;
697   pObj->Set(pJSRuntime->GetCurrentContext(),
698             WSToJSString(pJSRuntime, PropertyName),
699             v8::Boolean::New(pJSRuntime, bValue))
700       .FromJust();
701 }
702
703 void JS_PutObjectObject(IJS_Runtime* pJSRuntime,
704                         v8::Local<v8::Object> pObj,
705                         const wchar_t* PropertyName,
706                         v8::Local<v8::Object> pPut) {
707   if (pObj.IsEmpty())
708     return;
709   pObj->Set(pJSRuntime->GetCurrentContext(),
710             WSToJSString(pJSRuntime, PropertyName), pPut)
711       .FromJust();
712 }
713
714 void JS_PutObjectNull(IJS_Runtime* pJSRuntime,
715                       v8::Local<v8::Object> pObj,
716                       const wchar_t* PropertyName) {
717   if (pObj.IsEmpty())
718     return;
719   pObj->Set(pJSRuntime->GetCurrentContext(),
720             WSToJSString(pJSRuntime, PropertyName), v8::Local<v8::Object>())
721       .FromJust();
722 }
723
724 v8::Local<v8::Array> JS_NewArray(IJS_Runtime* pJSRuntime) {
725   return v8::Array::New(pJSRuntime);
726 }
727
728 unsigned JS_PutArrayElement(IJS_Runtime* pJSRuntime,
729                             v8::Local<v8::Array> pArray,
730                             unsigned index,
731                             v8::Local<v8::Value> pValue,
732                             FXJSVALUETYPE eType) {
733   if (pArray.IsEmpty())
734     return 0;
735   if (pArray->Set(pJSRuntime->GetCurrentContext(), index, pValue).IsNothing())
736     return 0;
737   return 1;
738 }
739
740 v8::Local<v8::Value> JS_GetArrayElement(IJS_Runtime* pJSRuntime,
741                                         v8::Local<v8::Array> pArray,
742                                         unsigned index) {
743   if (pArray.IsEmpty())
744     return v8::Local<v8::Value>();
745   v8::Local<v8::Value> val;
746   if (!pArray->Get(pJSRuntime->GetCurrentContext(), index).ToLocal(&val))
747     return v8::Local<v8::Value>();
748   return val;
749 }
750
751 unsigned JS_GetArrayLength(v8::Local<v8::Array> pArray) {
752   if (pArray.IsEmpty())
753     return 0;
754   return pArray->Length();
755 }
756
757 v8::Local<v8::Value> JS_NewNumber(IJS_Runtime* pJSRuntime, int number) {
758   return v8::Int32::New(pJSRuntime, number);
759 }
760
761 v8::Local<v8::Value> JS_NewNumber(IJS_Runtime* pJSRuntime, double number) {
762   return v8::Number::New(pJSRuntime, number);
763 }
764
765 v8::Local<v8::Value> JS_NewNumber(IJS_Runtime* pJSRuntime, float number) {
766   return v8::Number::New(pJSRuntime, (float)number);
767 }
768
769 v8::Local<v8::Value> JS_NewBoolean(IJS_Runtime* pJSRuntime, bool b) {
770   return v8::Boolean::New(pJSRuntime, b);
771 }
772
773 v8::Local<v8::Value> JS_NewObject(IJS_Runtime* pJSRuntime,
774                                   v8::Local<v8::Object> pObj) {
775   if (pObj.IsEmpty())
776     return v8::Local<v8::Value>();
777   return pObj->Clone();
778 }
779
780 v8::Local<v8::Value> JS_NewObject2(IJS_Runtime* pJSRuntime,
781                                    v8::Local<v8::Array> pObj) {
782   if (pObj.IsEmpty())
783     return v8::Local<v8::Value>();
784   return pObj->Clone();
785 }
786
787 v8::Local<v8::Value> JS_NewString(IJS_Runtime* pJSRuntime,
788                                   const wchar_t* string) {
789   return WSToJSString(pJSRuntime, string);
790 }
791
792 v8::Local<v8::Value> JS_NewString(IJS_Runtime* pJSRuntime,
793                                   const wchar_t* string,
794                                   unsigned nLen) {
795   return WSToJSString(pJSRuntime, string, nLen);
796 }
797
798 v8::Local<v8::Value> JS_NewNull() {
799   return v8::Local<v8::Value>();
800 }
801
802 v8::Local<v8::Value> JS_NewDate(IJS_Runtime* pJSRuntime, double d) {
803   return v8::Date::New(pJSRuntime->GetCurrentContext(), d).ToLocalChecked();
804 }
805
806 v8::Local<v8::Value> JS_NewValue(IJS_Runtime* pJSRuntime) {
807   return v8::Local<v8::Value>();
808 }
809
810 v8::Local<v8::Value> JS_GetListValue(IJS_Runtime* pJSRuntime,
811                                      v8::Local<v8::Value> pList,
812                                      int index) {
813   v8::Local<v8::Context> context = pJSRuntime->GetCurrentContext();
814   if (!pList.IsEmpty() && pList->IsObject()) {
815     v8::Local<v8::Object> obj;
816     if (pList->ToObject(context).ToLocal(&obj)) {
817       v8::Local<v8::Value> val;
818       if (obj->Get(context, index).ToLocal(&val))
819         return val;
820     }
821   }
822   return v8::Local<v8::Value>();
823 }
824
825 int JS_ToInt32(IJS_Runtime* pJSRuntime, v8::Local<v8::Value> pValue) {
826   if (pValue.IsEmpty())
827     return 0;
828   v8::Local<v8::Context> context = pJSRuntime->GetCurrentContext();
829   return pValue->ToInt32(context).ToLocalChecked()->Value();
830 }
831
832 bool JS_ToBoolean(IJS_Runtime* pJSRuntime, v8::Local<v8::Value> pValue) {
833   if (pValue.IsEmpty())
834     return false;
835   v8::Local<v8::Context> context = pJSRuntime->GetCurrentContext();
836   return pValue->ToBoolean(context).ToLocalChecked()->Value();
837 }
838
839 double JS_ToNumber(IJS_Runtime* pJSRuntime, v8::Local<v8::Value> pValue) {
840   if (pValue.IsEmpty())
841     return 0.0;
842   v8::Local<v8::Context> context = pJSRuntime->GetCurrentContext();
843   return pValue->ToNumber(context).ToLocalChecked()->Value();
844 }
845
846 v8::Local<v8::Object> JS_ToObject(IJS_Runtime* pJSRuntime,
847                                   v8::Local<v8::Value> pValue) {
848   if (pValue.IsEmpty())
849     return v8::Local<v8::Object>();
850   v8::Local<v8::Context> context = pJSRuntime->GetCurrentContext();
851   return pValue->ToObject(context).ToLocalChecked();
852 }
853
854 CFX_WideString JS_ToString(IJS_Runtime* pJSRuntime,
855                            v8::Local<v8::Value> pValue) {
856   if (pValue.IsEmpty())
857     return L"";
858   v8::Local<v8::Context> context = pJSRuntime->GetCurrentContext();
859   v8::String::Utf8Value s(pValue->ToString(context).ToLocalChecked());
860   return CFX_WideString::FromUTF8(*s, s.length());
861 }
862
863 v8::Local<v8::Array> JS_ToArray(IJS_Runtime* pJSRuntime,
864                                 v8::Local<v8::Value> pValue) {
865   if (pValue.IsEmpty())
866     return v8::Local<v8::Array>();
867   v8::Local<v8::Context> context = pJSRuntime->GetCurrentContext();
868   return v8::Local<v8::Array>::Cast(pValue->ToObject(context).ToLocalChecked());
869 }
870
871 void JS_ValueCopy(v8::Local<v8::Value>& pTo, v8::Local<v8::Value> pFrom) {
872   pTo = pFrom;
873 }
874
875 // JavaScript time implement begin.
876
877 double _getLocalTZA() {
878   if (!FSDK_IsSandBoxPolicyEnabled(FPDF_POLICY_MACHINETIME_ACCESS))
879     return 0;
880   time_t t = 0;
881   time(&t);
882   localtime(&t);
883 #if _MSC_VER >= 1900
884   // In gcc and in Visual Studio prior to VS 2015 'timezone' is a global
885   // variable declared in time.h. That variable was deprecated and in VS 2015
886   // is removed, with _get_timezone replacing it.
887   long timezone = 0;
888   _get_timezone(&timezone);
889 #endif
890   return (double)(-(timezone * 1000));
891 }
892
893 int _getDaylightSavingTA(double d) {
894   if (!FSDK_IsSandBoxPolicyEnabled(FPDF_POLICY_MACHINETIME_ACCESS))
895     return 0;
896   time_t t = (time_t)(d / 1000);
897   struct tm* tmp = localtime(&t);
898   if (tmp == NULL)
899     return 0;
900   if (tmp->tm_isdst > 0)
901     // One hour.
902     return (int)60 * 60 * 1000;
903   return 0;
904 }
905
906 double _Mod(double x, double y) {
907   double r = fmod(x, y);
908   if (r < 0)
909     r += y;
910   return r;
911 }
912
913 int _isfinite(double v) {
914 #if _MSC_VER
915   return ::_finite(v);
916 #else
917   return std::fabs(v) < std::numeric_limits<double>::max();
918 #endif
919 }
920
921 double _toInteger(double n) {
922   return (n >= 0) ? FXSYS_floor(n) : -FXSYS_floor(-n);
923 }
924
925 bool _isLeapYear(int year) {
926   return (year % 4 == 0) && ((year % 100 != 0) || (year % 400 != 0));
927 }
928
929 int _DayFromYear(int y) {
930   return (int)(365 * (y - 1970.0) + FXSYS_floor((y - 1969.0) / 4) -
931                FXSYS_floor((y - 1901.0) / 100) +
932                FXSYS_floor((y - 1601.0) / 400));
933 }
934
935 double _TimeFromYear(int y) {
936   return ((double)86400000) * _DayFromYear(y);
937 }
938
939 double _TimeFromYearMonth(int y, int m) {
940   static int daysMonth[12] = {0,   31,  59,  90,  120, 151,
941                               181, 212, 243, 273, 304, 334};
942   static int leapDaysMonth[12] = {0,   31,  60,  91,  121, 152,
943                                   182, 213, 244, 274, 305, 335};
944   int* pMonth = daysMonth;
945   if (_isLeapYear(y))
946     pMonth = leapDaysMonth;
947   return _TimeFromYear(y) + ((double)pMonth[m]) * 86400000;
948 }
949
950 int _Day(double t) {
951   return (int)FXSYS_floor(t / 86400000);
952 }
953
954 int _YearFromTime(double t) {
955   // estimate the time.
956   int y = 1970 + (int)(t / (365.0 * 86400000));
957   if (_TimeFromYear(y) <= t) {
958     while (_TimeFromYear(y + 1) <= t)
959       y++;
960   } else
961     while (_TimeFromYear(y - 1) > t)
962       y--;
963   return y;
964 }
965
966 int _DayWithinYear(double t) {
967   int year = _YearFromTime(t);
968   int day = _Day(t);
969   return day - _DayFromYear(year);
970 }
971
972 int _MonthFromTime(double t) {
973   int day = _DayWithinYear(t);
974   int year = _YearFromTime(t);
975   if (0 <= day && day < 31)
976     return 0;
977   if (31 <= day && day < 59 + _isLeapYear(year))
978     return 1;
979   if ((59 + _isLeapYear(year)) <= day && day < (90 + _isLeapYear(year)))
980     return 2;
981   if ((90 + _isLeapYear(year)) <= day && day < (120 + _isLeapYear(year)))
982     return 3;
983   if ((120 + _isLeapYear(year)) <= day && day < (151 + _isLeapYear(year)))
984     return 4;
985   if ((151 + _isLeapYear(year)) <= day && day < (181 + _isLeapYear(year)))
986     return 5;
987   if ((181 + _isLeapYear(year)) <= day && day < (212 + _isLeapYear(year)))
988     return 6;
989   if ((212 + _isLeapYear(year)) <= day && day < (243 + _isLeapYear(year)))
990     return 7;
991   if ((243 + _isLeapYear(year)) <= day && day < (273 + _isLeapYear(year)))
992     return 8;
993   if ((273 + _isLeapYear(year)) <= day && day < (304 + _isLeapYear(year)))
994     return 9;
995   if ((304 + _isLeapYear(year)) <= day && day < (334 + _isLeapYear(year)))
996     return 10;
997   if ((334 + _isLeapYear(year)) <= day && day < (365 + _isLeapYear(year)))
998     return 11;
999
1000   return -1;
1001 }
1002
1003 int _DateFromTime(double t) {
1004   int day = _DayWithinYear(t);
1005   int year = _YearFromTime(t);
1006   bool leap = _isLeapYear(year);
1007   int month = _MonthFromTime(t);
1008   switch (month) {
1009     case 0:
1010       return day + 1;
1011     case 1:
1012       return day - 30;
1013     case 2:
1014       return day - 58 - leap;
1015     case 3:
1016       return day - 89 - leap;
1017     case 4:
1018       return day - 119 - leap;
1019     case 5:
1020       return day - 150 - leap;
1021     case 6:
1022       return day - 180 - leap;
1023     case 7:
1024       return day - 211 - leap;
1025     case 8:
1026       return day - 242 - leap;
1027     case 9:
1028       return day - 272 - leap;
1029     case 10:
1030       return day - 303 - leap;
1031     case 11:
1032       return day - 333 - leap;
1033     default:
1034       return 0;
1035   }
1036 }
1037
1038 double JS_GetDateTime() {
1039   if (!FSDK_IsSandBoxPolicyEnabled(FPDF_POLICY_MACHINETIME_ACCESS))
1040     return 0;
1041   time_t t = time(NULL);
1042   struct tm* pTm = localtime(&t);
1043
1044   int year = pTm->tm_year + 1900;
1045   double t1 = _TimeFromYear(year);
1046
1047   return t1 + pTm->tm_yday * 86400000.0 + pTm->tm_hour * 3600000.0 +
1048          pTm->tm_min * 60000.0 + pTm->tm_sec * 1000.0;
1049 }
1050
1051 int JS_GetYearFromTime(double dt) {
1052   return _YearFromTime(dt);
1053 }
1054
1055 int JS_GetMonthFromTime(double dt) {
1056   return _MonthFromTime(dt);
1057 }
1058
1059 int JS_GetDayFromTime(double dt) {
1060   return _DateFromTime(dt);
1061 }
1062
1063 int JS_GetHourFromTime(double dt) {
1064   return (int)_Mod(FXSYS_floor((double)(dt / (60 * 60 * 1000))), 24);
1065 }
1066
1067 int JS_GetMinFromTime(double dt) {
1068   return (int)_Mod(FXSYS_floor((double)(dt / (60 * 1000))), 60);
1069 }
1070
1071 int JS_GetSecFromTime(double dt) {
1072   return (int)_Mod(FXSYS_floor((double)(dt / 1000)), 60);
1073 }
1074
1075 double JS_DateParse(const wchar_t* string) {
1076   v8::Isolate* pIsolate = v8::Isolate::GetCurrent();
1077   v8::Isolate::Scope isolate_scope(pIsolate);
1078   v8::HandleScope scope(pIsolate);
1079
1080   v8::Local<v8::Context> context = pIsolate->GetCurrentContext();
1081
1082   // Use the built-in object method.
1083   v8::Local<v8::Value> v =
1084       context->Global()
1085           ->Get(context, v8::String::NewFromUtf8(pIsolate, "Date",
1086                                                  v8::NewStringType::kNormal)
1087                              .ToLocalChecked())
1088           .ToLocalChecked();
1089   if (v->IsObject()) {
1090     v8::Local<v8::Object> o = v->ToObject(context).ToLocalChecked();
1091     v = o->Get(context, v8::String::NewFromUtf8(pIsolate, "parse",
1092                                                 v8::NewStringType::kNormal)
1093                             .ToLocalChecked())
1094             .ToLocalChecked();
1095     if (v->IsFunction()) {
1096       v8::Local<v8::Function> funC = v8::Local<v8::Function>::Cast(v);
1097
1098       const int argc = 1;
1099       v8::Local<v8::String> timeStr = WSToJSString(pIsolate, string);
1100       v8::Local<v8::Value> argv[argc] = {timeStr};
1101       v = funC->Call(context, context->Global(), argc, argv).ToLocalChecked();
1102       if (v->IsNumber()) {
1103         double date = v->ToNumber(context).ToLocalChecked()->Value();
1104         if (!_isfinite(date))
1105           return date;
1106         return date + _getLocalTZA() + _getDaylightSavingTA(date);
1107       }
1108     }
1109   }
1110   return 0;
1111 }
1112
1113 double JS_MakeDay(int nYear, int nMonth, int nDate) {
1114   if (!_isfinite(nYear) || !_isfinite(nMonth) || !_isfinite(nDate))
1115     return GetNan();
1116   double y = _toInteger(nYear);
1117   double m = _toInteger(nMonth);
1118   double dt = _toInteger(nDate);
1119   double ym = y + FXSYS_floor((double)m / 12);
1120   double mn = _Mod(m, 12);
1121
1122   double t = _TimeFromYearMonth((int)ym, (int)mn);
1123
1124   if (_YearFromTime(t) != ym || _MonthFromTime(t) != mn ||
1125       _DateFromTime(t) != 1)
1126     return GetNan();
1127   return _Day(t) + dt - 1;
1128 }
1129
1130 double JS_MakeTime(int nHour, int nMin, int nSec, int nMs) {
1131   if (!_isfinite(nHour) || !_isfinite(nMin) || !_isfinite(nSec) ||
1132       !_isfinite(nMs))
1133     return GetNan();
1134
1135   double h = _toInteger(nHour);
1136   double m = _toInteger(nMin);
1137   double s = _toInteger(nSec);
1138   double milli = _toInteger(nMs);
1139
1140   return h * 3600000 + m * 60000 + s * 1000 + milli;
1141 }
1142
1143 double JS_MakeDate(double day, double time) {
1144   if (!_isfinite(day) || !_isfinite(time))
1145     return GetNan();
1146
1147   return day * 86400000 + time;
1148 }
1149
1150 bool JS_PortIsNan(double d) {
1151   return d != d;
1152 }
1153
1154 double JS_LocalTime(double d) {
1155   return JS_GetDateTime() + _getDaylightSavingTA(d);
1156 }
1157
1158 // JavaScript time implement End.