| /* | |
| * 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 <JavaScriptCore/PropertyNameArray.h> | |
| 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<UserObjectImp *>(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<UserObjectImp *>(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; | |
| JSGlueGlobalObject* globalObject = static_cast<JSGlueGlobalObject*>(exec->dynamicGlobalObject()); | |
| if (globalObject && (globalObject->Flags() & kJSFlagConvertAssociativeArray)) { | |
| PropertyNameArray propNames; | |
| object->getPropertyNames(exec, propNames); | |
| PropertyNameArray::const_iterator iter = propNames.begin(); | |
| PropertyNameArray::const_iterator 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) | |
| { | |
| PropertyNameArray::const_iterator iter = propNames.begin(); | |
| PropertyNameArray::const_iterator 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; | |
| } | |