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