1/* 2 * Copyright (C) 2005, 2008, 2009 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 "UserObjectImp.h" 31 32#include <JavaScriptCore/JSString.h> 33#include <JavaScriptCore/PropertyNameArray.h> 34 35const ClassInfo UserObjectImp::s_info = { "UserObject", &JSNonFinalObject::s_info, 0, 0 }; 36 37UserObjectImp::UserObjectImp(JSGlobalData& globalData, Structure* structure, JSUserObject* userObject) 38 : JSNonFinalObject(globalData, structure) 39 , fJSUserObject((JSUserObject*)userObject->Retain()) 40{ 41} 42 43UserObjectImp::~UserObjectImp() 44{ 45 if (fJSUserObject) 46 fJSUserObject->Release(); 47} 48 49CallType UserObjectImp::getCallData(CallData& callData) 50{ 51 return fJSUserObject ? fJSUserObject->getCallData(callData) : CallTypeNone; 52} 53 54JSValue UserObjectImp::callAsFunction(ExecState *exec) 55{ 56 JSValue result = jsUndefined(); 57 JSUserObject* jsThisObj = KJSValueToJSObject(exec->hostThisValue().toThisObject(exec), exec); 58 if (jsThisObj) { 59 CFIndex argCount = exec->argumentCount(); 60 CFArrayCallBacks arrayCallBacks; 61 JSTypeGetCFArrayCallBacks(&arrayCallBacks); 62 CFMutableArrayRef jsArgs = CFArrayCreateMutable(0, 0, &arrayCallBacks); 63 if (jsArgs) { 64 for (CFIndex i = 0; i < argCount; i++) { 65 JSUserObject* jsArg = KJSValueToJSObject(exec->argument(i), exec); 66 CFArrayAppendValue(jsArgs, (void*)jsArg); 67 jsArg->Release(); 68 } 69 } 70 71 JSUserObject* jsResult; 72 { // scope 73 JSGlueAPICallback apiCallback(exec); 74 75 // getCallData should have guarded against a NULL fJSUserObject. 76 assert(fJSUserObject); 77 jsResult = fJSUserObject->CallFunction(jsThisObj, jsArgs); 78 } 79 80 if (jsResult) { 81 result = JSObjectKJSValue(jsResult); 82 jsResult->Release(); 83 } 84 85 ReleaseCFType(jsArgs); 86 jsThisObj->Release(); 87 } 88 return result; 89} 90 91 92void UserObjectImp::getOwnPropertyNames(ExecState *exec, PropertyNameArray& propertyNames, EnumerationMode mode) 93{ 94 JSUserObject* ptr = GetJSUserObject(); 95 if (ptr) { 96 CFArrayRef cfPropertyNames = ptr->CopyPropertyNames(); 97 if (cfPropertyNames) { 98 CFIndex count = CFArrayGetCount(cfPropertyNames); 99 CFIndex i; 100 for (i = 0; i < count; i++) { 101 CFStringRef propertyName = (CFStringRef)CFArrayGetValueAtIndex(cfPropertyNames, i); 102 propertyNames.add(CFStringToIdentifier(propertyName, exec)); 103 } 104 CFRelease(cfPropertyNames); 105 } 106 } 107 JSObject::getOwnPropertyNames(exec, propertyNames, mode); 108} 109 110JSValue UserObjectImp::userObjectGetter(ExecState*, JSValue slotBase, const Identifier& propertyName) 111{ 112 UserObjectImp *thisObj = static_cast<UserObjectImp *>(asObject(slotBase)); 113 // getOwnPropertySlot should have guarded against a null fJSUserObject. 114 assert(thisObj->fJSUserObject); 115 116 CFStringRef cfPropName = IdentifierToCFString(propertyName); 117 JSUserObject *jsResult = thisObj->fJSUserObject->CopyProperty(cfPropName); 118 ReleaseCFType(cfPropName); 119 JSValue result = JSObjectKJSValue(jsResult); 120 jsResult->Release(); 121 122 return result; 123} 124 125bool UserObjectImp::getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot& slot) 126{ 127 if (!fJSUserObject) 128 return false; 129 130 CFStringRef cfPropName = IdentifierToCFString(propertyName); 131 JSUserObject *jsResult = fJSUserObject->CopyProperty(cfPropName); 132 ReleaseCFType(cfPropName); 133 if (jsResult) { 134 slot.setCustom(this, userObjectGetter); 135 jsResult->Release(); 136 return true; 137 } else { 138 JSValue kjsValue = toPrimitive(exec); 139 if (!kjsValue.isUndefinedOrNull()) { 140 JSObject* kjsObject = kjsValue.toObject(exec); 141 if (kjsObject->getPropertySlot(exec, propertyName, slot)) 142 return true; 143 } 144 } 145 return JSObject::getOwnPropertySlot(exec, propertyName, slot); 146} 147 148void UserObjectImp::put(ExecState *exec, const Identifier &propertyName, JSValue value, PutPropertySlot&) 149{ 150 if (!fJSUserObject) 151 return; 152 153 CFStringRef cfPropName = IdentifierToCFString(propertyName); 154 JSUserObject *jsValueObj = KJSValueToJSObject(value, exec); 155 156 fJSUserObject->SetProperty(cfPropName, jsValueObj); 157 158 if (jsValueObj) jsValueObj->Release(); 159 ReleaseCFType(cfPropName); 160} 161 162JSUserObject* UserObjectImp::GetJSUserObject() const 163{ 164 return fJSUserObject; 165} 166 167JSValue UserObjectImp::toPrimitive(ExecState *exec, PreferredPrimitiveType) const 168{ 169 JSValue result = jsUndefined(); 170 JSUserObject* jsObjPtr = KJSValueToJSObject(toObject(exec, exec->lexicalGlobalObject()), exec); 171 CFTypeRef cfValue = jsObjPtr ? jsObjPtr->CopyCFValue() : 0; 172 if (cfValue) { 173 CFTypeID cfType = CFGetTypeID(cfValue); // toPrimitive 174 if (cfValue == GetCFNull()) { 175 result = jsNull(); 176 } 177 else if (cfType == CFBooleanGetTypeID()) { 178 if (cfValue == kCFBooleanTrue) { 179 result = jsBoolean(true); 180 } else { 181 result = jsBoolean(false); 182 } 183 } else if (cfType == CFStringGetTypeID()) { 184 result = jsString(exec, CFStringToUString((CFStringRef)cfValue)); 185 } else if (cfType == CFNumberGetTypeID()) { 186 double d = 0.0; 187 CFNumberGetValue((CFNumberRef)cfValue, kCFNumberDoubleType, &d); 188 result = jsNumber(d); 189 } else if (cfType == CFURLGetTypeID()) { 190 CFURLRef absURL = CFURLCopyAbsoluteURL((CFURLRef)cfValue); 191 if (absURL) { 192 result = jsString(exec, CFStringToUString(CFURLGetString(absURL))); 193 ReleaseCFType(absURL); 194 } 195 } 196 ReleaseCFType(cfValue); 197 } 198 if (jsObjPtr) 199 jsObjPtr->Release(); 200 return result; 201} 202 203 204bool UserObjectImp::toBoolean(ExecState *exec) const 205{ 206 bool result = false; 207 JSUserObject* jsObjPtr = KJSValueToJSObject(toObject(exec, exec->lexicalGlobalObject()), exec); 208 CFTypeRef cfValue = jsObjPtr ? jsObjPtr->CopyCFValue() : 0; 209 if (cfValue) 210 { 211 CFTypeID cfType = CFGetTypeID(cfValue); // toPrimitive 212 if (cfValue == GetCFNull()) 213 { 214 // 215 } 216 else if (cfType == CFBooleanGetTypeID()) 217 { 218 if (cfValue == kCFBooleanTrue) 219 { 220 result = true; 221 } 222 } 223 else if (cfType == CFStringGetTypeID()) 224 { 225 if (CFStringGetLength((CFStringRef)cfValue)) 226 { 227 result = true; 228 } 229 } 230 else if (cfType == CFNumberGetTypeID()) 231 { 232 if (cfValue != kCFNumberNaN) 233 { 234 double d; 235 if (CFNumberGetValue((CFNumberRef)cfValue, kCFNumberDoubleType, &d)) 236 { 237 if (d != 0) 238 { 239 result = true; 240 } 241 } 242 } 243 } 244 else if (cfType == CFArrayGetTypeID()) 245 { 246 if (CFArrayGetCount((CFArrayRef)cfValue)) 247 { 248 result = true; 249 } 250 } 251 else if (cfType == CFDictionaryGetTypeID()) 252 { 253 if (CFDictionaryGetCount((CFDictionaryRef)cfValue)) 254 { 255 result = true; 256 } 257 } 258 else if (cfType == CFSetGetTypeID()) 259 { 260 if (CFSetGetCount((CFSetRef)cfValue)) 261 { 262 result = true; 263 } 264 } 265 else if (cfType == CFURLGetTypeID()) 266 { 267 CFURLRef absURL = CFURLCopyAbsoluteURL((CFURLRef)cfValue); 268 if (absURL) 269 { 270 CFStringRef cfStr = CFURLGetString(absURL); 271 if (cfStr && CFStringGetLength(cfStr)) 272 { 273 result = true; 274 } 275 ReleaseCFType(absURL); 276 } 277 } 278 } 279 if (jsObjPtr) jsObjPtr->Release(); 280 ReleaseCFType(cfValue); 281 return result; 282} 283 284double UserObjectImp::toNumber(ExecState *exec) const 285{ 286 double result = 0; 287 JSUserObject* jsObjPtr = KJSValueToJSObject(toObject(exec, exec->lexicalGlobalObject()), exec); 288 CFTypeRef cfValue = jsObjPtr ? jsObjPtr->CopyCFValue() : 0; 289 if (cfValue) 290 { 291 CFTypeID cfType = CFGetTypeID(cfValue); 292 293 if (cfValue == GetCFNull()) 294 { 295 // 296 } 297 else if (cfType == CFBooleanGetTypeID()) 298 { 299 if (cfValue == kCFBooleanTrue) 300 { 301 result = 1; 302 } 303 } 304 else if (cfType == CFStringGetTypeID()) 305 { 306 result = CFStringGetDoubleValue((CFStringRef)cfValue); 307 } 308 else if (cfType == CFNumberGetTypeID()) 309 { 310 CFNumberGetValue((CFNumberRef)cfValue, kCFNumberDoubleType, &result); 311 } 312 } 313 ReleaseCFType(cfValue); 314 if (jsObjPtr) jsObjPtr->Release(); 315 return result; 316} 317 318UString UserObjectImp::toString(ExecState *exec) const 319{ 320 UString result; 321 JSUserObject* jsObjPtr = KJSValueToJSObject(toObject(exec, exec->lexicalGlobalObject()), exec); 322 CFTypeRef cfValue = jsObjPtr ? jsObjPtr->CopyCFValue() : 0; 323 if (cfValue) 324 { 325 CFTypeID cfType = CFGetTypeID(cfValue); 326 if (cfValue == GetCFNull()) 327 { 328 // 329 } 330 else if (cfType == CFBooleanGetTypeID()) 331 { 332 if (cfValue == kCFBooleanTrue) 333 { 334 result = "true"; 335 } 336 else 337 { 338 result = "false"; 339 } 340 } 341 else if (cfType == CFStringGetTypeID()) 342 { 343 result = CFStringToUString((CFStringRef)cfValue); 344 } 345 else if (cfType == CFNumberGetTypeID()) 346 { 347 if (cfValue == kCFNumberNaN) 348 { 349 result = "Nan"; 350 } 351 else if (CFNumberCompare(kCFNumberPositiveInfinity, (CFNumberRef)cfValue, 0) == 0) 352 { 353 result = "Infinity"; 354 } 355 else if (CFNumberCompare(kCFNumberNegativeInfinity, (CFNumberRef)cfValue, 0) == 0) 356 { 357 result = "-Infinity"; 358 } 359 else 360 { 361 CFStringRef cfNumStr; 362 double d = 0; 363 CFNumberGetValue((CFNumberRef)cfValue, kCFNumberDoubleType, &d); 364 if (CFNumberIsFloatType((CFNumberRef)cfValue)) 365 { 366 cfNumStr = CFStringCreateWithFormat(0, 0, CFSTR("%f"), d); 367 } 368 else 369 { 370 cfNumStr = CFStringCreateWithFormat(0, 0, CFSTR("%.0f"), d); 371 } 372 result = CFStringToUString(cfNumStr); 373 ReleaseCFType(cfNumStr); 374 } 375 } 376 else if (cfType == CFArrayGetTypeID()) 377 { 378 // 379 } 380 else if (cfType == CFDictionaryGetTypeID()) 381 { 382 // 383 } 384 else if (cfType == CFSetGetTypeID()) 385 { 386 // 387 } 388 else if (cfType == CFURLGetTypeID()) 389 { 390 CFURLRef absURL = CFURLCopyAbsoluteURL((CFURLRef)cfValue); 391 if (absURL) 392 { 393 CFStringRef cfStr = CFURLGetString(absURL); 394 if (cfStr) 395 { 396 result = CFStringToUString(cfStr); 397 } 398 ReleaseCFType(absURL); 399 } 400 } 401 } 402 ReleaseCFType(cfValue); 403 if (jsObjPtr) jsObjPtr->Release(); 404 return result; 405} 406 407void UserObjectImp::markChildren(MarkStack& markStack) 408{ 409 JSObject::markChildren(markStack); 410 if (fJSUserObject) 411 fJSUserObject->Mark(); 412} 413