• 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 #include <JavaScriptCore/WTFThreadData.h>
40 
41 struct ObjectImpList {
42     JSObject* imp;
43     ObjectImpList* next;
44     CFTypeRef data;
45 };
46 
47 static CFTypeRef KJSValueToCFTypeInternal(JSValue inValue, ExecState *exec, ObjectImpList* inImps);
48 static JSGlueGlobalObject* getThreadGlobalObject();
49 
50 //--------------------------------------------------------------------------
51 // CFStringToUString
52 //--------------------------------------------------------------------------
53 
CFStringToUString(CFStringRef inCFString)54 UString CFStringToUString(CFStringRef inCFString)
55 {
56     UString result;
57     if (inCFString) {
58         CFIndex len = CFStringGetLength(inCFString);
59         UniChar* buffer = (UniChar*)malloc(sizeof(UniChar) * len);
60         if (buffer)
61         {
62             CFStringGetCharacters(inCFString, CFRangeMake(0, len), buffer);
63             result = UString((const UChar *)buffer, len);
64             free(buffer);
65         }
66     }
67     return result;
68 }
69 
70 
71 //--------------------------------------------------------------------------
72 // UStringToCFString
73 //--------------------------------------------------------------------------
74 // Caller is responsible for releasing the returned CFStringRef
UStringToCFString(const UString & inUString)75 CFStringRef UStringToCFString(const UString& inUString)
76 {
77     return CFStringCreateWithCharacters(0, (const UniChar*)inUString.characters(), inUString.length());
78 }
79 
80 
81 //--------------------------------------------------------------------------
82 // CFStringToIdentifier
83 //--------------------------------------------------------------------------
84 
CFStringToIdentifier(CFStringRef inCFString,ExecState * exec)85 Identifier CFStringToIdentifier(CFStringRef inCFString, ExecState* exec)
86 {
87     return Identifier(exec, CFStringToUString(inCFString));
88 }
89 
90 
91 //--------------------------------------------------------------------------
92 // IdentifierToCFString
93 //--------------------------------------------------------------------------
94 // Caller is responsible for releasing the returned CFStringRef
IdentifierToCFString(const Identifier & inIdentifier)95 CFStringRef IdentifierToCFString(const Identifier& inIdentifier)
96 {
97     return UStringToCFString(inIdentifier.ustring());
98 }
99 
100 
101 //--------------------------------------------------------------------------
102 // KJSValueToJSObject
103 //--------------------------------------------------------------------------
KJSValueToJSObject(JSValue inValue,ExecState * exec)104 JSUserObject* KJSValueToJSObject(JSValue inValue, ExecState *exec)
105 {
106     JSUserObject* result = 0;
107 
108     if (inValue.inherits(&UserObjectImp::s_info)) {
109         UserObjectImp* userObjectImp = static_cast<UserObjectImp *>(asObject(inValue));
110         result = userObjectImp->GetJSUserObject();
111         if (result)
112             result->Retain();
113     } else {
114         JSValueWrapper* wrapperValue = new JSValueWrapper(inValue);
115         if (wrapperValue) {
116             JSObjectCallBacks callBacks;
117             JSValueWrapper::GetJSObectCallBacks(callBacks);
118             result = (JSUserObject*)JSObjectCreate(wrapperValue, &callBacks);
119             if (!result) {
120                 delete wrapperValue;
121             }
122         }
123     }
124     return result;
125 }
126 
127 //--------------------------------------------------------------------------
128 // JSObjectKJSValue
129 //--------------------------------------------------------------------------
JSObjectKJSValue(JSUserObject * ptr)130 JSValue JSObjectKJSValue(JSUserObject* ptr)
131 {
132     JSGlueAPIEntry entry;
133 
134     JSValue result = jsUndefined();
135     if (ptr)
136     {
137         bool handled = false;
138 
139         switch (ptr->DataType())
140         {
141             case kJSUserObjectDataTypeJSValueWrapper:
142             {
143                 JSValueWrapper* wrapper = (JSValueWrapper*)ptr->GetData();
144                 if (wrapper)
145                 {
146                     result = wrapper->GetValue();
147                     handled = true;
148                 }
149                 break;
150             }
151 
152             case kJSUserObjectDataTypeCFType:
153             {
154                 CFTypeRef cfType = (CFTypeRef*)ptr->GetData();
155                 if (cfType)
156                 {
157                     CFTypeID typeID = CFGetTypeID(cfType);
158                     if (typeID == CFStringGetTypeID())
159                     {
160                         result = jsString(getThreadGlobalExecState(), CFStringToUString((CFStringRef)cfType));
161                         handled = true;
162                     }
163                     else if (typeID == CFNumberGetTypeID())
164                     {
165                         double num;
166                         CFNumberGetValue((CFNumberRef)cfType, kCFNumberDoubleType, &num);
167                         result = jsNumber(num);
168                         handled = true;
169                     }
170                     else if (typeID == CFBooleanGetTypeID())
171                     {
172                         result = jsBoolean(CFBooleanGetValue((CFBooleanRef)cfType));
173                         handled = true;
174                     }
175                     else if (typeID == CFNullGetTypeID())
176                     {
177                         result = jsNull();
178                         handled = true;
179                     }
180                 }
181                 break;
182             }
183         }
184         if (!handled)
185         {
186             ExecState* exec = getThreadGlobalExecState();
187             result = new (exec) UserObjectImp(exec->globalData(), getThreadGlobalObject()->userObjectStructure(), ptr);
188         }
189     }
190     return result;
191 }
192 
193 
194 
195 
196 //--------------------------------------------------------------------------
197 // KJSValueToCFTypeInternal
198 //--------------------------------------------------------------------------
199 // Caller is responsible for releasing the returned CFTypeRef
KJSValueToCFTypeInternal(JSValue inValue,ExecState * exec,ObjectImpList * inImps)200 CFTypeRef KJSValueToCFTypeInternal(JSValue inValue, ExecState *exec, ObjectImpList* inImps)
201 {
202     if (!inValue)
203         return 0;
204 
205     CFTypeRef result = 0;
206 
207     JSGlueAPIEntry entry;
208 
209         if (inValue.isBoolean())
210             {
211                 result = inValue.toBoolean(exec) ? kCFBooleanTrue : kCFBooleanFalse;
212                 RetainCFType(result);
213                 return result;
214             }
215 
216         if (inValue.isString())
217             {
218                 UString uString = inValue.toString(exec);
219                 result = UStringToCFString(uString);
220                 return result;
221             }
222 
223         if (inValue.isNumber())
224             {
225                 double number1 = inValue.toNumber(exec);
226                 double number2 = (double)inValue.toInteger(exec);
227                 if (number1 ==  number2)
228                 {
229                     int intValue = (int)number2;
230                     result = CFNumberCreate(0, kCFNumberIntType, &intValue);
231                 }
232                 else
233                 {
234                     result = CFNumberCreate(0, kCFNumberDoubleType, &number1);
235                 }
236                 return result;
237             }
238 
239         if (inValue.isObject())
240             {
241                 if (inValue.inherits(&UserObjectImp::s_info)) {
242                     UserObjectImp* userObjectImp = static_cast<UserObjectImp *>(asObject(inValue));
243                     JSUserObject* ptr = userObjectImp->GetJSUserObject();
244                     if (ptr)
245                     {
246                         result = ptr->CopyCFValue();
247                     }
248                 }
249                 else
250                 {
251                     JSObject *object = inValue.toObject(exec);
252                     UInt8 isArray = false;
253 
254                     // if two objects reference each
255                     JSObject* imp = object;
256                     ObjectImpList* temp = inImps;
257                     while (temp) {
258                         if (imp == temp->imp) {
259                             return CFRetain(GetCFNull());
260                         }
261                         temp = temp->next;
262                     }
263 
264                     ObjectImpList imps;
265                     imps.next = inImps;
266                     imps.imp = imp;
267 
268 
269 //[...] HACK since we do not have access to the class info we use class name instead
270 #if 0
271                     if (object->inherits(&ArrayInstanceImp::s_info))
272 #else
273                     if (object->className() == "Array")
274 #endif
275                     {
276                         isArray = true;
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)
284                             {
285                                 Identifier propName = *iter;
286                                 UString ustr = propName.ustring();
287                                 const UniChar* uniChars = (const UniChar*)ustr.characters();
288                                 int size = ustr.length();
289                                 while (size--) {
290                                     if (uniChars[size] < '0' || uniChars[size] > '9') {
291                                         isArray = false;
292                                         break;
293                                     }
294                                 }
295                                 iter++;
296                             }
297                         }
298                     }
299 
300                     if (isArray)
301                     {
302                         // This is an KJS array
303                         unsigned int length = object->get(exec, Identifier(exec, "length")).toUInt32(exec);
304                         result = CFArrayCreateMutable(0, 0, &kCFTypeArrayCallBacks);
305                         if (result)
306                         {
307                             for (unsigned i = 0; i < length; i++)
308                             {
309                                 CFTypeRef cfValue = KJSValueToCFTypeInternal(object->get(exec, i), exec, &imps);
310                                 CFArrayAppendValue((CFMutableArrayRef)result, cfValue);
311                                 ReleaseCFType(cfValue);
312                             }
313                         }
314                     }
315                     else
316                     {
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);
320                         {
321                             result = CFDictionaryCreateMutable(0,
322                                                                0,
323                                                                &kCFTypeDictionaryKeyCallBacks,
324                                                                &kCFTypeDictionaryValueCallBacks);
325                             if (result)
326                             {
327                                 PropertyNameArray::const_iterator iter = propNames.begin();
328                                 PropertyNameArray::const_iterator end = propNames.end();
329                                 while(iter != end)
330                                 {
331                                     Identifier propName = *iter;
332                                     if (object->hasProperty(exec, propName))
333                                     {
334                                         CFStringRef cfKey = IdentifierToCFString(propName);
335                                         CFTypeRef cfValue = KJSValueToCFTypeInternal(object->get(exec, propName), exec, &imps);
336                                         if (cfKey && cfValue)
337                                         {
338                                             CFDictionaryAddValue((CFMutableDictionaryRef)result, cfKey, cfValue);
339                                         }
340                                         ReleaseCFType(cfKey);
341                                         ReleaseCFType(cfValue);
342                                     }
343                                     iter++;
344                                 }
345                             }
346                         }
347                     }
348                 }
349                 return result;
350             }
351 
352     if (inValue.isUndefinedOrNull())
353         {
354             result = RetainCFType(GetCFNull());
355             return result;
356         }
357 
358     ASSERT_NOT_REACHED();
359     return 0;
360 }
361 
KJSValueToCFType(JSValue inValue,ExecState * exec)362 CFTypeRef KJSValueToCFType(JSValue inValue, ExecState *exec)
363 {
364     return KJSValueToCFTypeInternal(inValue, exec, 0);
365 }
366 
GetCFNull(void)367 CFTypeRef GetCFNull(void)
368 {
369     static CFArrayRef sCFNull = CFArrayCreate(0, 0, 0, 0);
370     CFTypeRef result = JSGetCFNull();
371     if (!result)
372     {
373         result = sCFNull;
374     }
375     return result;
376 }
377 
378 /*
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
382  * where necessary.
383 
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.
391  */
392 
393 static pthread_key_t globalObjectKey;
394 static pthread_once_t globalObjectKeyOnce = PTHREAD_ONCE_INIT;
395 
unprotectGlobalObject(void * data)396 static void unprotectGlobalObject(void* data)
397 {
398     JSGlueAPIEntry entry;
399     gcUnprotect(static_cast<JSGlueGlobalObject*>(data));
400 }
401 
initializeGlobalObjectKey()402 static void initializeGlobalObjectKey()
403 {
404     pthread_key_create(&globalObjectKey, unprotectGlobalObject);
405 }
406 
getThreadGlobalData()407 JSGlobalData* getThreadGlobalData()
408 {
409     return &JSGlobalData::sharedInstance();
410 }
411 
getThreadGlobalObject()412 static JSGlueGlobalObject* getThreadGlobalObject()
413 {
414     pthread_once(&globalObjectKeyOnce, initializeGlobalObjectKey);
415     JSGlueGlobalObject* globalObject = static_cast<JSGlueGlobalObject*>(pthread_getspecific(globalObjectKey));
416     if (!globalObject) {
417         globalObject = new (getThreadGlobalData()) JSGlueGlobalObject(*getThreadGlobalData(), JSGlueGlobalObject::createStructure(*getThreadGlobalData(), jsNull()));
418         gcProtect(globalObject);
419         pthread_setspecific(globalObjectKey, globalObject);
420     }
421     return globalObject;
422 }
423 
getThreadGlobalExecState()424 ExecState* getThreadGlobalExecState()
425 {
426     ExecState* exec = getThreadGlobalObject()->globalExec();
427 
428     // Discard exceptions -- otherwise an exception would forestall JS
429     // evaluation throughout the thread
430     exec->clearException();
431     return exec;
432 }
433 
JSGlueAPIEntry()434 JSGlueAPIEntry::JSGlueAPIEntry()
435     : m_lock(LockForReal)
436     , m_storedIdentifierTable(wtfThreadData().currentIdentifierTable())
437 {
438     wtfThreadData().setCurrentIdentifierTable(getThreadGlobalData()->identifierTable);
439 }
440 
~JSGlueAPIEntry()441 JSGlueAPIEntry::~JSGlueAPIEntry()
442 {
443     wtfThreadData().setCurrentIdentifierTable(m_storedIdentifierTable);
444 }
445 
JSGlueAPICallback(ExecState * exec)446 JSGlueAPICallback::JSGlueAPICallback(ExecState* exec)
447     : m_dropLocks(exec)
448 {
449     wtfThreadData().resetCurrentIdentifierTable();
450 }
451 
~JSGlueAPICallback()452 JSGlueAPICallback::~JSGlueAPICallback()
453 {
454     wtfThreadData().setCurrentIdentifierTable(getThreadGlobalData()->identifierTable);
455 }
456