Home | History | Annotate | Download | only in JavaScriptGlue
      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 
     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
     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 
     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
     95 CFStringRef IdentifierToCFString(const Identifier& inIdentifier)
     96 {
     97     return UStringToCFString(inIdentifier.ustring());
     98 }
     99 
    100 
    101 //--------------------------------------------------------------------------
    102 // KJSValueToJSObject
    103 //--------------------------------------------------------------------------
    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 //--------------------------------------------------------------------------
    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
    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 
    362 CFTypeRef KJSValueToCFType(JSValue inValue, ExecState *exec)
    363 {
    364     return KJSValueToCFTypeInternal(inValue, exec, 0);
    365 }
    366 
    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 
    396 static void unprotectGlobalObject(void* data)
    397 {
    398     JSGlueAPIEntry entry;
    399     gcUnprotect(static_cast<JSGlueGlobalObject*>(data));
    400 }
    401 
    402 static void initializeGlobalObjectKey()
    403 {
    404     pthread_key_create(&globalObjectKey, unprotectGlobalObject);
    405 }
    406 
    407 JSGlobalData* getThreadGlobalData()
    408 {
    409     return &JSGlobalData::sharedInstance();
    410 }
    411 
    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 
    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 
    434 JSGlueAPIEntry::JSGlueAPIEntry()
    435     : m_lock(LockForReal)
    436     , m_storedIdentifierTable(wtfThreadData().currentIdentifierTable())
    437 {
    438     wtfThreadData().setCurrentIdentifierTable(getThreadGlobalData()->identifierTable);
    439 }
    440 
    441 JSGlueAPIEntry::~JSGlueAPIEntry()
    442 {
    443     wtfThreadData().setCurrentIdentifierTable(m_storedIdentifierTable);
    444 }
    445 
    446 JSGlueAPICallback::JSGlueAPICallback(ExecState* exec)
    447     : m_dropLocks(exec)
    448 {
    449     wtfThreadData().resetCurrentIdentifierTable();
    450 }
    451 
    452 JSGlueAPICallback::~JSGlueAPICallback()
    453 {
    454     wtfThreadData().setCurrentIdentifierTable(getThreadGlobalData()->identifierTable);
    455 }
    456