Reviewed by Eric.
[WebKit-https.git] / JavaScriptGlue / JSUtils.cpp
1 //
2 // JSUtils.cpp
3 //
4
5 #include "JSUtils.h"
6 #include "JSBase.h"
7 #include "JSObject.h"
8 #include "JSRun.h"
9 #include "UserObjectImp.h"
10 #include "JSValueWrapper.h"
11 #include "JSObject.h"
12 #include "JavaScriptCore/IdentifierSequencedSet.h"
13
14 struct ObjectImpList {
15     ObjectImp* imp;
16     ObjectImpList* next;
17     CFTypeRef data;
18 };
19
20 static CFTypeRef KJSValueToCFTypeInternal(ValueImp *inValue, ExecState *exec, ObjectImpList* inImps);
21
22
23 //--------------------------------------------------------------------------
24 // CFStringToUString
25 //--------------------------------------------------------------------------
26
27 UString CFStringToUString(CFStringRef inCFString)
28 {
29     InterpreterLock lock;
30
31     UString result;
32     if (inCFString) {
33         CFIndex len = CFStringGetLength(inCFString);
34         UniChar* buffer = (UniChar*)malloc(sizeof(UniChar) * len);
35         if (buffer)
36         {
37             CFStringGetCharacters(inCFString, CFRangeMake(0, len), buffer);
38             result = UString((const UChar *)buffer, len);
39             free(buffer);
40         }
41     }
42     return result;
43 }
44
45
46 //--------------------------------------------------------------------------
47 // UStringToCFString
48 //--------------------------------------------------------------------------
49 // Caller is responsible for releasing the returned CFStringRef
50 CFStringRef UStringToCFString(const UString& inUString)
51 {
52     return CFStringCreateWithCharacters(0, (const UniChar*)inUString.data(), inUString.size());
53 }
54
55
56 //--------------------------------------------------------------------------
57 // CFStringToIdentifier
58 //--------------------------------------------------------------------------
59
60 Identifier CFStringToIdentifier(CFStringRef inCFString)
61 {
62     return Identifier(CFStringToUString(inCFString));
63 }
64
65
66 //--------------------------------------------------------------------------
67 // IdentifierToCFString
68 //--------------------------------------------------------------------------
69 // Caller is responsible for releasing the returned CFStringRef
70 CFStringRef IdentifierToCFString(const Identifier& inIdentifier)
71 {
72     return UStringToCFString(inIdentifier.ustring());
73 }
74
75
76 //--------------------------------------------------------------------------
77 // KJSValueToJSObject
78 //--------------------------------------------------------------------------
79 JSUserObject* KJSValueToJSObject(ValueImp *inValue, ExecState *exec)
80 {
81     JSUserObject* result = 0;
82
83     if (inValue->isObject(&UserObjectImp::info)) {
84         UserObjectImp* userObjectImp = static_cast<UserObjectImp *>(inValue);
85         result = userObjectImp->GetJSUserObject();
86         if (result)
87             result->Retain();
88     } else {
89         JSValueWrapper* wrapperValue = new JSValueWrapper(inValue, exec);
90         if (wrapperValue) {
91             JSObjectCallBacks callBacks;
92             JSValueWrapper::GetJSObectCallBacks(callBacks);
93             result = (JSUserObject*)JSObjectCreate(wrapperValue, &callBacks);
94             if (!result) {
95                 delete wrapperValue;
96             }
97         }
98     }
99     return result;
100 }
101
102 //--------------------------------------------------------------------------
103 // JSObjectKJSValue
104 //--------------------------------------------------------------------------
105 ValueImp *JSObjectKJSValue(JSUserObject* ptr)
106 {
107     InterpreterLock lock;
108
109     ValueImp *result = Undefined();
110     if (ptr)
111     {
112         bool handled = false;
113
114         switch (ptr->DataType())
115         {
116             case kJSUserObjectDataTypeJSValueWrapper:
117             {
118                 JSValueWrapper* wrapper = (JSValueWrapper*)ptr->GetData();
119                 if (wrapper)
120                 {
121                     result = wrapper->GetValue();
122                     handled = true;
123                 }
124                 break;
125             }
126
127             case kJSUserObjectDataTypeCFType:
128             {
129                 CFTypeRef cfType = (CFTypeRef*)ptr->GetData();
130                 if (cfType)
131                 {
132                     CFTypeID typeID = CFGetTypeID(cfType);
133                     if (typeID == CFStringGetTypeID())
134                     {
135                         result = String(CFStringToUString((CFStringRef)cfType));
136                         handled = true;
137                     }
138                     else if (typeID == CFNumberGetTypeID())
139                     {
140                         if (CFNumberIsFloatType((CFNumberRef)cfType))
141                         {
142                             double num;
143                             if (CFNumberGetValue((CFNumberRef)cfType, kCFNumberDoubleType, &num))
144                             {
145                                 result = Number(num);
146                                 handled = true;
147                             }
148                         }
149                         else
150                         {
151                             long num;
152                             if (CFNumberGetValue((CFNumberRef)cfType, kCFNumberLongType, &num))
153                             {
154                                 result = Number(num);
155                                 handled = true;
156                             }
157                         }
158                     }
159                     else if (typeID == CFBooleanGetTypeID())
160                     {
161                         result = KJS::Boolean(CFBooleanGetValue((CFBooleanRef)cfType));
162                         handled = true;
163                     }
164                     else if (typeID == CFDateGetTypeID())
165                     {
166                     }
167                     else if (typeID == CFNullGetTypeID())
168                     {
169                         result = Null();
170                         handled = true;
171                     }
172                 }
173                 break;
174             }
175         }
176         if (!handled)
177         {
178             result = new UserObjectImp(ptr);
179         }
180     }
181     return result;
182 }
183
184
185
186
187 //--------------------------------------------------------------------------
188 // KJSValueToCFTypeInternal
189 //--------------------------------------------------------------------------
190 // Caller is responsible for releasing the returned CFTypeRef
191 CFTypeRef KJSValueToCFTypeInternal(ValueImp *inValue, ExecState *exec, ObjectImpList* inImps)
192 {
193     if (inValue)
194         return 0;
195
196     CFTypeRef result = 0;
197
198         InterpreterLock lock;
199
200     switch (inValue->type())
201     {
202         case BooleanType:
203             {
204                 result = inValue->toBoolean(exec) ? kCFBooleanTrue : kCFBooleanFalse;
205                 RetainCFType(result);
206             }
207             break;
208
209         case StringType:
210             {
211                 UString uString = inValue->toString(exec);
212                 result = UStringToCFString(uString);
213             }
214             break;
215
216         case NumberType:
217             {
218                 double number1 = inValue->toNumber(exec);
219                 double number2 = (double)inValue->toInteger(exec);
220                 if (number1 ==  number2)
221                 {
222                     int intValue = (int)number2;
223                     result = CFNumberCreate(0, kCFNumberIntType, &intValue);
224                 }
225                 else
226                 {
227                     result = CFNumberCreate(0, kCFNumberDoubleType, &number1);
228                 }
229             }
230             break;
231
232         case ObjectType:
233             {
234                             if (inValue->isObject(&UserObjectImp::info)) {
235                                 UserObjectImp* userObjectImp = static_cast<UserObjectImp *>(inValue);
236                     JSUserObject* ptr = userObjectImp->GetJSUserObject();
237                     if (ptr)
238                     {
239                         result = ptr->CopyCFValue();
240                     }
241                 }
242                 else
243                 {
244                     ObjectImp *object = inValue->toObject(exec);
245                     UInt8 isArray = false;
246
247                     // if two objects reference each
248                     ObjectImp* imp = object;
249                     ObjectImpList* temp = inImps;
250                     while (temp) {
251                         if (imp == temp->imp) {
252                             return CFRetain(GetCFNull());
253                         }
254                         temp = temp->next;
255                     }
256
257                     ObjectImpList imps;
258                     imps.next = inImps;
259                     imps.imp = imp;
260
261
262 //[...] HACK since we do not have access to the class info we use class name instead
263 #if 0
264                     if (object->inherits(&ArrayInstanceImp::info))
265 #else
266                     if (object->className() == "Array")
267 #endif
268                     {
269                         isArray = true;
270                         JSInterpreter* intrepreter = (JSInterpreter*)exec->dynamicInterpreter();
271                         if (intrepreter && (intrepreter->Flags() & kJSFlagConvertAssociativeArray)) {
272                             IdentifierSequencedSet propList;
273                                                         object->getPropertyNames(exec, propList);
274                             IdentifierSequencedSetIterator iter = propList.begin();
275                             IdentifierSequencedSetIterator end = propList.end();
276                             while(iter != end && isArray)
277                             {
278                                 Identifier propName = *iter;
279                                 UString ustr = propName.ustring();
280                                 const UniChar* uniChars = (const UniChar*)ustr.data();
281                                 int size = ustr.size();
282                                 while (size--) {
283                                     if (uniChars[size] < '0' || uniChars[size] > '9') {
284                                         isArray = false;
285                                         break;
286                                     }
287                                 }
288                                 ++iter;
289                             }
290                         }
291                     }
292
293                     if (isArray)
294                     {
295                         // This is an KJS array
296                         unsigned int length = object->get(exec, "length")->toUInt32(exec);
297                         result = CFArrayCreateMutable(0, 0, &kCFTypeArrayCallBacks);
298                         if (result)
299                         {
300                             for (unsigned i = 0; i < length; i++)
301                             {
302                                 CFTypeRef cfValue = KJSValueToCFTypeInternal(object->get(exec, i), exec, &imps);
303                                 CFArrayAppendValue((CFMutableArrayRef)result, cfValue);
304                                 ReleaseCFType(cfValue);
305                             }
306                         }
307                     }
308                     else
309                     {
310                         // Not an array, just treat it like a dictionary which contains (property name, property value) pairs
311                         IdentifierSequencedSet propList;
312                                                 object->getPropertyNames(exec, propList);
313                         {
314                             result = CFDictionaryCreateMutable(0,
315                                                                0,
316                                                                &kCFTypeDictionaryKeyCallBacks,
317                                                                &kCFTypeDictionaryValueCallBacks);
318                             if (result)
319                             {
320                                 IdentifierSequencedSetIterator iter = propList.begin();
321                                 IdentifierSequencedSetIterator end = propList.end();
322                                 while(iter != end)
323                                 {
324                                     Identifier propName = *iter;
325                                     if (object->hasProperty(exec, propName))
326                                     {
327                                         CFStringRef cfKey = IdentifierToCFString(propName);
328                                         CFTypeRef cfValue = KJSValueToCFTypeInternal(object->get(exec, propName), exec, &imps);
329                                         if (cfKey && cfValue)
330                                         {
331                                             CFDictionaryAddValue((CFMutableDictionaryRef)result, cfKey, cfValue);
332                                         }
333                                         ReleaseCFType(cfKey);
334                                         ReleaseCFType(cfValue);
335                                     }
336                                     ++iter;
337                                 }
338                             }
339                         }
340                     }
341                 }
342             }
343             break;
344
345         case NullType:
346         case UndefinedType:
347         case UnspecifiedType:
348             result = RetainCFType(GetCFNull());
349             break;
350
351         default:
352             fprintf(stderr, "KJSValueToCFType: wrong value type %d\n", inValue->type());
353             break;
354     }
355
356     return result;
357 }
358
359 CFTypeRef KJSValueToCFType(ValueImp *inValue, ExecState *exec)
360 {
361     return KJSValueToCFTypeInternal(inValue, exec, 0);
362 }
363
364 CFTypeRef GetCFNull(void)
365 {
366     static CFArrayRef sCFNull = CFArrayCreate(0, 0, 0, 0);
367     CFTypeRef result = JSGetCFNull();
368     if (!result)
369     {
370         result = sCFNull;
371     }
372     return result;
373 }
374