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>
40 struct ObjectImpList
{
46 static CFTypeRef
KJSValueToCFTypeInternal(JSValue
* inValue
, ExecState
*exec
, ObjectImpList
* inImps
);
47 static JSGlueGlobalObject
* getThreadGlobalObject();
49 //--------------------------------------------------------------------------
51 //--------------------------------------------------------------------------
53 UString
CFStringToUString(CFStringRef inCFString
)
57 CFIndex len
= CFStringGetLength(inCFString
);
58 UniChar
* buffer
= (UniChar
*)malloc(sizeof(UniChar
) * len
);
61 CFStringGetCharacters(inCFString
, CFRangeMake(0, len
), buffer
);
62 result
= UString((const UChar
*)buffer
, len
);
70 //--------------------------------------------------------------------------
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();
113 JSValueWrapper
* wrapperValue
= new JSValueWrapper(inValue
);
115 JSObjectCallBacks callBacks
;
116 JSValueWrapper::GetJSObectCallBacks(callBacks
);
117 result
= (JSUserObject
*)JSObjectCreate(wrapperValue
, &callBacks
);
126 //--------------------------------------------------------------------------
128 //--------------------------------------------------------------------------
129 JSValue
* JSObjectKJSValue(JSUserObject
* ptr
)
133 JSValue
* result
= jsUndefined();
136 bool handled
= false;
138 switch (ptr
->DataType())
140 case kJSUserObjectDataTypeJSValueWrapper
:
142 JSValueWrapper
* wrapper
= (JSValueWrapper
*)ptr
->GetData();
145 result
= wrapper
->GetValue();
151 case kJSUserObjectDataTypeCFType
:
153 CFTypeRef cfType
= (CFTypeRef
*)ptr
->GetData();
156 CFTypeID typeID
= CFGetTypeID(cfType
);
157 if (typeID
== CFStringGetTypeID())
159 result
= jsString(getThreadGlobalExecState(), CFStringToUString((CFStringRef
)cfType
));
162 else if (typeID
== CFNumberGetTypeID())
165 CFNumberGetValue((CFNumberRef
)cfType
, kCFNumberDoubleType
, &num
);
166 result
= jsNumber(getThreadGlobalExecState(), num
);
169 else if (typeID
== CFBooleanGetTypeID())
171 result
= jsBoolean(CFBooleanGetValue((CFBooleanRef
)cfType
));
174 else if (typeID
== CFNullGetTypeID())
185 ExecState
* exec
= getThreadGlobalExecState();
186 result
= new (exec
) UserObjectImp(getThreadGlobalObject()->userObjectStructure(), ptr
);
195 //--------------------------------------------------------------------------
196 // KJSValueToCFTypeInternal
197 //--------------------------------------------------------------------------
198 // Caller is responsible for releasing the returned CFTypeRef
199 CFTypeRef
KJSValueToCFTypeInternal(JSValue
* inValue
, ExecState
*exec
, ObjectImpList
* inImps
)
204 CFTypeRef result
= 0;
208 if (inValue
->isBoolean())
210 result
= inValue
->toBoolean(exec
) ? kCFBooleanTrue
: kCFBooleanFalse
;
211 RetainCFType(result
);
215 if (inValue
->isString())
217 UString uString
= inValue
->toString(exec
);
218 result
= UStringToCFString(uString
);
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
);
233 result
= CFNumberCreate(0, kCFNumberDoubleType
, &number1
);
238 if (inValue
->isObject())
240 if (inValue
->isObject(&UserObjectImp::info
)) {
241 UserObjectImp
* userObjectImp
= static_cast<UserObjectImp
*>(asObject(inValue
));
242 JSUserObject
* ptr
= userObjectImp
->GetJSUserObject();
245 result
= ptr
->CopyCFValue();
250 JSObject
*object
= inValue
->toObject(exec
);
251 UInt8 isArray
= false;
253 // if two objects reference each
254 JSObject
* imp
= object
;
255 ObjectImpList
* temp
= inImps
;
257 if (imp
== temp
->imp
) {
258 return CFRetain(GetCFNull());
268 //[...] HACK since we do not have access to the class info we use class name instead
270 if (object
->inherits(&ArrayInstanceImp::info
))
272 if (object
->className() == "Array")
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();
289 if (uniChars
[size
] < '0' || uniChars
[size
] > '9') {
301 // This is an KJS array
302 unsigned int length
= object
->get(exec
, Identifier(exec
, "length"))->toUInt32(exec
);
303 result
= CFArrayCreateMutable(0, 0, &kCFTypeArrayCallBacks
);
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
);
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
);
326 PropertyNameArray::const_iterator iter
= propNames
.begin();
327 PropertyNameArray::const_iterator end
= propNames
.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
);
351 if (inValue
->isUndefinedOrNull())
353 result
= RetainCFType(GetCFNull());
357 ASSERT_NOT_REACHED();
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();
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
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
)
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
));
411 RefPtr
<JSGlobalData
> globalData
= JSGlobalData::create();
412 globalObject
= new (globalData
.get()) JSGlueGlobalObject(JSGlueGlobalObject::createStructureID(jsNull()));
413 gcProtect(globalObject
);
414 pthread_setspecific(globalObjectKey
, 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();