| /* |
| * 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 "JavaScriptGlue.h" |
| #include "JSUtils.h" |
| #include "JSBase.h" |
| #include "JSObject.h" |
| #include "JSRun.h" |
| #include <JavaScriptCore/Completion.h> |
| #include <JavaScriptCore/InitializeThreading.h> |
| |
| static CFTypeRef sJSCFNullRef = 0; |
| |
| static void CFJSObjectDispose(void *data); |
| static JSObjectRef CFJSObjectCopyProperty(void *data, CFStringRef propertyName); |
| static void CFJSObjectSetProperty(void *data, CFStringRef propertyName, JSObjectRef jsValue); |
| static CFTypeRef CFJSObjectCopyCFValue(void *data); |
| static UInt8 CFJSObjectEqual(void *data1, void *data2); |
| static CFArrayRef CFJSObjectCopyPropertyNames(void *data); |
| |
| void *JSCFRetain(CFAllocatorRef allocator, const void *value); |
| void JSCFRelease(CFAllocatorRef allocator, const void *value); |
| |
| |
| void JSSetCFNull(CFTypeRef nullRef) |
| { |
| ReleaseCFType(sJSCFNullRef); |
| sJSCFNullRef = RetainCFType(nullRef); |
| } |
| |
| CFTypeRef JSGetCFNull(void) |
| { |
| return sJSCFNullRef; |
| } |
| |
| /* |
| JSRetain |
| */ |
| JSTypeRef JSRetain(JSTypeRef ref) |
| { |
| if (ref) |
| { |
| JSBase* ptr = (JSBase*)ref; |
| ptr->Retain(); |
| } |
| return ref; |
| } |
| |
| /* |
| JSRelease |
| */ |
| void JSRelease(JSTypeRef ref) |
| { |
| if (ref) |
| { |
| JSBase* ptr = (JSBase*)ref; |
| ptr->Release(); |
| } |
| } |
| |
| /* |
| JSCopyDescription |
| */ |
| CFStringRef JSCopyDescription(JSTypeRef ref) |
| { |
| CFStringRef result = 0; |
| if (ref) |
| { |
| JSBase* ptr = (JSBase*)ref; |
| ptr->CopyDescription(); |
| } |
| return result; |
| } |
| |
| /* |
| JSEqual |
| */ |
| UInt8 JSEqual(JSTypeRef ref1, JSTypeRef ref2) |
| { |
| UInt8 result = false; |
| if (ref1 && ref2) |
| { |
| JSBase* ptr = (JSBase*)ref1; |
| result = ptr->Equal((JSBase*)ref2); |
| } |
| return result; |
| } |
| |
| |
| /* |
| JSGetTypeID |
| */ |
| JSTypeID JSGetTypeID(JSTypeRef ref) |
| { |
| JSTypeID result = kJSInvalidTypeID; |
| if (ref) |
| { |
| JSBase* ptr = (JSBase*)ref; |
| result = ptr->GetTypeID(); |
| } |
| return result; |
| } |
| |
| |
| /* |
| JSGetRetainCount |
| */ |
| CFIndex JSGetRetainCount(JSTypeRef ref) |
| { |
| CFIndex result = -1; |
| if (ref) |
| { |
| JSBase* ptr = (JSBase*)ref; |
| result = ptr->RetainCount(); |
| } |
| return result; |
| } |
| |
| |
| |
| /* |
| JSObjectCreate |
| */ |
| JSObjectRef JSObjectCreate(void *data, JSObjectCallBacksPtr callBacks) |
| { |
| JSObjectRef result = JSObjectCreateInternal(data, callBacks, 0, kJSUserObjectDataTypeUnknown); |
| return result; |
| } |
| |
| /* |
| JSObjectCreateInternal |
| */ |
| JSObjectRef JSObjectCreateInternal(void *data, JSObjectCallBacksPtr callBacks, JSObjectMarkProcPtr markProc, int type) |
| { |
| JSObjectRef result = 0; |
| JSUserObject* ptr = new JSUserObject(callBacks, markProc, data, type); |
| result = (JSObjectRef)ptr; |
| return result; |
| } |
| |
| /* |
| JSObjectCopyCFValue |
| */ |
| CFTypeRef JSObjectCopyCFValue(JSObjectRef ref) |
| { |
| CFTypeRef result = 0; |
| JSUserObject* ptr = (JSUserObject*)ref; |
| if (ptr && (ptr->GetTypeID() == kJSObjectTypeID)) |
| { |
| result = ptr->CopyCFValue(); |
| } |
| return result; |
| } |
| |
| /* |
| JSObjectGetData |
| */ |
| void *JSObjectGetData(JSObjectRef ref) |
| { |
| void *result = 0; |
| JSUserObject* ptr = (JSUserObject*)ref; |
| if (ptr && (ptr->GetTypeID() == kJSObjectTypeID)) |
| { |
| result = ptr->GetData(); |
| } |
| return result; |
| } |
| |
| |
| /* |
| JSObjectCopyProperty |
| */ |
| JSObjectRef JSObjectCopyProperty(JSObjectRef ref, CFStringRef propertyName) |
| { |
| JSObjectRef result = 0; |
| JSUserObject* ptr = (JSUserObject*)ref; |
| if (ptr && (ptr->GetTypeID() == kJSObjectTypeID)) |
| { |
| result = (JSObjectRef)ptr->CopyProperty(propertyName); |
| } |
| return result; |
| } |
| |
| |
| /* |
| JSObjectSetProperty |
| */ |
| void JSObjectSetProperty(JSObjectRef ref, CFStringRef propertyName, JSObjectRef value) |
| { |
| JSUserObject* ptr = (JSUserObject*)ref; |
| if (ptr && (ptr->GetTypeID() == kJSObjectTypeID)) |
| { |
| ptr->SetProperty(propertyName, (JSUserObject*)value); |
| } |
| } |
| |
| |
| /* |
| JSObjectCallFunction |
| */ |
| JSObjectRef JSObjectCallFunction(JSObjectRef ref, JSObjectRef thisObj, CFArrayRef args) |
| { |
| JSObjectRef result = 0; |
| JSUserObject* ptr = (JSUserObject*)ref; |
| if (ptr && (ptr->GetTypeID() == kJSObjectTypeID)) |
| { |
| result = (JSObjectRef)ptr->CallFunction((JSUserObject*)thisObj, args); |
| } |
| return result; |
| } |
| |
| |
| /* |
| JSRunCreate |
| */ |
| JSRunRef JSRunCreate(CFStringRef jsSource, JSFlags inFlags) |
| { |
| initializeThreading(); |
| |
| JSRunRef result = 0; |
| if (jsSource) |
| { |
| JSGlueAPIEntry entry; |
| result = (JSRunRef) new JSRun(jsSource, inFlags); |
| } |
| return result; |
| } |
| |
| /* |
| JSRunCopySource |
| */ |
| CFStringRef JSRunCopySource(JSRunRef ref) |
| { |
| CFStringRef result = 0; |
| JSRun* ptr = (JSRun*)ref; |
| if (ptr) |
| { |
| result = UStringToCFString(ptr->GetSource()); |
| } |
| return result; |
| } |
| |
| |
| /* |
| JSRunCopyGlobalObject |
| */ |
| JSObjectRef JSRunCopyGlobalObject(JSRunRef ref) |
| { |
| JSObjectRef result = 0; |
| JSRun* ptr = (JSRun*)ref; |
| if (ptr) |
| { |
| JSGlobalObject* globalObject = ptr->GlobalObject(); |
| result = (JSObjectRef)KJSValueToJSObject(globalObject, globalObject->globalExec()); |
| } |
| return result; |
| } |
| |
| /* |
| JSRunEvaluate |
| */ |
| JSObjectRef JSRunEvaluate(JSRunRef ref) |
| { |
| JSObjectRef result = 0; |
| JSRun* ptr = (JSRun*)ref; |
| if (ptr) |
| { |
| JSGlueAPIEntry entry; |
| Completion completion = ptr->Evaluate(); |
| if (completion.isValueCompletion()) |
| { |
| result = (JSObjectRef)KJSValueToJSObject(completion.value(), ptr->GlobalObject()->globalExec()); |
| } |
| |
| if (completion.complType() == Throw) |
| { |
| JSFlags flags = ptr->Flags(); |
| if (flags & kJSFlagDebug) |
| { |
| CFTypeRef error = JSObjectCopyCFValue(result); |
| if (error) |
| { |
| CFShow(error); |
| CFRelease(error); |
| } |
| } |
| } |
| } |
| return result; |
| } |
| |
| /* |
| JSRunCheckSyntax |
| Return true if no syntax error |
| */ |
| bool JSRunCheckSyntax(JSRunRef ref) |
| { |
| bool result = false; |
| JSRun* ptr = (JSRun*)ref; |
| if (ptr) |
| { |
| JSGlueAPIEntry entry; |
| result = ptr->CheckSyntax(); |
| } |
| return result; |
| } |
| |
| /* |
| JSCollect - trigger garbage collection |
| */ |
| void JSCollect() |
| { |
| initializeThreading(); |
| |
| JSGlueAPIEntry entry; |
| Heap* heap = getThreadGlobalExecState()->heap(); |
| if (!heap->isBusy()) |
| heap->collectAllGarbage(); |
| } |
| |
| /* |
| JSTypeGetCFArrayCallBacks |
| */ |
| void JSTypeGetCFArrayCallBacks(CFArrayCallBacks* outCallBacks) |
| { |
| if (outCallBacks) |
| { |
| outCallBacks->version = 1; |
| outCallBacks->retain = (CFArrayRetainCallBack)JSCFRetain; |
| outCallBacks->release = (CFArrayReleaseCallBack)JSCFRelease; |
| outCallBacks->copyDescription = (CFArrayCopyDescriptionCallBack)JSCopyDescription; |
| outCallBacks->equal = (CFArrayEqualCallBack)JSEqual; |
| } |
| } |
| |
| |
| /* |
| JSCFRetain |
| */ |
| void *JSCFRetain(CFAllocatorRef allocator, const void *value) |
| { |
| JSRetain((JSTypeRef)value); |
| return (void*)value; |
| } |
| |
| /* |
| JSCFRelease |
| */ |
| void JSCFRelease(CFAllocatorRef allocator, const void *value) |
| { |
| JSRelease((JSTypeRef)value); |
| } |
| |
| |
| /* |
| JSObjectCreateWithCFType |
| */ |
| JSObjectRef JSObjectCreateWithCFType(CFTypeRef inRef) |
| { |
| JSObjectCallBacks callBacks; |
| JSObjectRef cfJSObject = nil; |
| if (inRef) |
| { |
| callBacks.dispose = CFJSObjectDispose; |
| callBacks.equal = CFJSObjectEqual; |
| callBacks.copyCFValue = CFJSObjectCopyCFValue; |
| callBacks.copyProperty = CFJSObjectCopyProperty; |
| callBacks.setProperty = CFJSObjectSetProperty; |
| callBacks.callFunction = 0; |
| callBacks.copyPropertyNames = CFJSObjectCopyPropertyNames; |
| cfJSObject = JSObjectCreateInternal((void*)CFRetain(inRef), &callBacks, 0, kJSUserObjectDataTypeCFType ); |
| } |
| return cfJSObject; |
| } |
| |
| /* |
| CFJSObjectDispose |
| */ |
| void CFJSObjectDispose(void *data) |
| { |
| if (data) |
| { |
| CFRelease((JSTypeRef)data); |
| } |
| } |
| |
| CFArrayRef JSObjectCopyPropertyNames(JSObjectRef ref) |
| { |
| CFArrayRef result = 0; |
| JSUserObject* ptr = (JSUserObject*)ref; |
| if (ptr && (ptr->GetTypeID() == kJSObjectTypeID)) |
| { |
| result = ptr->CopyPropertyNames(); |
| } |
| return result; |
| } |
| /* |
| CFJSObjectCopyProperty |
| */ |
| JSObjectRef CFJSObjectCopyProperty(void *data, CFStringRef propertyName) |
| { |
| JSObjectRef result = 0; |
| if (data && propertyName) |
| { |
| CFTypeRef cfResult = 0; |
| if (CFGetTypeID(data) == CFDictionaryGetTypeID()) |
| { |
| if (CFStringCompare(propertyName, CFSTR("length"), 0) == kCFCompareEqualTo) |
| { |
| int len = CFDictionaryGetCount((CFDictionaryRef)data); |
| cfResult = CFNumberCreate(0, kCFNumberIntType, &len); |
| } |
| else |
| { |
| cfResult = RetainCFType(CFDictionaryGetValue((CFDictionaryRef)data, propertyName)); |
| } |
| } |
| else if (CFGetTypeID(data) == CFArrayGetTypeID()) |
| { |
| if (CFStringCompare(propertyName, CFSTR("length"), 0) == kCFCompareEqualTo) |
| { |
| int len = CFArrayGetCount((CFArrayRef)data); |
| cfResult = CFNumberCreate(0, kCFNumberIntType, &len); |
| } |
| else |
| { |
| SInt32 index = CFStringGetIntValue(propertyName); |
| CFIndex arrayCount = CFArrayGetCount((CFArrayRef)data); |
| if (index >= 0 && index < arrayCount) |
| { |
| cfResult = RetainCFType(CFArrayGetValueAtIndex((CFArrayRef)data, index)); |
| } |
| } |
| } |
| else if (CFGetTypeID(data) == CFStringGetTypeID()) |
| { |
| if (CFStringCompare(propertyName, CFSTR("length"), 0) == kCFCompareEqualTo) |
| { |
| int len = CFStringGetLength((CFStringRef)data); |
| cfResult = CFNumberCreate(0, kCFNumberIntType, &len); |
| } |
| } |
| if (cfResult) |
| { |
| result = JSObjectCreateWithCFType(cfResult); |
| CFRelease(cfResult); |
| } |
| } |
| return result; |
| } |
| |
| |
| /* |
| CFJSObjectSetProperty |
| */ |
| void CFJSObjectSetProperty(void *data, CFStringRef propertyName, JSObjectRef jsValue) |
| { |
| if (data && propertyName) |
| { |
| CFTypeRef cfValue = JSObjectCopyCFValue(jsValue); |
| |
| if (cfValue) |
| { |
| if (CFGetTypeID(data) == CFDictionaryGetTypeID()) |
| { |
| CFDictionarySetValue((CFMutableDictionaryRef)data, propertyName, cfValue); |
| } |
| else if (CFGetTypeID(data) == CFArrayGetTypeID()) |
| { |
| SInt32 index = CFStringGetIntValue(propertyName); |
| CFIndex arrayCount = CFArrayGetCount((CFArrayRef)data); |
| if (index >= 0) |
| { |
| for (; arrayCount < index; arrayCount++) |
| { |
| CFArrayAppendValue((CFMutableArrayRef)data, GetCFNull()); |
| } |
| CFArraySetValueAtIndex((CFMutableArrayRef)data, index, cfValue); |
| } |
| } |
| CFRelease(cfValue); |
| } |
| else |
| { |
| if (CFGetTypeID(data) == CFDictionaryGetTypeID()) |
| { |
| CFDictionaryRemoveValue((CFMutableDictionaryRef)data, propertyName); |
| } |
| else if (CFGetTypeID(data) == CFArrayGetTypeID()) |
| { |
| SInt32 index = CFStringGetIntValue(propertyName); |
| CFIndex arrayCount = CFArrayGetCount((CFArrayRef)data); |
| if (index >= 0) |
| { |
| for (; arrayCount < index; arrayCount++) |
| { |
| CFArrayAppendValue((CFMutableArrayRef)data, GetCFNull()); |
| } |
| CFArraySetValueAtIndex((CFMutableArrayRef)data, index, GetCFNull()); |
| } |
| } |
| } |
| } |
| } |
| |
| |
| /* |
| CFJSObjectCopyCFValue |
| */ |
| CFTypeRef CFJSObjectCopyCFValue(void *data) |
| { |
| CFTypeRef result = 0; |
| if (data) |
| { |
| result = (CFTypeRef)CFRetain(data); |
| } |
| return result; |
| } |
| |
| /* |
| CFJSObjectCopyCFValue |
| */ |
| UInt8 CFJSObjectEqual(void *data1, void *data2) |
| { |
| UInt8 result = false; |
| if (data1 && data2) |
| { |
| CFEqual((CFTypeRef)data1, (CFTypeRef)data2); |
| } |
| return result; |
| } |
| |
| |
| /* |
| CFJSObjectCopyPropertyNames |
| */ |
| CFArrayRef CFJSObjectCopyPropertyNames(void *data) |
| { |
| CFMutableArrayRef result = 0; |
| if (data) |
| { |
| CFTypeID cfType = CFGetTypeID(data); |
| if (cfType == CFDictionaryGetTypeID()) |
| { |
| CFIndex count = CFDictionaryGetCount((CFDictionaryRef)data); |
| if (count) |
| { |
| CFTypeRef* keys = (CFTypeRef*)malloc(sizeof(CFTypeRef)*count); |
| if (keys) |
| { |
| int i; |
| CFDictionaryGetKeysAndValues((CFDictionaryRef)data, (const void **)keys, 0); |
| for (i = 0; i < count; i++) |
| { |
| CFStringRef key = (CFStringRef)keys[i]; |
| if (CFGetTypeID(key) != CFStringGetTypeID()) continue; |
| |
| if (!result) result = CFArrayCreateMutable(0, 0, &kCFTypeArrayCallBacks); |
| if (!result) continue; |
| |
| CFArrayAppendValue(result, key); |
| } |
| free(keys); |
| } |
| } |
| } |
| } |
| return result; |
| } |
| |
| |
| |
| |
| CFMutableArrayRef JSCreateCFArrayFromJSArray(CFArrayRef array) |
| { |
| CFIndex count = array ? CFArrayGetCount(array) : 0; |
| CFMutableArrayRef cfArray = CFArrayCreateMutable(0, 0, &kCFTypeArrayCallBacks); |
| CFIndex i; |
| |
| for (i = 0; cfArray && i < count; i++) |
| { |
| JSObjectRef jsValue = (JSObjectRef)CFArrayGetValueAtIndex(array, i); |
| CFTypeRef cfvalue = JSObjectCopyCFValue(jsValue); |
| if (cfvalue) |
| { |
| CFArrayAppendValue(cfArray, cfvalue); |
| CFRelease(cfvalue); |
| } |
| else |
| { |
| CFArrayAppendValue(cfArray, GetCFNull()); |
| } |
| } |
| return cfArray; |
| } |
| |
| CFMutableArrayRef JSCreateJSArrayFromCFArray(CFArrayRef array) |
| { |
| initializeThreading(); |
| |
| CFIndex count = array ? CFArrayGetCount(array) : 0; |
| CFArrayCallBacks arrayCallbacks; |
| CFMutableArrayRef jsArray; |
| CFIndex i; |
| |
| JSTypeGetCFArrayCallBacks(&arrayCallbacks); |
| jsArray = CFArrayCreateMutable(0, 0, &arrayCallbacks); |
| |
| for (i = 0; array && i < count; i++) |
| { |
| CFTypeRef cfValue = (CFTypeRef)CFArrayGetValueAtIndex(array, i); |
| JSObjectRef jsValue = JSObjectCreateWithCFType(cfValue); |
| |
| if (!jsValue) jsValue = JSObjectCreateWithCFType(GetCFNull()); |
| if (jsValue) |
| { |
| CFArrayAppendValue(jsArray, jsValue); |
| JSRelease(jsValue); |
| } |
| } |
| return jsArray; |
| } |
| |
| |
| void JSLockInterpreter() |
| { |
| initializeThreading(); |
| JSLock::lock(LockForReal); |
| } |
| |
| |
| void JSUnlockInterpreter() |
| { |
| JSLock::unlock(LockForReal); |
| } |