• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
CFStringToUString(CFStringRef inCFString)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
UStringToCFString(const UString & inUString)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 
CFStringToIdentifier(CFStringRef inCFString,ExecState * exec)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
IdentifierToCFString(const Identifier & inIdentifier)94 CFStringRef IdentifierToCFString(const Identifier& inIdentifier)
95 {
96     return UStringToCFString(inIdentifier.ustring());
97 }
98 
99 
100 //--------------------------------------------------------------------------
101 // KJSValueToJSObject
102 //--------------------------------------------------------------------------
KJSValueToJSObject(JSValue inValue,ExecState * exec)103 JSUserObject* KJSValueToJSObject(JSValue inValue, ExecState *exec)
104 {
105     JSUserObject* result = 0;
106 
107     if (inValue.inherits(&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 //--------------------------------------------------------------------------
JSObjectKJSValue(JSUserObject * ptr)129 JSValue JSObjectKJSValue(JSUserObject* ptr)
130 {
131     JSGlueAPIEntry entry;
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
KJSValueToCFTypeInternal(JSValue inValue,ExecState * exec,ObjectImpList * inImps)199 CFTypeRef KJSValueToCFTypeInternal(JSValue inValue, ExecState *exec, ObjectImpList* inImps)
200 {
201     if (!inValue)
202         return 0;
203 
204     CFTypeRef result = 0;
205 
206     JSGlueAPIEntry entry;
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.inherits(&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 
KJSValueToCFType(JSValue inValue,ExecState * exec)361 CFTypeRef KJSValueToCFType(JSValue inValue, ExecState *exec)
362 {
363     return KJSValueToCFTypeInternal(inValue, exec, 0);
364 }
365 
GetCFNull(void)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 
unprotectGlobalObject(void * data)395 static void unprotectGlobalObject(void* data)
396 {
397     JSGlueAPIEntry entry;
398     gcUnprotect(static_cast<JSGlueGlobalObject*>(data));
399 }
400 
initializeGlobalObjectKey()401 static void initializeGlobalObjectKey()
402 {
403     pthread_key_create(&globalObjectKey, unprotectGlobalObject);
404 }
405 
getThreadGlobalObject()406 static JSGlueGlobalObject* getThreadGlobalObject()
407 {
408     pthread_once(&globalObjectKeyOnce, initializeGlobalObjectKey);
409     JSGlueGlobalObject* globalObject = static_cast<JSGlueGlobalObject*>(pthread_getspecific(globalObjectKey));
410     if (!globalObject) {
411         globalObject = new (&JSGlobalData::sharedInstance()) JSGlueGlobalObject(JSGlueGlobalObject::createStructure(jsNull()));
412         gcProtect(globalObject);
413         pthread_setspecific(globalObjectKey, globalObject);
414     }
415     return globalObject;
416 }
417 
getThreadGlobalExecState()418 ExecState* getThreadGlobalExecState()
419 {
420     ExecState* exec = getThreadGlobalObject()->globalExec();
421 
422     // Discard exceptions -- otherwise an exception would forestall JS
423     // evaluation throughout the thread
424     exec->clearException();
425     return exec;
426 }
427 
JSGlueAPIEntry()428 JSGlueAPIEntry::JSGlueAPIEntry()
429     : m_lock(LockForReal)
430     , m_storedIdentifierTable(currentIdentifierTable())
431 {
432     setCurrentIdentifierTable(getThreadGlobalObject()->globalExec()->globalData().identifierTable);
433 }
434 
~JSGlueAPIEntry()435 JSGlueAPIEntry::~JSGlueAPIEntry()
436 {
437     setCurrentIdentifierTable(m_storedIdentifierTable);
438 }
439 
JSGlueAPICallback(ExecState * exec)440 JSGlueAPICallback::JSGlueAPICallback(ExecState* exec)
441     : m_dropLocks(exec)
442 {
443     resetCurrentIdentifierTable();
444 }
445 
~JSGlueAPICallback()446 JSGlueAPICallback::~JSGlueAPICallback()
447 {
448     setCurrentIdentifierTable(getThreadGlobalObject()->globalExec()->globalData().identifierTable);
449 }
450