/*
* 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;
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;
}