From 8e35f3cfc7fba1d1c829dc557ebad6409cbe16a2 Mon Sep 17 00:00:00 2001 From: The Android Open Source Project Date: Tue, 3 Mar 2009 19:30:52 -0800 Subject: auto import from //depot/cupcake/@135843 --- JavaScriptGlue/JSUtils.cpp | 427 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 427 insertions(+) create mode 100644 JavaScriptGlue/JSUtils.cpp (limited to 'JavaScriptGlue/JSUtils.cpp') diff --git a/JavaScriptGlue/JSUtils.cpp b/JavaScriptGlue/JSUtils.cpp new file mode 100644 index 0000000..f5425c4 --- /dev/null +++ b/JavaScriptGlue/JSUtils.cpp @@ -0,0 +1,427 @@ +/* + * Copyright (C) 2005, 2008 Apple 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 "JSValueWrapper.h" +#include "UserObjectImp.h" +#include +#include + +struct ObjectImpList { + JSObject* imp; + ObjectImpList* next; + CFTypeRef data; +}; + +static CFTypeRef KJSValueToCFTypeInternal(JSValue* inValue, ExecState *exec, ObjectImpList* inImps); +static JSGlueGlobalObject* getThreadGlobalObject(); + +//-------------------------------------------------------------------------- +// 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, ExecState* exec) +{ + return Identifier(exec, 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(asObject(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(true); + + 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(getThreadGlobalExecState(), CFStringToUString((CFStringRef)cfType)); + handled = true; + } + else if (typeID == CFNumberGetTypeID()) + { + double num; + CFNumberGetValue((CFNumberRef)cfType, kCFNumberDoubleType, &num); + result = jsNumber(getThreadGlobalExecState(), 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) + { + ExecState* exec = getThreadGlobalExecState(); + result = new (exec) UserObjectImp(getThreadGlobalObject()->userObjectStructure(), 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(true); + + if (inValue->isBoolean()) + { + result = inValue->toBoolean(exec) ? kCFBooleanTrue : kCFBooleanFalse; + RetainCFType(result); + return result; + } + + if (inValue->isString()) + { + UString uString = inValue->toString(exec); + result = UStringToCFString(uString); + return result; + } + + if (inValue->isNumber()) + { + 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); + } + return result; + } + + if (inValue->isObject()) + { + if (inValue->isObject(&UserObjectImp::info)) { + UserObjectImp* userObjectImp = static_cast(asObject(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(exec->dynamicGlobalObject()); + if (globalObject && (globalObject->Flags() & kJSFlagConvertAssociativeArray)) { + PropertyNameArray propNames(exec); + 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, Identifier(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(exec); + 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++; + } + } + } + } + } + return result; + } + + if (inValue->isUndefinedOrNull()) + { + result = RetainCFType(GetCFNull()); + return result; + } + + ASSERT_NOT_REACHED(); + return 0; +} + +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; +} + +/* + * This is a slight hack. The JSGlue API has no concept of execution state. + * However, execution state is an inherent part of JS, and JSCore requires it. + * So, we keep a single execution state for the whole thread and supply it + * where necessary. + + * The execution state holds two things: (1) exceptions; (2) the global object. + * JSGlue has no API for accessing exceptions, so we just discard them. As for + * the global object, JSGlue includes no calls that depend on it. Its property + * getters and setters are per-object; they don't walk up the enclosing scope. + * Functions called by JSObjectCallFunction may reference values in the enclosing + * scope, but they do so through an internally stored scope chain, so we don't + * need to supply the global scope. + */ + +static pthread_key_t globalObjectKey; +static pthread_once_t globalObjectKeyOnce = PTHREAD_ONCE_INIT; + +static void unprotectGlobalObject(void* data) +{ + JSLock lock(true); + gcUnprotect(static_cast(data)); +} + +static void initializeGlobalObjectKey() +{ + pthread_key_create(&globalObjectKey, unprotectGlobalObject); +} + +static JSGlueGlobalObject* getThreadGlobalObject() +{ + pthread_once(&globalObjectKeyOnce, initializeGlobalObjectKey); + JSGlueGlobalObject* globalObject = static_cast(pthread_getspecific(globalObjectKey)); + if (!globalObject) { + RefPtr globalData = JSGlobalData::create(); + globalObject = new (globalData.get()) JSGlueGlobalObject(JSGlueGlobalObject::createStructureID(jsNull())); + gcProtect(globalObject); + pthread_setspecific(globalObjectKey, globalObject); + } + return globalObject; +} + +ExecState* getThreadGlobalExecState() +{ + ExecState* exec = getThreadGlobalObject()->globalExec(); + + // Discard exceptions -- otherwise an exception would forestall JS + // evaluation throughout the thread + exec->clearException(); + return exec; +} -- cgit v1.1