2008-11-04 Cameron Zwarich <zwarich@apple.com>
[webkit/qt.git] / JavaScriptGlue / JSUtils.cpp
blobf5425c4e64d4d97d690046a1f5d15d4e8d684188
1 /*
2 * Copyright (C) 2005, 2008 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
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.
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.
29 #include "config.h"
30 #include "JSUtils.h"
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>
40 struct ObjectImpList {
41 JSObject* imp;
42 ObjectImpList* next;
43 CFTypeRef data;
46 static CFTypeRef KJSValueToCFTypeInternal(JSValue* inValue, ExecState *exec, ObjectImpList* inImps);
47 static JSGlueGlobalObject* getThreadGlobalObject();
49 //--------------------------------------------------------------------------
50 // CFStringToUString
51 //--------------------------------------------------------------------------
53 UString CFStringToUString(CFStringRef inCFString)
55 UString result;
56 if (inCFString) {
57 CFIndex len = CFStringGetLength(inCFString);
58 UniChar* buffer = (UniChar*)malloc(sizeof(UniChar) * len);
59 if (buffer)
61 CFStringGetCharacters(inCFString, CFRangeMake(0, len), buffer);
62 result = UString((const UChar *)buffer, len);
63 free(buffer);
66 return result;
70 //--------------------------------------------------------------------------
71 // UStringToCFString
72 //--------------------------------------------------------------------------
73 // Caller is responsible for releasing the returned CFStringRef
74 CFStringRef UStringToCFString(const UString& inUString)
76 return CFStringCreateWithCharacters(0, (const UniChar*)inUString.data(), inUString.size());
80 //--------------------------------------------------------------------------
81 // CFStringToIdentifier
82 //--------------------------------------------------------------------------
84 Identifier CFStringToIdentifier(CFStringRef inCFString, ExecState* exec)
86 return Identifier(exec, CFStringToUString(inCFString));
90 //--------------------------------------------------------------------------
91 // IdentifierToCFString
92 //--------------------------------------------------------------------------
93 // Caller is responsible for releasing the returned CFStringRef
94 CFStringRef IdentifierToCFString(const Identifier& inIdentifier)
96 return UStringToCFString(inIdentifier.ustring());
100 //--------------------------------------------------------------------------
101 // KJSValueToJSObject
102 //--------------------------------------------------------------------------
103 JSUserObject* KJSValueToJSObject(JSValue* inValue, ExecState *exec)
105 JSUserObject* result = 0;
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;
123 return result;
126 //--------------------------------------------------------------------------
127 // JSObjectKJSValue
128 //--------------------------------------------------------------------------
129 JSValue* JSObjectKJSValue(JSUserObject* ptr)
131 JSLock lock(true);
133 JSValue* result = jsUndefined();
134 if (ptr)
136 bool handled = false;
138 switch (ptr->DataType())
140 case kJSUserObjectDataTypeJSValueWrapper:
142 JSValueWrapper* wrapper = (JSValueWrapper*)ptr->GetData();
143 if (wrapper)
145 result = wrapper->GetValue();
146 handled = true;
148 break;
151 case kJSUserObjectDataTypeCFType:
153 CFTypeRef cfType = (CFTypeRef*)ptr->GetData();
154 if (cfType)
156 CFTypeID typeID = CFGetTypeID(cfType);
157 if (typeID == CFStringGetTypeID())
159 result = jsString(getThreadGlobalExecState(), CFStringToUString((CFStringRef)cfType));
160 handled = true;
162 else if (typeID == CFNumberGetTypeID())
164 double num;
165 CFNumberGetValue((CFNumberRef)cfType, kCFNumberDoubleType, &num);
166 result = jsNumber(getThreadGlobalExecState(), num);
167 handled = true;
169 else if (typeID == CFBooleanGetTypeID())
171 result = jsBoolean(CFBooleanGetValue((CFBooleanRef)cfType));
172 handled = true;
174 else if (typeID == CFNullGetTypeID())
176 result = jsNull();
177 handled = true;
180 break;
183 if (!handled)
185 ExecState* exec = getThreadGlobalExecState();
186 result = new (exec) UserObjectImp(getThreadGlobalObject()->userObjectStructure(), ptr);
189 return result;
195 //--------------------------------------------------------------------------
196 // KJSValueToCFTypeInternal
197 //--------------------------------------------------------------------------
198 // Caller is responsible for releasing the returned CFTypeRef
199 CFTypeRef KJSValueToCFTypeInternal(JSValue* inValue, ExecState *exec, ObjectImpList* inImps)
201 if (!inValue)
202 return 0;
204 CFTypeRef result = 0;
206 JSLock lock(true);
208 if (inValue->isBoolean())
210 result = inValue->toBoolean(exec) ? kCFBooleanTrue : kCFBooleanFalse;
211 RetainCFType(result);
212 return result;
215 if (inValue->isString())
217 UString uString = inValue->toString(exec);
218 result = UStringToCFString(uString);
219 return result;
222 if (inValue->isNumber())
224 double number1 = inValue->toNumber(exec);
225 double number2 = (double)inValue->toInteger(exec);
226 if (number1 == number2)
228 int intValue = (int)number2;
229 result = CFNumberCreate(0, kCFNumberIntType, &intValue);
231 else
233 result = CFNumberCreate(0, kCFNumberDoubleType, &number1);
235 return result;
238 if (inValue->isObject())
240 if (inValue->isObject(&UserObjectImp::info)) {
241 UserObjectImp* userObjectImp = static_cast<UserObjectImp *>(asObject(inValue));
242 JSUserObject* ptr = userObjectImp->GetJSUserObject();
243 if (ptr)
245 result = ptr->CopyCFValue();
248 else
250 JSObject *object = inValue->toObject(exec);
251 UInt8 isArray = false;
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());
260 temp = temp->next;
263 ObjectImpList imps;
264 imps.next = inImps;
265 imps.imp = imp;
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
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)
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;
294 iter++;
299 if (isArray)
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)
306 for (unsigned i = 0; i < length; i++)
308 CFTypeRef cfValue = KJSValueToCFTypeInternal(object->get(exec, i), exec, &imps);
309 CFArrayAppendValue((CFMutableArrayRef)result, cfValue);
310 ReleaseCFType(cfValue);
314 else
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);
320 result = CFDictionaryCreateMutable(0,
322 &kCFTypeDictionaryKeyCallBacks,
323 &kCFTypeDictionaryValueCallBacks);
324 if (result)
326 PropertyNameArray::const_iterator iter = propNames.begin();
327 PropertyNameArray::const_iterator end = propNames.end();
328 while(iter != end)
330 Identifier propName = *iter;
331 if (object->hasProperty(exec, propName))
333 CFStringRef cfKey = IdentifierToCFString(propName);
334 CFTypeRef cfValue = KJSValueToCFTypeInternal(object->get(exec, propName), exec, &imps);
335 if (cfKey && cfValue)
337 CFDictionaryAddValue((CFMutableDictionaryRef)result, cfKey, cfValue);
339 ReleaseCFType(cfKey);
340 ReleaseCFType(cfValue);
342 iter++;
348 return result;
351 if (inValue->isUndefinedOrNull())
353 result = RetainCFType(GetCFNull());
354 return result;
357 ASSERT_NOT_REACHED();
358 return 0;
361 CFTypeRef KJSValueToCFType(JSValue* inValue, ExecState *exec)
363 return KJSValueToCFTypeInternal(inValue, exec, 0);
366 CFTypeRef GetCFNull(void)
368 static CFArrayRef sCFNull = CFArrayCreate(0, 0, 0, 0);
369 CFTypeRef result = JSGetCFNull();
370 if (!result)
372 result = sCFNull;
374 return result;
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.
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.
392 static pthread_key_t globalObjectKey;
393 static pthread_once_t globalObjectKeyOnce = PTHREAD_ONCE_INIT;
395 static void unprotectGlobalObject(void* data)
397 JSLock lock(true);
398 gcUnprotect(static_cast<JSGlueGlobalObject*>(data));
401 static void initializeGlobalObjectKey()
403 pthread_key_create(&globalObjectKey, unprotectGlobalObject);
406 static JSGlueGlobalObject* getThreadGlobalObject()
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::createStructureID(jsNull()));
413 gcProtect(globalObject);
414 pthread_setspecific(globalObjectKey, globalObject);
416 return globalObject;
419 ExecState* getThreadGlobalExecState()
421 ExecState* exec = getThreadGlobalObject()->globalExec();
423 // Discard exceptions -- otherwise an exception would forestall JS
424 // evaluation throughout the thread
425 exec->clearException();
426 return exec;