Versioning.
[WebKit-https.git] / JavaScriptGlue / JSUtils.cpp
1 /*
2  * Copyright (C) 2005, 2008 Apple 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
32 #include "JSBase.h"
33 #include "JSObject.h"
34 #include "JSRun.h"
35 #include "JSValueWrapper.h"
36 #include "UserObjectImp.h"
37 #include <JavaScriptCore/JSString.h>
38 #include <JavaScriptCore/PropertyNameArray.h>
39
40 struct ObjectImpList {
41     JSObject* imp;
42     ObjectImpList* next;
43     CFTypeRef data;
44 };
45
46 static CFTypeRef KJSValueToCFTypeInternal(JSValue* inValue, ExecState *exec, ObjectImpList* inImps);
47 static JSGlueGlobalObject* getThreadGlobalObject();
48
49 //--------------------------------------------------------------------------
50 // CFStringToUString
51 //--------------------------------------------------------------------------
52
53 UString CFStringToUString(CFStringRef inCFString)
54 {
55     UString result;
56     if (inCFString) {
57         CFIndex len = CFStringGetLength(inCFString);
58         UniChar* buffer = (UniChar*)malloc(sizeof(UniChar) * len);
59         if (buffer)
60         {
61             CFStringGetCharacters(inCFString, CFRangeMake(0, len), buffer);
62             result = UString((const UChar *)buffer, len);
63             free(buffer);
64         }
65     }
66     return result;
67 }
68
69
70 //--------------------------------------------------------------------------
71 // UStringToCFString
72 //--------------------------------------------------------------------------
73 // Caller is responsible for releasing the returned CFStringRef
74 CFStringRef UStringToCFString(const UString& inUString)
75 {
76     return CFStringCreateWithCharacters(0, (const UniChar*)inUString.data(), inUString.size());
77 }
78
79
80 //--------------------------------------------------------------------------
81 // CFStringToIdentifier
82 //--------------------------------------------------------------------------
83
84 Identifier CFStringToIdentifier(CFStringRef inCFString, ExecState* exec)
85 {
86     return Identifier(exec, CFStringToUString(inCFString));
87 }
88
89
90 //--------------------------------------------------------------------------
91 // IdentifierToCFString
92 //--------------------------------------------------------------------------
93 // Caller is responsible for releasing the returned CFStringRef
94 CFStringRef IdentifierToCFString(const Identifier& inIdentifier)
95 {
96     return UStringToCFString(inIdentifier.ustring());
97 }
98
99
100 //--------------------------------------------------------------------------
101 // KJSValueToJSObject
102 //--------------------------------------------------------------------------
103 JSUserObject* KJSValueToJSObject(JSValue* inValue, ExecState *exec)
104 {
105     JSUserObject* result = 0;
106
107     if (inValue->isObject(&UserObjectImp::info)) {
108         UserObjectImp* userObjectImp = static_cast<UserObjectImp *>(asObject(inValue));
109         result = userObjectImp->GetJSUserObject();
110         if (result)
111             result->Retain();
112     } else {
113         JSValueWrapper* wrapperValue = new JSValueWrapper(inValue);
114         if (wrapperValue) {
115             JSObjectCallBacks callBacks;
116             JSValueWrapper::GetJSObectCallBacks(callBacks);
117             result = (JSUserObject*)JSObjectCreate(wrapperValue, &callBacks);
118             if (!result) {
119                 delete wrapperValue;
120             }
121         }
122     }
123     return result;
124 }
125
126 //--------------------------------------------------------------------------
127 // JSObjectKJSValue
128 //--------------------------------------------------------------------------
129 JSValue* JSObjectKJSValue(JSUserObject* ptr)
130 {
131     JSLock lock(true);
132
133     JSValue* result = jsUndefined();
134     if (ptr)
135     {
136         bool handled = false;
137
138         switch (ptr->DataType())
139         {
140             case kJSUserObjectDataTypeJSValueWrapper:
141             {
142                 JSValueWrapper* wrapper = (JSValueWrapper*)ptr->GetData();
143                 if (wrapper)
144                 {
145                     result = wrapper->GetValue();
146                     handled = true;
147                 }
148                 break;
149             }
150
151             case kJSUserObjectDataTypeCFType:
152             {
153                 CFTypeRef cfType = (CFTypeRef*)ptr->GetData();
154                 if (cfType)
155                 {
156                     CFTypeID typeID = CFGetTypeID(cfType);
157                     if (typeID == CFStringGetTypeID())
158                     {
159                         result = jsString(getThreadGlobalExecState(), CFStringToUString((CFStringRef)cfType));
160                         handled = true;
161                     }
162                     else if (typeID == CFNumberGetTypeID())
163                     {
164                         double num;
165                         CFNumberGetValue((CFNumberRef)cfType, kCFNumberDoubleType, &num);
166                         result = jsNumber(getThreadGlobalExecState(), num);
167                         handled = true;
168                     }
169                     else if (typeID == CFBooleanGetTypeID())
170                     {
171                         result = jsBoolean(CFBooleanGetValue((CFBooleanRef)cfType));
172                         handled = true;
173                     }
174                     else if (typeID == CFNullGetTypeID())
175                     {
176                         result = jsNull();
177                         handled = true;
178                     }
179                 }
180                 break;
181             }
182         }
183         if (!handled)
184         {
185             ExecState* exec = getThreadGlobalExecState();
186             result = new (exec) UserObjectImp(getThreadGlobalObject()->userObjectStructure(), ptr);
187         }
188     }
189     return result;
190 }
191
192
193
194
195 //--------------------------------------------------------------------------
196 // KJSValueToCFTypeInternal
197 //--------------------------------------------------------------------------
198 // Caller is responsible for releasing the returned CFTypeRef
199 CFTypeRef KJSValueToCFTypeInternal(JSValue* inValue, ExecState *exec, ObjectImpList* inImps)
200 {
201     if (!inValue)
202         return 0;
203
204     CFTypeRef result = 0;
205
206     JSLock lock(true);
207
208         if (inValue->isBoolean())
209             {
210                 result = inValue->toBoolean(exec) ? kCFBooleanTrue : kCFBooleanFalse;
211                 RetainCFType(result);
212                 return result;
213             }
214
215         if (inValue->isString())
216             {
217                 UString uString = inValue->toString(exec);
218                 result = UStringToCFString(uString);
219                 return result;
220             }
221
222         if (inValue->isNumber())
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                 return result;
236             }
237
238         if (inValue->isObject())
239             {
240                             if (inValue->isObject(&UserObjectImp::info)) {
241                                 UserObjectImp* userObjectImp = static_cast<UserObjectImp *>(asObject(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(exec);
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, Identifier(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(exec);
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                 return result;
349             }
350
351     if (inValue->isUndefinedOrNull())
352         {
353             result = RetainCFType(GetCFNull());
354             return result;
355         }
356
357     ASSERT_NOT_REACHED();
358     return 0;
359 }
360
361 CFTypeRef KJSValueToCFType(JSValue* inValue, ExecState *exec)
362 {
363     return KJSValueToCFTypeInternal(inValue, exec, 0);
364 }
365
366 CFTypeRef GetCFNull(void)
367 {
368     static CFArrayRef sCFNull = CFArrayCreate(0, 0, 0, 0);
369     CFTypeRef result = JSGetCFNull();
370     if (!result)
371     {
372         result = sCFNull;
373     }
374     return result;
375 }
376
377 /*
378  * This is a slight hack. The JSGlue API has no concept of execution state.
379  * However, execution state is an inherent part of JS, and JSCore requires it.
380  * So, we keep a single execution state for the whole thread and supply it
381  * where necessary.
382
383  * The execution state holds two things: (1) exceptions; (2) the global object. 
384  * JSGlue has no API for accessing exceptions, so we just discard them. As for
385  * the global object, JSGlue includes no calls that depend on it. Its property
386  * getters and setters are per-object; they don't walk up the enclosing scope. 
387  * Functions called by JSObjectCallFunction may reference values in the enclosing 
388  * scope, but they do so through an internally stored scope chain, so we don't 
389  * need to supply the global scope.
390  */      
391
392 static pthread_key_t globalObjectKey;
393 static pthread_once_t globalObjectKeyOnce = PTHREAD_ONCE_INIT;
394
395 static void unprotectGlobalObject(void* data) 
396 {
397     JSLock lock(true);
398     gcUnprotect(static_cast<JSGlueGlobalObject*>(data));
399 }
400
401 static void initializeGlobalObjectKey()
402 {
403     pthread_key_create(&globalObjectKey, unprotectGlobalObject);
404 }
405
406 static JSGlueGlobalObject* getThreadGlobalObject()
407 {
408     pthread_once(&globalObjectKeyOnce, initializeGlobalObjectKey);
409     JSGlueGlobalObject* globalObject = static_cast<JSGlueGlobalObject*>(pthread_getspecific(globalObjectKey));
410     if (!globalObject) {
411         RefPtr<JSGlobalData> globalData = JSGlobalData::create();
412         globalObject = new (globalData.get()) JSGlueGlobalObject(JSGlueGlobalObject::createStructure(jsNull()));
413         gcProtect(globalObject);
414         pthread_setspecific(globalObjectKey, globalObject);
415     }
416     return globalObject;
417 }
418
419 ExecState* getThreadGlobalExecState()
420 {
421     ExecState* exec = getThreadGlobalObject()->globalExec();
422
423     // Discard exceptions -- otherwise an exception would forestall JS 
424     // evaluation throughout the thread
425     exec->clearException();
426     return exec;
427 }