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
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.
35 #include "JSValueWrapper.h"
36 #include "UserObjectImp.h"
37 #include <JavaScriptCore/JSString.h>
38 #include <JavaScriptCore/PropertyNameArray.h>
39 #include <JavaScriptCore/WTFThreadData.h>
41 struct ObjectImpList {
47 static CFTypeRef KJSValueToCFTypeInternal(JSValue inValue, ExecState *exec, ObjectImpList* inImps);
48 static JSGlueGlobalObject* getThreadGlobalObject();
50 //--------------------------------------------------------------------------
52 //--------------------------------------------------------------------------
54 UString CFStringToUString(CFStringRef inCFString)
58 CFIndex len = CFStringGetLength(inCFString);
59 UniChar* buffer = (UniChar*)malloc(sizeof(UniChar) * len);
62 CFStringGetCharacters(inCFString, CFRangeMake(0, len), buffer);
63 result = UString((const UChar *)buffer, len);
71 //--------------------------------------------------------------------------
73 //--------------------------------------------------------------------------
74 // Caller is responsible for releasing the returned CFStringRef
75 CFStringRef UStringToCFString(const UString& inUString)
77 return CFStringCreateWithCharacters(0, (const UniChar*)inUString.characters(), inUString.length());
81 //--------------------------------------------------------------------------
82 // CFStringToIdentifier
83 //--------------------------------------------------------------------------
85 Identifier CFStringToIdentifier(CFStringRef inCFString, ExecState* exec)
87 return Identifier(exec, CFStringToUString(inCFString));
91 //--------------------------------------------------------------------------
92 // IdentifierToCFString
93 //--------------------------------------------------------------------------
94 // Caller is responsible for releasing the returned CFStringRef
95 CFStringRef IdentifierToCFString(const Identifier& inIdentifier)
97 return UStringToCFString(inIdentifier.ustring());
101 //--------------------------------------------------------------------------
102 // KJSValueToJSObject
103 //--------------------------------------------------------------------------
104 JSUserObject* KJSValueToJSObject(JSValue inValue, ExecState *exec)
106 JSUserObject* result = 0;
108 if (inValue.inherits(&UserObjectImp::info)) {
109 UserObjectImp* userObjectImp = static_cast<UserObjectImp *>(asObject(inValue));
110 result = userObjectImp->GetJSUserObject();
114 JSValueWrapper* wrapperValue = new JSValueWrapper(inValue);
116 JSObjectCallBacks callBacks;
117 JSValueWrapper::GetJSObectCallBacks(callBacks);
118 result = (JSUserObject*)JSObjectCreate(wrapperValue, &callBacks);
127 //--------------------------------------------------------------------------
129 //--------------------------------------------------------------------------
130 JSValue JSObjectKJSValue(JSUserObject* ptr)
132 JSGlueAPIEntry entry;
134 JSValue result = jsUndefined();
137 bool handled = false;
139 switch (ptr->DataType())
141 case kJSUserObjectDataTypeJSValueWrapper:
143 JSValueWrapper* wrapper = (JSValueWrapper*)ptr->GetData();
146 result = wrapper->GetValue();
152 case kJSUserObjectDataTypeCFType:
154 CFTypeRef cfType = (CFTypeRef*)ptr->GetData();
157 CFTypeID typeID = CFGetTypeID(cfType);
158 if (typeID == CFStringGetTypeID())
160 result = jsString(getThreadGlobalExecState(), CFStringToUString((CFStringRef)cfType));
163 else if (typeID == CFNumberGetTypeID())
166 CFNumberGetValue((CFNumberRef)cfType, kCFNumberDoubleType, &num);
167 result = jsNumber(getThreadGlobalExecState(), num);
170 else if (typeID == CFBooleanGetTypeID())
172 result = jsBoolean(CFBooleanGetValue((CFBooleanRef)cfType));
175 else if (typeID == CFNullGetTypeID())
186 ExecState* exec = getThreadGlobalExecState();
187 result = new (exec) UserObjectImp(getThreadGlobalObject()->userObjectStructure(), ptr);
196 //--------------------------------------------------------------------------
197 // KJSValueToCFTypeInternal
198 //--------------------------------------------------------------------------
199 // Caller is responsible for releasing the returned CFTypeRef
200 CFTypeRef KJSValueToCFTypeInternal(JSValue inValue, ExecState *exec, ObjectImpList* inImps)
205 CFTypeRef result = 0;
207 JSGlueAPIEntry entry;
209 if (inValue.isBoolean())
211 result = inValue.toBoolean(exec) ? kCFBooleanTrue : kCFBooleanFalse;
212 RetainCFType(result);
216 if (inValue.isString())
218 UString uString = inValue.toString(exec);
219 result = UStringToCFString(uString);
223 if (inValue.isNumber())
225 double number1 = inValue.toNumber(exec);
226 double number2 = (double)inValue.toInteger(exec);
227 if (number1 == number2)
229 int intValue = (int)number2;
230 result = CFNumberCreate(0, kCFNumberIntType, &intValue);
234 result = CFNumberCreate(0, kCFNumberDoubleType, &number1);
239 if (inValue.isObject())
241 if (inValue.inherits(&UserObjectImp::info)) {
242 UserObjectImp* userObjectImp = static_cast<UserObjectImp *>(asObject(inValue));
243 JSUserObject* ptr = userObjectImp->GetJSUserObject();
246 result = ptr->CopyCFValue();
251 JSObject *object = inValue.toObject(exec);
252 UInt8 isArray = false;
254 // if two objects reference each
255 JSObject* imp = object;
256 ObjectImpList* temp = inImps;
258 if (imp == temp->imp) {
259 return CFRetain(GetCFNull());
269 //[...] HACK since we do not have access to the class info we use class name instead
271 if (object->inherits(&ArrayInstanceImp::info))
273 if (object->className() == "Array")
277 JSGlueGlobalObject* globalObject = static_cast<JSGlueGlobalObject*>(exec->dynamicGlobalObject());
278 if (globalObject && (globalObject->Flags() & kJSFlagConvertAssociativeArray)) {
279 PropertyNameArray propNames(exec);
280 object->getPropertyNames(exec, propNames);
281 PropertyNameArray::const_iterator iter = propNames.begin();
282 PropertyNameArray::const_iterator end = propNames.end();
283 while(iter != end && isArray)
285 Identifier propName = *iter;
286 UString ustr = propName.ustring();
287 const UniChar* uniChars = (const UniChar*)ustr.characters();
288 int size = ustr.length();
290 if (uniChars[size] < '0' || uniChars[size] > '9') {
302 // This is an KJS array
303 unsigned int length = object->get(exec, Identifier(exec, "length")).toUInt32(exec);
304 result = CFArrayCreateMutable(0, 0, &kCFTypeArrayCallBacks);
307 for (unsigned i = 0; i < length; i++)
309 CFTypeRef cfValue = KJSValueToCFTypeInternal(object->get(exec, i), exec, &imps);
310 CFArrayAppendValue((CFMutableArrayRef)result, cfValue);
311 ReleaseCFType(cfValue);
317 // Not an array, just treat it like a dictionary which contains (property name, property value) pairs
318 PropertyNameArray propNames(exec);
319 object->getPropertyNames(exec, propNames);
321 result = CFDictionaryCreateMutable(0,
323 &kCFTypeDictionaryKeyCallBacks,
324 &kCFTypeDictionaryValueCallBacks);
327 PropertyNameArray::const_iterator iter = propNames.begin();
328 PropertyNameArray::const_iterator end = propNames.end();
331 Identifier propName = *iter;
332 if (object->hasProperty(exec, propName))
334 CFStringRef cfKey = IdentifierToCFString(propName);
335 CFTypeRef cfValue = KJSValueToCFTypeInternal(object->get(exec, propName), exec, &imps);
336 if (cfKey && cfValue)
338 CFDictionaryAddValue((CFMutableDictionaryRef)result, cfKey, cfValue);
340 ReleaseCFType(cfKey);
341 ReleaseCFType(cfValue);
352 if (inValue.isUndefinedOrNull())
354 result = RetainCFType(GetCFNull());
358 ASSERT_NOT_REACHED();
362 CFTypeRef KJSValueToCFType(JSValue inValue, ExecState *exec)
364 return KJSValueToCFTypeInternal(inValue, exec, 0);
367 CFTypeRef GetCFNull(void)
369 static CFArrayRef sCFNull = CFArrayCreate(0, 0, 0, 0);
370 CFTypeRef result = JSGetCFNull();
379 * This is a slight hack. The JSGlue API has no concept of execution state.
380 * However, execution state is an inherent part of JS, and JSCore requires it.
381 * So, we keep a single execution state for the whole thread and supply it
384 * The execution state holds two things: (1) exceptions; (2) the global object.
385 * JSGlue has no API for accessing exceptions, so we just discard them. As for
386 * the global object, JSGlue includes no calls that depend on it. Its property
387 * getters and setters are per-object; they don't walk up the enclosing scope.
388 * Functions called by JSObjectCallFunction may reference values in the enclosing
389 * scope, but they do so through an internally stored scope chain, so we don't
390 * need to supply the global scope.
393 static pthread_key_t globalObjectKey;
394 static pthread_once_t globalObjectKeyOnce = PTHREAD_ONCE_INIT;
396 static void unprotectGlobalObject(void* data)
398 JSGlueAPIEntry entry;
399 gcUnprotect(static_cast<JSGlueGlobalObject*>(data));
402 static void initializeGlobalObjectKey()
404 pthread_key_create(&globalObjectKey, unprotectGlobalObject);
407 JSGlobalData* getThreadGlobalData()
409 return &JSGlobalData::sharedInstance();
412 static JSGlueGlobalObject* getThreadGlobalObject()
414 pthread_once(&globalObjectKeyOnce, initializeGlobalObjectKey);
415 JSGlueGlobalObject* globalObject = static_cast<JSGlueGlobalObject*>(pthread_getspecific(globalObjectKey));
417 globalObject = new (getThreadGlobalData()) JSGlueGlobalObject(JSGlueGlobalObject::createStructure(jsNull()));
418 gcProtect(globalObject);
419 pthread_setspecific(globalObjectKey, globalObject);
424 ExecState* getThreadGlobalExecState()
426 ExecState* exec = getThreadGlobalObject()->globalExec();
428 // Discard exceptions -- otherwise an exception would forestall JS
429 // evaluation throughout the thread
430 exec->clearException();
434 JSGlueAPIEntry::JSGlueAPIEntry()
435 : m_lock(LockForReal)
436 , m_storedIdentifierTable(wtfThreadData().currentIdentifierTable())
438 wtfThreadData().setCurrentIdentifierTable(getThreadGlobalData()->identifierTable);
441 JSGlueAPIEntry::~JSGlueAPIEntry()
443 wtfThreadData().setCurrentIdentifierTable(m_storedIdentifierTable);
446 JSGlueAPICallback::JSGlueAPICallback(ExecState* exec)
449 wtfThreadData().resetCurrentIdentifierTable();
452 JSGlueAPICallback::~JSGlueAPICallback()
454 wtfThreadData().setCurrentIdentifierTable(getThreadGlobalData()->identifierTable);