Reviewed by Darin.
[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/PropertyNameArray.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                             PropertyNameArray propNames;
298                             object->getPropertyNames(exec, propNames);
299                             PropertyNameArrayIterator iter = propNames.begin();
300                             PropertyNameArrayIterator end = propNames.end();
301                             while(iter != end && isArray)
302                             {
303                                 Identifier propName = *iter;
304                                 UString ustr = propName.ustring();
305                                 const UniChar* uniChars = (const UniChar*)ustr.data();
306                                 int size = ustr.size();
307                                 while (size--) {
308                                     if (uniChars[size] < '0' || uniChars[size] > '9') {
309                                         isArray = false;
310                                         break;
311                                     }
312                                 }
313                                 iter++;
314                             }
315                         }
316                     }
317
318                     if (isArray)
319                     {
320                         // This is an KJS array
321                         unsigned int length = object->get(exec, "length")->toUInt32(exec);
322                         result = CFArrayCreateMutable(0, 0, &kCFTypeArrayCallBacks);
323                         if (result)
324                         {
325                             for (unsigned i = 0; i < length; i++)
326                             {
327                                 CFTypeRef cfValue = KJSValueToCFTypeInternal(object->get(exec, i), exec, &imps);
328                                 CFArrayAppendValue((CFMutableArrayRef)result, cfValue);
329                                 ReleaseCFType(cfValue);
330                             }
331                         }
332                     }
333                     else
334                     {
335                         // Not an array, just treat it like a dictionary which contains (property name, property value) pairs
336                         PropertyNameArray propNames;
337                         object->getPropertyNames(exec, propNames);
338                         {
339                             result = CFDictionaryCreateMutable(0,
340                                                                0,
341                                                                &kCFTypeDictionaryKeyCallBacks,
342                                                                &kCFTypeDictionaryValueCallBacks);
343                             if (result)
344                             {
345                                 PropertyNameArrayIterator iter = propNames.begin();
346                                 PropertyNameArrayIterator end = propNames.end();
347                                 while(iter != end)
348                                 {
349                                     Identifier propName = *iter;
350                                     if (object->hasProperty(exec, propName))
351                                     {
352                                         CFStringRef cfKey = IdentifierToCFString(propName);
353                                         CFTypeRef cfValue = KJSValueToCFTypeInternal(object->get(exec, propName), exec, &imps);
354                                         if (cfKey && cfValue)
355                                         {
356                                             CFDictionaryAddValue((CFMutableDictionaryRef)result, cfKey, cfValue);
357                                         }
358                                         ReleaseCFType(cfKey);
359                                         ReleaseCFType(cfValue);
360                                     }
361                                     iter++;
362                                 }
363                             }
364                         }
365                     }
366                 }
367             }
368             break;
369
370         case NullType:
371         case UndefinedType:
372         case UnspecifiedType:
373             result = RetainCFType(GetCFNull());
374             break;
375
376         default:
377             fprintf(stderr, "KJSValueToCFType: wrong value type %d\n", inValue->type());
378             break;
379     }
380
381     return result;
382 }
383
384 CFTypeRef KJSValueToCFType(JSValue *inValue, ExecState *exec)
385 {
386     return KJSValueToCFTypeInternal(inValue, exec, 0);
387 }
388
389 CFTypeRef GetCFNull(void)
390 {
391     static CFArrayRef sCFNull = CFArrayCreate(0, 0, 0, 0);
392     CFTypeRef result = JSGetCFNull();
393     if (!result)
394     {
395         result = sCFNull;
396     }
397     return result;
398 }
399