• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "global.h"
8 
9 #include "JS_Context.h"
10 #include "JS_Define.h"
11 #include "JS_EventHandler.h"
12 #include "JS_GlobalData.h"
13 #include "JS_Object.h"
14 #include "JS_Value.h"
15 #include "core/include/fxcrt/fx_ext.h"
16 #include "fpdfsdk/include/javascript/IJavaScript.h"
17 #include "resource.h"
18 
19 /* ---------------------------- global ---------------------------- */
20 
21 // Helper class for compile-time calculation of hash values in order to
22 // avoid having global object initializers.
23 template <unsigned ACC, wchar_t... Ns>
24 struct CHash;
25 
26 // Only needed to hash single-character strings.
27 template <wchar_t N>
28 struct CHash<N> {
29   static const unsigned value = N;
30 };
31 
32 template <unsigned ACC, wchar_t N>
33 struct CHash<ACC, N> {
34   static const unsigned value = (ACC * 1313LLU + N) & 0xFFFFFFFF;
35 };
36 
37 template <unsigned ACC, wchar_t N, wchar_t... Ns>
38 struct CHash<ACC, N, Ns...> {
39   static const unsigned value = CHash<CHash<ACC, N>::value, Ns...>::value;
40 };
41 
42 const unsigned int JSCONST_nStringHash =
43     CHash<'s', 't', 'r', 'i', 'n', 'g'>::value;
44 const unsigned int JSCONST_nNumberHash =
45     CHash<'n', 'u', 'm', 'b', 'e', 'r'>::value;
46 const unsigned int JSCONST_nBoolHash =
47     CHash<'b', 'o', 'o', 'l', 'e', 'a', 'n'>::value;
48 const unsigned int JSCONST_nDateHash = CHash<'d', 'a', 't', 'e'>::value;
49 const unsigned int JSCONST_nObjectHash =
50     CHash<'o', 'b', 'j', 'e', 'c', 't'>::value;
51 const unsigned int JSCONST_nFXobjHash = CHash<'f', 'x', 'o', 'b', 'j'>::value;
52 const unsigned int JSCONST_nNullHash = CHash<'n', 'u', 'l', 'l'>::value;
53 const unsigned int JSCONST_nUndefHash =
54     CHash<'u', 'n', 'd', 'e', 'f', 'i', 'n', 'e', 'd'>::value;
55 
JS_CalcHash(const wchar_t * main)56 static unsigned JS_CalcHash(const wchar_t* main) {
57   return (unsigned)FX_HashCode_String_GetW(main, FXSYS_wcslen(main));
58 }
59 
60 #ifdef _DEBUG
61 class HashVerify {
62  public:
63   HashVerify();
64 } g_hashVerify;
65 
HashVerify()66 HashVerify::HashVerify() {
67   ASSERT(JSCONST_nStringHash == JS_CalcHash(kFXJSValueNameString));
68   ASSERT(JSCONST_nNumberHash == JS_CalcHash(kFXJSValueNameNumber));
69   ASSERT(JSCONST_nBoolHash == JS_CalcHash(kFXJSValueNameBoolean));
70   ASSERT(JSCONST_nDateHash == JS_CalcHash(kFXJSValueNameDate));
71   ASSERT(JSCONST_nObjectHash == JS_CalcHash(kFXJSValueNameObject));
72   ASSERT(JSCONST_nFXobjHash == JS_CalcHash(kFXJSValueNameFxobj));
73   ASSERT(JSCONST_nNullHash == JS_CalcHash(kFXJSValueNameNull));
74   ASSERT(JSCONST_nUndefHash == JS_CalcHash(kFXJSValueNameUndefined));
75 }
76 #endif
77 
78 BEGIN_JS_STATIC_CONST(CJS_Global)
79 END_JS_STATIC_CONST()
80 
81 BEGIN_JS_STATIC_PROP(CJS_Global)
82 END_JS_STATIC_PROP()
83 
84 BEGIN_JS_STATIC_METHOD(CJS_Global)
85 JS_STATIC_METHOD_ENTRY(setPersistent)
86 END_JS_STATIC_METHOD()
87 
88 IMPLEMENT_SPECIAL_JS_CLASS(CJS_Global, JSGlobalAlternate, global);
89 
InitInstance(IJS_Runtime * pIRuntime)90 void CJS_Global::InitInstance(IJS_Runtime* pIRuntime) {
91   CJS_Runtime* pRuntime = static_cast<CJS_Runtime*>(pIRuntime);
92   JSGlobalAlternate* pGlobal =
93       static_cast<JSGlobalAlternate*>(GetEmbedObject());
94   pGlobal->Initial(pRuntime->GetReaderApp());
95 }
96 
JSGlobalAlternate(CJS_Object * pJSObject)97 JSGlobalAlternate::JSGlobalAlternate(CJS_Object* pJSObject)
98     : CJS_EmbedObj(pJSObject), m_pApp(NULL) {
99 }
100 
~JSGlobalAlternate()101 JSGlobalAlternate::~JSGlobalAlternate() {
102   DestroyGlobalPersisitentVariables();
103   m_pGlobalData->Release();
104 }
105 
Initial(CPDFDoc_Environment * pApp)106 void JSGlobalAlternate::Initial(CPDFDoc_Environment* pApp) {
107   m_pApp = pApp;
108   m_pGlobalData = CJS_GlobalData::GetRetainedInstance(pApp);
109   UpdateGlobalPersistentVariables();
110 }
111 
QueryProperty(const FX_WCHAR * propname)112 FX_BOOL JSGlobalAlternate::QueryProperty(const FX_WCHAR* propname) {
113   return CFX_WideString(propname) != L"setPersistent";
114 }
115 
DelProperty(IJS_Context * cc,const FX_WCHAR * propname,CFX_WideString & sError)116 FX_BOOL JSGlobalAlternate::DelProperty(IJS_Context* cc,
117                                        const FX_WCHAR* propname,
118                                        CFX_WideString& sError) {
119   auto it = m_mapGlobal.find(CFX_ByteString::FromUnicode(propname));
120   if (it == m_mapGlobal.end())
121     return FALSE;
122 
123   it->second->bDeleted = TRUE;
124   return TRUE;
125 }
126 
DoProperty(IJS_Context * cc,const FX_WCHAR * propname,CJS_PropValue & vp,CFX_WideString & sError)127 FX_BOOL JSGlobalAlternate::DoProperty(IJS_Context* cc,
128                                       const FX_WCHAR* propname,
129                                       CJS_PropValue& vp,
130                                       CFX_WideString& sError) {
131   if (vp.IsSetting()) {
132     CFX_ByteString sPropName = CFX_ByteString::FromUnicode(propname);
133     switch (vp.GetType()) {
134       case CJS_Value::VT_number: {
135         double dData;
136         vp >> dData;
137         return SetGlobalVariables(sPropName, JS_GLOBALDATA_TYPE_NUMBER, dData,
138                                   false, "", v8::Local<v8::Object>(), FALSE);
139       }
140       case CJS_Value::VT_boolean: {
141         bool bData;
142         vp >> bData;
143         return SetGlobalVariables(sPropName, JS_GLOBALDATA_TYPE_BOOLEAN, 0,
144                                   bData, "", v8::Local<v8::Object>(), FALSE);
145       }
146       case CJS_Value::VT_string: {
147         CFX_ByteString sData;
148         vp >> sData;
149         return SetGlobalVariables(sPropName, JS_GLOBALDATA_TYPE_STRING, 0,
150                                   false, sData, v8::Local<v8::Object>(), FALSE);
151       }
152       case CJS_Value::VT_object: {
153         v8::Local<v8::Object> pData;
154         vp >> pData;
155         return SetGlobalVariables(sPropName, JS_GLOBALDATA_TYPE_OBJECT, 0,
156                                   false, "", pData, FALSE);
157       }
158       case CJS_Value::VT_null: {
159         return SetGlobalVariables(sPropName, JS_GLOBALDATA_TYPE_NULL, 0, false,
160                                   "", v8::Local<v8::Object>(), FALSE);
161       }
162       case CJS_Value::VT_undefined: {
163         DelProperty(cc, propname, sError);
164         return TRUE;
165       }
166       default:
167         break;
168     }
169   } else {
170     auto it = m_mapGlobal.find(CFX_ByteString::FromUnicode(propname));
171     if (it == m_mapGlobal.end()) {
172       vp.SetNull();
173       return TRUE;
174     }
175     JSGlobalData* pData = it->second;
176     if (pData->bDeleted) {
177       vp.SetNull();
178       return TRUE;
179     }
180     switch (pData->nType) {
181       case JS_GLOBALDATA_TYPE_NUMBER:
182         vp << pData->dData;
183         return TRUE;
184       case JS_GLOBALDATA_TYPE_BOOLEAN:
185         vp << pData->bData;
186         return TRUE;
187       case JS_GLOBALDATA_TYPE_STRING:
188         vp << pData->sData;
189         return TRUE;
190       case JS_GLOBALDATA_TYPE_OBJECT: {
191         v8::Local<v8::Object> obj = v8::Local<v8::Object>::New(
192             vp.GetJSRuntime()->GetIsolate(), pData->pData);
193         vp << obj;
194         return TRUE;
195       }
196       case JS_GLOBALDATA_TYPE_NULL:
197         vp.SetNull();
198         return TRUE;
199       default:
200         break;
201     }
202   }
203   return FALSE;
204 }
205 
setPersistent(IJS_Context * cc,const std::vector<CJS_Value> & params,CJS_Value & vRet,CFX_WideString & sError)206 FX_BOOL JSGlobalAlternate::setPersistent(IJS_Context* cc,
207                                          const std::vector<CJS_Value>& params,
208                                          CJS_Value& vRet,
209                                          CFX_WideString& sError) {
210   CJS_Context* pContext = static_cast<CJS_Context*>(cc);
211   if (params.size() != 2) {
212     sError = JSGetStringFromID(pContext, IDS_STRING_JSPARAMERROR);
213     return FALSE;
214   }
215 
216   auto it = m_mapGlobal.find(params[0].ToCFXByteString());
217   if (it != m_mapGlobal.end()) {
218     JSGlobalData* pData = it->second;
219     if (!pData->bDeleted) {
220       pData->bPersistent = params[1].ToBool();
221       return TRUE;
222     }
223   }
224 
225   sError = JSGetStringFromID(pContext, IDS_STRING_JSNOGLOBAL);
226   return FALSE;
227 }
228 
UpdateGlobalPersistentVariables()229 void JSGlobalAlternate::UpdateGlobalPersistentVariables() {
230   for (int i = 0, sz = m_pGlobalData->GetSize(); i < sz; i++) {
231     CJS_GlobalData_Element* pData = m_pGlobalData->GetAt(i);
232     switch (pData->data.nType) {
233       case JS_GLOBALDATA_TYPE_NUMBER:
234         SetGlobalVariables(pData->data.sKey, JS_GLOBALDATA_TYPE_NUMBER,
235                            pData->data.dData, false, "",
236                            v8::Local<v8::Object>(), pData->bPersistent == 1);
237         FXJS_PutObjectNumber(NULL, m_pJSObject->ToV8Object(),
238                              pData->data.sKey.UTF8Decode().c_str(),
239                              pData->data.dData);
240         break;
241       case JS_GLOBALDATA_TYPE_BOOLEAN:
242         SetGlobalVariables(pData->data.sKey, JS_GLOBALDATA_TYPE_BOOLEAN, 0,
243                            (bool)(pData->data.bData == 1), "",
244                            v8::Local<v8::Object>(), pData->bPersistent == 1);
245         FXJS_PutObjectBoolean(NULL, m_pJSObject->ToV8Object(),
246                               pData->data.sKey.UTF8Decode().c_str(),
247                               (bool)(pData->data.bData == 1));
248         break;
249       case JS_GLOBALDATA_TYPE_STRING:
250         SetGlobalVariables(pData->data.sKey, JS_GLOBALDATA_TYPE_STRING, 0,
251                            false, pData->data.sData, v8::Local<v8::Object>(),
252                            pData->bPersistent == 1);
253         FXJS_PutObjectString(NULL, m_pJSObject->ToV8Object(),
254                              pData->data.sKey.UTF8Decode().c_str(),
255                              pData->data.sData.UTF8Decode().c_str());
256         break;
257       case JS_GLOBALDATA_TYPE_OBJECT: {
258         v8::Isolate* pRuntime = m_pJSObject->ToV8Object()->GetIsolate();
259         v8::Local<v8::Object> pObj = FXJS_NewFxDynamicObj(pRuntime, NULL, -1);
260 
261         PutObjectProperty(pObj, &pData->data);
262 
263         SetGlobalVariables(pData->data.sKey, JS_GLOBALDATA_TYPE_OBJECT, 0,
264                            false, "", pObj, pData->bPersistent == 1);
265         FXJS_PutObjectObject(NULL, m_pJSObject->ToV8Object(),
266                              pData->data.sKey.UTF8Decode().c_str(), pObj);
267 
268       } break;
269       case JS_GLOBALDATA_TYPE_NULL:
270         SetGlobalVariables(pData->data.sKey, JS_GLOBALDATA_TYPE_NULL, 0, false,
271                            "", v8::Local<v8::Object>(),
272                            pData->bPersistent == 1);
273         FXJS_PutObjectNull(NULL, m_pJSObject->ToV8Object(),
274                            pData->data.sKey.UTF8Decode().c_str());
275         break;
276     }
277   }
278 }
279 
CommitGlobalPersisitentVariables(IJS_Context * cc)280 void JSGlobalAlternate::CommitGlobalPersisitentVariables(IJS_Context* cc) {
281   for (auto it = m_mapGlobal.begin(); it != m_mapGlobal.end(); ++it) {
282     CFX_ByteString name = it->first;
283     JSGlobalData* pData = it->second;
284     if (pData->bDeleted) {
285       m_pGlobalData->DeleteGlobalVariable(name);
286     } else {
287       switch (pData->nType) {
288         case JS_GLOBALDATA_TYPE_NUMBER:
289           m_pGlobalData->SetGlobalVariableNumber(name, pData->dData);
290           m_pGlobalData->SetGlobalVariablePersistent(name, pData->bPersistent);
291           break;
292         case JS_GLOBALDATA_TYPE_BOOLEAN:
293           m_pGlobalData->SetGlobalVariableBoolean(name, pData->bData);
294           m_pGlobalData->SetGlobalVariablePersistent(name, pData->bPersistent);
295           break;
296         case JS_GLOBALDATA_TYPE_STRING:
297           m_pGlobalData->SetGlobalVariableString(name, pData->sData);
298           m_pGlobalData->SetGlobalVariablePersistent(name, pData->bPersistent);
299           break;
300         case JS_GLOBALDATA_TYPE_OBJECT:
301           {
302             CJS_GlobalVariableArray array;
303             v8::Local<v8::Object> obj = v8::Local<v8::Object>::New(
304                 GetJSObject()->GetIsolate(), pData->pData);
305             ObjectToArray(cc, obj, array);
306             m_pGlobalData->SetGlobalVariableObject(name, array);
307             m_pGlobalData->SetGlobalVariablePersistent(name,
308                                                        pData->bPersistent);
309           }
310           break;
311         case JS_GLOBALDATA_TYPE_NULL:
312           m_pGlobalData->SetGlobalVariableNull(name);
313           m_pGlobalData->SetGlobalVariablePersistent(name, pData->bPersistent);
314           break;
315       }
316     }
317   }
318 }
319 
ObjectToArray(IJS_Context * cc,v8::Local<v8::Object> pObj,CJS_GlobalVariableArray & array)320 void JSGlobalAlternate::ObjectToArray(IJS_Context* cc,
321                                       v8::Local<v8::Object> pObj,
322                                       CJS_GlobalVariableArray& array) {
323   v8::Isolate* isolate = pObj->GetIsolate();
324   CJS_Runtime* pRuntime = CJS_Runtime::FromContext(cc);
325 
326   v8::Local<v8::Array> pKeyList = FXJS_GetObjectElementNames(isolate, pObj);
327   int nObjElements = pKeyList->Length();
328   for (int i = 0; i < nObjElements; i++) {
329     CFX_WideString ws =
330         FXJS_ToString(isolate, FXJS_GetArrayElement(isolate, pKeyList, i));
331     CFX_ByteString sKey = ws.UTF8Encode();
332 
333     v8::Local<v8::Value> v = FXJS_GetObjectElement(isolate, pObj, ws.c_str());
334     switch (GET_VALUE_TYPE(v)) {
335       case CJS_Value::VT_number: {
336         CJS_KeyValue* pObjElement = new CJS_KeyValue;
337         pObjElement->nType = JS_GLOBALDATA_TYPE_NUMBER;
338         pObjElement->sKey = sKey;
339         pObjElement->dData = FXJS_ToNumber(isolate, v);
340         array.Add(pObjElement);
341       } break;
342       case CJS_Value::VT_boolean: {
343         CJS_KeyValue* pObjElement = new CJS_KeyValue;
344         pObjElement->nType = JS_GLOBALDATA_TYPE_BOOLEAN;
345         pObjElement->sKey = sKey;
346         pObjElement->dData = FXJS_ToBoolean(isolate, v);
347         array.Add(pObjElement);
348       } break;
349       case CJS_Value::VT_string: {
350         CFX_ByteString sValue =
351             CJS_Value(pRuntime, v, CJS_Value::VT_string).ToCFXByteString();
352         CJS_KeyValue* pObjElement = new CJS_KeyValue;
353         pObjElement->nType = JS_GLOBALDATA_TYPE_STRING;
354         pObjElement->sKey = sKey;
355         pObjElement->sData = sValue;
356         array.Add(pObjElement);
357       } break;
358       case CJS_Value::VT_object: {
359         CJS_KeyValue* pObjElement = new CJS_KeyValue;
360         pObjElement->nType = JS_GLOBALDATA_TYPE_OBJECT;
361         pObjElement->sKey = sKey;
362         ObjectToArray(cc, FXJS_ToObject(isolate, v), pObjElement->objData);
363         array.Add(pObjElement);
364       } break;
365       case CJS_Value::VT_null: {
366         CJS_KeyValue* pObjElement = new CJS_KeyValue;
367         pObjElement->nType = JS_GLOBALDATA_TYPE_NULL;
368         pObjElement->sKey = sKey;
369         array.Add(pObjElement);
370       } break;
371       default:
372         break;
373     }
374   }
375 }
376 
PutObjectProperty(v8::Local<v8::Object> pObj,CJS_KeyValue * pData)377 void JSGlobalAlternate::PutObjectProperty(v8::Local<v8::Object> pObj,
378                                           CJS_KeyValue* pData) {
379   for (int i = 0, sz = pData->objData.Count(); i < sz; i++) {
380     CJS_KeyValue* pObjData = pData->objData.GetAt(i);
381     switch (pObjData->nType) {
382       case JS_GLOBALDATA_TYPE_NUMBER:
383         FXJS_PutObjectNumber(NULL, pObj, pObjData->sKey.UTF8Decode().c_str(),
384                              pObjData->dData);
385         break;
386       case JS_GLOBALDATA_TYPE_BOOLEAN:
387         FXJS_PutObjectBoolean(NULL, pObj, pObjData->sKey.UTF8Decode().c_str(),
388                               pObjData->bData == 1);
389         break;
390       case JS_GLOBALDATA_TYPE_STRING:
391         FXJS_PutObjectString(NULL, pObj, pObjData->sKey.UTF8Decode().c_str(),
392                              pObjData->sData.UTF8Decode().c_str());
393         break;
394       case JS_GLOBALDATA_TYPE_OBJECT: {
395         v8::Isolate* pRuntime = m_pJSObject->ToV8Object()->GetIsolate();
396         v8::Local<v8::Object> pNewObj =
397             FXJS_NewFxDynamicObj(pRuntime, NULL, -1);
398         PutObjectProperty(pNewObj, pObjData);
399         FXJS_PutObjectObject(NULL, pObj, pObjData->sKey.UTF8Decode().c_str(),
400                              pNewObj);
401       } break;
402       case JS_GLOBALDATA_TYPE_NULL:
403         FXJS_PutObjectNull(NULL, pObj, pObjData->sKey.UTF8Decode().c_str());
404         break;
405     }
406   }
407 }
408 
DestroyGlobalPersisitentVariables()409 void JSGlobalAlternate::DestroyGlobalPersisitentVariables() {
410   for (const auto& pair : m_mapGlobal) {
411     delete pair.second;
412   }
413   m_mapGlobal.clear();
414 }
415 
SetGlobalVariables(const FX_CHAR * propname,int nType,double dData,bool bData,const CFX_ByteString & sData,v8::Local<v8::Object> pData,bool bDefaultPersistent)416 FX_BOOL JSGlobalAlternate::SetGlobalVariables(const FX_CHAR* propname,
417                                               int nType,
418                                               double dData,
419                                               bool bData,
420                                               const CFX_ByteString& sData,
421                                               v8::Local<v8::Object> pData,
422                                               bool bDefaultPersistent) {
423   if (!propname)
424     return FALSE;
425 
426   auto it = m_mapGlobal.find(propname);
427   if (it != m_mapGlobal.end()) {
428     JSGlobalData* pTemp = it->second;
429     if (pTemp->bDeleted || pTemp->nType != nType) {
430       pTemp->dData = 0;
431       pTemp->bData = 0;
432       pTemp->sData = "";
433       pTemp->nType = nType;
434     }
435 
436     pTemp->bDeleted = FALSE;
437     switch (nType) {
438       case JS_GLOBALDATA_TYPE_NUMBER: {
439         pTemp->dData = dData;
440       } break;
441       case JS_GLOBALDATA_TYPE_BOOLEAN: {
442         pTemp->bData = bData;
443       } break;
444       case JS_GLOBALDATA_TYPE_STRING: {
445         pTemp->sData = sData;
446       } break;
447       case JS_GLOBALDATA_TYPE_OBJECT: {
448         pTemp->pData.Reset(pData->GetIsolate(), pData);
449       } break;
450       case JS_GLOBALDATA_TYPE_NULL:
451         break;
452       default:
453         return FALSE;
454     }
455     return TRUE;
456   }
457 
458   JSGlobalData* pNewData = NULL;
459 
460   switch (nType) {
461     case JS_GLOBALDATA_TYPE_NUMBER: {
462       pNewData = new JSGlobalData;
463       pNewData->nType = JS_GLOBALDATA_TYPE_NUMBER;
464       pNewData->dData = dData;
465       pNewData->bPersistent = bDefaultPersistent;
466     } break;
467     case JS_GLOBALDATA_TYPE_BOOLEAN: {
468       pNewData = new JSGlobalData;
469       pNewData->nType = JS_GLOBALDATA_TYPE_BOOLEAN;
470       pNewData->bData = bData;
471       pNewData->bPersistent = bDefaultPersistent;
472     } break;
473     case JS_GLOBALDATA_TYPE_STRING: {
474       pNewData = new JSGlobalData;
475       pNewData->nType = JS_GLOBALDATA_TYPE_STRING;
476       pNewData->sData = sData;
477       pNewData->bPersistent = bDefaultPersistent;
478     } break;
479     case JS_GLOBALDATA_TYPE_OBJECT: {
480       pNewData = new JSGlobalData;
481       pNewData->nType = JS_GLOBALDATA_TYPE_OBJECT;
482       pNewData->pData.Reset(pData->GetIsolate(), pData);
483       pNewData->bPersistent = bDefaultPersistent;
484     } break;
485     case JS_GLOBALDATA_TYPE_NULL: {
486       pNewData = new JSGlobalData;
487       pNewData->nType = JS_GLOBALDATA_TYPE_NULL;
488       pNewData->bPersistent = bDefaultPersistent;
489     } break;
490     default:
491       return FALSE;
492   }
493 
494   m_mapGlobal[propname] = pNewData;
495   return TRUE;
496 }
497 
GET_VALUE_TYPE(v8::Local<v8::Value> p)498 CJS_Value::Type GET_VALUE_TYPE(v8::Local<v8::Value> p) {
499   const unsigned int nHash = JS_CalcHash(FXJS_GetTypeof(p));
500 
501   if (nHash == JSCONST_nUndefHash)
502     return CJS_Value::VT_undefined;
503   if (nHash == JSCONST_nNullHash)
504     return CJS_Value::VT_null;
505   if (nHash == JSCONST_nStringHash)
506     return CJS_Value::VT_string;
507   if (nHash == JSCONST_nNumberHash)
508     return CJS_Value::VT_number;
509   if (nHash == JSCONST_nBoolHash)
510     return CJS_Value::VT_boolean;
511   if (nHash == JSCONST_nDateHash)
512     return CJS_Value::VT_date;
513   if (nHash == JSCONST_nObjectHash)
514     return CJS_Value::VT_object;
515   if (nHash == JSCONST_nFXobjHash)
516     return CJS_Value::VT_fxobject;
517 
518   return CJS_Value::VT_unknown;
519 }
520