Reviewed by Maciej.
[WebKit-https.git] / JavaScriptGlue / JSUtils.cpp
1 /*
2  * Copyright (C) 2005 Apple Computer, Inc.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1.  Redistributions of source code must retain the above copyright
9  *     notice, this list of conditions and the following disclaimer. 
10  * 2.  Redistributions in binary form must reproduce the above copyright
11  *     notice, this list of conditions and the following disclaimer in the
12  *     documentation and/or other materials provided with the distribution. 
13  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14  *     its contributors may be used to endorse or promote products derived
15  *     from this software without specific prior written permission. 
16  *
17  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #include "config.h"
30 #include "JSUtils.h"
31 #include "JSBase.h"
32 #include "JSObject.h"
33 #include "JSRun.h"
34 #include "UserObjectImp.h"
35 #include "JSValueWrapper.h"
36 #include "JSObject.h"
37 #include "JavaScriptCore/reference_list.h"
38
39 struct ObjectImpList {
40     JSObject* imp;
41     ObjectImpList* next;
42     CFTypeRef data;
43 };
44
45 static CFTypeRef KJSValueToCFTypeInternal(JSValue *inValue, ExecState *exec, ObjectImpList* inImps);
46
47
48 //--------------------------------------------------------------------------
49 // CFStringToUString
50 //--------------------------------------------------------------------------
51
52 UString CFStringToUString(CFStringRef inCFString)
53 {
54     JSLock lock;
55
56     UString result;
57     if (inCFString) {
58         CFIndex len = CFStringGetLength(inCFString);
59         UniChar* buffer = (UniChar*)malloc(sizeof(UniChar) * len);
60         if (buffer)
61         {
62             CFStringGetCharacters(inCFString, CFRangeMake(0, len), buffer);
63             result = UString((const UChar *)buffer, len);
64             free(buffer);
65         }
66     }
67     return result;
68 }
69
70
71 //--------------------------------------------------------------------------
72 // UStringToCFString
73 //--------------------------------------------------------------------------
74 // Caller is responsible for releasing the returned CFStringRef
75 CFStringRef UStringToCFString(const UString& inUString)
76 {
77     return CFStringCreateWithCharacters(0, (const UniChar*)inUString.data(), inUString.size());
78 }
79
80
81 //--------------------------------------------------------------------------
82 // CFStringToIdentifier
83 //--------------------------------------------------------------------------
84
85 Identifier CFStringToIdentifier(CFStringRef inCFString)
86 {
87     return Identifier(CFStringToUString(inCFString));
88 }
89
90
91 //--------------------------------------------------------------------------
92 // IdentifierToCFString
93 //--------------------------------------------------------------------------
94 // Caller is responsible for releasing the returned CFStringRef
95 CFStringRef IdentifierToCFString(const Identifier& inIdentifier)
96 {
97     return UStringToCFString(inIdentifier.ustring());
98 }
99
100
101 //--------------------------------------------------------------------------
102 // KJSValueToJSObject
103 //--------------------------------------------------------------------------
104 JSUserObject* KJSValueToJSObject(JSValue *inValue, ExecState *exec)
105 {
106     JSUserObject* result = 0;
107
108     if (inValue->isObject(&UserObjectImp::info)) {
109         UserObjectImp* userObjectImp = static_cast<UserObjectImp *>(inValue);
110         result = userObjectImp->GetJSUserObject();
111         if (result)
112             result->Retain();
113     } else {
114         JSValueWrapper* wrapperValue = new JSValueWrapper(inValue);
115         if (wrapperValue) {
116             JSObjectCallBacks callBacks;
117             JSValueWrapper::GetJSObectCallBacks(callBacks);
118             result = (JSUserObject*)JSObjectCreate(wrapperValue, &callBacks);
119             if (!result) {
120                 delete wrapperValue;
121             }
122         }
123     }
124     return result;
125 }
126
127 //--------------------------------------------------------------------------
128 // JSObjectKJSValue
129 //--------------------------------------------------------------------------
130 JSValue *JSObjectKJSValue(JSUserObject* ptr)
131 {
132     JSLock lock;
133
134     JSValue *result = jsUndefined();
135     if (ptr)
136     {
137         bool handled = false;
138
139         switch (ptr->DataType())
140         {
141             case kJSUserObjectDataTypeJSValueWrapper:
142             {
143                 JSValueWrapper* wrapper = (JSValueWrapper*)ptr->GetData();
144                 if (wrapper)
145                 {
146                     result = wrapper->GetValue();
147                     handled = true;
148                 }
149                 break;
150             }
151
152             case kJSUserObjectDataTypeCFType:
153             {
154                 CFTypeRef cfType = (CFTypeRef*)ptr->GetData();
155                 if (cfType)
156                 {
157                     CFTypeID typeID = CFGetTypeID(cfType);
158                     if (typeID == CFStringGetTypeID())
159                     {
160                         result = jsString(CFStringToUString((CFStringRef)cfType));
161                         handled = true;
162                     }
163                     else if (typeID == CFNumberGetTypeID())
164                     {
165                         if (CFNumberIsFloatType((CFNumberRef)cfType))
166                         {
167                             double num;
168                             if (CFNumberGetValue((CFNumberRef)cfType, kCFNumberDoubleType, &num))
169                             {
170                                 result = jsNumber(num);
171                                 handled = true;
172                             }
173                         }
174                         else
175                         {
176                             long num;
177                             if (CFNumberGetValue((CFNumberRef)cfType, kCFNumberLongType, &num))
178                             {
179                                 result = jsNumber(num);
180                                 handled = true;
181                             }
182                         }
183                     }
184                     else if (typeID == CFBooleanGetTypeID())
185                     {
186                         result = jsBoolean(CFBooleanGetValue((CFBooleanRef)cfType));
187                         handled = true;
188                     }
189                     else if (typeID == CFDateGetTypeID())
190                     {
191                     }
192                     else if (typeID == CFNullGetTypeID())
193                     {
194                         result = jsNull();
195                         handled = true;
196                     }
197                 }
198                 break;
199             }
200         }
201         if (!handled)
202         {
203             result = new UserObjectImp(ptr);
204         }
205     }
206     return result;
207 }
208
209
210
211
212 //--------------------------------------------------------------------------
213 // KJSValueToCFTypeInternal
214 //--------------------------------------------------------------------------
215 // Caller is responsible for releasing the returned CFTypeRef
216 CFTypeRef KJSValueToCFTypeInternal(JSValue *inValue, ExecState *exec, ObjectImpList* inImps)
217 {
218     if (!inValue)
219         return 0;
220
221     CFTypeRef result = 0;
222
223     JSLock lock;
224
225     switch (inValue->type())
226     {
227         case BooleanType:
228             {
229                 result = inValue->toBoolean(exec) ? kCFBooleanTrue : kCFBooleanFalse;
230                 RetainCFType(result);
231             }
232             break;
233
234         case StringType:
235             {
236                 UString uString = inValue->toString(exec);
237                 result = UStringToCFString(uString);
238             }
239             break;
240
241         case NumberType:
242             {
243                 double number1 = inValue->toNumber(exec);
244                 double number2 = (double)inValue->toInteger(exec);
245                 if (number1 ==  number2)
246                 {
247                     int intValue = (int)number2;
248                     result = CFNumberCreate(0, kCFNumberIntType, &intValue);
249                 }
250                 else
251                 {
252                     result = CFNumberCreate(0, kCFNumberDoubleType, &number1);
253                 }
254             }
255             break;
256
257         case ObjectType:
258             {
259                             if (inValue->isObject(&UserObjectImp::info)) {
260                                 UserObjectImp* userObjectImp = static_cast<UserObjectImp *>(inValue);
261                     JSUserObject* ptr = userObjectImp->GetJSUserObject();
262                     if (ptr)
263                     {
264                         result = ptr->CopyCFValue();
265                     }
266                 }
267                 else
268                 {
269                     JSObject *object = inValue->toObject(exec);
270                     UInt8 isArray = false;
271
272                     // if two objects reference each
273                     JSObject* imp = object;
274                     ObjectImpList* temp = inImps;
275                     while (temp) {
276                         if (imp == temp->imp) {
277                             return CFRetain(GetCFNull());
278                         }
279                         temp = temp->next;
280                     }
281
282                     ObjectImpList imps;
283                     imps.next = inImps;
284                     imps.imp = imp;
285
286
287 //[...] HACK since we do not have access to the class info we use class name instead
288 #if 0
289                     if (object->inherits(&ArrayInstanceImp::info))
290 #else
291                     if (object->className() == "Array")
292 #endif
293                     {
294                         isArray = true;
295                         JSInterpreter* intrepreter = (JSInterpreter*)exec->dynamicInterpreter();
296                         if (intrepreter && (intrepreter->Flags() & kJSFlagConvertAssociativeArray)) {
297                             ReferenceList propList = object->propList(exec);
298                             ReferenceListIterator iter = propList.begin();
299                             ReferenceListIterator end = propList.end();
300                             while(iter != end && isArray)
301                             {
302                                 Identifier propName = iter->getPropertyName(exec);
303                                 UString ustr = propName.ustring();
304                                 const UniChar* uniChars = (const UniChar*)ustr.data();
305                                 int size = ustr.size();
306                                 while (size--) {
307                                     if (uniChars[size] < '0' || uniChars[size] > '9') {
308                                         isArray = false;
309                                         break;
310                                     }
311                                 }
312                                 iter++;
313                             }
314                         }
315                     }
316
317                     if (isArray)
318                     {
319                         // This is an KJS array
320                         unsigned int length = object->get(exec, "length")->toUInt32(exec);
321                         result = CFArrayCreateMutable(0, 0, &kCFTypeArrayCallBacks);
322                         if (result)
323                         {
324                             for (unsigned i = 0; i < length; i++)
325                             {
326                                 CFTypeRef cfValue = KJSValueToCFTypeInternal(object->get(exec, i), exec, &imps);
327                                 CFArrayAppendValue((CFMutableArrayRef)result, cfValue);
328                                 ReleaseCFType(cfValue);
329                             }
330                         }
331                     }
332                     else
333                     {
334                         // Not an array, just treat it like a dictionary which contains (property name, property value) pairs
335                         ReferenceList propList = object->propList(exec);
336                         {
337                             result = CFDictionaryCreateMutable(0,
338                                                                0,
339                                                                &kCFTypeDictionaryKeyCallBacks,
340                                                                &kCFTypeDictionaryValueCallBacks);
341                             if (result)
342                             {
343                                 ReferenceListIterator iter = propList.begin();
344                                 ReferenceListIterator end = propList.end();
345                                 while(iter != end)
346                                 {
347                                     Identifier propName = iter->getPropertyName(exec);
348                                     if (object->hasProperty(exec, propName))
349                                     {
350                                         CFStringRef cfKey = IdentifierToCFString(propName);
351                                         CFTypeRef cfValue = KJSValueToCFTypeInternal(object->get(exec, propName), exec, &imps);
352                                         if (cfKey && cfValue)
353                                         {
354                                             CFDictionaryAddValue((CFMutableDictionaryRef)result, cfKey, cfValue);
355                                         }
356                                         ReleaseCFType(cfKey);
357                                         ReleaseCFType(cfValue);
358                                     }
359                                     iter++;
360                                 }
361                             }
362                         }
363                     }
364                 }
365             }
366             break;
367
368         case NullType:
369         case UndefinedType:
370         case UnspecifiedType:
371             result = RetainCFType(GetCFNull());
372             break;
373
374         default:
375             fprintf(stderr, "KJSValueToCFType: wrong value type %d\n", inValue->type());
376             break;
377     }
378
379     return result;
380 }
381
382 CFTypeRef KJSValueToCFType(JSValue *inValue, ExecState *exec)
383 {
384     return KJSValueToCFTypeInternal(inValue, exec, 0);
385 }
386
387 CFTypeRef GetCFNull(void)
388 {
389     static CFArrayRef sCFNull = CFArrayCreate(0, 0, 0, 0);
390     CFTypeRef result = JSGetCFNull();
391     if (!result)
392     {
393         result = sCFNull;
394     }
395     return result;
396 }
397