/* * Copyright (C) 2005 Apple Computer, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #include "JSUtils.h" #include "JSBase.h" #include "JSObject.h" #include "JSRun.h" #include "UserObjectImp.h" #include "JSValueWrapper.h" #include "JSObject.h" #include struct ObjectImpList { JSObject* imp; ObjectImpList* next; CFTypeRef data; }; static CFTypeRef KJSValueToCFTypeInternal(JSValue *inValue, ExecState *exec, ObjectImpList* inImps); //-------------------------------------------------------------------------- // CFStringToUString //-------------------------------------------------------------------------- UString CFStringToUString(CFStringRef inCFString) { UString result; if (inCFString) { CFIndex len = CFStringGetLength(inCFString); UniChar* buffer = (UniChar*)malloc(sizeof(UniChar) * len); if (buffer) { CFStringGetCharacters(inCFString, CFRangeMake(0, len), buffer); result = UString((const UChar *)buffer, len); free(buffer); } } return result; } //-------------------------------------------------------------------------- // UStringToCFString //-------------------------------------------------------------------------- // Caller is responsible for releasing the returned CFStringRef CFStringRef UStringToCFString(const UString& inUString) { return CFStringCreateWithCharacters(0, (const UniChar*)inUString.data(), inUString.size()); } //-------------------------------------------------------------------------- // CFStringToIdentifier //-------------------------------------------------------------------------- Identifier CFStringToIdentifier(CFStringRef inCFString) { return Identifier(CFStringToUString(inCFString)); } //-------------------------------------------------------------------------- // IdentifierToCFString //-------------------------------------------------------------------------- // Caller is responsible for releasing the returned CFStringRef CFStringRef IdentifierToCFString(const Identifier& inIdentifier) { return UStringToCFString(inIdentifier.ustring()); } //-------------------------------------------------------------------------- // KJSValueToJSObject //-------------------------------------------------------------------------- JSUserObject* KJSValueToJSObject(JSValue *inValue, ExecState *exec) { JSUserObject* result = 0; if (inValue->isObject(&UserObjectImp::info)) { UserObjectImp* userObjectImp = static_cast(inValue); result = userObjectImp->GetJSUserObject(); if (result) result->Retain(); } else { JSValueWrapper* wrapperValue = new JSValueWrapper(inValue); if (wrapperValue) { JSObjectCallBacks callBacks; JSValueWrapper::GetJSObectCallBacks(callBacks); result = (JSUserObject*)JSObjectCreate(wrapperValue, &callBacks); if (!result) { delete wrapperValue; } } } return result; } //-------------------------------------------------------------------------- // JSObjectKJSValue //-------------------------------------------------------------------------- JSValue *JSObjectKJSValue(JSUserObject* ptr) { JSLock lock; JSValue *result = jsUndefined(); if (ptr) { bool handled = false; switch (ptr->DataType()) { case kJSUserObjectDataTypeJSValueWrapper: { JSValueWrapper* wrapper = (JSValueWrapper*)ptr->GetData(); if (wrapper) { result = wrapper->GetValue(); handled = true; } break; } case kJSUserObjectDataTypeCFType: { CFTypeRef cfType = (CFTypeRef*)ptr->GetData(); if (cfType) { CFTypeID typeID = CFGetTypeID(cfType); if (typeID == CFStringGetTypeID()) { result = jsString(CFStringToUString((CFStringRef)cfType)); handled = true; } else if (typeID == CFNumberGetTypeID()) { double num; CFNumberGetValue((CFNumberRef)cfType, kCFNumberDoubleType, &num); result = jsNumber(num); handled = true; } else if (typeID == CFBooleanGetTypeID()) { result = jsBoolean(CFBooleanGetValue((CFBooleanRef)cfType)); handled = true; } else if (typeID == CFNullGetTypeID()) { result = jsNull(); handled = true; } } break; } } if (!handled) { result = new UserObjectImp(ptr); } } return result; } //-------------------------------------------------------------------------- // KJSValueToCFTypeInternal //-------------------------------------------------------------------------- // Caller is responsible for releasing the returned CFTypeRef CFTypeRef KJSValueToCFTypeInternal(JSValue *inValue, ExecState *exec, ObjectImpList* inImps) { if (!inValue) return 0; CFTypeRef result = 0; JSLock lock; switch (inValue->type()) { case BooleanType: { result = inValue->toBoolean(exec) ? kCFBooleanTrue : kCFBooleanFalse; RetainCFType(result); } break; case StringType: { UString uString = inValue->toString(exec); result = UStringToCFString(uString); } break; case NumberType: { double number1 = inValue->toNumber(exec); double number2 = (double)inValue->toInteger(exec); if (number1 == number2) { int intValue = (int)number2; result = CFNumberCreate(0, kCFNumberIntType, &intValue); } else { result = CFNumberCreate(0, kCFNumberDoubleType, &number1); } } break; case ObjectType: { if (inValue->isObject(&UserObjectImp::info)) { UserObjectImp* userObjectImp = static_cast(inValue); JSUserObject* ptr = userObjectImp->GetJSUserObject(); if (ptr) { result = ptr->CopyCFValue(); } } else { JSObject *object = inValue->toObject(exec); UInt8 isArray = false; // if two objects reference each JSObject* imp = object; ObjectImpList* temp = inImps; while (temp) { if (imp == temp->imp) { return CFRetain(GetCFNull()); } temp = temp->next; } ObjectImpList imps; imps.next = inImps; imps.imp = imp; //[...] HACK since we do not have access to the class info we use class name instead #if 0 if (object->inherits(&ArrayInstanceImp::info)) #else if (object->className() == "Array") #endif { isArray = true; JSInterpreter* intrepreter = (JSInterpreter*)exec->dynamicInterpreter(); if (intrepreter && (intrepreter->Flags() & kJSFlagConvertAssociativeArray)) { PropertyNameArray propNames; object->getPropertyNames(exec, propNames); PropertyNameArrayIterator iter = propNames.begin(); PropertyNameArrayIterator end = propNames.end(); while(iter != end && isArray) { Identifier propName = *iter; UString ustr = propName.ustring(); const UniChar* uniChars = (const UniChar*)ustr.data(); int size = ustr.size(); while (size--) { if (uniChars[size] < '0' || uniChars[size] > '9') { isArray = false; break; } } iter++; } } } if (isArray) { // This is an KJS array unsigned int length = object->get(exec, "length")->toUInt32(exec); result = CFArrayCreateMutable(0, 0, &kCFTypeArrayCallBacks); if (result) { for (unsigned i = 0; i < length; i++) { CFTypeRef cfValue = KJSValueToCFTypeInternal(object->get(exec, i), exec, &imps); CFArrayAppendValue((CFMutableArrayRef)result, cfValue); ReleaseCFType(cfValue); } } } else { // Not an array, just treat it like a dictionary which contains (property name, property value) pairs PropertyNameArray propNames; object->getPropertyNames(exec, propNames); { result = CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); if (result) { PropertyNameArrayIterator iter = propNames.begin(); PropertyNameArrayIterator end = propNames.end(); while(iter != end) { Identifier propName = *iter; if (object->hasProperty(exec, propName)) { CFStringRef cfKey = IdentifierToCFString(propName); CFTypeRef cfValue = KJSValueToCFTypeInternal(object->get(exec, propName), exec, &imps); if (cfKey && cfValue) { CFDictionaryAddValue((CFMutableDictionaryRef)result, cfKey, cfValue); } ReleaseCFType(cfKey); ReleaseCFType(cfValue); } iter++; } } } } } } break; case NullType: case UndefinedType: case UnspecifiedType: result = RetainCFType(GetCFNull()); break; default: fprintf(stderr, "KJSValueToCFType: wrong value type %d\n", inValue->type()); break; } return result; } CFTypeRef KJSValueToCFType(JSValue *inValue, ExecState *exec) { return KJSValueToCFTypeInternal(inValue, exec, 0); } CFTypeRef GetCFNull(void) { static CFArrayRef sCFNull = CFArrayCreate(0, 0, 0, 0); CFTypeRef result = JSGetCFNull(); if (!result) { result = sCFNull; } return result; }