diff options
Diffstat (limited to 'JavaScriptCore/bindings')
61 files changed, 12660 insertions, 0 deletions
diff --git a/JavaScriptCore/bindings/NP_jsobject.cpp b/JavaScriptCore/bindings/NP_jsobject.cpp new file mode 100644 index 0000000..059b5df --- /dev/null +++ b/JavaScriptCore/bindings/NP_jsobject.cpp @@ -0,0 +1,406 @@ +/* + * Copyright (C) 2004, 2006 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 COMPUTER, INC. OR + * 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" + +#if !PLATFORM(DARWIN) || !defined(__LP64__) + +#include "NP_jsobject.h" + +#include "JSGlobalObject.h" +#include "PropertyNameArray.h" +#include "c_utility.h" +#include "interpreter.h" +#include "npruntime_impl.h" +#include "npruntime_priv.h" +#include "object.h" +#include "runtime_root.h" + +using namespace KJS; +using namespace KJS::Bindings; + +static void getListFromVariantArgs(ExecState* exec, const NPVariant* args, unsigned argCount, RootObject* rootObject, List& aList) +{ + for (unsigned i = 0; i < argCount; i++) + aList.append(convertNPVariantToValue(exec, &args[i], rootObject)); +} + +static NPObject* jsAllocate(NPP, NPClass*) +{ + return (NPObject*)malloc(sizeof(JavaScriptObject)); +} + +static void jsDeallocate(NPObject* npObj) +{ + JavaScriptObject* obj = (JavaScriptObject*)npObj; + + if (obj->rootObject && obj->rootObject->isValid()) + obj->rootObject->gcUnprotect(obj->imp); + + if (obj->rootObject) + obj->rootObject->deref(); + + free(obj); +} + +static NPClass javascriptClass = { 1, jsAllocate, jsDeallocate, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +static NPClass noScriptClass = { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + +NPClass* NPScriptObjectClass = &javascriptClass; +static NPClass* NPNoScriptObjectClass = &noScriptClass; + +NPObject* _NPN_CreateScriptObject(NPP npp, JSObject* imp, PassRefPtr<RootObject> rootObject) +{ + JavaScriptObject* obj = (JavaScriptObject*)_NPN_CreateObject(npp, NPScriptObjectClass); + + obj->rootObject = rootObject.releaseRef(); + + if (obj->rootObject) + obj->rootObject->gcProtect(imp); + obj->imp = imp; + + return (NPObject*)obj; +} + +NPObject *_NPN_CreateNoScriptObject(void) +{ + return _NPN_CreateObject(0, NPNoScriptObjectClass); +} + +bool _NPN_InvokeDefault(NPP, NPObject* o, const NPVariant* args, uint32_t argCount, NPVariant* result) +{ + if (o->_class == NPScriptObjectClass) { + JavaScriptObject* obj = (JavaScriptObject*)o; + + VOID_TO_NPVARIANT(*result); + + // Lookup the function object. + RootObject* rootObject = obj->rootObject; + if (!rootObject || !rootObject->isValid()) + return false; + + ExecState* exec = rootObject->globalObject()->globalExec(); + JSLock lock; + + // Call the function object. + JSObject *funcImp = static_cast<JSObject*>(obj->imp); + if (!funcImp->implementsCall()) + return false; + + List argList; + getListFromVariantArgs(exec, args, argCount, rootObject, argList); + rootObject->globalObject()->startTimeoutCheck(); + JSValue *resultV = funcImp->call (exec, funcImp, argList); + rootObject->globalObject()->stopTimeoutCheck(); + + // Convert and return the result of the function call. + convertValueToNPVariant(exec, resultV, result); + return true; + } + + if (o->_class->invokeDefault) + return o->_class->invokeDefault(o, args, argCount, result); + VOID_TO_NPVARIANT(*result); + return true; +} + +bool _NPN_Invoke(NPP npp, NPObject* o, NPIdentifier methodName, const NPVariant* args, uint32_t argCount, NPVariant* result) +{ + if (o->_class == NPScriptObjectClass) { + JavaScriptObject* obj = (JavaScriptObject*)o; + + PrivateIdentifier* i = (PrivateIdentifier*)methodName; + if (!i->isString) + return false; + + // Special case the "eval" method. + if (methodName == _NPN_GetStringIdentifier("eval")) { + if (argCount != 1) + return false; + if (args[0].type != NPVariantType_String) + return false; + return _NPN_Evaluate(npp, o, (NPString *)&args[0].value.stringValue, result); + } + + // Lookup the function object. + RootObject* rootObject = obj->rootObject; + if (!rootObject || !rootObject->isValid()) + return false; + + ExecState* exec = rootObject->globalObject()->globalExec(); + JSLock lock; + JSValue* func = obj->imp->get(exec, identifierFromNPIdentifier(i->value.string)); + if (func->isNull()) { + NULL_TO_NPVARIANT(*result); + return false; + } + if (func->isUndefined()) { + VOID_TO_NPVARIANT(*result); + return false; + } + // Call the function object. + JSObject *funcImp = static_cast<JSObject*>(func); + JSObject *thisObj = const_cast<JSObject*>(obj->imp); + List argList; + getListFromVariantArgs(exec, args, argCount, rootObject, argList); + rootObject->globalObject()->startTimeoutCheck(); + JSValue *resultV = funcImp->call (exec, thisObj, argList); + rootObject->globalObject()->stopTimeoutCheck(); + + // Convert and return the result of the function call. + convertValueToNPVariant(exec, resultV, result); + return true; + } + + if (o->_class->invoke) + return o->_class->invoke(o, methodName, args, argCount, result); + + VOID_TO_NPVARIANT(*result); + return true; +} + +bool _NPN_Evaluate(NPP, NPObject* o, NPString* s, NPVariant* variant) +{ + if (o->_class == NPScriptObjectClass) { + JavaScriptObject* obj = (JavaScriptObject*)o; + + RootObject* rootObject = obj->rootObject; + if (!rootObject || !rootObject->isValid()) + return false; + + ExecState* exec = rootObject->globalObject()->globalExec(); + + JSLock lock; + NPUTF16* scriptString; + unsigned int UTF16Length; + convertNPStringToUTF16(s, &scriptString, &UTF16Length); // requires free() of returned memory + rootObject->globalObject()->startTimeoutCheck(); + Completion completion = Interpreter::evaluate(rootObject->globalObject()->globalExec(), UString(), 0, UString(reinterpret_cast<const UChar*>(scriptString), UTF16Length)); + rootObject->globalObject()->stopTimeoutCheck(); + ComplType type = completion.complType(); + + JSValue* result; + if (type == Normal) { + result = completion.value(); + if (!result) + result = jsUndefined(); + } else + result = jsUndefined(); + + free(scriptString); + + convertValueToNPVariant(exec, result, variant); + + return true; + } + + VOID_TO_NPVARIANT(*variant); + return false; +} + +bool _NPN_GetProperty(NPP, NPObject* o, NPIdentifier propertyName, NPVariant* variant) +{ + if (o->_class == NPScriptObjectClass) { + JavaScriptObject* obj = (JavaScriptObject*)o; + + RootObject* rootObject = obj->rootObject; + if (!rootObject || !rootObject->isValid()) + return false; + + ExecState* exec = rootObject->globalObject()->globalExec(); + PrivateIdentifier* i = (PrivateIdentifier*)propertyName; + + JSLock lock; + JSValue *result; + if (i->isString) + result = obj->imp->get(exec, identifierFromNPIdentifier(i->value.string)); + else + result = obj->imp->get(exec, i->value.number); + + convertValueToNPVariant(exec, result, variant); + return true; + } + + if (o->_class->hasProperty && o->_class->getProperty) { + if (o->_class->hasProperty(o, propertyName)) + return o->_class->getProperty(o, propertyName, variant); + return false; + } + + VOID_TO_NPVARIANT(*variant); + return false; +} + +bool _NPN_SetProperty(NPP, NPObject* o, NPIdentifier propertyName, const NPVariant* variant) +{ + if (o->_class == NPScriptObjectClass) { + JavaScriptObject* obj = (JavaScriptObject*)o; + + RootObject* rootObject = obj->rootObject; + if (!rootObject || !rootObject->isValid()) + return false; + + ExecState* exec = rootObject->globalObject()->globalExec(); + JSLock lock; + PrivateIdentifier* i = (PrivateIdentifier*)propertyName; + if (i->isString) + obj->imp->put(exec, identifierFromNPIdentifier(i->value.string), convertNPVariantToValue(exec, variant, rootObject)); + else + obj->imp->put(exec, i->value.number, convertNPVariantToValue(exec, variant, rootObject)); + return true; + } + + if (o->_class->setProperty) + return o->_class->setProperty(o, propertyName, variant); + + return false; +} + +bool _NPN_RemoveProperty(NPP, NPObject* o, NPIdentifier propertyName) +{ + if (o->_class == NPScriptObjectClass) { + JavaScriptObject* obj = (JavaScriptObject*)o; + + RootObject* rootObject = obj->rootObject; + if (!rootObject || !rootObject->isValid()) + return false; + + ExecState* exec = rootObject->globalObject()->globalExec(); + PrivateIdentifier* i = (PrivateIdentifier*)propertyName; + if (i->isString) { + if (!obj->imp->hasProperty(exec, identifierFromNPIdentifier(i->value.string))) + return false; + } else { + if (!obj->imp->hasProperty(exec, i->value.number)) + return false; + } + + JSLock lock; + if (i->isString) + obj->imp->deleteProperty(exec, identifierFromNPIdentifier(i->value.string)); + else + obj->imp->deleteProperty(exec, i->value.number); + + return true; + } + return false; +} + +bool _NPN_HasProperty(NPP, NPObject* o, NPIdentifier propertyName) +{ + if (o->_class == NPScriptObjectClass) { + JavaScriptObject* obj = (JavaScriptObject*)o; + + RootObject* rootObject = obj->rootObject; + if (!rootObject || !rootObject->isValid()) + return false; + + ExecState* exec = rootObject->globalObject()->globalExec(); + PrivateIdentifier* i = (PrivateIdentifier*)propertyName; + JSLock lock; + if (i->isString) + return obj->imp->hasProperty(exec, identifierFromNPIdentifier(i->value.string)); + return obj->imp->hasProperty(exec, i->value.number); + } + + if (o->_class->hasProperty) + return o->_class->hasProperty(o, propertyName); + + return false; +} + +bool _NPN_HasMethod(NPP, NPObject* o, NPIdentifier methodName) +{ + if (o->_class == NPScriptObjectClass) { + JavaScriptObject* obj = (JavaScriptObject*)o; + + PrivateIdentifier* i = (PrivateIdentifier*)methodName; + if (!i->isString) + return false; + + RootObject* rootObject = obj->rootObject; + if (!rootObject || !rootObject->isValid()) + return false; + + ExecState* exec = rootObject->globalObject()->globalExec(); + JSLock lock; + JSValue* func = obj->imp->get(exec, identifierFromNPIdentifier(i->value.string)); + return !func->isUndefined(); + } + + if (o->_class->hasMethod) + return o->_class->hasMethod(o, methodName); + + return false; +} + +void _NPN_SetException(NPObject* o, const NPUTF8* message) +{ + if (o->_class == NPScriptObjectClass) { + JavaScriptObject* obj = (JavaScriptObject*)o; + RootObject* rootObject = obj->rootObject; + if (!rootObject || !rootObject->isValid()) + return; + + ExecState* exec = rootObject->globalObject()->globalExec(); + JSLock lock; + throwError(exec, GeneralError, message); + } +} + +bool _NPN_Enumerate(NPP, NPObject *o, NPIdentifier **identifier, uint32_t *count) +{ + if (o->_class == NPScriptObjectClass) { + JavaScriptObject* obj = (JavaScriptObject*)o; + + RootObject* rootObject = obj->rootObject; + if (!rootObject || !rootObject->isValid()) + return false; + + ExecState* exec = rootObject->globalObject()->globalExec(); + JSLock lock; + PropertyNameArray propertyNames; + + obj->imp->getPropertyNames(exec, propertyNames); + unsigned size = static_cast<unsigned>(propertyNames.size()); + // FIXME: This should really call NPN_MemAlloc but that's in WebKit + NPIdentifier *identifiers = static_cast<NPIdentifier*>(malloc(sizeof(NPIdentifier) * size)); + + for (unsigned i = 0; i < size; i++) + identifiers[i] = _NPN_GetStringIdentifier(propertyNames[i].ustring().UTF8String().c_str()); + + *identifier = identifiers; + *count = size; + + return true; + } + + if (NP_CLASS_STRUCT_VERSION_HAS_ENUM(o->_class) && o->_class->enumerate) + return o->_class->enumerate(o, identifier, count); + + return false; +} + +#endif diff --git a/JavaScriptCore/bindings/NP_jsobject.h b/JavaScriptCore/bindings/NP_jsobject.h new file mode 100644 index 0000000..dd9ccae --- /dev/null +++ b/JavaScriptCore/bindings/NP_jsobject.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2004, 2006 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 COMPUTER, INC. OR + * 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. + */ + +#ifndef NP_JSOBJECT_H +#define NP_JSOBJECT_H + +#if !PLATFORM(DARWIN) || !defined(__LP64__) + +#include "npruntime_internal.h" +#include <wtf/Forward.h> + +namespace KJS { + class JSObject; + namespace Bindings { + class RootObject; + } +} + +extern NPClass* NPScriptObjectClass; + +struct JavaScriptObject +{ + NPObject object; + KJS::JSObject* imp; + KJS::Bindings::RootObject* rootObject; +}; + +NPObject* _NPN_CreateScriptObject(NPP npp, KJS::JSObject*, PassRefPtr<KJS::Bindings::RootObject> rootObject); +NPObject* _NPN_CreateNoScriptObject(void); + +#endif +#endif diff --git a/JavaScriptCore/bindings/c/c_class.cpp b/JavaScriptCore/bindings/c/c_class.cpp new file mode 100644 index 0000000..f0bb8da --- /dev/null +++ b/JavaScriptCore/bindings/c/c_class.cpp @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2003, 2006 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 COMPUTER, INC. OR + * 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" + +#if !PLATFORM(DARWIN) || !defined(__LP64__) + +#include "c_class.h" + +#include "c_instance.h" +#include "c_runtime.h" +#include "identifier.h" +#include "npruntime_impl.h" + +namespace KJS { namespace Bindings { + +CClass::CClass(NPClass* aClass) +{ + _isa = aClass; +} + +CClass::~CClass() +{ + JSLock lock; + + deleteAllValues(_methods); + _methods.clear(); + + deleteAllValues(_fields); + _fields.clear(); +} + +typedef HashMap<NPClass*, CClass*> ClassesByIsAMap; +static ClassesByIsAMap* classesByIsA = 0; + +CClass* CClass::classForIsA(NPClass* isa) +{ + if (!classesByIsA) + classesByIsA = new ClassesByIsAMap; + + CClass* aClass = classesByIsA->get(isa); + if (!aClass) { + aClass = new CClass(isa); + classesByIsA->set(isa, aClass); + } + + return aClass; +} + +const char* CClass::name() const +{ + return ""; +} + +MethodList CClass::methodsNamed(const Identifier& identifier, Instance* instance) const +{ + MethodList methodList; + + Method* method = _methods.get(identifier.ustring().rep()); + if (method) { + methodList.append(method); + return methodList; + } + + NPIdentifier ident = _NPN_GetStringIdentifier(identifier.ascii()); + const CInstance* inst = static_cast<const CInstance*>(instance); + NPObject* obj = inst->getObject(); + if (_isa->hasMethod && _isa->hasMethod(obj, ident)){ + Method* aMethod = new CMethod(ident); // deleted in the CClass destructor + { + JSLock lock; + _methods.set(identifier.ustring().rep(), aMethod); + } + methodList.append(aMethod); + } + + return methodList; +} + +Field* CClass::fieldNamed(const Identifier& identifier, Instance* instance) const +{ + Field* aField = _fields.get(identifier.ustring().rep()); + if (aField) + return aField; + + NPIdentifier ident = _NPN_GetStringIdentifier(identifier.ascii()); + const CInstance* inst = static_cast<const CInstance*>(instance); + NPObject* obj = inst->getObject(); + if (_isa->hasProperty && _isa->hasProperty(obj, ident)){ + aField = new CField(ident); // deleted in the CClass destructor + { + JSLock lock; + _fields.set(identifier.ustring().rep(), aField); + } + } + return aField; +} + +} } // namespace KJS::Bindings + +#endif diff --git a/JavaScriptCore/bindings/c/c_class.h b/JavaScriptCore/bindings/c/c_class.h new file mode 100644 index 0000000..8134d5d --- /dev/null +++ b/JavaScriptCore/bindings/c/c_class.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2003 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 COMPUTER, INC. OR + * 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. + */ + +#if !PLATFORM(DARWIN) || !defined(__LP64__) + +#ifndef BINDINGS_C_CLASS_H_ +#define BINDINGS_C_CLASS_H_ + +#include "npruntime_internal.h" +#include "runtime.h" +#include <wtf/HashMap.h> + +namespace KJS { +namespace Bindings { + +class CClass : public Class { +protected: + CClass(NPClass*); // Use classForIsA to create a CClass. + +public: + static CClass* classForIsA(NPClass*); + virtual ~CClass(); + + virtual const char* name() const; + virtual MethodList methodsNamed(const Identifier&, Instance*) const; + virtual Field* fieldNamed(const Identifier&, Instance*) const; + +private: + NPClass* _isa; + mutable MethodMap _methods; + mutable FieldMap _fields; +}; + +} // namespace Bindings +} // namespace KJS + +#endif +#endif diff --git a/JavaScriptCore/bindings/c/c_instance.cpp b/JavaScriptCore/bindings/c/c_instance.cpp new file mode 100644 index 0000000..3d04054 --- /dev/null +++ b/JavaScriptCore/bindings/c/c_instance.cpp @@ -0,0 +1,212 @@ +/* + * Copyright (C) 2003, 2006 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 COMPUTER, INC. OR + * 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" + +#if !PLATFORM(DARWIN) || !defined(__LP64__) + +#include "c_instance.h" + +#include "c_class.h" +#include "c_runtime.h" +#include "c_utility.h" +#include "list.h" +#include "npruntime_impl.h" +#include "PropertyNameArray.h" +#include "runtime_root.h" +#include <wtf/Assertions.h> +#include <wtf/StringExtras.h> +#include <wtf/Vector.h> + +namespace KJS { +namespace Bindings { + +CInstance::CInstance(NPObject* o, PassRefPtr<RootObject> rootObject) + : Instance(rootObject) +{ + _object = _NPN_RetainObject(o); + _class = 0; +} + +CInstance::~CInstance() +{ + _NPN_ReleaseObject(_object); +} + +Class *CInstance::getClass() const +{ + if (!_class) + _class = CClass::classForIsA(_object->_class); + return _class; +} + +void CInstance::begin() +{ + // Do nothing. +} + +void CInstance::end() +{ + // Do nothing. +} + +bool CInstance::implementsCall() const +{ + return (_object->_class->invokeDefault != 0); +} + +JSValue* CInstance::invokeMethod(ExecState* exec, const MethodList& methodList, const List& args) +{ + // Overloading methods are not allowed by NPObjects. Should only be one + // name match for a particular method. + ASSERT(methodList.size() == 1); + + CMethod* method = static_cast<CMethod*>(methodList[0]); + + NPIdentifier ident = _NPN_GetStringIdentifier(method->name()); + if (!_object->_class->hasMethod(_object, ident)) + return jsUndefined(); + + unsigned count = args.size(); + Vector<NPVariant, 128> cArgs(count); + + unsigned i; + for (i = 0; i < count; i++) + convertValueToNPVariant(exec, args.at(i), &cArgs[i]); + + // Invoke the 'C' method. + NPVariant resultVariant; + VOID_TO_NPVARIANT(resultVariant); + + { + JSLock::DropAllLocks dropAllLocks; + _object->_class->invoke(_object, ident, cArgs.data(), count, &resultVariant); + } + + for (i = 0; i < count; i++) + _NPN_ReleaseVariantValue(&cArgs[i]); + + JSValue* resultValue = convertNPVariantToValue(exec, &resultVariant, _rootObject.get()); + _NPN_ReleaseVariantValue(&resultVariant); + return resultValue; +} + + +JSValue* CInstance::invokeDefaultMethod(ExecState* exec, const List& args) +{ + if (!_object->_class->invokeDefault) + return jsUndefined(); + + unsigned count = args.size(); + Vector<NPVariant, 128> cArgs(count); + + unsigned i; + for (i = 0; i < count; i++) + convertValueToNPVariant(exec, args.at(i), &cArgs[i]); + + // Invoke the 'C' method. + NPVariant resultVariant; + VOID_TO_NPVARIANT(resultVariant); + { + JSLock::DropAllLocks dropAllLocks; + _object->_class->invokeDefault(_object, cArgs.data(), count, &resultVariant); + } + + for (i = 0; i < count; i++) + _NPN_ReleaseVariantValue(&cArgs[i]); + + JSValue* resultValue = convertNPVariantToValue(exec, &resultVariant, _rootObject.get()); + _NPN_ReleaseVariantValue(&resultVariant); + return resultValue; +} + + +JSValue* CInstance::defaultValue(JSType hint) const +{ + if (hint == StringType) + return stringValue(); + if (hint == NumberType) + return numberValue(); + if (hint == BooleanType) + return booleanValue(); + return valueOf(); +} + +JSValue* CInstance::stringValue() const +{ + char buf[1024]; + snprintf(buf, sizeof(buf), "NPObject %p, NPClass %p", _object, _object->_class); + return jsString(buf); +} + +JSValue* CInstance::numberValue() const +{ + // FIXME: Implement something sensible. + return jsNumber(0); +} + +JSValue* CInstance::booleanValue() const +{ + // FIXME: Implement something sensible. + return jsBoolean(false); +} + +JSValue* CInstance::valueOf() const +{ + return stringValue(); +} + +void CInstance::getPropertyNames(ExecState*, PropertyNameArray& nameArray) +{ + if (!NP_CLASS_STRUCT_VERSION_HAS_ENUM(_object->_class) || + !_object->_class->enumerate) + return; + + unsigned count; + NPIdentifier* identifiers; + + { + JSLock::DropAllLocks dropAllLocks; + if (!_object->_class->enumerate(_object, &identifiers, &count)) + return; + } + + for (unsigned i = 0; i < count; i++) { + PrivateIdentifier* identifier = static_cast<PrivateIdentifier*>(identifiers[i]); + + if (identifier->isString) + nameArray.add(identifierFromNPIdentifier(identifier->value.string)); + else + nameArray.add(Identifier::from(identifier->value.number)); + } + + // FIXME: This should really call NPN_MemFree but that's in WebKit + free(identifiers); +} + +} +} + +#endif diff --git a/JavaScriptCore/bindings/c/c_instance.h b/JavaScriptCore/bindings/c/c_instance.h new file mode 100644 index 0000000..9991a18 --- /dev/null +++ b/JavaScriptCore/bindings/c/c_instance.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2003 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 COMPUTER, INC. OR + * 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. + */ + +#ifndef BINDINGS_C_INSTANCE_H_ +#define BINDINGS_C_INSTANCE_H_ + +#if !PLATFORM(DARWIN) || !defined(__LP64__) + +#include "runtime.h" +#include <wtf/Noncopyable.h> + +typedef struct NPObject NPObject; + +namespace KJS { + +namespace Bindings { + +class CClass; + +class CInstance : public Instance { +public: + CInstance (NPObject*, PassRefPtr<RootObject>); + ~CInstance (); + + virtual Class *getClass() const; + + virtual void begin(); + virtual void end(); + + virtual JSValue *valueOf() const; + virtual JSValue *defaultValue (JSType hint) const; + + virtual bool implementsCall() const; + + virtual JSValue *invokeMethod (ExecState *exec, const MethodList &method, const List &args); + virtual JSValue *invokeDefaultMethod (ExecState *exec, const List &args); + virtual void getPropertyNames(ExecState*, PropertyNameArray&); + + JSValue *stringValue() const; + JSValue *numberValue() const; + JSValue *booleanValue() const; + + NPObject *getObject() const { return _object; } + + virtual BindingLanguage getBindingLanguage() const { return CLanguage; } + +private: + mutable CClass *_class; + NPObject *_object; +}; + +} // namespace Bindings + +} // namespace KJS + +#endif +#endif diff --git a/JavaScriptCore/bindings/c/c_runtime.cpp b/JavaScriptCore/bindings/c/c_runtime.cpp new file mode 100644 index 0000000..c5636cd --- /dev/null +++ b/JavaScriptCore/bindings/c/c_runtime.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2004 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 COMPUTER, INC. OR + * 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" + +#if !PLATFORM(DARWIN) || !defined(__LP64__) + +#include "c_runtime.h" + +#include "c_instance.h" +#include "c_utility.h" +#include "npruntime_impl.h" + +namespace KJS { +namespace Bindings { + +// ---------------------- CMethod ---------------------- + +const char* CMethod::name() const +{ + PrivateIdentifier *i = (PrivateIdentifier *)_methodIdentifier; + return i->isString ? i->value.string : 0; +} + +// ---------------------- CField ---------------------- + +const char* CField::name() const +{ + PrivateIdentifier *i = (PrivateIdentifier *)_fieldIdentifier; + return i->isString ? i->value.string : 0; +} + +JSValue* CField::valueFromInstance(ExecState* exec, const Instance* inst) const +{ + const CInstance* instance = static_cast<const CInstance*>(inst); + NPObject* obj = instance->getObject(); + if (obj->_class->getProperty) { + NPVariant property; + VOID_TO_NPVARIANT(property); + + bool result; + { + JSLock::DropAllLocks dropAllLocks; + result = obj->_class->getProperty(obj, _fieldIdentifier, &property); + } + if (result) { + JSValue* result = convertNPVariantToValue(exec, &property, instance->rootObject()); + _NPN_ReleaseVariantValue(&property); + return result; + } + } + return jsUndefined(); +} + +void CField::setValueToInstance(ExecState *exec, const Instance *inst, JSValue *aValue) const +{ + const CInstance* instance = static_cast<const CInstance*>(inst); + NPObject* obj = instance->getObject(); + if (obj->_class->setProperty) { + NPVariant variant; + convertValueToNPVariant(exec, aValue, &variant); + + { + JSLock::DropAllLocks dropAllLocks; + obj->_class->setProperty(obj, _fieldIdentifier, &variant); + } + + _NPN_ReleaseVariantValue(&variant); + } +} + +} } + +#endif diff --git a/JavaScriptCore/bindings/c/c_runtime.h b/JavaScriptCore/bindings/c/c_runtime.h new file mode 100644 index 0000000..469008a --- /dev/null +++ b/JavaScriptCore/bindings/c/c_runtime.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2004, 2006 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 COMPUTER, INC. OR + * 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. + */ + +#ifndef BINDINGS_C_RUNTIME_H_ +#define BINDINGS_C_RUNTIME_H_ + +#if !PLATFORM(DARWIN) || !defined(__LP64__) + +#include "npruntime_internal.h" +#include "runtime.h" + +namespace KJS { +namespace Bindings { + +class CField : public Field { +public: + CField(NPIdentifier ident) : _fieldIdentifier(ident) { } + + virtual JSValue* valueFromInstance(ExecState*, const Instance*) const; + virtual void setValueToInstance(ExecState*, const Instance*, JSValue*) const; + virtual const char* name() const; + +private: + NPIdentifier _fieldIdentifier; +}; + + +class CMethod : public Method +{ +public: + CMethod(NPIdentifier ident) : _methodIdentifier(ident) { } + + virtual const char* name() const; + virtual int numParameters() const { return 0; } + +private: + NPIdentifier _methodIdentifier; +}; + +} // namespace Bindings +} // namespace KJS + +#endif +#endif diff --git a/JavaScriptCore/bindings/c/c_utility.cpp b/JavaScriptCore/bindings/c/c_utility.cpp new file mode 100644 index 0000000..7e2a19f --- /dev/null +++ b/JavaScriptCore/bindings/c/c_utility.cpp @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2004, 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com) + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 COMPUTER, INC. OR + * 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" + +#if !PLATFORM(DARWIN) || !defined(__LP64__) + +#include "c_utility.h" + +#include "NP_jsobject.h" +#include "c_instance.h" +#include "JSGlobalObject.h" +#include "npruntime_impl.h" +#include "npruntime_priv.h" +#include "runtime_object.h" +#include "runtime_root.h" +#include "Platform.h" +#include <wtf/Assertions.h> +#include <wtf/unicode/UTF8.h> + +using namespace WTF::Unicode; + +namespace KJS { namespace Bindings { + +// Requires free() of returned UTF16Chars. +static void convertUTF8ToUTF16WithLatin1Fallback(const NPUTF8* UTF8Chars, int UTF8Length, NPUTF16** UTF16Chars, unsigned int* UTF16Length) +{ + ASSERT(UTF8Chars || UTF8Length == 0); + ASSERT(UTF16Chars); + + if (UTF8Length == -1) + UTF8Length = static_cast<int>(strlen(UTF8Chars)); + + *UTF16Length = UTF8Length; + *UTF16Chars = static_cast<NPUTF16*>(malloc(sizeof(NPUTF16) * (*UTF16Length))); + + const char* sourcestart = UTF8Chars; + const char* sourceend = sourcestart + UTF8Length; + + ::UChar* targetstart = reinterpret_cast< ::UChar*>(*UTF16Chars); + ::UChar* targetend = targetstart + UTF8Length; + + ConversionResult result = convertUTF8ToUTF16(&sourcestart, sourceend, &targetstart, targetend); + + *UTF16Length = targetstart - reinterpret_cast< ::UChar*>(*UTF16Chars); + + // Check to see if the conversion was successful + // Some plugins return invalid UTF-8 in NPVariantType_String, see <http://bugs.webkit.org/show_bug.cgi?id=5163> + // There is no "bad data" for latin1. It is unlikely that the plugin was really sending text in this encoding, + // but it should have used UTF-8, and now we are simply avoiding a crash. + if (result != conversionOK) { + *UTF16Length = UTF8Length; + + if (!*UTF16Chars) // If the memory wasn't allocated, allocate it. + *UTF16Chars = (NPUTF16*)malloc(sizeof(NPUTF16) * (*UTF16Length)); + + for (unsigned i = 0; i < *UTF16Length; i++) + (*UTF16Chars)[i] = UTF8Chars[i] & 0xFF; + } +} + +// Variant value must be released with NPReleaseVariantValue() +void convertValueToNPVariant(ExecState *exec, JSValue *value, NPVariant *result) +{ + JSLock lock; + + JSType type = value->type(); + + VOID_TO_NPVARIANT(*result); + + if (type == StringType) { + UString ustring = value->toString(exec); + CString cstring = ustring.UTF8String(); + NPString string = { (const NPUTF8 *)cstring.c_str(), static_cast<uint32_t>(cstring.size()) }; + NPN_InitializeVariantWithStringCopy(result, &string); + } else if (type == NumberType) { + DOUBLE_TO_NPVARIANT(value->toNumber(exec), *result); + } else if (type == BooleanType) { + BOOLEAN_TO_NPVARIANT(value->toBoolean(exec), *result); + } else if (type == UnspecifiedType) { + VOID_TO_NPVARIANT(*result); + } else if (type == NullType) { + NULL_TO_NPVARIANT(*result); + } else if (type == ObjectType) { + JSObject* object = static_cast<JSObject*>(value); + if (object->classInfo() == &RuntimeObjectImp::info) { + RuntimeObjectImp* imp = static_cast<RuntimeObjectImp *>(value); + CInstance* instance = static_cast<CInstance*>(imp->getInternalInstance()); + if (instance) { + NPObject* obj = instance->getObject(); + _NPN_RetainObject(obj); + OBJECT_TO_NPVARIANT(obj, *result); + } + } else { + JSGlobalObject* globalObject = exec->dynamicGlobalObject(); + + RootObject* rootObject = findRootObject(globalObject); + if (rootObject) { + NPObject* npObject = _NPN_CreateScriptObject(0, object, rootObject); + OBJECT_TO_NPVARIANT(npObject, *result); + } + } + } +} + +JSValue *convertNPVariantToValue(ExecState*, const NPVariant* variant, RootObject* rootObject) +{ + JSLock lock; + + NPVariantType type = variant->type; + + if (type == NPVariantType_Bool) + return jsBoolean(NPVARIANT_TO_BOOLEAN(*variant)); + if (type == NPVariantType_Null) + return jsNull(); + if (type == NPVariantType_Void) + return jsUndefined(); + if (type == NPVariantType_Int32) + return jsNumber(NPVARIANT_TO_INT32(*variant)); + if (type == NPVariantType_Double) + return jsNumber(NPVARIANT_TO_DOUBLE(*variant)); + if (type == NPVariantType_String) { + NPUTF16 *stringValue; + unsigned int UTF16Length; + convertNPStringToUTF16(&variant->value.stringValue, &stringValue, &UTF16Length); // requires free() of returned memory + UString resultString((const UChar *)stringValue,UTF16Length); + free(stringValue); + return jsString(resultString); + } + if (type == NPVariantType_Object) { + NPObject *obj = variant->value.objectValue; + + if (obj->_class == NPScriptObjectClass) + // Get JSObject from NP_JavaScriptObject. + return ((JavaScriptObject *)obj)->imp; + + // Wrap NPObject in a CInstance. + return Instance::createRuntimeObject(Instance::CLanguage, obj, rootObject); + } + + return jsUndefined(); +} + +// Requires free() of returned UTF16Chars. +void convertNPStringToUTF16(const NPString *string, NPUTF16 **UTF16Chars, unsigned int *UTF16Length) +{ + convertUTF8ToUTF16WithLatin1Fallback(string->UTF8Characters, string->UTF8Length, UTF16Chars, UTF16Length); +} + +Identifier identifierFromNPIdentifier(const NPUTF8* name) +{ + NPUTF16 *methodName; + unsigned UTF16Length; + convertUTF8ToUTF16WithLatin1Fallback(name, -1, &methodName, &UTF16Length); // requires free() of returned memory. + Identifier identifier((const KJS::UChar*)methodName, UTF16Length); + free(methodName); + return identifier; +} + +} } + +#endif diff --git a/JavaScriptCore/bindings/c/c_utility.h b/JavaScriptCore/bindings/c/c_utility.h new file mode 100644 index 0000000..18bc0d4 --- /dev/null +++ b/JavaScriptCore/bindings/c/c_utility.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2004 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 COMPUTER, INC. OR + * 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. + */ + +#ifndef C_UTILITY_H_ +#define C_UTILITY_H_ + +#if !PLATFORM(DARWIN) || !defined(__LP64__) + +#include "npruntime_internal.h" + +namespace KJS { + +class ExecState; +class Identifier; +class JSValue; + +namespace Bindings { + +class RootObject; + +typedef uint16_t NPUTF16; + +enum NP_ValueType { + NP_NumberValueType, + NP_StringValueType, + NP_BooleanValueType, + NP_NullValueType, + NP_UndefinedValueType, + NP_ObjectValueType, + NP_InvalidValueType +}; + +void convertNPStringToUTF16(const NPString*, NPUTF16** UTF16Chars, unsigned int* UTF16Length); +void convertValueToNPVariant(ExecState*, JSValue*, NPVariant* result); +JSValue* convertNPVariantToValue(ExecState*, const NPVariant*, RootObject*); +Identifier identifierFromNPIdentifier(const NPUTF8* name); + +struct PrivateIdentifier { + union { + const NPUTF8* string; + int32_t number; + } value; + bool isString; +}; + +} } + +#endif +#endif diff --git a/JavaScriptCore/bindings/jni/jni_class.cpp b/JavaScriptCore/bindings/jni/jni_class.cpp new file mode 100644 index 0000000..72b1a73 --- /dev/null +++ b/JavaScriptCore/bindings/jni/jni_class.cpp @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2003 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 COMPUTER, INC. OR + * 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 <jni_class.h> + +#include "identifier.h" +#include <jni_utility.h> +#include <jni_runtime.h> + +using namespace KJS::Bindings; + +JavaClass::JavaClass(jobject anInstance) +{ + jobject aClass = callJNIObjectMethod(anInstance, "getClass", "()Ljava/lang/Class;"); + + if (!aClass) { + fprintf(stderr, "%s: unable to call getClass on instance %p\n", __PRETTY_FUNCTION__, anInstance); + return; + } + + jstring className = (jstring)callJNIObjectMethod(aClass, "getName", "()Ljava/lang/String;"); + const char *classNameC = getCharactersFromJString(className); + _name = strdup(classNameC); + releaseCharactersForJString(className, classNameC); + + int i; + JNIEnv *env = getJNIEnv(); + + // Get the fields + jarray fields = (jarray)callJNIObjectMethod(aClass, "getFields", "()[Ljava/lang/reflect/Field;"); + int numFields = env->GetArrayLength(fields); + for (i = 0; i < numFields; i++) { + jobject aJField = env->GetObjectArrayElement((jobjectArray)fields, i); + Field *aField = new JavaField(env, aJField); // deleted in the JavaClass destructor + { + JSLock lock; + _fields.set(Identifier(aField->name()).ustring().rep(), aField); + } + env->DeleteLocalRef(aJField); + } + + // Get the methods + jarray methods = (jarray)callJNIObjectMethod(aClass, "getMethods", "()[Ljava/lang/reflect/Method;"); + int numMethods = env->GetArrayLength(methods); + for (i = 0; i < numMethods; i++) { + jobject aJMethod = env->GetObjectArrayElement((jobjectArray)methods, i); + Method *aMethod = new JavaMethod(env, aJMethod); // deleted in the JavaClass destructor + MethodList* methodList; + { + JSLock lock; + + methodList = _methods.get(Identifier(aMethod->name()).ustring().rep()); + if (!methodList) { + methodList = new MethodList(); + _methods.set(Identifier(aMethod->name()).ustring().rep(), methodList); + } + } + methodList->append(aMethod); + env->DeleteLocalRef(aJMethod); + } +} + +JavaClass::~JavaClass() { + free((void *)_name); + + JSLock lock; + + deleteAllValues(_fields); + _fields.clear(); + + MethodListMap::const_iterator end = _methods.end(); + for (MethodListMap::const_iterator it = _methods.begin(); it != end; ++it) { + const MethodList* methodList = it->second; + deleteAllValues(*methodList); + delete methodList; + } + _methods.clear(); +} + +MethodList JavaClass::methodsNamed(const Identifier& identifier, Instance*) const +{ + MethodList *methodList = _methods.get(identifier.ustring().rep()); + + if (methodList) + return *methodList; + return MethodList(); +} + +Field *JavaClass::fieldNamed(const Identifier& identifier, Instance*) const +{ + return _fields.get(identifier.ustring().rep()); +} + +bool JavaClass::isNumberClass() const +{ + return ((strcmp(_name, "java.lang.Byte") == 0 || + strcmp(_name, "java.lang.Short") == 0 || + strcmp(_name, "java.lang.Integer") == 0 || + strcmp(_name, "java.lang.Long") == 0 || + strcmp(_name, "java.lang.Float") == 0 || + strcmp(_name, "java.lang.Double") == 0) ); +} + +bool JavaClass::isBooleanClass() const +{ + return strcmp(_name, "java.lang.Boolean") == 0; +} + +bool JavaClass::isStringClass() const +{ + return strcmp(_name, "java.lang.String") == 0; +} diff --git a/JavaScriptCore/bindings/jni/jni_class.h b/JavaScriptCore/bindings/jni/jni_class.h new file mode 100644 index 0000000..7c5dbcc --- /dev/null +++ b/JavaScriptCore/bindings/jni/jni_class.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2003 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 COMPUTER, INC. OR + * 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. + */ + +#ifndef JNI_CLASS_H_ +#define JNI_CLASS_H_ + +#include <jni_runtime.h> +#include <wtf/HashMap.h> + +namespace KJS { + +namespace Bindings { + +class JavaClass : public Class { +public: + JavaClass (jobject anInstance); + ~JavaClass (); + + virtual const char *name() const { return _name; }; + + virtual MethodList methodsNamed(const Identifier&, Instance* instance) const; + virtual Field *fieldNamed(const Identifier&, Instance* instance) const; + + bool isNumberClass() const; + bool isBooleanClass() const; + bool isStringClass() const; + +private: + JavaClass (); // prevent default construction + + const char *_name; + FieldMap _fields; + MethodListMap _methods; +}; + +} // namespace Bindings + +} // namespace KJS + +#endif // JNI_CLASS_H_ diff --git a/JavaScriptCore/bindings/jni/jni_instance.cpp b/JavaScriptCore/bindings/jni/jni_instance.cpp new file mode 100644 index 0000000..d5d89aa --- /dev/null +++ b/JavaScriptCore/bindings/jni/jni_instance.cpp @@ -0,0 +1,353 @@ +/* + * Copyright (C) 2003 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 COMPUTER, INC. OR + * 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 "jni_class.h" +#include "jni_instance.h" +#include "jni_runtime.h" +#include "jni_utility.h" +#include "runtime_object.h" +#include "runtime_root.h" + +#ifdef NDEBUG +#define JS_LOG(formatAndArgs...) ((void)0) +#else +#define JS_LOG(formatAndArgs...) { \ + fprintf (stderr, "%s:%d -- %s: ", __FILE__, __LINE__, __FUNCTION__); \ + fprintf(stderr, formatAndArgs); \ +} +#endif + +using namespace KJS::Bindings; +using namespace KJS; + +JavaInstance::JavaInstance (jobject instance, PassRefPtr<RootObject> rootObject) + : Instance(rootObject) +{ + _instance = new JObjectWrapper (instance); + _class = 0; +} + +JavaInstance::~JavaInstance () +{ + delete _class; +} + +#define NUM_LOCAL_REFS 64 + +void JavaInstance::begin() +{ + getJNIEnv()->PushLocalFrame (NUM_LOCAL_REFS); +} + +void JavaInstance::end() +{ + getJNIEnv()->PopLocalFrame (NULL); +} + +Class *JavaInstance::getClass() const +{ + if (_class == 0) + _class = new JavaClass (_instance->_instance); + return _class; +} + +JSValue *JavaInstance::stringValue() const +{ + JSLock lock; + + jstring stringValue = (jstring)callJNIObjectMethod (_instance->_instance, "toString", "()Ljava/lang/String;"); + JNIEnv *env = getJNIEnv(); + const jchar *c = getUCharactersFromJStringInEnv(env, stringValue); + UString u((const UChar *)c, (int)env->GetStringLength(stringValue)); + releaseUCharactersForJStringInEnv(env, stringValue, c); + return jsString(u); +} + +JSValue *JavaInstance::numberValue() const +{ + jdouble doubleValue = callJNIDoubleMethod (_instance->_instance, "doubleValue", "()D"); + return jsNumber(doubleValue); +} + +JSValue *JavaInstance::booleanValue() const +{ + jboolean booleanValue = callJNIBooleanMethod (_instance->_instance, "booleanValue", "()Z"); + return jsBoolean(booleanValue); +} + +JSValue *JavaInstance::invokeMethod (ExecState *exec, const MethodList &methodList, const List &args) +{ + int i, count = args.size(); + jvalue *jArgs; + JSValue *resultValue; + Method *method = 0; + size_t numMethods = methodList.size(); + + // Try to find a good match for the overloaded method. The + // fundamental problem is that JavaScript doesn have the + // notion of method overloading and Java does. We could + // get a bit more sophisticated and attempt to does some + // type checking as we as checking the number of parameters. + Method *aMethod; + for (size_t methodIndex = 0; methodIndex < numMethods; methodIndex++) { + aMethod = methodList[methodIndex]; + if (aMethod->numParameters() == count) { + method = aMethod; + break; + } + } + if (method == 0) { + JS_LOG ("unable to find an appropiate method\n"); + return jsUndefined(); + } + + const JavaMethod *jMethod = static_cast<const JavaMethod*>(method); + JS_LOG ("call %s %s on %p\n", method->name(), jMethod->signature(), _instance->_instance); + + if (count > 0) { + jArgs = (jvalue *)malloc (count * sizeof(jvalue)); + } + else + jArgs = 0; + + for (i = 0; i < count; i++) { + JavaParameter* aParameter = jMethod->parameterAt(i); + jArgs[i] = convertValueToJValue (exec, args.at(i), aParameter->getJNIType(), aParameter->type()); + JS_LOG("arg[%d] = %s\n", i, args.at(i)->toString(exec).ascii()); + } + + jvalue result; + + // Try to use the JNI abstraction first, otherwise fall back to + // nornmal JNI. The JNI dispatch abstraction allows the Java plugin + // to dispatch the call on the appropriate internal VM thread. + RootObject* rootObject = this->rootObject(); + if (!rootObject) + return jsUndefined(); + + bool handled = false; + if (rootObject->nativeHandle()) { + jobject obj = _instance->_instance; + JSValue *exceptionDescription = NULL; + const char *callingURL = 0; // FIXME, need to propagate calling URL to Java + handled = dispatchJNICall(rootObject->nativeHandle(), obj, jMethod->isStatic(), jMethod->JNIReturnType(), jMethod->methodID(obj), jArgs, result, callingURL, exceptionDescription); + if (exceptionDescription) { + throwError(exec, GeneralError, exceptionDescription->toString(exec)); + free (jArgs); + return jsUndefined(); + } + } + + // The following code can be conditionally removed once we have a Tiger update that + // contains the new Java plugin. It is needed for builds prior to Tiger. + if (!handled) { + jobject obj = _instance->_instance; + switch (jMethod->JNIReturnType()){ + case void_type: { + callJNIVoidMethodIDA (obj, jMethod->methodID(obj), jArgs); + } + break; + + case object_type: { + result.l = callJNIObjectMethodIDA (obj, jMethod->methodID(obj), jArgs); + } + break; + + case boolean_type: { + result.z = callJNIBooleanMethodIDA (obj, jMethod->methodID(obj), jArgs); + } + break; + + case byte_type: { + result.b = callJNIByteMethodIDA (obj, jMethod->methodID(obj), jArgs); + } + break; + + case char_type: { + result.c = callJNICharMethodIDA (obj, jMethod->methodID(obj), jArgs); + } + break; + + case short_type: { + result.s = callJNIShortMethodIDA (obj, jMethod->methodID(obj), jArgs); + } + break; + + case int_type: { + result.i = callJNIIntMethodIDA (obj, jMethod->methodID(obj), jArgs); + } + break; + + case long_type: { + result.j = callJNILongMethodIDA (obj, jMethod->methodID(obj), jArgs); + } + break; + + case float_type: { + result.f = callJNIFloatMethodIDA (obj, jMethod->methodID(obj), jArgs); + } + break; + + case double_type: { + result.d = callJNIDoubleMethodIDA (obj, jMethod->methodID(obj), jArgs); + } + break; + + case invalid_type: + default: { + } + break; + } + } + + switch (jMethod->JNIReturnType()){ + case void_type: { + resultValue = jsUndefined(); + } + break; + + case object_type: { + if (result.l != 0) { + const char *arrayType = jMethod->returnType(); + if (arrayType[0] == '[') { + resultValue = JavaArray::convertJObjectToArray(exec, result.l, arrayType, rootObject); + } + else { + resultValue = Instance::createRuntimeObject(Instance::JavaLanguage, result.l, rootObject); + } + } + else { + resultValue = jsUndefined(); + } + } + break; + + case boolean_type: { + resultValue = jsBoolean(result.z); + } + break; + + case byte_type: { + resultValue = jsNumber(result.b); + } + break; + + case char_type: { + resultValue = jsNumber(result.c); + } + break; + + case short_type: { + resultValue = jsNumber(result.s); + } + break; + + case int_type: { + resultValue = jsNumber(result.i); + } + break; + + case long_type: { + resultValue = jsNumber(result.j); + } + break; + + case float_type: { + resultValue = jsNumber(result.f); + } + break; + + case double_type: { + resultValue = jsNumber(result.d); + } + break; + + case invalid_type: + default: { + resultValue = jsUndefined(); + } + break; + } + + free (jArgs); + + return resultValue; +} + +JSValue *JavaInstance::defaultValue (JSType hint) const +{ + if (hint == StringType) { + return stringValue(); + } + else if (hint == NumberType) { + return numberValue(); + } + else if (hint == BooleanType) { + return booleanValue(); + } + else if (hint == UnspecifiedType) { + JavaClass *aClass = static_cast<JavaClass*>(getClass()); + if (aClass->isStringClass()) { + return stringValue(); + } + else if (aClass->isNumberClass()) { + return numberValue(); + } + else if (aClass->isBooleanClass()) { + return booleanValue(); + } + } + + return valueOf(); +} + +JSValue *JavaInstance::valueOf() const +{ + return stringValue(); +} + +JObjectWrapper::JObjectWrapper(jobject instance) +: _refCount(0) +{ + assert (instance != 0); + + // Cache the JNIEnv used to get the global ref for this java instanace. + // It'll be used to delete the reference. + _env = getJNIEnv(); + + _instance = _env->NewGlobalRef (instance); + + JS_LOG ("new global ref %p for %p\n", _instance, instance); + + if (_instance == NULL) { + fprintf (stderr, "%s: could not get GlobalRef for %p\n", __PRETTY_FUNCTION__, instance); + } +} + +JObjectWrapper::~JObjectWrapper() { + JS_LOG ("deleting global ref %p\n", _instance); + _env->DeleteGlobalRef (_instance); +} diff --git a/JavaScriptCore/bindings/jni/jni_instance.h b/JavaScriptCore/bindings/jni/jni_instance.h new file mode 100644 index 0000000..eb89e3f --- /dev/null +++ b/JavaScriptCore/bindings/jni/jni_instance.h @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2003 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 COMPUTER, INC. OR + * 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. + */ +#ifndef _JNI_INSTANCE_H_ +#define _JNI_INSTANCE_H_ + +#include "runtime.h" + +#include <JavaVM/jni.h> + + +namespace KJS { + +namespace Bindings { + +class JavaClass; + +class JObjectWrapper +{ +friend class RefPtr<JObjectWrapper>; +friend class JavaArray; +friend class JavaField; +friend class JavaInstance; +friend class JavaMethod; + +protected: + JObjectWrapper(jobject instance); + ~JObjectWrapper(); + + void ref() { _refCount++; } + void deref() + { + if (--_refCount == 0) + delete this; + } + + jobject _instance; + +private: + JNIEnv *_env; + unsigned int _refCount; +}; + +class JavaInstance : public Instance +{ +public: + JavaInstance(jobject instance, PassRefPtr<RootObject>); + ~JavaInstance(); + + virtual Class *getClass() const; + + virtual void begin(); + virtual void end(); + + virtual JSValue *valueOf() const; + virtual JSValue *defaultValue (JSType hint) const; + + virtual JSValue *invokeMethod (ExecState *exec, const MethodList &method, const List &args); + + jobject javaInstance() const { return _instance->_instance; } + + JSValue *stringValue() const; + JSValue *numberValue() const; + JSValue *booleanValue() const; + + virtual BindingLanguage getBindingLanguage() const { return JavaLanguage; } + +private: + RefPtr<JObjectWrapper> _instance; + mutable JavaClass *_class; +}; + +} // namespace Bindings + +} // namespace KJS + +#endif diff --git a/JavaScriptCore/bindings/jni/jni_jsobject.cpp b/JavaScriptCore/bindings/jni/jni_jsobject.cpp new file mode 100644 index 0000000..347f24f --- /dev/null +++ b/JavaScriptCore/bindings/jni/jni_jsobject.cpp @@ -0,0 +1,585 @@ +/* + * Copyright (C) 2003 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 COMPUTER, INC. OR + * 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 "identifier.h" +#include "internal.h" +#include "interpreter.h" +#include "jni_jsobject.h" +#include "jni_runtime.h" +#include "jni_utility.h" +#include "JSGlobalObject.h" +#include "list.h" +#include "runtime_object.h" +#include "runtime_root.h" +#include <CoreFoundation/CoreFoundation.h> +#include <wtf/Assertions.h> + +using namespace KJS::Bindings; +using namespace KJS; + +#ifdef NDEBUG +#define JS_LOG(formatAndArgs...) ((void)0) +#else +#define JS_LOG(formatAndArgs...) { \ + fprintf (stderr, "%s(%p,%p): ", __PRETTY_FUNCTION__, RootObject::runLoop(), CFRunLoopGetCurrent()); \ + fprintf(stderr, formatAndArgs); \ +} +#endif + +#define UndefinedHandle 1 + +static bool isJavaScriptThread() +{ + return (RootObject::runLoop() == CFRunLoopGetCurrent()); +} + +jvalue JavaJSObject::invoke (JSObjectCallContext *context) +{ + jvalue result; + + bzero ((void *)&result, sizeof(jvalue)); + + if (!isJavaScriptThread()) { + // Send the call context to the thread that is allowed to + // call JavaScript. + RootObject::dispatchToJavaScriptThread(context); + result = context->result; + } + else { + jlong nativeHandle = context->nativeHandle; + if (nativeHandle == UndefinedHandle || nativeHandle == 0) { + return result; + } + + if (context->type == CreateNative) { + result.j = JavaJSObject::createNative(nativeHandle); + } + else { + JSObject *imp = jlong_to_impptr(nativeHandle); + if (!findProtectingRootObject(imp)) { + fprintf (stderr, "%s:%d: Attempt to access JavaScript from destroyed applet, type %d.\n", __FILE__, __LINE__, context->type); + return result; + } + + switch (context->type){ + case Call: { + result.l = JavaJSObject(nativeHandle).call(context->string, context->args); + break; + } + + case Eval: { + result.l = JavaJSObject(nativeHandle).eval(context->string); + break; + } + + case GetMember: { + result.l = JavaJSObject(nativeHandle).getMember(context->string); + break; + } + + case SetMember: { + JavaJSObject(nativeHandle).setMember(context->string, context->value); + break; + } + + case RemoveMember: { + JavaJSObject(nativeHandle).removeMember(context->string); + break; + } + + case GetSlot: { + result.l = JavaJSObject(nativeHandle).getSlot(context->index); + break; + } + + case SetSlot: { + JavaJSObject(nativeHandle).setSlot(context->index, context->value); + break; + } + + case ToString: { + result.l = (jobject) JavaJSObject(nativeHandle).toString(); + break; + } + + case Finalize: { + JavaJSObject(nativeHandle).finalize(); + break; + } + + default: { + fprintf (stderr, "%s: invalid JavaScript call\n", __PRETTY_FUNCTION__); + } + } + } + context->result = result; + } + + return result; +} + + +JavaJSObject::JavaJSObject(jlong nativeJSObject) +{ + _imp = jlong_to_impptr(nativeJSObject); + + ASSERT(_imp); + _rootObject = findProtectingRootObject(_imp); + ASSERT(_rootObject); +} + +RootObject* JavaJSObject::rootObject() const +{ + return _rootObject && _rootObject->isValid() ? _rootObject.get() : 0; +} + +jobject JavaJSObject::call(jstring methodName, jobjectArray args) const +{ + JS_LOG ("methodName = %s\n", JavaString(methodName).UTF8String()); + + RootObject* rootObject = this->rootObject(); + if (!rootObject) + return 0; + + // Lookup the function object. + ExecState* exec = rootObject->globalObject()->globalExec(); + JSLock lock; + + Identifier identifier(JavaString(methodName).ustring()); + JSValue *func = _imp->get (exec, identifier); + if (func->isUndefinedOrNull()) + return 0; + + // Call the function object. + JSObject *funcImp = static_cast<JSObject*>(func); + JSObject *thisObj = const_cast<JSObject*>(_imp); + List argList; + getListFromJArray(args, argList); + rootObject->globalObject()->startTimeoutCheck(); + JSValue *result = funcImp->call(exec, thisObj, argList); + rootObject->globalObject()->stopTimeoutCheck(); + + return convertValueToJObject(result); +} + +jobject JavaJSObject::eval(jstring script) const +{ + JS_LOG ("script = %s\n", JavaString(script).UTF8String()); + + JSObject *thisObj = const_cast<JSObject*>(_imp); + JSValue *result; + + JSLock lock; + + RootObject* rootObject = this->rootObject(); + if (!rootObject) + return 0; + + rootObject->globalObject()->startTimeoutCheck(); + Completion completion = Interpreter::evaluate(rootObject->globalObject()->globalExec(), UString(), 0, JavaString(script).ustring(),thisObj); + rootObject->globalObject()->stopTimeoutCheck(); + ComplType type = completion.complType(); + + if (type == Normal) { + result = completion.value(); + if (!result) + result = jsUndefined(); + } else + result = jsUndefined(); + + return convertValueToJObject (result); +} + +jobject JavaJSObject::getMember(jstring memberName) const +{ + JS_LOG ("(%p) memberName = %s\n", _imp, JavaString(memberName).UTF8String()); + + RootObject* rootObject = this->rootObject(); + if (!rootObject) + return 0; + + ExecState* exec = rootObject->globalObject()->globalExec(); + + JSLock lock; + JSValue *result = _imp->get (exec, Identifier (JavaString(memberName).ustring())); + + return convertValueToJObject(result); +} + +void JavaJSObject::setMember(jstring memberName, jobject value) const +{ + JS_LOG ("memberName = %s, value = %p\n", JavaString(memberName).UTF8String(), value); + + RootObject* rootObject = this->rootObject(); + if (!rootObject) + return; + + ExecState* exec = rootObject->globalObject()->globalExec(); + JSLock lock; + _imp->put(exec, Identifier (JavaString(memberName).ustring()), convertJObjectToValue(value)); +} + + +void JavaJSObject::removeMember(jstring memberName) const +{ + JS_LOG ("memberName = %s\n", JavaString(memberName).UTF8String()); + + RootObject* rootObject = this->rootObject(); + if (!rootObject) + return; + + ExecState* exec = rootObject->globalObject()->globalExec(); + JSLock lock; + _imp->deleteProperty(exec, Identifier (JavaString(memberName).ustring())); +} + + +jobject JavaJSObject::getSlot(jint index) const +{ +#ifdef __LP64__ + JS_LOG ("index = %d\n", index); +#else + JS_LOG ("index = %ld\n", index); +#endif + + RootObject* rootObject = this->rootObject(); + if (!rootObject) + return 0; + + ExecState* exec = rootObject->globalObject()->globalExec(); + + JSLock lock; + JSValue *result = _imp->get (exec, (unsigned)index); + + return convertValueToJObject(result); +} + + +void JavaJSObject::setSlot(jint index, jobject value) const +{ +#ifdef __LP64__ + JS_LOG ("index = %d, value = %p\n", index, value); +#else + JS_LOG ("index = %ld, value = %p\n", index, value); +#endif + + RootObject* rootObject = this->rootObject(); + if (!rootObject) + return; + + ExecState* exec = rootObject->globalObject()->globalExec(); + JSLock lock; + _imp->put(exec, (unsigned)index, convertJObjectToValue(value)); +} + + +jstring JavaJSObject::toString() const +{ + JS_LOG ("\n"); + + RootObject* rootObject = this->rootObject(); + if (!rootObject) + return 0; + + JSLock lock; + JSObject *thisObj = const_cast<JSObject*>(_imp); + ExecState* exec = rootObject->globalObject()->globalExec(); + + return (jstring)convertValueToJValue (exec, thisObj, object_type, "java.lang.String").l; +} + +void JavaJSObject::finalize() const +{ + if (RootObject* rootObject = this->rootObject()) + rootObject->gcUnprotect(_imp); +} + +// We're either creating a 'Root' object (via a call to JavaJSObject.getWindow()), or +// another JavaJSObject. +jlong JavaJSObject::createNative(jlong nativeHandle) +{ + JS_LOG ("nativeHandle = %d\n", (int)nativeHandle); + + if (nativeHandle == UndefinedHandle) + return nativeHandle; + + if (findProtectingRootObject(jlong_to_impptr(nativeHandle))) + return nativeHandle; + + CreateRootObjectFunction createRootObject = RootObject::createRootObject(); + if (!createRootObject) + return ptr_to_jlong(0); + + RefPtr<RootObject> rootObject = createRootObject(jlong_to_ptr(nativeHandle)); + + // If rootObject is !NULL We must have been called via netscape.javascript.JavaJSObject.getWindow(), + // otherwise we are being called after creating a JavaJSObject in + // JavaJSObject::convertValueToJObject(). + if (rootObject) { + JSObject* globalObject = rootObject->globalObject(); + // We call gcProtect here to get the object into the root object's "protect set" which + // is used to test if a native handle is valid as well as getting the root object given the handle. + rootObject->gcProtect(globalObject); + return ptr_to_jlong(globalObject); + } + + return nativeHandle; +} + +jobject JavaJSObject::convertValueToJObject (JSValue *value) const +{ + JSLock lock; + + RootObject* rootObject = this->rootObject(); + if (!rootObject) + return 0; + + ExecState* exec = rootObject->globalObject()->globalExec(); + JNIEnv *env = getJNIEnv(); + jobject result = 0; + + // See section 22.7 of 'JavaScript: The Definitive Guide, 4th Edition', + // figure 22-5. + // number -> java.lang.Double + // string -> java.lang.String + // boolean -> java.lang.Boolean + // Java instance -> Java instance + // Everything else -> JavaJSObject + + JSType type = value->type(); + if (type == NumberType) { + jclass JSObjectClass = env->FindClass ("java/lang/Double"); + jmethodID constructorID = env->GetMethodID (JSObjectClass, "<init>", "(D)V"); + if (constructorID != NULL) { + result = env->NewObject (JSObjectClass, constructorID, (jdouble)value->toNumber(exec)); + } + } + else if (type == StringType) { + UString stringValue = value->toString(exec); + JNIEnv *env = getJNIEnv(); + result = env->NewString ((const jchar *)stringValue.data(), stringValue.size()); + } + else if (type == BooleanType) { + jclass JSObjectClass = env->FindClass ("java/lang/Boolean"); + jmethodID constructorID = env->GetMethodID (JSObjectClass, "<init>", "(Z)V"); + if (constructorID != NULL) { + result = env->NewObject (JSObjectClass, constructorID, (jboolean)value->toBoolean(exec)); + } + } + else { + // Create a JavaJSObject. + jlong nativeHandle; + + if (type == ObjectType){ + JSObject *imp = static_cast<JSObject*>(value); + + // We either have a wrapper around a Java instance or a JavaScript + // object. If we have a wrapper around a Java instance, return that + // instance, otherwise create a new Java JavaJSObject with the JSObject* + // as it's nativeHandle. + if (imp->classInfo() && strcmp(imp->classInfo()->className, "RuntimeObject") == 0) { + RuntimeObjectImp *runtimeImp = static_cast<RuntimeObjectImp*>(value); + JavaInstance *runtimeInstance = static_cast<JavaInstance *>(runtimeImp->getInternalInstance()); + if (!runtimeInstance) + return 0; + + return runtimeInstance->javaInstance(); + } + else { + nativeHandle = ptr_to_jlong(imp); + rootObject->gcProtect(imp); + } + } + // All other types will result in an undefined object. + else { + nativeHandle = UndefinedHandle; + } + + // Now create the Java JavaJSObject. Look for the JavaJSObject in it's new (Tiger) + // location and in the original Java 1.4.2 location. + jclass JSObjectClass; + + JSObjectClass = env->FindClass ("sun/plugin/javascript/webkit/JSObject"); + if (!JSObjectClass) { + env->ExceptionDescribe(); + env->ExceptionClear(); + JSObjectClass = env->FindClass ("apple/applet/JSObject"); + } + + jmethodID constructorID = env->GetMethodID (JSObjectClass, "<init>", "(J)V"); + if (constructorID != NULL) { + result = env->NewObject (JSObjectClass, constructorID, nativeHandle); + } + } + + return result; +} + +JSValue *JavaJSObject::convertJObjectToValue (jobject theObject) const +{ + // Instances of netscape.javascript.JSObject get converted back to + // JavaScript objects. All other objects are wrapped. It's not + // possible to pass primitive types from the Java to JavaScript. + // See section 22.7 of 'JavaScript: The Definitive Guide, 4th Edition', + // figure 22-4. + jobject classOfInstance = callJNIObjectMethod(theObject, "getClass", "()Ljava/lang/Class;"); + jstring className = (jstring)callJNIObjectMethod(classOfInstance, "getName", "()Ljava/lang/String;"); + + // Only the sun.plugin.javascript.webkit.JSObject has a member called nativeJSObject. This class is + // created above to wrap internal browser objects. The constructor of this class takes the native + // pointer and stores it in this object, so that it can be retrieved below. + if (strcmp(JavaString(className).UTF8String(), "sun.plugin.javascript.webkit.JSObject") == 0) { + // Pull the nativeJSObject value from the Java instance. This is a + // pointer to the JSObject. + JNIEnv *env = getJNIEnv(); + jfieldID fieldID = env->GetFieldID((jclass)classOfInstance, "nativeJSObject", "J"); + if (fieldID == NULL) { + return jsUndefined(); + } + jlong nativeHandle = env->GetLongField(theObject, fieldID); + if (nativeHandle == UndefinedHandle) { + return jsUndefined(); + } + JSObject *imp = static_cast<JSObject*>(jlong_to_impptr(nativeHandle)); + return imp; + } + + JSLock lock; + JavaInstance* javaInstance = new JavaInstance(theObject, _rootObject); + return KJS::Bindings::Instance::createRuntimeObject(javaInstance); +} + +void JavaJSObject::getListFromJArray(jobjectArray jArray, List& list) const +{ + JNIEnv *env = getJNIEnv(); + int i, numObjects = jArray ? env->GetArrayLength (jArray) : 0; + + for (i = 0; i < numObjects; i++) { + jobject anObject = env->GetObjectArrayElement ((jobjectArray)jArray, i); + if (anObject) { + list.append(convertJObjectToValue(anObject)); + env->DeleteLocalRef (anObject); + } + else { + env->ExceptionDescribe(); + env->ExceptionClear(); + } + } +} + +extern "C" { + +jlong KJS_JSCreateNativeJSObject (JNIEnv*, jclass, jstring, jlong nativeHandle, jboolean) +{ + JSObjectCallContext context; + context.type = CreateNative; + context.nativeHandle = nativeHandle; + return JavaJSObject::invoke (&context).j; +} + +void KJS_JSObject_JSFinalize (JNIEnv*, jclass, jlong nativeHandle) +{ + JSObjectCallContext context; + context.type = Finalize; + context.nativeHandle = nativeHandle; + JavaJSObject::invoke (&context); +} + +jobject KJS_JSObject_JSObjectCall (JNIEnv*, jclass, jlong nativeHandle, jstring, jstring methodName, jobjectArray args, jboolean) +{ + JSObjectCallContext context; + context.type = Call; + context.nativeHandle = nativeHandle; + context.string = methodName; + context.args = args; + return JavaJSObject::invoke (&context).l; +} + +jobject KJS_JSObject_JSObjectEval (JNIEnv*, jclass, jlong nativeHandle, jstring, jstring jscript, jboolean) +{ + JSObjectCallContext context; + context.type = Eval; + context.nativeHandle = nativeHandle; + context.string = jscript; + return JavaJSObject::invoke (&context).l; +} + +jobject KJS_JSObject_JSObjectGetMember (JNIEnv*, jclass, jlong nativeHandle, jstring, jstring jname, jboolean) +{ + JSObjectCallContext context; + context.type = GetMember; + context.nativeHandle = nativeHandle; + context.string = jname; + return JavaJSObject::invoke (&context).l; +} + +void KJS_JSObject_JSObjectSetMember (JNIEnv*, jclass, jlong nativeHandle, jstring, jstring jname, jobject value, jboolean) +{ + JSObjectCallContext context; + context.type = SetMember; + context.nativeHandle = nativeHandle; + context.string = jname; + context.value = value; + JavaJSObject::invoke (&context); +} + +void KJS_JSObject_JSObjectRemoveMember (JNIEnv*, jclass, jlong nativeHandle, jstring, jstring jname, jboolean) +{ + JSObjectCallContext context; + context.type = RemoveMember; + context.nativeHandle = nativeHandle; + context.string = jname; + JavaJSObject::invoke (&context); +} + +jobject KJS_JSObject_JSObjectGetSlot (JNIEnv*, jclass, jlong nativeHandle, jstring, jint jindex, jboolean) +{ + JSObjectCallContext context; + context.type = GetSlot; + context.nativeHandle = nativeHandle; + context.index = jindex; + return JavaJSObject::invoke (&context).l; +} + +void KJS_JSObject_JSObjectSetSlot (JNIEnv*, jclass, jlong nativeHandle, jstring, jint jindex, jobject value, jboolean) +{ + JSObjectCallContext context; + context.type = SetSlot; + context.nativeHandle = nativeHandle; + context.index = jindex; + context.value = value; + JavaJSObject::invoke (&context); +} + +jstring KJS_JSObject_JSObjectToString (JNIEnv*, jclass, jlong nativeHandle) +{ + JSObjectCallContext context; + context.type = ToString; + context.nativeHandle = nativeHandle; + return (jstring)JavaJSObject::invoke (&context).l; +} + +} diff --git a/JavaScriptCore/bindings/jni/jni_jsobject.h b/JavaScriptCore/bindings/jni/jni_jsobject.h new file mode 100644 index 0000000..75f37a2 --- /dev/null +++ b/JavaScriptCore/bindings/jni/jni_jsobject.h @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2003 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 COMPUTER, INC. OR + * 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. + */ + +#ifndef JAVASCRIPTCORE_BINDINGS_JNI_JSOBJECT_H +#define JAVASCRIPTCORE_BINDINGS_JNI_JSOBJECT_H + +#include <CoreFoundation/CoreFoundation.h> + +#include <JavaVM/jni.h> +#include <wtf/RefPtr.h> + +#define jlong_to_ptr(a) ((void*)(uintptr_t)(a)) +#define jlong_to_impptr(a) (static_cast<KJS::JSObject*>(((void*)(uintptr_t)(a)))) +#define ptr_to_jlong(a) ((jlong)(uintptr_t)(a)) + +namespace KJS { + +class List; +class JSObject; +class JSValue; + +namespace Bindings { + +class RootObject; + +enum JSObjectCallType { + CreateNative, + Call, + Eval, + GetMember, + SetMember, + RemoveMember, + GetSlot, + SetSlot, + ToString, + Finalize +}; + +struct JSObjectCallContext +{ + JSObjectCallType type; + jlong nativeHandle; + jstring string; + jobjectArray args; + jint index; + jobject value; + CFRunLoopRef originatingLoop; + jvalue result; +}; + +class JavaJSObject +{ +public: + JavaJSObject(jlong nativeHandle); + + static jlong createNative(jlong nativeHandle); + jobject call(jstring methodName, jobjectArray args) const; + jobject eval(jstring script) const; + jobject getMember(jstring memberName) const; + void setMember(jstring memberName, jobject value) const; + void removeMember(jstring memberName) const; + jobject getSlot(jint index) const; + void setSlot(jint index, jobject value) const; + jstring toString() const; + void finalize() const; + + static jvalue invoke(JSObjectCallContext*); + + jobject convertValueToJObject(JSValue*) const; + JSValue* convertJObjectToValue(jobject) const; + void getListFromJArray(jobjectArray, List&) const; + + RootObject* rootObject() const; + +private: + RefPtr<RootObject> _rootObject; + JSObject* _imp; +}; + + +} // namespace Bindings + +} // namespace KJS + +extern "C" { + +// The Java VM calls these functions to handle calls to methods in Java's JSObject class. +jlong KJS_JSCreateNativeJSObject(JNIEnv*, jclass, jstring jurl, jlong nativeHandle, jboolean ctx); +void KJS_JSObject_JSFinalize(JNIEnv*, jclass, jlong nativeJSObject); +jobject KJS_JSObject_JSObjectCall(JNIEnv*, jclass, jlong nativeJSObject, jstring jurl, jstring methodName, jobjectArray args, jboolean ctx); +jobject KJS_JSObject_JSObjectEval(JNIEnv*, jclass, jlong nativeJSObject, jstring jurl, jstring jscript, jboolean ctx); +jobject KJS_JSObject_JSObjectGetMember(JNIEnv*, jclass, jlong nativeJSObject, jstring jurl, jstring jname, jboolean ctx); +void KJS_JSObject_JSObjectSetMember(JNIEnv*, jclass, jlong nativeJSObject, jstring jurl, jstring jname, jobject value, jboolean ctx); +void KJS_JSObject_JSObjectRemoveMember(JNIEnv*, jclass, jlong nativeJSObject, jstring jurl, jstring jname, jboolean ctx); +jobject KJS_JSObject_JSObjectGetSlot(JNIEnv*, jclass, jlong nativeJSObject, jstring jurl, jint jindex, jboolean ctx); +void KJS_JSObject_JSObjectSetSlot(JNIEnv*, jclass, jlong nativeJSObject, jstring jurl, jint jindex, jobject value, jboolean ctx); +jstring KJS_JSObject_JSObjectToString(JNIEnv*, jclass, jlong nativeJSObject); + +} + +#endif diff --git a/JavaScriptCore/bindings/jni/jni_objc.mm b/JavaScriptCore/bindings/jni/jni_objc.mm new file mode 100644 index 0000000..43d2da0 --- /dev/null +++ b/JavaScriptCore/bindings/jni/jni_objc.mm @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2004 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 COMPUTER, INC. OR + * 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" +#import <Foundation/Foundation.h> +#import <JavaScriptCore/jni_utility.h> +#import <JavaScriptCore/objc_utility.h> + +using namespace KJS::Bindings; + +@interface NSObject (WebScriptingPrivate) +- (jvalue)webPlugInCallJava:(jobject)object method:(jmethodID)method returnType:(JNIType)returnType arguments:(jvalue*)args; +- (jvalue)webPlugInCallJava:(jobject)object + isStatic:(BOOL)isStatic + returnType:(JNIType)returnType + method:(jmethodID)method + arguments:(jvalue*)args + callingURL:(NSURL *)url + exceptionDescription:(NSString **)exceptionString; +@end + +bool KJS::Bindings::dispatchJNICall (const void *targetAppletView, jobject obj, bool isStatic, JNIType returnType, jmethodID methodID, jvalue *args, jvalue &result, const char*, JSValue *&exceptionDescription) +{ + id view = (id)targetAppletView; + + // As array_type is not known by the Mac JVM, change it to a compatible type. + if (returnType == array_type) + returnType = object_type; + + if ([view respondsToSelector:@selector(webPlugInCallJava:isStatic:returnType:method:arguments:callingURL:exceptionDescription:)]) { + NSString *_exceptionDescription = 0; + + // Passing nil as the calling URL will cause the Java plugin to use the URL + // of the page that contains the applet. The execution restrictions + // implemented in WebCore will guarantee that only appropriate JavaScript + // can reference the applet. + { + JSLock::DropAllLocks dropAllLocks; + result = [view webPlugInCallJava:obj isStatic:isStatic returnType:returnType method:methodID arguments:args callingURL:nil exceptionDescription:&_exceptionDescription]; + } + + if (_exceptionDescription != 0) { + exceptionDescription = convertNSStringToString(_exceptionDescription); + } + return true; + } + else if ([view respondsToSelector:@selector(webPlugInCallJava:method:returnType:arguments:)]) { + JSLock::DropAllLocks dropAllLocks; + result = [view webPlugInCallJava:obj method:methodID returnType:returnType arguments:args]; + return true; + } + + bzero (&result, sizeof(jvalue)); + return false; +} diff --git a/JavaScriptCore/bindings/jni/jni_runtime.cpp b/JavaScriptCore/bindings/jni/jni_runtime.cpp new file mode 100644 index 0000000..7d7cb31 --- /dev/null +++ b/JavaScriptCore/bindings/jni/jni_runtime.cpp @@ -0,0 +1,545 @@ +/* + * Copyright (C) 2003 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 COMPUTER, INC. OR + * 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 <internal.h> +#include <ustring.h> +#include <value.h> + +#include <jni_utility.h> +#include <jni_runtime.h> + +#include <runtime_array.h> +#include <runtime_object.h> +#include <runtime_root.h> + +#ifdef NDEBUG +#define JS_LOG(formatAndArgs...) ((void)0) +#else +#define JS_LOG(formatAndArgs...) { \ + fprintf (stderr, "%s:%d -- %s: ", __FILE__, __LINE__, __FUNCTION__); \ + fprintf(stderr, formatAndArgs); \ +} +#endif + +using namespace KJS; +using namespace KJS::Bindings; + + +JavaParameter::JavaParameter (JNIEnv *env, jstring type) +{ + _type = JavaString (env, type); + _JNIType = JNITypeFromClassName (_type.UTF8String()); +} + +JavaField::JavaField (JNIEnv *env, jobject aField) +{ + // Get field type + jobject fieldType = callJNIObjectMethod (aField, "getType", "()Ljava/lang/Class;"); + jstring fieldTypeName = (jstring)callJNIObjectMethod (fieldType, "getName", "()Ljava/lang/String;"); + _type = JavaString(env, fieldTypeName); + _JNIType = JNITypeFromClassName (_type.UTF8String()); + + // Get field name + jstring fieldName = (jstring)callJNIObjectMethod (aField, "getName", "()Ljava/lang/String;"); + _name = JavaString(env, fieldName); + + _field = new JObjectWrapper(aField); +} + +JSValue* JavaArray::convertJObjectToArray(ExecState* exec, jobject anObject, const char* type, PassRefPtr<RootObject> rootObject) +{ + if (type[0] != '[') + return jsUndefined(); + + return new RuntimeArray(exec, new JavaArray((jobject)anObject, type, rootObject)); +} + +jvalue JavaField::dispatchValueFromInstance(ExecState *exec, const JavaInstance *instance, const char *name, const char *sig, JNIType returnType) const +{ + jobject jinstance = instance->javaInstance(); + jobject fieldJInstance = _field->_instance; + JNIEnv *env = getJNIEnv(); + jvalue result; + + bzero (&result, sizeof(jvalue)); + jclass cls = env->GetObjectClass(fieldJInstance); + if ( cls != NULL ) { + jmethodID mid = env->GetMethodID(cls, name, sig); + if ( mid != NULL ) + { + RootObject* rootObject = instance->rootObject(); + if (rootObject && rootObject->nativeHandle()) { + JSValue *exceptionDescription = NULL; + jvalue args[1]; + + args[0].l = jinstance; + dispatchJNICall(rootObject->nativeHandle(), fieldJInstance, false, returnType, mid, args, result, 0, exceptionDescription); + if (exceptionDescription) + throwError(exec, GeneralError, exceptionDescription->toString(exec)); + } + } + } + return result; +} + +JSValue *JavaField::valueFromInstance(ExecState *exec, const Instance *i) const +{ + const JavaInstance *instance = static_cast<const JavaInstance *>(i); + + JSValue *jsresult = jsUndefined(); + + switch (_JNIType) { + case array_type: + case object_type: { + jvalue result = dispatchValueFromInstance (exec, instance, "get", "(Ljava/lang/Object;)Ljava/lang/Object;", object_type); + jobject anObject = result.l; + + const char *arrayType = type(); + if (arrayType[0] == '[') { + jsresult = JavaArray::convertJObjectToArray(exec, anObject, arrayType, instance->rootObject()); + } + else if (anObject != 0){ + jsresult = Instance::createRuntimeObject(Instance::JavaLanguage, anObject, instance->rootObject()); + } + } + break; + + case boolean_type: + jsresult = jsBoolean(dispatchValueFromInstance(exec, instance, "getBoolean", "(Ljava/lang/Object;)Z", boolean_type).z); + break; + + case byte_type: + case char_type: + case short_type: + + case int_type: { + jint value; + jvalue result = dispatchValueFromInstance (exec, instance, "getInt", "(Ljava/lang/Object;)I", int_type); + value = result.i; + jsresult = jsNumber((int)value); + } + break; + + case long_type: + case float_type: + case double_type: { + jdouble value; + jvalue result = dispatchValueFromInstance (exec, instance, "getDouble", "(Ljava/lang/Object;)D", double_type); + value = result.i; + jsresult = jsNumber((double)value); + } + break; + default: + break; + } + + JS_LOG ("getting %s = %s\n", name(), jsresult->toString(exec).ascii()); + + return jsresult; +} + +void JavaField::dispatchSetValueToInstance(ExecState *exec, const JavaInstance *instance, jvalue javaValue, const char *name, const char *sig) const +{ + jobject jinstance = instance->javaInstance(); + jobject fieldJInstance = _field->_instance; + JNIEnv *env = getJNIEnv(); + + jclass cls = env->GetObjectClass(fieldJInstance); + if ( cls != NULL ) { + jmethodID mid = env->GetMethodID(cls, name, sig); + if ( mid != NULL ) + { + RootObject* rootObject = instance->rootObject(); + if (rootObject && rootObject->nativeHandle()) { + JSValue *exceptionDescription = NULL; + jvalue args[2]; + jvalue result; + + args[0].l = jinstance; + args[1] = javaValue; + dispatchJNICall(rootObject->nativeHandle(), fieldJInstance, false, void_type, mid, args, result, 0, exceptionDescription); + if (exceptionDescription) + throwError(exec, GeneralError, exceptionDescription->toString(exec)); + } + } + } +} + +void JavaField::setValueToInstance(ExecState *exec, const Instance *i, JSValue *aValue) const +{ + const JavaInstance *instance = static_cast<const JavaInstance *>(i); + jvalue javaValue = convertValueToJValue (exec, aValue, _JNIType, type()); + + JS_LOG ("setting value %s to %s\n", name(), aValue->toString(exec).ascii()); + + switch (_JNIType) { + case array_type: + case object_type: { + dispatchSetValueToInstance (exec, instance, javaValue, "set", "(Ljava/lang/Object;Ljava/lang/Object;)V"); + } + break; + + case boolean_type: { + dispatchSetValueToInstance (exec, instance, javaValue, "setBoolean", "(Ljava/lang/Object;Z)V"); + } + break; + + case byte_type: { + dispatchSetValueToInstance (exec, instance, javaValue, "setByte", "(Ljava/lang/Object;B)V"); + } + break; + + case char_type: { + dispatchSetValueToInstance (exec, instance, javaValue, "setChar", "(Ljava/lang/Object;C)V"); + } + break; + + case short_type: { + dispatchSetValueToInstance (exec, instance, javaValue, "setShort", "(Ljava/lang/Object;S)V"); + } + break; + + case int_type: { + dispatchSetValueToInstance (exec, instance, javaValue, "setInt", "(Ljava/lang/Object;I)V"); + } + break; + + case long_type: { + dispatchSetValueToInstance (exec, instance, javaValue, "setLong", "(Ljava/lang/Object;J)V"); + } + break; + + case float_type: { + dispatchSetValueToInstance (exec, instance, javaValue, "setFloat", "(Ljava/lang/Object;F)V"); + } + break; + + case double_type: { + dispatchSetValueToInstance (exec, instance, javaValue, "setDouble", "(Ljava/lang/Object;D)V"); + } + break; + default: + break; + } +} + +JavaMethod::JavaMethod (JNIEnv *env, jobject aMethod) +{ + // Get return type + jobject returnType = callJNIObjectMethod (aMethod, "getReturnType", "()Ljava/lang/Class;"); + jstring returnTypeName = (jstring)callJNIObjectMethod (returnType, "getName", "()Ljava/lang/String;"); + _returnType =JavaString (env, returnTypeName); + _JNIReturnType = JNITypeFromClassName (_returnType.UTF8String()); + env->DeleteLocalRef (returnType); + env->DeleteLocalRef (returnTypeName); + + // Get method name + jstring methodName = (jstring)callJNIObjectMethod (aMethod, "getName", "()Ljava/lang/String;"); + _name = JavaString (env, methodName); + env->DeleteLocalRef (methodName); + + // Get parameters + jarray jparameters = (jarray)callJNIObjectMethod (aMethod, "getParameterTypes", "()[Ljava/lang/Class;"); + _numParameters = env->GetArrayLength (jparameters); + _parameters = new JavaParameter[_numParameters]; + + int i; + for (i = 0; i < _numParameters; i++) { + jobject aParameter = env->GetObjectArrayElement ((jobjectArray)jparameters, i); + jstring parameterName = (jstring)callJNIObjectMethod (aParameter, "getName", "()Ljava/lang/String;"); + _parameters[i] = JavaParameter(env, parameterName); + env->DeleteLocalRef (aParameter); + env->DeleteLocalRef (parameterName); + } + env->DeleteLocalRef (jparameters); + + // Created lazily. + _signature = 0; + _methodID = 0; + + jclass modifierClass = env->FindClass("java/lang/reflect/Modifier"); + int modifiers = callJNIIntMethod (aMethod, "getModifiers", "()I"); + _isStatic = (bool)callJNIStaticBooleanMethod (modifierClass, "isStatic", "(I)Z", modifiers); +} + +JavaMethod::~JavaMethod() +{ + if (_signature) + free(_signature); + delete [] _parameters; +}; + +// JNI method signatures use '/' between components of a class name, but +// we get '.' between components from the reflection API. +static void appendClassName(UString& aString, const char* className) +{ + ASSERT(JSLock::lockCount() > 0); + + char *result, *cp = strdup(className); + + result = cp; + while (*cp) { + if (*cp == '.') + *cp = '/'; + cp++; + } + + aString.append(result); + + free (result); +} + +const char *JavaMethod::signature() const +{ + if (!_signature) { + JSLock lock; + + UString signatureBuilder("("); + for (int i = 0; i < _numParameters; i++) { + JavaParameter* aParameter = parameterAt(i); + JNIType _JNIType = aParameter->getJNIType(); + if (_JNIType == array_type) + appendClassName(signatureBuilder, aParameter->type()); + else { + signatureBuilder.append(signatureFromPrimitiveType(_JNIType)); + if (_JNIType == object_type) { + appendClassName(signatureBuilder, aParameter->type()); + signatureBuilder.append(";"); + } + } + } + signatureBuilder.append(")"); + + const char *returnType = _returnType.UTF8String(); + if (_JNIReturnType == array_type) { + appendClassName(signatureBuilder, returnType); + } else { + signatureBuilder.append(signatureFromPrimitiveType(_JNIReturnType)); + if (_JNIReturnType == object_type) { + appendClassName(signatureBuilder, returnType); + signatureBuilder.append(";"); + } + } + + _signature = strdup(signatureBuilder.ascii()); + } + + return _signature; +} + +JNIType JavaMethod::JNIReturnType() const +{ + return _JNIReturnType; +} + +jmethodID JavaMethod::methodID (jobject obj) const +{ + if (_methodID == 0) { + _methodID = getMethodID (obj, name(), signature()); + } + return _methodID; +} + + +JavaArray::JavaArray(jobject array, const char* type, PassRefPtr<RootObject> rootObject) + : Array(rootObject) +{ + _array = new JObjectWrapper(array); + // Java array are fixed length, so we can cache length. + JNIEnv *env = getJNIEnv(); + _length = env->GetArrayLength((jarray)_array->_instance); + _type = strdup(type); + _rootObject = rootObject; +} + +JavaArray::~JavaArray () +{ + free ((void *)_type); +} + +RootObject* JavaArray::rootObject() const +{ + return _rootObject && _rootObject->isValid() ? _rootObject.get() : 0; +} + +void JavaArray::setValueAt(ExecState *exec, unsigned int index, JSValue *aValue) const +{ + JNIEnv *env = getJNIEnv(); + char *javaClassName = 0; + + JNIType arrayType = JNITypeFromPrimitiveType(_type[1]); + if (_type[1] == 'L'){ + // The type of the array will be something like: + // "[Ljava.lang.string;". This is guaranteed, so no need + // for extra sanity checks. + javaClassName = strdup(&_type[2]); + javaClassName[strchr(javaClassName, ';')-javaClassName] = 0; + } + jvalue aJValue = convertValueToJValue (exec, aValue, arrayType, javaClassName); + + switch (arrayType) { + case object_type: { + env->SetObjectArrayElement((jobjectArray)javaArray(), index, aJValue.l); + break; + } + + case boolean_type: { + env->SetBooleanArrayRegion((jbooleanArray)javaArray(), index, 1, &aJValue.z); + break; + } + + case byte_type: { + env->SetByteArrayRegion((jbyteArray)javaArray(), index, 1, &aJValue.b); + break; + } + + case char_type: { + env->SetCharArrayRegion((jcharArray)javaArray(), index, 1, &aJValue.c); + break; + } + + case short_type: { + env->SetShortArrayRegion((jshortArray)javaArray(), index, 1, &aJValue.s); + break; + } + + case int_type: { + env->SetIntArrayRegion((jintArray)javaArray(), index, 1, &aJValue.i); + break; + } + + case long_type: { + env->SetLongArrayRegion((jlongArray)javaArray(), index, 1, &aJValue.j); + } + + case float_type: { + env->SetFloatArrayRegion((jfloatArray)javaArray(), index, 1, &aJValue.f); + break; + } + + case double_type: { + env->SetDoubleArrayRegion((jdoubleArray)javaArray(), index, 1, &aJValue.d); + break; + } + default: + break; + } + + if (javaClassName) + free ((void *)javaClassName); +} + + +JSValue *JavaArray::valueAt(ExecState *exec, unsigned int index) const +{ + JNIEnv *env = getJNIEnv(); + JNIType arrayType = JNITypeFromPrimitiveType(_type[1]); + switch (arrayType) { + case object_type: { + jobjectArray objectArray = (jobjectArray)javaArray(); + jobject anObject; + anObject = env->GetObjectArrayElement(objectArray, index); + + // No object? + if (!anObject) { + return jsNull(); + } + + // Nested array? + if (_type[1] == '[') { + return JavaArray::convertJObjectToArray(exec, anObject, _type+1, rootObject()); + } + // or array of other object type? + return Instance::createRuntimeObject(Instance::JavaLanguage, anObject, rootObject()); + } + + case boolean_type: { + jbooleanArray booleanArray = (jbooleanArray)javaArray(); + jboolean aBoolean; + env->GetBooleanArrayRegion(booleanArray, index, 1, &aBoolean); + return jsBoolean(aBoolean); + } + + case byte_type: { + jbyteArray byteArray = (jbyteArray)javaArray(); + jbyte aByte; + env->GetByteArrayRegion(byteArray, index, 1, &aByte); + return jsNumber(aByte); + } + + case char_type: { + jcharArray charArray = (jcharArray)javaArray(); + jchar aChar; + env->GetCharArrayRegion(charArray, index, 1, &aChar); + return jsNumber(aChar); + break; + } + + case short_type: { + jshortArray shortArray = (jshortArray)javaArray(); + jshort aShort; + env->GetShortArrayRegion(shortArray, index, 1, &aShort); + return jsNumber(aShort); + } + + case int_type: { + jintArray intArray = (jintArray)javaArray(); + jint anInt; + env->GetIntArrayRegion(intArray, index, 1, &anInt); + return jsNumber(anInt); + } + + case long_type: { + jlongArray longArray = (jlongArray)javaArray(); + jlong aLong; + env->GetLongArrayRegion(longArray, index, 1, &aLong); + return jsNumber(aLong); + } + + case float_type: { + jfloatArray floatArray = (jfloatArray)javaArray(); + jfloat aFloat; + env->GetFloatArrayRegion(floatArray, index, 1, &aFloat); + return jsNumber(aFloat); + } + + case double_type: { + jdoubleArray doubleArray = (jdoubleArray)javaArray(); + jdouble aDouble; + env->GetDoubleArrayRegion(doubleArray, index, 1, &aDouble); + return jsNumber(aDouble); + } + default: + break; + } + return jsUndefined(); +} + +unsigned int JavaArray::getLength() const +{ + return _length; +} + + diff --git a/JavaScriptCore/bindings/jni/jni_runtime.h b/JavaScriptCore/bindings/jni/jni_runtime.h new file mode 100644 index 0000000..9ea1371 --- /dev/null +++ b/JavaScriptCore/bindings/jni/jni_runtime.h @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2003 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 COMPUTER, INC. OR + * 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. + */ +#ifndef _JNI_RUNTIME_H_ +#define _JNI_RUNTIME_H_ + +#include <jni_utility.h> +#include <jni_instance.h> + + +namespace KJS +{ + +namespace Bindings +{ + +typedef const char* RuntimeType; + +class JavaString +{ +public: + JavaString() + { + JSLock lock; + _rep = UString().rep(); + } + + void _commonInit (JNIEnv *e, jstring s) + { + int _size = e->GetStringLength (s); + const jchar *uc = getUCharactersFromJStringInEnv (e, s); + { + JSLock lock; + _rep = UString((UChar *)uc,_size).rep(); + } + releaseUCharactersForJStringInEnv (e, s, uc); + } + + JavaString (JNIEnv *e, jstring s) { + _commonInit (e, s); + } + + JavaString (jstring s) { + _commonInit (getJNIEnv(), s); + } + + ~JavaString() + { + JSLock lock; + _rep = 0; + } + + const char *UTF8String() const { + if (_utf8String.c_str() == 0) { + JSLock lock; + _utf8String = UString(_rep).UTF8String(); + } + return _utf8String.c_str(); + } + const jchar *uchars() const { return (const jchar *)_rep->data(); } + int length() const { return _rep->size(); } + UString ustring() const { return UString(_rep); } + +private: + RefPtr<UString::Rep> _rep; + mutable CString _utf8String; +}; + +class JavaParameter +{ +public: + JavaParameter () : _JNIType(invalid_type) {}; + JavaParameter (JNIEnv *env, jstring type); + virtual ~JavaParameter() { } + + RuntimeType type() const { return _type.UTF8String(); } + JNIType getJNIType() const { return _JNIType; } + +private: + JavaString _type; + JNIType _JNIType; +}; + + +class JavaField : public Field +{ +public: + JavaField (JNIEnv *env, jobject aField); + + virtual JSValue *valueFromInstance(ExecState *exec, const Instance *instance) const; + virtual void setValueToInstance(ExecState *exec, const Instance *instance, JSValue *aValue) const; + + virtual const char *name() const { return _name.UTF8String(); } + virtual RuntimeType type() const { return _type.UTF8String(); } + + JNIType getJNIType() const { return _JNIType; } + +private: + void dispatchSetValueToInstance(ExecState *exec, const JavaInstance *instance, jvalue javaValue, const char *name, const char *sig) const; + jvalue dispatchValueFromInstance(ExecState *exec, const JavaInstance *instance, const char *name, const char *sig, JNIType returnType) const; + + JavaString _name; + JavaString _type; + JNIType _JNIType; + RefPtr<JObjectWrapper> _field; +}; + + +class JavaMethod : public Method +{ +public: + JavaMethod(JNIEnv* env, jobject aMethod); + ~JavaMethod(); + + virtual const char *name() const { return _name.UTF8String(); }; + RuntimeType returnType() const { return _returnType.UTF8String(); }; + JavaParameter* parameterAt(int i) const { return &_parameters[i]; }; + int numParameters() const { return _numParameters; }; + + const char *signature() const; + JNIType JNIReturnType() const; + + jmethodID methodID (jobject obj) const; + + bool isStatic() const { return _isStatic; } + +private: + JavaParameter* _parameters; + int _numParameters; + JavaString _name; + mutable char* _signature; + JavaString _returnType; + JNIType _JNIReturnType; + mutable jmethodID _methodID; + bool _isStatic; +}; + +class JavaArray : public Array +{ +public: + JavaArray(jobject array, const char* type, PassRefPtr<RootObject>); + virtual ~JavaArray(); + + RootObject* rootObject() const; + + virtual void setValueAt(ExecState *exec, unsigned int index, JSValue *aValue) const; + virtual JSValue *valueAt(ExecState *exec, unsigned int index) const; + virtual unsigned int getLength() const; + + jobject javaArray() const { return _array->_instance; } + + static JSValue* convertJObjectToArray (ExecState* exec, jobject anObject, const char* type, PassRefPtr<RootObject>); + +private: + RefPtr<JObjectWrapper> _array; + unsigned int _length; + const char *_type; +}; + +} // namespace Bindings + +} // namespace KJS + +#endif diff --git a/JavaScriptCore/bindings/jni/jni_utility.cpp b/JavaScriptCore/bindings/jni/jni_utility.cpp new file mode 100644 index 0000000..8a8c656 --- /dev/null +++ b/JavaScriptCore/bindings/jni/jni_utility.cpp @@ -0,0 +1,987 @@ +/* + * Copyright (C) 2003 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 COMPUTER, INC. OR + * 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 "jni_utility.h" + +#include "list.h" +#include "jni_runtime.h" +#include "runtime_array.h" +#include "runtime_object.h" +#include <dlfcn.h> + +namespace KJS { + +namespace Bindings { + +static jint KJS_GetCreatedJavaVMs(JavaVM** vmBuf, jsize bufLen, jsize* nVMs) +{ + static void* javaVMFramework = 0; + if (!javaVMFramework) + javaVMFramework = dlopen("/System/Library/Frameworks/JavaVM.framework/JavaVM", RTLD_LAZY); + if (!javaVMFramework) + return JNI_ERR; + + static jint(*functionPointer)(JavaVM**, jsize, jsize *) = 0; + if (!functionPointer) + functionPointer = (jint(*)(JavaVM**, jsize, jsize *))dlsym(javaVMFramework, "JNI_GetCreatedJavaVMs"); + if (!functionPointer) + return JNI_ERR; + return functionPointer(vmBuf, bufLen, nVMs); +} + +static JavaVM *jvm = 0; + +// Provide the ability for an outside component to specify the JavaVM to use +// If the jvm value is set, the getJavaVM function below will just return. +// In getJNIEnv(), if AttachCurrentThread is called to a VM that is already +// attached, the result is a no-op. +void setJavaVM(JavaVM *javaVM) +{ + jvm = javaVM; +} + +JavaVM *getJavaVM() +{ + if (jvm) + return jvm; + + JavaVM *jvmArray[1]; + jsize bufLen = 1; + jsize nJVMs = 0; + jint jniError = 0; + + // Assumes JVM is already running ..., one per process + jniError = KJS_GetCreatedJavaVMs(jvmArray, bufLen, &nJVMs); + if ( jniError == JNI_OK && nJVMs > 0 ) { + jvm = jvmArray[0]; + } + else + fprintf(stderr, "%s: JNI_GetCreatedJavaVMs failed, returned %ld\n", __PRETTY_FUNCTION__, (long)jniError); + + return jvm; +} + +JNIEnv* getJNIEnv() +{ + union { + JNIEnv* env; + void* dummy; + } u; + jint jniError = 0; + + jniError = (getJavaVM())->AttachCurrentThread(&u.dummy, NULL); + if (jniError == JNI_OK) + return u.env; + else + fprintf(stderr, "%s: AttachCurrentThread failed, returned %ld\n", __PRETTY_FUNCTION__, (long)jniError); + return NULL; +} + +static jvalue callJNIMethod (JNIType type, jobject obj, const char *name, const char *sig, va_list args) +{ + JavaVM *jvm = getJavaVM(); + JNIEnv *env = getJNIEnv(); + jvalue result; + + bzero (&result, sizeof(jvalue)); + if ( obj != NULL && jvm != NULL && env != NULL) { + jclass cls = env->GetObjectClass(obj); + if ( cls != NULL ) { + jmethodID mid = env->GetMethodID(cls, name, sig); + if ( mid != NULL ) + { + switch (type) { + case void_type: + env->functions->CallVoidMethodV(env, obj, mid, args); + break; + case array_type: + case object_type: + result.l = env->functions->CallObjectMethodV(env, obj, mid, args); + break; + case boolean_type: + result.z = env->functions->CallBooleanMethodV(env, obj, mid, args); + break; + case byte_type: + result.b = env->functions->CallByteMethodV(env, obj, mid, args); + break; + case char_type: + result.c = env->functions->CallCharMethodV(env, obj, mid, args); + break; + case short_type: + result.s = env->functions->CallShortMethodV(env, obj, mid, args); + break; + case int_type: + result.i = env->functions->CallIntMethodV(env, obj, mid, args); + break; + case long_type: + result.j = env->functions->CallLongMethodV(env, obj, mid, args); + break; + case float_type: + result.f = env->functions->CallFloatMethodV(env, obj, mid, args); + break; + case double_type: + result.d = env->functions->CallDoubleMethodV(env, obj, mid, args); + break; + default: + fprintf(stderr, "%s: invalid function type (%d)\n", __PRETTY_FUNCTION__, (int)type); + } + } + else + { + fprintf(stderr, "%s: Could not find method: %s for %p\n", __PRETTY_FUNCTION__, name, obj); + env->ExceptionDescribe(); + env->ExceptionClear(); + fprintf (stderr, "\n"); + } + + env->DeleteLocalRef(cls); + } + else { + fprintf(stderr, "%s: Could not find class for %p\n", __PRETTY_FUNCTION__, obj); + } + } + + return result; +} + +static jvalue callJNIStaticMethod (JNIType type, jclass cls, const char *name, const char *sig, va_list args) +{ + JavaVM *jvm = getJavaVM(); + JNIEnv *env = getJNIEnv(); + jvalue result; + + bzero (&result, sizeof(jvalue)); + if ( cls != NULL && jvm != NULL && env != NULL) { + jmethodID mid = env->GetStaticMethodID(cls, name, sig); + if ( mid != NULL ) + { + switch (type) { + case void_type: + env->functions->CallStaticVoidMethodV(env, cls, mid, args); + break; + case array_type: + case object_type: + result.l = env->functions->CallStaticObjectMethodV(env, cls, mid, args); + break; + case boolean_type: + result.z = env->functions->CallStaticBooleanMethodV(env, cls, mid, args); + break; + case byte_type: + result.b = env->functions->CallStaticByteMethodV(env, cls, mid, args); + break; + case char_type: + result.c = env->functions->CallStaticCharMethodV(env, cls, mid, args); + break; + case short_type: + result.s = env->functions->CallStaticShortMethodV(env, cls, mid, args); + break; + case int_type: + result.i = env->functions->CallStaticIntMethodV(env, cls, mid, args); + break; + case long_type: + result.j = env->functions->CallStaticLongMethodV(env, cls, mid, args); + break; + case float_type: + result.f = env->functions->CallStaticFloatMethodV(env, cls, mid, args); + break; + case double_type: + result.d = env->functions->CallStaticDoubleMethodV(env, cls, mid, args); + break; + default: + fprintf(stderr, "%s: invalid function type (%d)\n", __PRETTY_FUNCTION__, (int)type); + } + } + else + { + fprintf(stderr, "%s: Could not find method: %s for %p\n", __PRETTY_FUNCTION__, name, cls); + env->ExceptionDescribe(); + env->ExceptionClear(); + fprintf (stderr, "\n"); + } + } + + return result; +} + +static jvalue callJNIMethodIDA (JNIType type, jobject obj, jmethodID mid, jvalue *args) +{ + JNIEnv *env = getJNIEnv(); + jvalue result; + + bzero (&result, sizeof(jvalue)); + if ( obj != NULL && mid != NULL ) + { + switch (type) { + case void_type: + env->functions->CallVoidMethodA(env, obj, mid, args); + break; + case array_type: + case object_type: + result.l = env->functions->CallObjectMethodA(env, obj, mid, args); + break; + case boolean_type: + result.z = env->functions->CallBooleanMethodA(env, obj, mid, args); + break; + case byte_type: + result.b = env->functions->CallByteMethodA(env, obj, mid, args); + break; + case char_type: + result.c = env->functions->CallCharMethodA(env, obj, mid, args); + break; + case short_type: + result.s = env->functions->CallShortMethodA(env, obj, mid, args); + break; + case int_type: + result.i = env->functions->CallIntMethodA(env, obj, mid, args); + break; + case long_type: + result.j = env->functions->CallLongMethodA(env, obj, mid, args); + break; + case float_type: + result.f = env->functions->CallFloatMethodA(env, obj, mid, args); + break; + case double_type: + result.d = env->functions->CallDoubleMethodA(env, obj, mid, args); + break; + default: + fprintf(stderr, "%s: invalid function type (%d)\n", __PRETTY_FUNCTION__, (int)type); + } + } + + return result; +} + +static jvalue callJNIMethodA (JNIType type, jobject obj, const char *name, const char *sig, jvalue *args) +{ + JavaVM *jvm = getJavaVM(); + JNIEnv *env = getJNIEnv(); + jvalue result; + + bzero (&result, sizeof(jvalue)); + if ( obj != NULL && jvm != NULL && env != NULL) { + jclass cls = env->GetObjectClass(obj); + if ( cls != NULL ) { + jmethodID mid = env->GetMethodID(cls, name, sig); + if ( mid != NULL ) { + result = callJNIMethodIDA (type, obj, mid, args); + } + else { + fprintf(stderr, "%s: Could not find method: %s\n", __PRETTY_FUNCTION__, name); + env->ExceptionDescribe(); + env->ExceptionClear(); + fprintf (stderr, "\n"); + } + + env->DeleteLocalRef(cls); + } + else { + fprintf(stderr, "%s: Could not find class for object\n", __PRETTY_FUNCTION__); + } + } + + return result; +} + +jmethodID getMethodID (jobject obj, const char *name, const char *sig) +{ + JNIEnv *env = getJNIEnv(); + jmethodID mid = 0; + + if ( env != NULL) { + jclass cls = env->GetObjectClass(obj); + if ( cls != NULL ) { + mid = env->GetMethodID(cls, name, sig); + if (!mid) { + env->ExceptionClear(); + mid = env->GetStaticMethodID(cls, name, sig); + if (!mid) { + env->ExceptionClear(); + } + } + } + env->DeleteLocalRef(cls); + } + return mid; +} + + +#define CALL_JNI_METHOD(function_type,obj,name,sig) \ + va_list args;\ + va_start (args, sig);\ + \ + jvalue result = callJNIMethod(function_type, obj, name, sig, args);\ + \ + va_end (args); + +#define CALL_JNI_STATIC_METHOD(function_type,cls,name,sig) \ + va_list args;\ + va_start (args, sig);\ + \ + jvalue result = callJNIStaticMethod(function_type, cls, name, sig, args);\ + \ + va_end (args); + +void callJNIVoidMethod (jobject obj, const char *name, const char *sig, ... ) +{ + CALL_JNI_METHOD (void_type, obj, name, sig); +} + +jobject callJNIObjectMethod (jobject obj, const char *name, const char *sig, ... ) +{ + CALL_JNI_METHOD (object_type, obj, name, sig); + return result.l; +} + +jboolean callJNIBooleanMethod( jobject obj, const char *name, const char *sig, ... ) +{ + CALL_JNI_METHOD (boolean_type, obj, name, sig); + return result.z; +} + +jboolean callJNIStaticBooleanMethod (jclass cls, const char *name, const char *sig, ... ) +{ + CALL_JNI_STATIC_METHOD (boolean_type, cls, name, sig); + return result.z; +} + +jbyte callJNIByteMethod( jobject obj, const char *name, const char *sig, ... ) +{ + CALL_JNI_METHOD (byte_type, obj, name, sig); + return result.b; +} + +jchar callJNICharMethod (jobject obj, const char *name, const char *sig, ... ) +{ + CALL_JNI_METHOD (char_type, obj, name, sig); + return result.c; +} + +jshort callJNIShortMethod (jobject obj, const char *name, const char *sig, ... ) +{ + CALL_JNI_METHOD (short_type, obj, name, sig); + return result.s; +} + +jint callJNIIntMethod (jobject obj, const char *name, const char *sig, ... ) +{ + CALL_JNI_METHOD (int_type, obj, name, sig); + return result.i; +} + +jlong callJNILongMethod (jobject obj, const char *name, const char *sig, ... ) +{ + CALL_JNI_METHOD (long_type, obj, name, sig); + return result.j; +} + +jfloat callJNIFloatMethod (jobject obj, const char *name, const char *sig, ... ) +{ + CALL_JNI_METHOD (float_type, obj, name, sig); + return result.f; +} + +jdouble callJNIDoubleMethod (jobject obj, const char *name, const char *sig, ... ) +{ + CALL_JNI_METHOD (double_type, obj, name, sig); + return result.d; +} + +void callJNIVoidMethodA (jobject obj, const char *name, const char *sig, jvalue *args) +{ + jvalue result = callJNIMethodA (void_type, obj, name, sig, args); +} + +jobject callJNIObjectMethodA (jobject obj, const char *name, const char *sig, jvalue *args) +{ + jvalue result = callJNIMethodA (object_type, obj, name, sig, args); + return result.l; +} + +jbyte callJNIByteMethodA ( jobject obj, const char *name, const char *sig, jvalue *args) +{ + jvalue result = callJNIMethodA (byte_type, obj, name, sig, args); + return result.b; +} + +jchar callJNICharMethodA (jobject obj, const char *name, const char *sig, jvalue *args) +{ + jvalue result = callJNIMethodA (char_type, obj, name, sig, args); + return result.c; +} + +jshort callJNIShortMethodA (jobject obj, const char *name, const char *sig, jvalue *args) +{ + jvalue result = callJNIMethodA (short_type, obj, name, sig, args); + return result.s; +} + +jint callJNIIntMethodA (jobject obj, const char *name, const char *sig, jvalue *args) +{ + jvalue result = callJNIMethodA (int_type, obj, name, sig, args); + return result.i; +} + +jlong callJNILongMethodA (jobject obj, const char *name, const char *sig, jvalue *args) +{ + jvalue result = callJNIMethodA (long_type, obj, name, sig, args); + return result.j; +} + +jfloat callJNIFloatMethodA (jobject obj, const char *name, const char *sig, jvalue *args) +{ + jvalue result = callJNIMethodA (float_type, obj, name, sig, args); + return result.f; +} + +jdouble callJNIDoubleMethodA (jobject obj, const char *name, const char *sig, jvalue *args) +{ + jvalue result = callJNIMethodA (double_type, obj, name, sig, args); + return result.d; +} + +jboolean callJNIBooleanMethodA (jobject obj, const char *name, const char *sig, jvalue *args) +{ + jvalue result = callJNIMethodA (boolean_type, obj, name, sig, args); + return result.z; +} + +void callJNIVoidMethodIDA (jobject obj, jmethodID methodID, jvalue *args) +{ + jvalue result = callJNIMethodIDA (void_type, obj, methodID, args); +} + +jobject callJNIObjectMethodIDA (jobject obj, jmethodID methodID, jvalue *args) +{ + jvalue result = callJNIMethodIDA (object_type, obj, methodID, args); + return result.l; +} + +jbyte callJNIByteMethodIDA ( jobject obj, jmethodID methodID, jvalue *args) +{ + jvalue result = callJNIMethodIDA (byte_type, obj, methodID, args); + return result.b; +} + +jchar callJNICharMethodIDA (jobject obj, jmethodID methodID, jvalue *args) +{ + jvalue result = callJNIMethodIDA (char_type, obj, methodID, args); + return result.c; +} + +jshort callJNIShortMethodIDA (jobject obj, jmethodID methodID, jvalue *args) +{ + jvalue result = callJNIMethodIDA (short_type, obj, methodID, args); + return result.s; +} + +jint callJNIIntMethodIDA (jobject obj, jmethodID methodID, jvalue *args) +{ + jvalue result = callJNIMethodIDA (int_type, obj, methodID, args); + return result.i; +} + +jlong callJNILongMethodIDA (jobject obj, jmethodID methodID, jvalue *args) +{ + jvalue result = callJNIMethodIDA (long_type, obj, methodID, args); + return result.j; +} + +jfloat callJNIFloatMethodIDA (jobject obj, jmethodID methodID, jvalue *args) +{ + jvalue result = callJNIMethodIDA (float_type, obj, methodID, args); + return result.f; +} + +jdouble callJNIDoubleMethodIDA (jobject obj, jmethodID methodID, jvalue *args) +{ + jvalue result = callJNIMethodIDA (double_type, obj, methodID, args); + return result.d; +} + +jboolean callJNIBooleanMethodIDA (jobject obj, jmethodID methodID, jvalue *args) +{ + jvalue result = callJNIMethodIDA (boolean_type, obj, methodID, args); + return result.z; +} + +const char *getCharactersFromJString (jstring aJString) +{ + return getCharactersFromJStringInEnv (getJNIEnv(), aJString); +} + +void releaseCharactersForJString (jstring aJString, const char *s) +{ + releaseCharactersForJStringInEnv (getJNIEnv(), aJString, s); +} + +const char *getCharactersFromJStringInEnv (JNIEnv *env, jstring aJString) +{ + jboolean isCopy; + const char *s = env->GetStringUTFChars((jstring)aJString, &isCopy); + if (!s) { + env->ExceptionDescribe(); + env->ExceptionClear(); + fprintf (stderr, "\n"); + } + return s; +} + +void releaseCharactersForJStringInEnv (JNIEnv *env, jstring aJString, const char *s) +{ + env->ReleaseStringUTFChars (aJString, s); +} + +const jchar *getUCharactersFromJStringInEnv (JNIEnv *env, jstring aJString) +{ + jboolean isCopy; + const jchar *s = env->GetStringChars((jstring)aJString, &isCopy); + if (!s) { + env->ExceptionDescribe(); + env->ExceptionClear(); + fprintf (stderr, "\n"); + } + return s; +} + +void releaseUCharactersForJStringInEnv (JNIEnv *env, jstring aJString, const jchar *s) +{ + env->ReleaseStringChars (aJString, s); +} + +JNIType JNITypeFromClassName(const char *name) +{ + JNIType type; + + if (strcmp("byte",name) == 0) + type = byte_type; + else if (strcmp("short",name) == 0) + type = short_type; + else if (strcmp("int",name) == 0) + type = int_type; + else if (strcmp("long",name) == 0) + type = long_type; + else if (strcmp("float",name) == 0) + type = float_type; + else if (strcmp("double",name) == 0) + type = double_type; + else if (strcmp("char",name) == 0) + type = char_type; + else if (strcmp("boolean",name) == 0) + type = boolean_type; + else if (strcmp("void",name) == 0) + type = void_type; + else if ('[' == name[0]) + type = array_type; + else + type = object_type; + + return type; +} + +const char *signatureFromPrimitiveType(JNIType type) +{ + switch (type){ + case void_type: + return "V"; + + case array_type: + return "["; + + case object_type: + return "L"; + + case boolean_type: + return "Z"; + + case byte_type: + return "B"; + + case char_type: + return "C"; + + case short_type: + return "S"; + + case int_type: + return "I"; + + case long_type: + return "J"; + + case float_type: + return "F"; + + case double_type: + return "D"; + + case invalid_type: + default: + break; + } + return ""; +} + +JNIType JNITypeFromPrimitiveType(char type) +{ + switch (type){ + case 'V': + return void_type; + + case 'L': + return object_type; + + case '[': + return array_type; + + case 'Z': + return boolean_type; + + case 'B': + return byte_type; + + case 'C': + return char_type; + + case 'S': + return short_type; + + case 'I': + return int_type; + + case 'J': + return long_type; + + case 'F': + return float_type; + + case 'D': + return double_type; + + default: + break; + } + return invalid_type; +} + +jvalue getJNIField( jobject obj, JNIType type, const char *name, const char *signature) +{ + JavaVM *jvm = getJavaVM(); + JNIEnv *env = getJNIEnv(); + jvalue result; + + bzero (&result, sizeof(jvalue)); + if ( obj != NULL && jvm != NULL && env != NULL) { + jclass cls = env->GetObjectClass(obj); + if ( cls != NULL ) { + jfieldID field = env->GetFieldID(cls, name, signature); + if ( field != NULL ) { + switch (type) { + case array_type: + case object_type: + result.l = env->functions->GetObjectField(env, obj, field); + break; + case boolean_type: + result.z = env->functions->GetBooleanField(env, obj, field); + break; + case byte_type: + result.b = env->functions->GetByteField(env, obj, field); + break; + case char_type: + result.c = env->functions->GetCharField(env, obj, field); + break; + case short_type: + result.s = env->functions->GetShortField(env, obj, field); + break; + case int_type: + result.i = env->functions->GetIntField(env, obj, field); + break; + case long_type: + result.j = env->functions->GetLongField(env, obj, field); + break; + case float_type: + result.f = env->functions->GetFloatField(env, obj, field); + break; + case double_type: + result.d = env->functions->GetDoubleField(env, obj, field); + break; + default: + fprintf(stderr, "%s: invalid field type (%d)\n", __PRETTY_FUNCTION__, (int)type); + } + } + else + { + fprintf(stderr, "%s: Could not find field: %s\n", __PRETTY_FUNCTION__, name); + env->ExceptionDescribe(); + env->ExceptionClear(); + fprintf (stderr, "\n"); + } + + env->DeleteLocalRef(cls); + } + else { + fprintf(stderr, "%s: Could not find class for object\n", __PRETTY_FUNCTION__); + } + } + + return result; +} + +static jobject convertArrayInstanceToJavaArray(ExecState *exec, JSValue *value, const char *javaClassName) { + + ASSERT(JSLock::lockCount() > 0); + + JNIEnv *env = getJNIEnv(); + // As JS Arrays can contain a mixture of objects, assume we can convert to + // the requested Java Array type requested, unless the array type is some object array + // other than a string. + ArrayInstance *jsArray = static_cast<ArrayInstance *>(value); + unsigned length = jsArray->getLength(); + jobjectArray jarray = 0; + + // Build the correct array type + switch (JNITypeFromPrimitiveType(javaClassName[1])) { + case object_type: { + // Only support string object types + if (0 == strcmp("[Ljava.lang.String;", javaClassName)) { + jarray = (jobjectArray)env->NewObjectArray(length, + env->FindClass("java/lang/String"), + env->NewStringUTF("")); + for(unsigned i = 0; i < length; i++) { + JSValue* item = jsArray->getItem(i); + UString stringValue = item->toString(exec); + env->SetObjectArrayElement(jarray,i, + env->functions->NewString(env, (const jchar *)stringValue.data(), stringValue.size())); + } + } + break; + } + + case boolean_type: { + jarray = (jobjectArray)env->NewBooleanArray(length); + for(unsigned i = 0; i < length; i++) { + JSValue* item = jsArray->getItem(i); + jboolean value = (jboolean)item->toNumber(exec); + env->SetBooleanArrayRegion((jbooleanArray)jarray, (jsize)i, (jsize)1, &value); + } + break; + } + + case byte_type: { + jarray = (jobjectArray)env->NewByteArray(length); + for(unsigned i = 0; i < length; i++) { + JSValue* item = jsArray->getItem(i); + jbyte value = (jbyte)item->toNumber(exec); + env->SetByteArrayRegion((jbyteArray)jarray, (jsize)i, (jsize)1, &value); + } + break; + } + + case char_type: { + jarray = (jobjectArray)env->NewCharArray(length); + for(unsigned i = 0; i < length; i++) { + JSValue* item = jsArray->getItem(i); + UString stringValue = item->toString(exec); + jchar value = 0; + if (stringValue.size() > 0) + value = ((const jchar*)stringValue.data())[0]; + env->SetCharArrayRegion((jcharArray)jarray, (jsize)i, (jsize)1, &value); + } + break; + } + + case short_type: { + jarray = (jobjectArray)env->NewShortArray(length); + for(unsigned i = 0; i < length; i++) { + JSValue* item = jsArray->getItem(i); + jshort value = (jshort)item->toNumber(exec); + env->SetShortArrayRegion((jshortArray)jarray, (jsize)i, (jsize)1, &value); + } + break; + } + + case int_type: { + jarray = (jobjectArray)env->NewIntArray(length); + for(unsigned i = 0; i < length; i++) { + JSValue* item = jsArray->getItem(i); + jint value = (jint)item->toNumber(exec); + env->SetIntArrayRegion((jintArray)jarray, (jsize)i, (jsize)1, &value); + } + break; + } + + case long_type: { + jarray = (jobjectArray)env->NewLongArray(length); + for(unsigned i = 0; i < length; i++) { + JSValue* item = jsArray->getItem(i); + jlong value = (jlong)item->toNumber(exec); + env->SetLongArrayRegion((jlongArray)jarray, (jsize)i, (jsize)1, &value); + } + break; + } + + case float_type: { + jarray = (jobjectArray)env->NewFloatArray(length); + for(unsigned i = 0; i < length; i++) { + JSValue* item = jsArray->getItem(i); + jfloat value = (jfloat)item->toNumber(exec); + env->SetFloatArrayRegion((jfloatArray)jarray, (jsize)i, (jsize)1, &value); + } + break; + } + + case double_type: { + jarray = (jobjectArray)env->NewDoubleArray(length); + for(unsigned i = 0; i < length; i++) { + JSValue* item = jsArray->getItem(i); + jdouble value = (jdouble)item->toNumber(exec); + env->SetDoubleArrayRegion((jdoubleArray)jarray, (jsize)i, (jsize)1, &value); + } + break; + } + + case array_type: // don't handle embedded arrays + case void_type: // Don't expect arrays of void objects + case invalid_type: // Array of unknown objects + break; + } + + // if it was not one of the cases handled, then null is returned + return jarray; +} + + +jvalue convertValueToJValue (ExecState *exec, JSValue *value, JNIType _JNIType, const char *javaClassName) +{ + JSLock lock; + + jvalue result; + + switch (_JNIType){ + case array_type: + case object_type: { + result.l = (jobject)0; + + // First see if we have a Java instance. + if (value->isObject()){ + JSObject *objectImp = static_cast<JSObject*>(value); + if (objectImp->classInfo() == &RuntimeObjectImp::info) { + RuntimeObjectImp *imp = static_cast<RuntimeObjectImp *>(value); + JavaInstance *instance = static_cast<JavaInstance*>(imp->getInternalInstance()); + if (instance) + result.l = instance->javaInstance(); + } + else if (objectImp->classInfo() == &RuntimeArray::info) { + // Input is a JavaScript Array that was originally created from a Java Array + RuntimeArray *imp = static_cast<RuntimeArray *>(value); + JavaArray *array = static_cast<JavaArray*>(imp->getConcreteArray()); + result.l = array->javaArray(); + } + else if (objectImp->classInfo() == &ArrayInstance::info) { + // Input is a Javascript Array. We need to create it to a Java Array. + result.l = convertArrayInstanceToJavaArray(exec, value, javaClassName); + } + } + + // Now convert value to a string if the target type is a java.lang.string, and we're not + // converting from a Null. + if (result.l == 0 && strcmp(javaClassName, "java.lang.String") == 0) { +#ifdef CONVERT_NULL_TO_EMPTY_STRING + if (value->isNull()) { + JNIEnv *env = getJNIEnv(); + jchar buf[2]; + jobject javaString = env->functions->NewString (env, buf, 0); + result.l = javaString; + } + else +#else + if (!value->isNull()) +#endif + { + UString stringValue = value->toString(exec); + JNIEnv *env = getJNIEnv(); + jobject javaString = env->functions->NewString (env, (const jchar *)stringValue.data(), stringValue.size()); + result.l = javaString; + } + } else if (result.l == 0) + bzero (&result, sizeof(jvalue)); // Handle it the same as a void case + } + break; + + case boolean_type: { + result.z = (jboolean)value->toNumber(exec); + } + break; + + case byte_type: { + result.b = (jbyte)value->toNumber(exec); + } + break; + + case char_type: { + result.c = (jchar)value->toNumber(exec); + } + break; + + case short_type: { + result.s = (jshort)value->toNumber(exec); + } + break; + + case int_type: { + result.i = (jint)value->toNumber(exec); + } + break; + + case long_type: { + result.j = (jlong)value->toNumber(exec); + } + break; + + case float_type: { + result.f = (jfloat)value->toNumber(exec); + } + break; + + case double_type: { + result.d = (jdouble)value->toNumber(exec); + } + break; + + break; + + case invalid_type: + default: + case void_type: { + bzero (&result, sizeof(jvalue)); + } + break; + } + return result; +} + +} // end of namespace Bindings + +} // end of namespace KJS diff --git a/JavaScriptCore/bindings/jni/jni_utility.h b/JavaScriptCore/bindings/jni/jni_utility.h new file mode 100644 index 0000000..69aa739 --- /dev/null +++ b/JavaScriptCore/bindings/jni/jni_utility.h @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2003 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 COMPUTER, INC. OR + * 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. + */ +#ifndef _JNI_UTILITY_H_ +#define _JNI_UTILITY_H_ + +#include <list.h> + +#include <JavaVM/jni.h> + +// The order of these items can not be modified as they are tightly +// bound with the JVM on Mac OSX. If new types need to be added, they +// should be added to the end. It is used in jni_obc.mm when calling +// through to the JVM. Newly added items need to be made compatible +// in that file. +typedef enum { + invalid_type = 0, + void_type, + object_type, + boolean_type, + byte_type, + char_type, + short_type, + int_type, + long_type, + float_type, + double_type, + array_type +} JNIType; + +namespace KJS { + +namespace Bindings { + +class JavaParameter; + +const char *getCharactersFromJString(jstring aJString); +void releaseCharactersForJString(jstring aJString, const char *s); + +const char *getCharactersFromJStringInEnv(JNIEnv *env, jstring aJString); +void releaseCharactersForJStringInEnv(JNIEnv *env, jstring aJString, const char *s); +const jchar *getUCharactersFromJStringInEnv(JNIEnv *env, jstring aJString); +void releaseUCharactersForJStringInEnv(JNIEnv *env, jstring aJString, const jchar *s); + +JNIType JNITypeFromClassName(const char *name); +JNIType JNITypeFromPrimitiveType(char type); +const char *signatureFromPrimitiveType(JNIType type); + +jvalue convertValueToJValue(ExecState *exec, JSValue *value, JNIType _JNIType, const char *javaClassName); + +jvalue getJNIField(jobject obj, JNIType type, const char *name, const char *signature); + +jmethodID getMethodID(jobject obj, const char *name, const char *sig); + +jobject callJNIObjectMethod(jobject obj, const char *name, const char *sig, ... ); +void callJNIVoidMethod(jobject obj, const char *name, const char *sig, ... ); +jboolean callJNIBooleanMethod(jobject obj, const char *name, const char *sig, ... ); +jboolean callJNIStaticBooleanMethod(jclass cls, const char *name, const char *sig, ... ); +jbyte callJNIByteMethod(jobject obj, const char *name, const char *sig, ... ); +jchar callJNICharMethod(jobject obj, const char *name, const char *sig, ... ); +jshort callJNIShortMethod(jobject obj, const char *name, const char *sig, ... ); +jint callJNIIntMethod(jobject obj, const char *name, const char *sig, ... ); +jlong callJNILongMethod(jobject obj, const char *name, const char *sig, ... ); +jfloat callJNIFloatMethod(jobject obj, const char *name, const char *sig, ... ); +jdouble callJNIDoubleMethod(jobject obj, const char *name, const char *sig, ... ); + +jobject callJNIObjectMethodA(jobject obj, const char *name, const char *sig, jvalue *args); +void callJNIVoidMethodA(jobject obj, const char *name, const char *sig, jvalue *args); +jboolean callJNIBooleanMethodA(jobject obj, const char *name, const char *sig, jvalue *args); +jbyte callJNIByteMethodA(jobject obj, const char *name, const char *sig, jvalue *args); +jchar callJNICharMethodA(jobject obj, const char *name, const char *sig, jvalue *args); +jshort callJNIShortMethodA(jobject obj, const char *name, const char *sig, jvalue *args); +jint callJNIIntMethodA(jobject obj, const char *name, const char *sig, jvalue *args); +jlong callJNILongMethodA(jobject obj, const char *name, const char *sig, jvalue *args); +jfloat callJNIFloatMethodA(jobject obj, const char *name, const char *sig, jvalue *args); +jdouble callJNIDoubleMethodA(jobject obj, const char *name, const char *sig, jvalue *args); + +jobject callJNIObjectMethodIDA(jobject obj, jmethodID methodID, jvalue *args); +void callJNIVoidMethodIDA(jobject obj, jmethodID methodID, jvalue *args); +jboolean callJNIBooleanMethodIDA(jobject obj, jmethodID methodID, jvalue *args); +jbyte callJNIByteMethodIDA(jobject obj, jmethodID methodID, jvalue *args); +jchar callJNICharMethodIDA(jobject obj, jmethodID methodID, jvalue *args); +jshort callJNIShortMethodIDA(jobject obj, jmethodID methodID, jvalue *args); +jint callJNIIntMethodIDA(jobject obj, jmethodID methodID, jvalue *args); +jlong callJNILongMethodIDA(jobject obj, jmethodID methodID, jvalue *args); +jfloat callJNIFloatMethodIDA(jobject obj, jmethodID methodID, jvalue *args); +jdouble callJNIDoubleMethodIDA(jobject obj, jmethodID methodID, jvalue *args); + +JavaVM *getJavaVM(); +void setJavaVM(JavaVM *javaVM); +JNIEnv *getJNIEnv(); + +bool dispatchJNICall(const void *targetAppletView, jobject obj, bool isStatic, JNIType returnType, jmethodID methodID, jvalue *args, jvalue &result, const char *callingURL, JSValue *&exceptionDescription); + +} // namespace Bindings + +} // namespace KJS + +#endif // _JNI_UTILITY_H_ diff --git a/JavaScriptCore/bindings/make_testbindings b/JavaScriptCore/bindings/make_testbindings new file mode 100755 index 0000000..1f528fe --- /dev/null +++ b/JavaScriptCore/bindings/make_testbindings @@ -0,0 +1,2 @@ +cc -g -o testbindingsM testbindings.mm -I../kjs -F$SYMROOTS -framework JavaScriptCore -framework Foundation -lstdc++ +cc -g -o testbindingsC testbindings.cpp -I../kjs -F$SYMROOTS -framework JavaScriptCore -framework Foundation -lstdc++ diff --git a/JavaScriptCore/bindings/npapi.h b/JavaScriptCore/bindings/npapi.h new file mode 100644 index 0000000..ba8b6c7 --- /dev/null +++ b/JavaScriptCore/bindings/npapi.h @@ -0,0 +1,714 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + + + /* + * Netscape client plug-in API spec + */ + + +#ifndef _NPAPI_H_ +#define _NPAPI_H_ + +#ifdef INCLUDE_JAVA +#include "jri.h" /* Java Runtime Interface */ +#else +#define jref void * +#define JRIEnv void +#endif + +#ifdef _WIN32 +# ifndef XP_WIN +# define XP_WIN 1 +# endif /* XP_WIN */ +#endif /* _WIN32 */ + +#ifdef __MWERKS__ +# define _declspec __declspec +# ifdef macintosh +# ifndef XP_MAC +# define XP_MAC 1 +# endif /* XP_MAC */ +# endif /* macintosh */ +# ifdef __INTEL__ +# undef NULL +# ifndef XP_WIN +# define XP_WIN 1 +# endif /* __INTEL__ */ +# endif /* XP_PC */ +#endif /* __MWERKS__ */ + +#if defined(__APPLE_CC__) && !defined(__MACOS_CLASSIC__) && !defined(XP_UNIX) +# define XP_MACOSX +#endif + +#ifdef XP_MAC + #include <Quickdraw.h> + #include <Events.h> +#endif + +#ifdef XP_MACOSX + #include <Carbon/Carbon.h> + #include <ApplicationServices/ApplicationServices.h> + #include <OpenGL/OpenGL.h> +#endif + +#ifdef XP_UNIX + #include <X11/Xlib.h> + #include <X11/Xutil.h> + #include <stdio.h> +#endif + +#ifdef XP_WIN + #include <windows.h> +#endif + +#if defined(XP_MACOSX) && defined(__LP64__) +#error 64-bit Netscape plug-ins are not supported on Mac OS X +#endif + +/*----------------------------------------------------------------------*/ +/* Plugin Version Constants */ +/*----------------------------------------------------------------------*/ + +#define NP_VERSION_MAJOR 0 +#define NP_VERSION_MINOR 18 + + + +/*----------------------------------------------------------------------*/ +/* Definition of Basic Types */ +/*----------------------------------------------------------------------*/ + +#ifndef _UINT16 +#define _UINT16 +typedef unsigned short uint16; +#endif + +#ifndef _UINT32 +#define _UINT32 +#ifdef __LP64__ +typedef unsigned int uint32; +#else /* __LP64__ */ +typedef unsigned long uint32; +#endif /* __LP64__ */ +#endif + +#ifndef _INT16 +#define _INT16 +typedef short int16; +#endif + +#ifndef _INT32 +#define _INT32 +#ifdef __LP64__ +typedef int int32; +#else /* __LP64__ */ +typedef long int32; +#endif /* __LP64__ */ +#endif + +#ifndef FALSE +#define FALSE (0) +#endif +#ifndef TRUE +#define TRUE (1) +#endif +#ifndef NULL +#define NULL (0L) +#endif + +typedef unsigned char NPBool; +typedef int16 NPError; +typedef int16 NPReason; +typedef char* NPMIMEType; + + + +/*----------------------------------------------------------------------*/ +/* Structures and definitions */ +/*----------------------------------------------------------------------*/ + +#if !defined(__LP64__) +#if defined(XP_MAC) || defined(XP_MACOSX) +#pragma options align=mac68k +#endif +#endif /* __LP64__ */ + +/* + * NPP is a plug-in's opaque instance handle + */ +typedef struct _NPP +{ + void* pdata; /* plug-in private data */ + void* ndata; /* netscape private data */ +} NPP_t; + +typedef NPP_t* NPP; + + +typedef struct _NPStream +{ + void* pdata; /* plug-in private data */ + void* ndata; /* netscape private data */ + const char* url; + uint32 end; + uint32 lastmodified; + void* notifyData; + const char* headers; /* Response headers from host. + * Exists only for >= NPVERS_HAS_RESPONSE_HEADERS. + * Used for HTTP only; NULL for non-HTTP. + * Available from NPP_NewStream onwards. + * Plugin should copy this data before storing it. + * Includes HTTP status line and all headers, + * preferably verbatim as received from server, + * headers formatted as in HTTP ("Header: Value"), + * and newlines (\n, NOT \r\n) separating lines. + * Terminated by \n\0 (NOT \n\n\0). */ +} NPStream; + + +typedef struct _NPByteRange +{ + int32 offset; /* negative offset means from the end */ + uint32 length; + struct _NPByteRange* next; +} NPByteRange; + + +typedef struct _NPSavedData +{ + int32 len; + void* buf; +} NPSavedData; + + +typedef struct _NPRect +{ + uint16 top; + uint16 left; + uint16 bottom; + uint16 right; +} NPRect; + + +#ifdef XP_UNIX +/* + * Unix specific structures and definitions + */ + +/* + * Callback Structures. + * + * These are used to pass additional platform specific information. + */ +enum { + NP_SETWINDOW = 1, + NP_PRINT +}; + +typedef struct +{ + int32 type; +} NPAnyCallbackStruct; + +typedef struct +{ + int32 type; + Display* display; + Visual* visual; + Colormap colormap; + unsigned int depth; +} NPSetWindowCallbackStruct; + +typedef struct +{ + int32 type; + FILE* fp; +} NPPrintCallbackStruct; + +#endif /* XP_UNIX */ + +/* + * The following masks are applied on certain platforms to NPNV and + * NPPV selectors that pass around pointers to COM interfaces. Newer + * compilers on some platforms may generate vtables that are not + * compatible with older compilers. To prevent older plugins from + * not understanding a new browser's ABI, these masks change the + * values of those selectors on those platforms. To remain backwards + * compatible with differenet versions of the browser, plugins can + * use these masks to dynamically determine and use the correct C++ + * ABI that the browser is expecting. This does not apply to Windows + * as Microsoft's COM ABI will likely not change. + */ + +#define NP_ABI_GCC3_MASK 0x10000000 +/* + * gcc 3.x generated vtables on UNIX and OSX are incompatible with + * previous compilers. + */ +#if (defined (XP_UNIX) && defined(__GNUC__) && (__GNUC__ >= 3)) +#define _NP_ABI_MIXIN_FOR_GCC3 NP_ABI_GCC3_MASK +#else +#define _NP_ABI_MIXIN_FOR_GCC3 0 +#endif + +#define NP_ABI_MACHO_MASK 0x01000000 +/* + * On OSX, the Mach-O executable format is significantly + * different than CFM. In addition to having a different + * C++ ABI, it also has has different C calling convention. + * You must use glue code when calling between CFM and + * Mach-O C functions. + */ +#if (defined(TARGET_RT_MAC_MACHO)) +#define _NP_ABI_MIXIN_FOR_MACHO NP_ABI_MACHO_MASK +#else +#define _NP_ABI_MIXIN_FOR_MACHO 0 +#endif + + +#define NP_ABI_MASK (_NP_ABI_MIXIN_FOR_GCC3 | _NP_ABI_MIXIN_FOR_MACHO) + +/* + * List of variable names for which NPP_GetValue shall be implemented + */ +typedef enum { + NPPVpluginNameString = 1, + NPPVpluginDescriptionString, + NPPVpluginWindowBool, + NPPVpluginTransparentBool, + + NPPVjavaClass, /* Not implemented in WebKit */ + NPPVpluginWindowSize, /* Not implemented in WebKit */ + NPPVpluginTimerInterval, /* Not implemented in WebKit */ + + NPPVpluginScriptableInstance = (10 | NP_ABI_MASK), /* Not implemented in WebKit */ + NPPVpluginScriptableIID = 11, /* Not implemented in WebKit */ + + /* 12 and over are available on Mozilla builds starting with 0.9.9 */ + NPPVjavascriptPushCallerBool = 12, /* Not implemented in WebKit */ + NPPVpluginKeepLibraryInMemory = 13, /* Not implemented in WebKit */ + NPPVpluginNeedsXEmbed = 14, /* Not implemented in WebKit */ + + /* Get the NPObject for scripting the plugin. */ + NPPVpluginScriptableNPObject = 15, + + /* Get the plugin value (as \0-terminated UTF-8 string data) for + * form submission if the plugin is part of a form. Use + * NPN_MemAlloc() to allocate memory for the string data. + */ + NPPVformValue = 16, /* Not implemented in WebKit */ +#ifdef XP_MACOSX + /* Used for negotiating drawing models */ + NPPVpluginDrawingModel = 1000 +#endif +} NPPVariable; + +/* + * List of variable names for which NPN_GetValue is implemented by Mozilla + */ +typedef enum { + NPNVxDisplay = 1, + NPNVxtAppContext, + NPNVnetscapeWindow, + NPNVjavascriptEnabledBool, + NPNVasdEnabledBool, + NPNVisOfflineBool, + + /* 10 and over are available on Mozilla builds starting with 0.9.4 */ + NPNVserviceManager = (10 | NP_ABI_MASK), /* Not implemented in WebKit */ + NPNVDOMElement = (11 | NP_ABI_MASK), /* Not implemented in WebKit */ + NPNVDOMWindow = (12 | NP_ABI_MASK), /* Not implemented in WebKit */ + NPNVToolkit = (13 | NP_ABI_MASK), /* Not implemented in WebKit */ + NPNVSupportsXEmbedBool = 14, /* Not implemented in WebKit */ + + /* Get the NPObject wrapper for the browser window. */ + NPNVWindowNPObject = 15, + + /* Get the NPObject wrapper for the plugins DOM element. */ + NPNVPluginElementNPObject + +#ifdef XP_MACOSX + , NPNVpluginDrawingModel = 1000 /* The NPDrawingModel specified by the plugin */ + +#ifndef NP_NO_QUICKDRAW + , NPNVsupportsQuickDrawBool = 2000 /* TRUE if the browser supports the QuickDraw drawing model */ +#endif + , NPNVsupportsCoreGraphicsBool = 2001 /* TRUE if the browser supports the CoreGraphics drawing model */ + , NPNVsupportsOpenGLBool = 2002 /* TRUE if the browser supports the OpenGL drawing model (CGL on Mac) */ +#endif /* XP_MACOSX */ +} NPNVariable; + +/* + * The type of a NPWindow - it specifies the type of the data structure + * returned in the window field. + */ +typedef enum { + NPWindowTypeWindow = 1, + NPWindowTypeDrawable +} NPWindowType; + +#ifdef XP_MACOSX + +/* + * The drawing model for a Mac OS X plugin. These are the possible values for the NPNVpluginDrawingModel variable. + */ + +typedef enum { +#ifndef NP_NO_QUICKDRAW + NPDrawingModelQuickDraw = 0, +#endif + NPDrawingModelCoreGraphics = 1, + NPDrawingModelOpenGL = 2 +} NPDrawingModel; + +#endif + +typedef struct _NPWindow +{ + void* window; /* Platform specific window handle */ + int32 x; /* Position of top left corner relative */ + int32 y; /* to a netscape page. */ + uint32 width; /* Maximum window size */ + uint32 height; + NPRect clipRect; /* Clipping rectangle in port coordinates */ + /* Used by MAC only. */ +#ifdef XP_UNIX + void * ws_info; /* Platform-dependent additonal data */ +#endif /* XP_UNIX */ + NPWindowType type; /* Is this a window or a drawable? */ +} NPWindow; + + +typedef struct _NPFullPrint +{ + NPBool pluginPrinted; /* Set TRUE if plugin handled fullscreen */ + /* printing */ + NPBool printOne; /* TRUE if plugin should print one copy */ + /* to default printer */ + void* platformPrint; /* Platform-specific printing info */ +} NPFullPrint; + +typedef struct _NPEmbedPrint +{ + NPWindow window; + void* platformPrint; /* Platform-specific printing info */ +} NPEmbedPrint; + +typedef struct _NPPrint +{ + uint16 mode; /* NP_FULL or NP_EMBED */ + union + { + NPFullPrint fullPrint; /* if mode is NP_FULL */ + NPEmbedPrint embedPrint; /* if mode is NP_EMBED */ + } print; +} NPPrint; + +#if defined(XP_MAC) || defined(XP_MACOSX) +typedef EventRecord NPEvent; +#elif defined(XP_WIN) +typedef struct _NPEvent +{ + uint16 event; + uint32 wParam; + uint32 lParam; +} NPEvent; +#elif defined (XP_UNIX) +typedef XEvent NPEvent; +#else +typedef void* NPEvent; +#endif /* XP_MAC */ + +#if defined(XP_MAC) +typedef RgnHandle NPRegion; +#elif defined(XP_MACOSX) +/* + * NPRegion's type depends on the drawing model specified by the plugin (see NPNVpluginDrawingModel). + * NPQDRegion represents a QuickDraw RgnHandle and is used with the QuickDraw drawing model. + * NPCGRegion repesents a graphical region when using any other drawing model. + */ +typedef void *NPRegion; +#ifndef NP_NO_QUICKDRAW +typedef RgnHandle NPQDRegion; +#endif +typedef CGPathRef NPCGRegion; +#elif defined(XP_WIN) +typedef HRGN NPRegion; +#elif defined(XP_UNIX) +typedef Region NPRegion; +#else +typedef void *NPRegion; +#endif /* XP_MAC */ + +#ifdef XP_MACOSX + +/* + * NP_CGContext is the type of the NPWindow's 'window' when the plugin specifies NPDrawingModelCoreGraphics + * as its drawing model. + */ + +typedef struct NP_CGContext +{ + CGContextRef context; + WindowRef window; +} NP_CGContext; + +/* + * NP_GLContext is the type of the NPWindow's 'window' when the plugin specifies NPDrawingModelOpenGL as its + * drawing model. + */ + +typedef struct NP_GLContext +{ + CGLContextObj context; + WindowRef window; +} NP_GLContext; + +#endif /* XP_MACOSX */ + +#if defined(XP_MAC) || defined(XP_MACOSX) + +/* + * Mac-specific structures and definitions. + */ + +#ifndef NP_NO_QUICKDRAW + +/* + * NP_Port is the type of the NPWindow's 'window' when the plugin specifies NPDrawingModelQuickDraw as its + * drawing model, or the plugin does not specify a drawing model. + * + * It is not recommended that new plugins use NPDrawingModelQuickDraw or NP_Port, as QuickDraw has been + * deprecated in Mac OS X 10.5. CoreGraphics is the preferred drawing API. + * + * NP_Port is not available in 64-bit. + */ + +typedef struct NP_Port +{ + CGrafPtr port; /* Grafport */ + int32 portx; /* position inside the topmost window */ + int32 porty; +} NP_Port; + +#endif /* NP_NO_QUICKDRAW */ + +/* + * Non-standard event types that can be passed to HandleEvent + */ +#define getFocusEvent (osEvt + 16) +#define loseFocusEvent (osEvt + 17) +#define adjustCursorEvent (osEvt + 18) + +#endif /* XP_MAC */ + + +/* + * Values for mode passed to NPP_New: + */ +#define NP_EMBED 1 +#define NP_FULL 2 + +/* + * Values for stream type passed to NPP_NewStream: + */ +#define NP_NORMAL 1 +#define NP_SEEK 2 +#define NP_ASFILE 3 +#define NP_ASFILEONLY 4 + +#define NP_MAXREADY (((unsigned)(~0)<<1)>>1) + +#if !defined(__LP64__) +#if defined(XP_MAC) || defined(XP_MACOSX) +#pragma options align=reset +#endif +#endif /* __LP64__ */ + + +/*----------------------------------------------------------------------*/ +/* Error and Reason Code definitions */ +/*----------------------------------------------------------------------*/ + +/* + * Values of type NPError: + */ +#define NPERR_BASE 0 +#define NPERR_NO_ERROR (NPERR_BASE + 0) +#define NPERR_GENERIC_ERROR (NPERR_BASE + 1) +#define NPERR_INVALID_INSTANCE_ERROR (NPERR_BASE + 2) +#define NPERR_INVALID_FUNCTABLE_ERROR (NPERR_BASE + 3) +#define NPERR_MODULE_LOAD_FAILED_ERROR (NPERR_BASE + 4) +#define NPERR_OUT_OF_MEMORY_ERROR (NPERR_BASE + 5) +#define NPERR_INVALID_PLUGIN_ERROR (NPERR_BASE + 6) +#define NPERR_INVALID_PLUGIN_DIR_ERROR (NPERR_BASE + 7) +#define NPERR_INCOMPATIBLE_VERSION_ERROR (NPERR_BASE + 8) +#define NPERR_INVALID_PARAM (NPERR_BASE + 9) +#define NPERR_INVALID_URL (NPERR_BASE + 10) +#define NPERR_FILE_NOT_FOUND (NPERR_BASE + 11) +#define NPERR_NO_DATA (NPERR_BASE + 12) +#define NPERR_STREAM_NOT_SEEKABLE (NPERR_BASE + 13) + +/* + * Values of type NPReason: + */ +#define NPRES_BASE 0 +#define NPRES_DONE (NPRES_BASE + 0) +#define NPRES_NETWORK_ERR (NPRES_BASE + 1) +#define NPRES_USER_BREAK (NPRES_BASE + 2) + +/* + * Don't use these obsolete error codes any more. + */ +#define NP_NOERR NP_NOERR_is_obsolete_use_NPERR_NO_ERROR +#define NP_EINVAL NP_EINVAL_is_obsolete_use_NPERR_GENERIC_ERROR +#define NP_EABORT NP_EABORT_is_obsolete_use_NPRES_USER_BREAK + +/* + * Version feature information + */ +#define NPVERS_HAS_STREAMOUTPUT 8 +#define NPVERS_HAS_NOTIFICATION 9 +#define NPVERS_HAS_LIVECONNECT 9 +#define NPVERS_WIN16_HAS_LIVECONNECT 9 +#define NPVERS_68K_HAS_LIVECONNECT 11 +#define NPVERS_HAS_WINDOWLESS 11 +#define NPVERS_HAS_XPCONNECT_SCRIPTING 13 /* Not implemented in WebKit */ +#define NPVERS_HAS_NPRUNTIME_SCRIPTING 14 +#define NPVERS_HAS_FORM_VALUES 15 /* Not implemented in WebKit; see bug 13061 */ +#define NPVERS_HAS_POPUPS_ENABLED_STATE 16 /* Not implemented in WebKit */ +#define NPVERS_HAS_RESPONSE_HEADERS 17 +#define NPVERS_HAS_NPOBJECT_ENUM 18 + + +/*----------------------------------------------------------------------*/ +/* Function Prototypes */ +/*----------------------------------------------------------------------*/ + +#if defined(_WINDOWS) && !defined(WIN32) +#define NP_LOADDS _loadds +#else +#define NP_LOADDS +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * NPP_* functions are provided by the plugin and called by the navigator. + */ + +#ifdef XP_UNIX +char* NPP_GetMIMEDescription(void); +#endif /* XP_UNIX */ + +NPError NPP_Initialize(void); +void NPP_Shutdown(void); +NPError NP_LOADDS NPP_New(NPMIMEType pluginType, NPP instance, + uint16 mode, int16 argc, char* argn[], + char* argv[], NPSavedData* saved); +NPError NP_LOADDS NPP_Destroy(NPP instance, NPSavedData** save); +NPError NP_LOADDS NPP_SetWindow(NPP instance, NPWindow* window); +NPError NP_LOADDS NPP_NewStream(NPP instance, NPMIMEType type, + NPStream* stream, NPBool seekable, + uint16* stype); +NPError NP_LOADDS NPP_DestroyStream(NPP instance, NPStream* stream, + NPReason reason); +int32 NP_LOADDS NPP_WriteReady(NPP instance, NPStream* stream); +int32 NP_LOADDS NPP_Write(NPP instance, NPStream* stream, int32 offset, + int32 len, void* buffer); +void NP_LOADDS NPP_StreamAsFile(NPP instance, NPStream* stream, + const char* fname); +void NP_LOADDS NPP_Print(NPP instance, NPPrint* platformPrint); +int16 NPP_HandleEvent(NPP instance, void* event); +void NP_LOADDS NPP_URLNotify(NPP instance, const char* url, + NPReason reason, void* notifyData); +jref NP_LOADDS NPP_GetJavaClass(void); +NPError NPP_GetValue(NPP instance, NPPVariable variable, + void *value); +NPError NPP_SetValue(NPP instance, NPNVariable variable, + void *value); + +/* + * NPN_* functions are provided by the navigator and called by the plugin. + */ + +void NPN_Version(int* plugin_major, int* plugin_minor, + int* netscape_major, int* netscape_minor); +NPError NPN_GetURLNotify(NPP instance, const char* url, + const char* target, void* notifyData); +NPError NPN_GetURL(NPP instance, const char* url, + const char* target); +NPError NPN_PostURLNotify(NPP instance, const char* url, + const char* target, uint32 len, + const char* buf, NPBool file, + void* notifyData); +NPError NPN_PostURL(NPP instance, const char* url, + const char* target, uint32 len, + const char* buf, NPBool file); +NPError NPN_RequestRead(NPStream* stream, NPByteRange* rangeList); +NPError NPN_NewStream(NPP instance, NPMIMEType type, + const char* target, NPStream** stream); +int32 NPN_Write(NPP instance, NPStream* stream, int32 len, + void* buffer); +NPError NPN_DestroyStream(NPP instance, NPStream* stream, + NPReason reason); +void NPN_Status(NPP instance, const char* message); +const char* NPN_UserAgent(NPP instance); +void* NPN_MemAlloc(uint32 size); +void NPN_MemFree(void* ptr); +uint32 NPN_MemFlush(uint32 size); +void NPN_ReloadPlugins(NPBool reloadPages); +JRIEnv* NPN_GetJavaEnv(void); +jref NPN_GetJavaPeer(NPP instance); +NPError NPN_GetValue(NPP instance, NPNVariable variable, + void *value); +NPError NPN_SetValue(NPP instance, NPPVariable variable, + void *value); +void NPN_InvalidateRect(NPP instance, NPRect *invalidRect); +void NPN_InvalidateRegion(NPP instance, NPRegion invalidRegion); +void NPN_ForceRedraw(NPP instance); +void NPN_PushPopupsEnabledState(NPP instance, NPBool enabled); +void NPN_PopPopupsEnabledState(NPP instance); + +#ifdef __cplusplus +} /* end extern "C" */ +#endif + +#endif /* _NPAPI_H_ */ diff --git a/JavaScriptCore/bindings/npruntime.cpp b/JavaScriptCore/bindings/npruntime.cpp new file mode 100644 index 0000000..ea7ea63 --- /dev/null +++ b/JavaScriptCore/bindings/npruntime.cpp @@ -0,0 +1,226 @@ +/* + * Copyright (C) 2004, 2006 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 COMPUTER, INC. OR + * 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" + +#if !PLATFORM(DARWIN) || !defined(__LP64__) + +#include "npruntime_internal.h" +#include "npruntime_impl.h" +#include "npruntime_priv.h" + +#include "JSLock.h" +#include "c_utility.h" +#include "identifier.h" +#include <wtf/Assertions.h> +#include <wtf/HashMap.h> + +using namespace KJS::Bindings; + +typedef HashMap<RefPtr<KJS::UString::Rep>, PrivateIdentifier*> StringIdentifierMap; + +static StringIdentifierMap* getStringIdentifierMap() +{ + static StringIdentifierMap* stringIdentifierMap = 0; + if (!stringIdentifierMap) + stringIdentifierMap = new StringIdentifierMap; + return stringIdentifierMap; +} + +typedef HashMap<int, PrivateIdentifier*> IntIdentifierMap; + +static IntIdentifierMap* getIntIdentifierMap() +{ + static IntIdentifierMap* intIdentifierMap = 0; + if (!intIdentifierMap) + intIdentifierMap = new IntIdentifierMap; + return intIdentifierMap; +} + +NPIdentifier _NPN_GetStringIdentifier(const NPUTF8* name) +{ + ASSERT(name); + + if (name) { + PrivateIdentifier* identifier = 0; + + KJS::JSLock lock; + + identifier = getStringIdentifierMap()->get(identifierFromNPIdentifier(name).ustring().rep()); + if (identifier == 0) { + identifier = (PrivateIdentifier*)malloc(sizeof(PrivateIdentifier)); + // We never release identifier names, so this dictionary will grow, as will + // the memory for the identifier name strings. + identifier->isString = true; + identifier->value.string = strdup(name); + + getStringIdentifierMap()->set(identifierFromNPIdentifier(name).ustring().rep(), identifier); + } + return (NPIdentifier)identifier; + } + + return 0; +} + +void _NPN_GetStringIdentifiers(const NPUTF8** names, int32_t nameCount, NPIdentifier* identifiers) +{ + ASSERT(names); + ASSERT(identifiers); + + if (names && identifiers) + for (int i = 0; i < nameCount; i++) + identifiers[i] = _NPN_GetStringIdentifier(names[i]); +} + +NPIdentifier _NPN_GetIntIdentifier(int32_t intid) +{ + PrivateIdentifier* identifier; + + if (intid == 0 || intid == -1) { + static PrivateIdentifier* negativeOneAndZeroIdentifiers[2]; + + identifier = negativeOneAndZeroIdentifiers[intid + 1]; + if (!identifier) { + identifier = (PrivateIdentifier*)malloc(sizeof(PrivateIdentifier)); + identifier->isString = false; + identifier->value.number = intid; + + negativeOneAndZeroIdentifiers[intid + 1] = identifier; + } + } else { + identifier = getIntIdentifierMap()->get(intid); + if (!identifier) { + identifier = (PrivateIdentifier*)malloc(sizeof(PrivateIdentifier)); + // We never release identifier names, so this dictionary will grow. + identifier->isString = false; + identifier->value.number = intid; + + getIntIdentifierMap()->set(intid, identifier); + } + } + return (NPIdentifier)identifier; +} + +bool _NPN_IdentifierIsString(NPIdentifier identifier) +{ + PrivateIdentifier* i = (PrivateIdentifier*)identifier; + return i->isString; +} + +NPUTF8 *_NPN_UTF8FromIdentifier(NPIdentifier identifier) +{ + PrivateIdentifier* i = (PrivateIdentifier*)identifier; + if (!i->isString || !i->value.string) + return NULL; + + return (NPUTF8 *)strdup(i->value.string); +} + +int32_t _NPN_IntFromIdentifier(NPIdentifier identifier) +{ + PrivateIdentifier* i = (PrivateIdentifier*)identifier; + if (!i->isString) + return 0; + return i->value.number; +} + +void NPN_InitializeVariantWithStringCopy(NPVariant* variant, const NPString* value) +{ + variant->type = NPVariantType_String; + variant->value.stringValue.UTF8Length = value->UTF8Length; + variant->value.stringValue.UTF8Characters = (NPUTF8 *)malloc(sizeof(NPUTF8) * value->UTF8Length); + memcpy((void*)variant->value.stringValue.UTF8Characters, value->UTF8Characters, sizeof(NPUTF8) * value->UTF8Length); +} + +void _NPN_ReleaseVariantValue(NPVariant* variant) +{ + ASSERT(variant); + + if (variant->type == NPVariantType_Object) { + _NPN_ReleaseObject(variant->value.objectValue); + variant->value.objectValue = 0; + } else if (variant->type == NPVariantType_String) { + free((void*)variant->value.stringValue.UTF8Characters); + variant->value.stringValue.UTF8Characters = 0; + variant->value.stringValue.UTF8Length = 0; + } + + variant->type = NPVariantType_Void; +} + +NPObject *_NPN_CreateObject(NPP npp, NPClass* aClass) +{ + ASSERT(aClass); + + if (aClass) { + NPObject* obj; + if (aClass->allocate != NULL) + obj = aClass->allocate(npp, aClass); + else + obj = (NPObject*)malloc(sizeof(NPObject)); + + obj->_class = aClass; + obj->referenceCount = 1; + + return obj; + } + + return 0; +} + +NPObject* _NPN_RetainObject(NPObject* obj) +{ + ASSERT(obj); + + if (obj) + obj->referenceCount++; + + return obj; +} + +void _NPN_ReleaseObject(NPObject* obj) +{ + ASSERT(obj); + ASSERT(obj->referenceCount >= 1); + + if (obj && obj->referenceCount >= 1) { + if (--obj->referenceCount == 0) + _NPN_DeallocateObject(obj); + } +} + +void _NPN_DeallocateObject(NPObject *obj) +{ + ASSERT(obj); + + if (obj) { + if (obj->_class->deallocate) + obj->_class->deallocate(obj); + else + free(obj); + } +} + +#endif diff --git a/JavaScriptCore/bindings/npruntime.h b/JavaScriptCore/bindings/npruntime.h new file mode 100644 index 0000000..fd1c192 --- /dev/null +++ b/JavaScriptCore/bindings/npruntime.h @@ -0,0 +1,354 @@ +/* + * Copyright (C) 2004, Apple Computer, Inc. and The Mozilla Foundation. + * 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 names of Apple Computer, Inc. ("Apple") or The Mozilla + * Foundation ("Mozilla") nor the names of their contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE, MOZILLA AND THEIR 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, MOZILLA OR + * THEIR 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. + * + * Revision 1 (March 4, 2004): + * Initial proposal. + * + * Revision 2 (March 10, 2004): + * All calls into script were made asynchronous. Results are + * provided via the NPScriptResultFunctionPtr callback. + * + * Revision 3 (March 10, 2004): + * Corrected comments to not refer to class retain/release FunctionPtrs. + * + * Revision 4 (March 11, 2004): + * Added additional convenience NPN_SetExceptionWithUTF8(). + * Changed NPHasPropertyFunctionPtr and NPHasMethodFunctionPtr to take NPClass + * pointers instead of NPObject pointers. + * Added NPIsValidIdentifier(). + * + * Revision 5 (March 17, 2004): + * Added context parameter to result callbacks from ScriptObject functions. + * + * Revision 6 (March 29, 2004): + * Renamed functions implemented by user agent to NPN_*. Removed _ from + * type names. + * Renamed "JavaScript" types to "Script". + * + * Revision 7 (April 21, 2004): + * NPIdentifier becomes a void*, was int32_t + * Remove NP_IsValidIdentifier, renamed NP_IdentifierFromUTF8 to NP_GetIdentifier + * Added NPVariant and modified functions to use this new type. + * + * Revision 8 (July 9, 2004): + * Updated to joint Apple-Mozilla license. + * + * Revision 9 (August 12, 2004): + * Changed NPVariantType enum values to form PVariantType_XXX + * Added NPP arguments to NPObject functions. + * Replaced NPVariant functions with macros. + */ +#ifndef _NP_RUNTIME_H_ +#define _NP_RUNTIME_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdint.h> +#include "npapi.h" + +#if defined(XP_MACOSX) && defined(__LP64__) +#error 64-bit Netscape plug-ins are not supported on Mac OS X +#endif + +/* + This API is used to facilitate binding code written in C to script + objects. The API in this header does not assume the presence of a + user agent. That is, it can be used to bind C code to scripting + environments outside of the context of a user agent. + + However, the normal use of the this API is in the context of a + scripting environment running in a browser or other user agent. + In particular it is used to support the extended Netscape + script-ability API for plugins (NP-SAP). NP-SAP is an extension + of the Netscape plugin API. As such we have adopted the use of + the "NP" prefix for this API. + + The following NP{N|P}Variables were added to the Netscape plugin + API (in npapi.h): + + NPNVWindowNPObject + NPNVPluginElementNPObject + NPPVpluginScriptableNPObject + + These variables are exposed through NPN_GetValue() and + NPP_GetValue() (respectively) and are used to establish the + initial binding between the user agent and native code. The DOM + objects in the user agent can be examined and manipulated using + the NPN_ functions that operate on NPObjects described in this + header. + + To the extent possible the assumptions about the scripting + language used by the scripting environment have been minimized. +*/ + + +/* + Objects (non-primitive data) passed between 'C' and script is + always wrapped in an NPObject. The 'interface' of an NPObject is + described by an NPClass. +*/ +typedef struct NPObject NPObject; +typedef struct NPClass NPClass; + +typedef char NPUTF8; +typedef struct _NPString { + const NPUTF8 *UTF8Characters; + uint32_t UTF8Length; +} NPString; + +typedef enum { + NPVariantType_Void, + NPVariantType_Null, + NPVariantType_Bool, + NPVariantType_Int32, + NPVariantType_Double, + NPVariantType_String, + NPVariantType_Object +} NPVariantType; + +typedef struct _NPVariant { + NPVariantType type; + union { + bool boolValue; + int32_t intValue; + double doubleValue; + NPString stringValue; + NPObject *objectValue; + } value; +} NPVariant; + +/* + NPN_ReleaseVariantValue is called on all 'out' parameters references. + Specifically it is called on variants that are resultant out parameters + in NPGetPropertyFunctionPtr and NPInvokeFunctionPtr. Resultant variants + from these two functions should be initialized using the + NPN_InitializeVariantXXX() functions. + + After calling NPReleaseVariantValue, the type of the variant will + be set to NPVariantUndefinedType. +*/ +void NPN_ReleaseVariantValue (NPVariant *variant); + +#define NPVARIANT_IS_VOID(_v) ((_v).type == NPVariantType_Void) +#define NPVARIANT_IS_NULL(_v) ((_v).type == NPVariantType_Null) +#define NPVARIANT_IS_BOOLEAN(_v) ((_v).type == NPVariantType_Bool) +#define NPVARIANT_IS_INT32(_v) ((_v).type == NPVariantType_Int32) +#define NPVARIANT_IS_DOUBLE(_v) ((_v).type == NPVariantType_Double) +#define NPVARIANT_IS_STRING(_v) ((_v).type == NPVariantType_String) +#define NPVARIANT_IS_OBJECT(_v) ((_v).type == NPVariantType_Object) + +#define NPVARIANT_TO_BOOLEAN(_v) ((_v).value.boolValue) +#define NPVARIANT_TO_INT32(_v) ((_v).value.intValue) +#define NPVARIANT_TO_DOUBLE(_v) ((_v).value.doubleValue) +#define NPVARIANT_TO_STRING(_v) ((_v).value.stringValue) +#define NPVARIANT_TO_OBJECT(_v) ((_v).value.objectValue) + +#define NP_BEGIN_MACRO do { +#define NP_END_MACRO } while (0) + +#define VOID_TO_NPVARIANT(_v) NP_BEGIN_MACRO (_v).type = NPVariantType_Void; (_v).value.objectValue = NULL; NP_END_MACRO +#define NULL_TO_NPVARIANT(_v) NP_BEGIN_MACRO (_v).type = NPVariantType_Null; (_v).value.objectValue = NULL; NP_END_MACRO +#define BOOLEAN_TO_NPVARIANT(_val, _v) NP_BEGIN_MACRO (_v).type = NPVariantType_Bool; (_v).value.boolValue = !!(_val); NP_END_MACRO +#define INT32_TO_NPVARIANT(_val, _v) NP_BEGIN_MACRO (_v).type = NPVariantType_Int32; (_v).value.intValue = _val; NP_END_MACRO +#define DOUBLE_TO_NPVARIANT(_val, _v) NP_BEGIN_MACRO (_v).type = NPVariantType_Double; (_v).value.doubleValue = _val; NP_END_MACRO +#define STRINGZ_TO_NPVARIANT(_val, _v) NP_BEGIN_MACRO (_v).type = NPVariantType_String; NPString str = { _val, strlen(_val) }; (_v).value.stringValue = str; NP_END_MACRO +#define STRINGN_TO_NPVARIANT(_val, _len, _v) NP_BEGIN_MACRO (_v).type = NPVariantType_String; NPString str = { _val, _len }; (_v).value.stringValue = str; NP_END_MACRO +#define OBJECT_TO_NPVARIANT(_val, _v) NP_BEGIN_MACRO (_v).type = NPVariantType_Object; (_v).value.objectValue = _val; NP_END_MACRO + +/* + Type mappings (JavaScript types have been used for illustration + purposes): + + JavaScript to C (NPVariant with type:) + undefined NPVariantType_Void + null NPVariantType_Null + Boolean NPVariantType_Bool + Number NPVariantType_Double or NPVariantType_Int32 + String NPVariantType_String + Object NPVariantType_Object + + C (NPVariant with type:) to JavaScript + NPVariantType_Void undefined + NPVariantType_Null null + NPVariantType_Bool Boolean + NPVariantType_Int32 Number + NPVariantType_Double Number + NPVariantType_String String + NPVariantType_Object Object +*/ + +typedef void *NPIdentifier; + +/* + NPObjects have methods and properties. Methods and properties are + identified with NPIdentifiers. These identifiers may be reflected + in script. NPIdentifiers can be either strings or integers, IOW, + methods and properties can be identified by either strings or + integers (i.e. foo["bar"] vs foo[1]). NPIdentifiers can be + compared using ==. In case of any errors, the requested + NPIdentifier(s) will be NULL. +*/ +NPIdentifier NPN_GetStringIdentifier(const NPUTF8 *name); +void NPN_GetStringIdentifiers(const NPUTF8 **names, int32_t nameCount, NPIdentifier *identifiers); +NPIdentifier NPN_GetIntIdentifier(int32_t intid); +bool NPN_IdentifierIsString(NPIdentifier identifier); + +/* + The NPUTF8 returned from NPN_UTF8FromIdentifier SHOULD be freed. +*/ +NPUTF8 *NPN_UTF8FromIdentifier(NPIdentifier identifier); + +/* + Get the integer represented by identifier. If identifier is not an + integer identifier, the behaviour is undefined. +*/ +int32_t NPN_IntFromIdentifier(NPIdentifier identifier); + +/* + NPObject behavior is implemented using the following set of + callback functions. + + The NPVariant *result argument of these functions (where + applicable) should be released using NPN_ReleaseVariantValue(). +*/ +typedef NPObject *(*NPAllocateFunctionPtr)(NPP npp, NPClass *aClass); +typedef void (*NPDeallocateFunctionPtr)(NPObject *obj); +typedef void (*NPInvalidateFunctionPtr)(NPObject *obj); +typedef bool (*NPHasMethodFunctionPtr)(NPObject *obj, NPIdentifier name); +typedef bool (*NPInvokeFunctionPtr)(NPObject *obj, NPIdentifier name, const NPVariant *args, uint32_t argCount, NPVariant *result); +typedef bool (*NPInvokeDefaultFunctionPtr)(NPObject *npobj, const NPVariant *args, uint32_t argCount, NPVariant *result); +typedef bool (*NPHasPropertyFunctionPtr)(NPObject *obj, NPIdentifier name); +typedef bool (*NPGetPropertyFunctionPtr)(NPObject *obj, NPIdentifier name, NPVariant *result); +typedef bool (*NPSetPropertyFunctionPtr)(NPObject *obj, NPIdentifier name, const NPVariant *value); +typedef bool (*NPRemovePropertyFunctionPtr)(NPObject *npobj, NPIdentifier name); +typedef bool (*NPEnumerationFunctionPtr)(NPObject *npobj, NPIdentifier **value, uint32_t *count); + +/* + NPObjects returned by create have a reference count of one. It is the caller's responsibility + to release the returned object. + + NPInvokeFunctionPtr function may return false to indicate a the method could not be invoked. + + NPGetPropertyFunctionPtr and NPSetPropertyFunctionPtr may return false to indicate a property doesn't + exist. + + NPInvalidateFunctionPtr is called by the scripting environment when the native code is + shutdown. Any attempt to message a NPObject instance after the invalidate + callback has been called will result in undefined behavior, even if the + native code is still retaining those NPObject instances. + (The runtime will typically return immediately, with 0 or NULL, from an attempt to + dispatch to a NPObject, but this behavior should not be depended upon.) + + The NPEnumerationFunctionPtr function may pass an array of + NPIdentifiers back to the caller. The callee allocs the memory of + the array using NPN_MemAlloc(), and it's the caller's responsibility + to release it using NPN_MemFree(). +*/ +struct NPClass +{ + uint32_t structVersion; + NPAllocateFunctionPtr allocate; + NPDeallocateFunctionPtr deallocate; + NPInvalidateFunctionPtr invalidate; + NPHasMethodFunctionPtr hasMethod; + NPInvokeFunctionPtr invoke; + NPInvokeDefaultFunctionPtr invokeDefault; + NPHasPropertyFunctionPtr hasProperty; + NPGetPropertyFunctionPtr getProperty; + NPSetPropertyFunctionPtr setProperty; + NPRemovePropertyFunctionPtr removeProperty; + NPEnumerationFunctionPtr enumerate; +}; + +#define NP_CLASS_STRUCT_VERSION 2 +#define NP_CLASS_STRUCT_VERSION_ENUM 2 +#define NP_CLASS_STRUCT_VERSION_HAS_ENUM(npclass) \ + ((npclass)->structVersion >= NP_CLASS_STRUCT_VERSION_ENUM) + +struct NPObject { + NPClass *_class; + uint32_t referenceCount; + // Additional space may be allocated here by types of NPObjects +}; + +/* + If the class has an allocate function, NPN_CreateObject invokes that function, + otherwise a NPObject is allocated and returned. If a class has an allocate + function it is the responsibility of that implementation to set the initial retain + count to 1. +*/ +NPObject *NPN_CreateObject(NPP npp, NPClass *aClass); + +/* + Increment the NPObject's reference count. +*/ +NPObject *NPN_RetainObject (NPObject *obj); + +/* + Decremented the NPObject's reference count. If the reference + count goes to zero, the class's destroy function is invoke if + specified, otherwise the object is freed directly. +*/ +void NPN_ReleaseObject (NPObject *obj); + +/* + Functions to access script objects represented by NPObject. + + Calls to script objects are synchronous. If a function returns a + value, it will be supplied via the result NPVariant + argument. Successful calls will return true, false will be + returned in case of an error. + + Calls made from plugin code to script must be made from the thread + on which the plugin was initialized. +*/ +bool NPN_Invoke(NPP npp, NPObject *npobj, NPIdentifier methodName, const NPVariant *args, uint32_t argCount, NPVariant *result); +bool NPN_InvokeDefault(NPP npp, NPObject *npobj, const NPVariant *args, uint32_t argCount, NPVariant *result); +bool NPN_Evaluate(NPP npp, NPObject *npobj, NPString *script, NPVariant *result); +bool NPN_GetProperty(NPP npp, NPObject *npobj, NPIdentifier propertyName, NPVariant *result); +bool NPN_SetProperty(NPP npp, NPObject *npobj, NPIdentifier propertyName, const NPVariant *value); +bool NPN_RemoveProperty(NPP npp, NPObject *npobj, NPIdentifier propertyName); +bool NPN_HasProperty(NPP npp, NPObject *npobj, NPIdentifier propertyName); +bool NPN_HasMethod(NPP npp, NPObject *npobj, NPIdentifier methodName); +bool NPN_Enumerate(NPP npp, NPObject *npobj, NPIdentifier **identifier, uint32_t *count); + +/* + NPN_SetException may be called to trigger a script exception upon return + from entry points into NPObjects. +*/ +void NPN_SetException (NPObject *obj, const NPUTF8 *message); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/JavaScriptCore/bindings/npruntime_impl.h b/JavaScriptCore/bindings/npruntime_impl.h new file mode 100644 index 0000000..84e63ce --- /dev/null +++ b/JavaScriptCore/bindings/npruntime_impl.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2004 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 COMPUTER, INC. OR + * 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. + */ + +#ifndef _NP_RUNTIME_IMPL_H_ +#define _NP_RUNTIME_IMPL_H_ + +#if !PLATFORM(DARWIN) || !defined(__LP64__) + +#include "npruntime_internal.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern void _NPN_ReleaseVariantValue(NPVariant *variant); +extern NPIdentifier _NPN_GetStringIdentifier(const NPUTF8 *name); +extern void _NPN_GetStringIdentifiers(const NPUTF8 **names, int32_t nameCount, NPIdentifier *identifiers); +extern NPIdentifier _NPN_GetIntIdentifier(int32_t intid); +extern bool _NPN_IdentifierIsString(NPIdentifier identifier); +extern NPUTF8 *_NPN_UTF8FromIdentifier(NPIdentifier identifier); +extern int32_t _NPN_IntFromIdentifier(NPIdentifier identifier); +extern NPObject *_NPN_CreateObject(NPP npp, NPClass *aClass); +extern NPObject *_NPN_RetainObject(NPObject *obj); +extern void _NPN_ReleaseObject(NPObject *obj); +extern void _NPN_DeallocateObject(NPObject *obj); +extern bool _NPN_Invoke(NPP npp, NPObject *npobj, NPIdentifier methodName, const NPVariant *args, uint32_t argCount, NPVariant *result); +extern bool _NPN_InvokeDefault(NPP npp, NPObject *npobj, const NPVariant *args, uint32_t argCount, NPVariant *result); +extern bool _NPN_Evaluate(NPP npp, NPObject *npobj, NPString *script, NPVariant *result); +extern bool _NPN_GetProperty(NPP npp, NPObject *npobj, NPIdentifier propertyName, NPVariant *result); +extern bool _NPN_SetProperty(NPP npp, NPObject *npobj, NPIdentifier propertyName, const NPVariant *value); +extern bool _NPN_RemoveProperty(NPP npp, NPObject *npobj, NPIdentifier propertyName); +extern bool _NPN_HasProperty(NPP npp, NPObject *npobj, NPIdentifier propertyName); +extern bool _NPN_HasMethod(NPP npp, NPObject *npobj, NPIdentifier methodName); +extern void _NPN_SetException(NPObject *obj, const NPUTF8 *message); +extern bool _NPN_Enumerate(NPP npp, NPObject *npobj, NPIdentifier **identifier, uint32_t *count); + +#ifdef __cplusplus +} /* end extern "C" */ +#endif + +#endif +#endif diff --git a/JavaScriptCore/bindings/npruntime_internal.h b/JavaScriptCore/bindings/npruntime_internal.h new file mode 100644 index 0000000..f5357cd --- /dev/null +++ b/JavaScriptCore/bindings/npruntime_internal.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2007 Collabora, Ltd. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * This is a internal include header for npapi.h + * + * Some of the #defines which are in X11 headers conflict with type and enum + * names in JavaScriptCore and WebCore + * This header #undefs those defines to fix the conflicts + * If you need to include npapi.h or npruntime.h when building on X11, + * include this file instead of the actual npapi.h or npruntime.h + */ + +#include "npruntime.h" + +#ifdef XP_UNIX + #include <X11/Xresource.h> + + #undef None + #undef Above + #undef Below + #undef Auto + #undef Complex + #undef Status +#endif diff --git a/JavaScriptCore/bindings/npruntime_priv.h b/JavaScriptCore/bindings/npruntime_priv.h new file mode 100644 index 0000000..b19b16a --- /dev/null +++ b/JavaScriptCore/bindings/npruntime_priv.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2003, 2006 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 COMPUTER, INC. OR + * 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. + */ + +#ifndef NP_RUNTIME_PRIV_H_ +#define NP_RUNTIME_PRIV_H_ + +#if !PLATFORM(DARWIN) || !defined(__LP64__) + +#include "npruntime_internal.h" + +/* + NPN_InitializeVariantWithStringCopy() will copy string data. The string data + will be deallocated by calls to NPReleaseVariantValue(). +*/ +void NPN_InitializeVariantWithStringCopy(NPVariant*, const NPString*); + +#endif +#endif + diff --git a/JavaScriptCore/bindings/objc/WebScriptObject.h b/JavaScriptCore/bindings/objc/WebScriptObject.h new file mode 100644 index 0000000..14d77d7 --- /dev/null +++ b/JavaScriptCore/bindings/objc/WebScriptObject.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2006 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 COMPUTER, INC. OR + * 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. + */ + +#import <Foundation/Foundation.h> + +#include "internal.h" +#include "runtime_root.h" + +@class WebUndefined; + +@protocol WebScriptObject ++ (NSString *)webScriptNameForSelector:(SEL)aSelector; ++ (BOOL)isSelectorExcludedFromWebScript:(SEL)aSelector; ++ (NSString *)webScriptNameForKey:(const char *)name; ++ (BOOL)isKeyExcludedFromWebScript:(const char *)name; + ++ (id)_convertValueToObjcValue:(KJS::JSValue *)value originRootObject:(KJS::Bindings::RootObject*)originRootObject rootObject:(KJS::Bindings::RootObject*)rootObject; +- _initWithJSObject:(KJS::JSObject*)imp originRootObject:(PassRefPtr<KJS::Bindings::RootObject>)originRootObject rootObject:(PassRefPtr<KJS::Bindings::RootObject>)rootObject; +- (KJS::JSObject *)_imp; +@end + +@protocol WebUndefined ++ (WebUndefined *)undefined; +@end diff --git a/JavaScriptCore/bindings/objc/objc_class.h b/JavaScriptCore/bindings/objc/objc_class.h new file mode 100644 index 0000000..3faee18 --- /dev/null +++ b/JavaScriptCore/bindings/objc/objc_class.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2003 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 COMPUTER, INC. OR + * 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. + */ + +#ifndef KJS_BINDINGS_OBJC_CLASS_H +#define KJS_BINDINGS_OBJC_CLASS_H + +#include <JavaScriptCore/objc_runtime.h> + +namespace KJS { +namespace Bindings { + +class ObjcClass : public Class +{ +protected: + ObjcClass (ClassStructPtr aClass); // Use classForIsA to create an ObjcClass. + +public: + // Return the cached ObjC of the specified name. + static ObjcClass *classForIsA(ClassStructPtr); + + virtual const char *name() const; + + virtual MethodList methodsNamed(const Identifier&, Instance *instance) const; + virtual Field *fieldNamed(const Identifier&, Instance *instance) const; + + virtual JSValue *fallbackObject(ExecState *exec, Instance *instance, const Identifier &propertyName); + + ClassStructPtr isa() { return _isa; } + +private: + ClassStructPtr _isa; + RetainPtr<CFMutableDictionaryRef> _methods; + RetainPtr<CFMutableDictionaryRef> _fields; +}; + +} // namespace Bindings +} // namespace KJS + +#endif diff --git a/JavaScriptCore/bindings/objc/objc_class.mm b/JavaScriptCore/bindings/objc/objc_class.mm new file mode 100644 index 0000000..e3a72f7 --- /dev/null +++ b/JavaScriptCore/bindings/objc/objc_class.mm @@ -0,0 +1,258 @@ +/* + * Copyright (C) 2004 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 COMPUTER, INC. OR + * 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 "objc_class.h" + +#include "objc_instance.h" +#include "WebScriptObject.h" + +namespace KJS { +namespace Bindings { + +static void deleteMethod(CFAllocatorRef, const void* value) +{ + delete static_cast<const Method*>(value); +} + +static void deleteField(CFAllocatorRef, const void* value) +{ + delete static_cast<const Field*>(value); +} + +const CFDictionaryValueCallBacks MethodDictionaryValueCallBacks = { 0, 0, &deleteMethod, 0 , 0 }; +const CFDictionaryValueCallBacks FieldDictionaryValueCallBacks = { 0, 0, &deleteField, 0 , 0 }; + +ObjcClass::ObjcClass(ClassStructPtr aClass) + : _isa(aClass) + , _methods(AdoptCF, CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &MethodDictionaryValueCallBacks)) + , _fields(AdoptCF, CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &FieldDictionaryValueCallBacks)) +{ +} + +static CFMutableDictionaryRef classesByIsA = 0; + +static void _createClassesByIsAIfNecessary() +{ + if (!classesByIsA) + classesByIsA = CFDictionaryCreateMutable(NULL, 0, NULL, NULL); +} + +ObjcClass* ObjcClass::classForIsA(ClassStructPtr isa) +{ + _createClassesByIsAIfNecessary(); + + ObjcClass* aClass = (ObjcClass*)CFDictionaryGetValue(classesByIsA, isa); + if (!aClass) { + aClass = new ObjcClass(isa); + CFDictionaryAddValue(classesByIsA, isa, aClass); + } + + return aClass; +} + +const char* ObjcClass::name() const +{ + return object_getClassName(_isa); +} + +MethodList ObjcClass::methodsNamed(const Identifier& identifier, Instance*) const +{ + MethodList methodList; + char fixedSizeBuffer[1024]; + char* buffer = fixedSizeBuffer; + const char* JSName = identifier.ascii(); + if (!convertJSMethodNameToObjc(JSName, buffer, sizeof(fixedSizeBuffer))) { + int length = strlen(JSName) + 1; + buffer = new char[length]; + if (!buffer || !convertJSMethodNameToObjc(JSName, buffer, length)) + return methodList; + } + + + RetainPtr<CFStringRef> methodName(AdoptCF, CFStringCreateWithCString(NULL, buffer, kCFStringEncodingASCII)); + Method* method = (Method*)CFDictionaryGetValue(_methods.get(), methodName.get()); + if (method) { + methodList.append(method); + return methodList; + } + + ClassStructPtr thisClass = _isa; + while (thisClass && methodList.isEmpty()) { +#if defined(OBJC_API_VERSION) && OBJC_API_VERSION >= 2 + unsigned numMethodsInClass = 0; + MethodStructPtr* objcMethodList = class_copyMethodList(thisClass, &numMethodsInClass); +#else + void* iterator = 0; + struct objc_method_list* objcMethodList; + while ((objcMethodList = class_nextMethodList(thisClass, &iterator))) { + unsigned numMethodsInClass = objcMethodList->method_count; +#endif + for (unsigned i = 0; i < numMethodsInClass; i++) { +#if defined(OBJC_API_VERSION) && OBJC_API_VERSION >= 2 + MethodStructPtr objcMethod = objcMethodList[i]; + SEL objcMethodSelector = method_getName(objcMethod); +#else + struct objc_method* objcMethod = &objcMethodList->method_list[i]; + SEL objcMethodSelector = objcMethod->method_name; +#endif + const char* objcMethodSelectorName = sel_getName(objcMethodSelector); + NSString* mappedName = nil; + + // See if the class wants to exclude the selector from visibility in JavaScript. + if ([thisClass respondsToSelector:@selector(isSelectorExcludedFromWebScript:)]) + if ([thisClass isSelectorExcludedFromWebScript:objcMethodSelector]) + continue; + + // See if the class want to provide a different name for the selector in JavaScript. + // Note that we do not do any checks to guarantee uniqueness. That's the responsiblity + // of the class. + if ([thisClass respondsToSelector:@selector(webScriptNameForSelector:)]) + mappedName = [thisClass webScriptNameForSelector:objcMethodSelector]; + + if ((mappedName && [mappedName isEqual:(NSString*)methodName.get()]) || strcmp(objcMethodSelectorName, buffer) == 0) { + Method* aMethod = new ObjcMethod(thisClass, objcMethodSelectorName); // deleted when the dictionary is destroyed + CFDictionaryAddValue(_methods.get(), methodName.get(), aMethod); + methodList.append(aMethod); + break; + } + } +#if defined(OBJC_API_VERSION) && OBJC_API_VERSION >= 2 + thisClass = class_getSuperclass(thisClass); + free(objcMethodList); +#else + } + thisClass = thisClass->super_class; +#endif + } + + if (buffer != fixedSizeBuffer) + delete [] buffer; + + return methodList; +} + +Field* ObjcClass::fieldNamed(const Identifier& identifier, Instance* instance) const +{ + ClassStructPtr thisClass = _isa; + + const char* name = identifier.ascii(); + RetainPtr<CFStringRef> fieldName(AdoptCF, CFStringCreateWithCString(NULL, name, kCFStringEncodingASCII)); + Field* aField = (Field*)CFDictionaryGetValue(_fields.get(), fieldName.get()); + if (aField) + return aField; + + id targetObject = (static_cast<ObjcInstance*>(instance))->getObject(); + id attributes = [targetObject attributeKeys]; + if (attributes) { + // Class overrides attributeKeys, use that array of key names. + unsigned count = [attributes count]; + for (unsigned i = 0; i < count; i++) { + NSString* keyName = [attributes objectAtIndex:i]; + const char* UTF8KeyName = [keyName UTF8String]; // ObjC actually only supports ASCII names. + + // See if the class wants to exclude the selector from visibility in JavaScript. + if ([thisClass respondsToSelector:@selector(isKeyExcludedFromWebScript:)]) + if ([thisClass isKeyExcludedFromWebScript:UTF8KeyName]) + continue; + + // See if the class want to provide a different name for the selector in JavaScript. + // Note that we do not do any checks to guarantee uniqueness. That's the responsiblity + // of the class. + NSString* mappedName = nil; + if ([thisClass respondsToSelector:@selector(webScriptNameForKey:)]) + mappedName = [thisClass webScriptNameForKey:UTF8KeyName]; + + if ((mappedName && [mappedName isEqual:(NSString*)fieldName.get()]) || [keyName isEqual:(NSString*)fieldName.get()]) { + aField = new ObjcField((CFStringRef)keyName); // deleted when the dictionary is destroyed + CFDictionaryAddValue(_fields.get(), fieldName.get(), aField); + break; + } + } + } else { + // Class doesn't override attributeKeys, so fall back on class runtime + // introspection. + + while (thisClass) { +#if defined(OBJC_API_VERSION) && OBJC_API_VERSION >= 2 + unsigned numFieldsInClass = 0; + IvarStructPtr* ivarsInClass = class_copyIvarList(thisClass, &numFieldsInClass); +#else + struct objc_ivar_list* fieldsInClass = thisClass->ivars; + if (fieldsInClass) { + unsigned numFieldsInClass = fieldsInClass->ivar_count; +#endif + for (unsigned i = 0; i < numFieldsInClass; i++) { +#if defined(OBJC_API_VERSION) && OBJC_API_VERSION >= 2 + IvarStructPtr objcIVar = ivarsInClass[i]; + const char* objcIvarName = ivar_getName(objcIVar); +#else + IvarStructPtr objcIVar = &fieldsInClass->ivar_list[i]; + const char* objcIvarName = objcIVar->ivar_name; +#endif + NSString* mappedName = 0; + + // See if the class wants to exclude the selector from visibility in JavaScript. + if ([thisClass respondsToSelector:@selector(isKeyExcludedFromWebScript:)]) + if ([thisClass isKeyExcludedFromWebScript:objcIvarName]) + continue; + + // See if the class want to provide a different name for the selector in JavaScript. + // Note that we do not do any checks to guarantee uniqueness. That's the responsiblity + // of the class. + if ([thisClass respondsToSelector:@selector(webScriptNameForKey:)]) + mappedName = [thisClass webScriptNameForKey:objcIvarName]; + + if ((mappedName && [mappedName isEqual:(NSString*)fieldName.get()]) || strcmp(objcIvarName, name) == 0) { + aField = new ObjcField(objcIVar); // deleted when the dictionary is destroyed + CFDictionaryAddValue(_fields.get(), fieldName.get(), aField); + break; + } + } +#if defined(OBJC_API_VERSION) && OBJC_API_VERSION >= 2 + thisClass = class_getSuperclass(thisClass); + free(ivarsInClass); +#else + } + thisClass = thisClass->super_class; +#endif + } + } + + return aField; +} + +JSValue* ObjcClass::fallbackObject(ExecState*, Instance* instance, const Identifier &propertyName) +{ + ObjcInstance* objcInstance = static_cast<ObjcInstance*>(instance); + id targetObject = objcInstance->getObject(); + + if (![targetObject respondsToSelector:@selector(invokeUndefinedMethodFromWebScript:withArguments:)]) + return jsUndefined(); + return new ObjcFallbackObjectImp(objcInstance, propertyName); +} + +} +} diff --git a/JavaScriptCore/bindings/objc/objc_header.h b/JavaScriptCore/bindings/objc/objc_header.h new file mode 100644 index 0000000..07954a1 --- /dev/null +++ b/JavaScriptCore/bindings/objc/objc_header.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2004 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 COMPUTER, INC. OR + * 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. + */ + +#ifndef KJS_BINDINGS_OBJC_HEADER_H +#define KJS_BINDINGS_OBJC_HEADER_H + +#ifdef __OBJC__ + +#include <objc/objc.h> +#include <objc/objc-class.h> +#include <objc/objc-runtime.h> + +typedef Class ClassStructPtr; +typedef id ObjectStructPtr; +typedef Method MethodStructPtr; +typedef Ivar IvarStructPtr; + +@class NSMethodSignature; + +#else + +typedef struct objc_class* ClassStructPtr; +typedef struct objc_object* ObjectStructPtr; +typedef struct objc_method* MethodStructPtr; +typedef struct objc_ivar* IvarStructPtr; + +class NSMethodSignature; + +#endif + +#endif diff --git a/JavaScriptCore/bindings/objc/objc_instance.h b/JavaScriptCore/bindings/objc/objc_instance.h new file mode 100644 index 0000000..2233791 --- /dev/null +++ b/JavaScriptCore/bindings/objc/objc_instance.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2003 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 COMPUTER, INC. OR + * 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. + */ + +#ifndef BINDINGS_OBJC_INSTANCE_H_ +#define BINDINGS_OBJC_INSTANCE_H_ + +#include <JavaScriptCore/objc_class.h> +#include <JavaScriptCore/objc_utility.h> + +namespace KJS { + +namespace Bindings { + +class ObjcClass; + +class ObjcInstance : public Instance { +public: + ObjcInstance(ObjectStructPtr instance, PassRefPtr<RootObject>); + + ~ObjcInstance(); + + virtual Class *getClass() const; + + virtual void begin(); + virtual void end(); + + virtual JSValue *valueOf() const; + virtual JSValue *defaultValue(JSType hint) const; + + virtual bool implementsCall() const; + + virtual JSValue *invokeMethod(ExecState *exec, const MethodList &method, const List &args); + virtual JSValue *invokeDefaultMethod(ExecState *exec, const List &args); + + virtual bool supportsSetValueOfUndefinedField(); + virtual void setValueOfUndefinedField(ExecState *exec, const Identifier &property, JSValue *aValue); + + virtual JSValue *getValueOfUndefinedField(ExecState *exec, const Identifier &property, JSType hint) const; + + ObjectStructPtr getObject() const { return _instance.get(); } + + JSValue *stringValue() const; + JSValue *numberValue() const; + JSValue *booleanValue() const; + + virtual BindingLanguage getBindingLanguage() const { return ObjectiveCLanguage; } + +private: + RetainPtr<ObjectStructPtr> _instance; + mutable ObjcClass *_class; + ObjectStructPtr _pool; + int _beginCount; +}; + +} // namespace Bindings + +} // namespace KJS + +#endif // BINDINGS_OBJC_INSTANCE_H_ diff --git a/JavaScriptCore/bindings/objc/objc_instance.mm b/JavaScriptCore/bindings/objc/objc_instance.mm new file mode 100644 index 0000000..57ebfe8 --- /dev/null +++ b/JavaScriptCore/bindings/objc/objc_instance.mm @@ -0,0 +1,349 @@ +/* + * Copyright (C) 2004 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 COMPUTER, INC. OR + * 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. + */ + +#import "config.h" +#import "objc_instance.h" + +#import "WebScriptObject.h" +#include <wtf/Assertions.h> + +#ifdef NDEBUG +#define OBJC_LOG(formatAndArgs...) ((void)0) +#else +#define OBJC_LOG(formatAndArgs...) { \ + fprintf (stderr, "%s:%d -- %s: ", __FILE__, __LINE__, __FUNCTION__); \ + fprintf(stderr, formatAndArgs); \ +} +#endif + +using namespace KJS::Bindings; +using namespace KJS; + +ObjcInstance::ObjcInstance(ObjectStructPtr instance, PassRefPtr<RootObject> rootObject) + : Instance(rootObject) + , _instance(instance) + , _class(0) + , _pool(0) + , _beginCount(0) +{ +} + +ObjcInstance::~ObjcInstance() +{ + begin(); // -finalizeForWebScript and -dealloc/-finalize may require autorelease pools. + if ([_instance.get() respondsToSelector:@selector(finalizeForWebScript)]) + [_instance.get() performSelector:@selector(finalizeForWebScript)]; + _instance = 0; + end(); +} + +void ObjcInstance::begin() +{ + if (!_pool) + _pool = [[NSAutoreleasePool alloc] init]; + _beginCount++; +} + +void ObjcInstance::end() +{ + _beginCount--; + ASSERT(_beginCount >= 0); + if (!_beginCount) { + [_pool drain]; + _pool = 0; + } +} + +Bindings::Class* ObjcInstance::getClass() const +{ + if (!_instance) + return 0; + if (!_class) + _class = ObjcClass::classForIsA(_instance->isa); + return static_cast<Bindings::Class*>(_class); +} + +bool ObjcInstance::implementsCall() const +{ + return [_instance.get() respondsToSelector:@selector(invokeDefaultMethodWithArguments:)]; +} + +JSValue* ObjcInstance::invokeMethod(ExecState* exec, const MethodList &methodList, const List &args) +{ + JSValue* result = jsUndefined(); + + JSLock::DropAllLocks dropAllLocks; // Can't put this inside the @try scope because it unwinds incorrectly. + + // Overloading methods is not allowed in ObjectiveC. Should only be one + // name match for a particular method. + ASSERT(methodList.size() == 1); + +@try { + ObjcMethod* method = 0; + method = static_cast<ObjcMethod*>(methodList[0]); + NSMethodSignature* signature = method->getMethodSignature(); + NSInvocation* invocation = [NSInvocation invocationWithMethodSignature:signature]; +#if defined(OBJC_API_VERSION) && OBJC_API_VERSION >= 2 + [invocation setSelector:sel_registerName(method->name())]; +#else + [invocation setSelector:(SEL)method->name()]; +#endif + [invocation setTarget:_instance.get()]; + + if (method->isFallbackMethod()) { + if (objcValueTypeForType([signature methodReturnType]) != ObjcObjectType) { + NSLog(@"Incorrect signature for invokeUndefinedMethodFromWebScript:withArguments: -- return type must be object."); + return result; + } + + // Invoke invokeUndefinedMethodFromWebScript:withArguments:, pass JavaScript function + // name as first (actually at 2) argument and array of args as second. + NSString* jsName = (NSString* )method->javaScriptName(); + [invocation setArgument:&jsName atIndex:2]; + + NSMutableArray* objcArgs = [NSMutableArray array]; + int count = args.size(); + for (int i = 0; i < count; i++) { + ObjcValue value = convertValueToObjcValue(exec, args.at(i), ObjcObjectType); + [objcArgs addObject:value.objectValue]; + } + [invocation setArgument:&objcArgs atIndex:3]; + } else { + unsigned count = [signature numberOfArguments]; + for (unsigned i = 2; i < count ; i++) { + const char* type = [signature getArgumentTypeAtIndex:i]; + ObjcValueType objcValueType = objcValueTypeForType(type); + + // Must have a valid argument type. This method signature should have + // been filtered already to ensure that it has acceptable argument + // types. + ASSERT(objcValueType != ObjcInvalidType && objcValueType != ObjcVoidType); + + ObjcValue value = convertValueToObjcValue(exec, args.at(i-2), objcValueType); + + switch (objcValueType) { + case ObjcObjectType: + [invocation setArgument:&value.objectValue atIndex:i]; + break; + case ObjcCharType: + case ObjcUnsignedCharType: + [invocation setArgument:&value.charValue atIndex:i]; + break; + case ObjcShortType: + case ObjcUnsignedShortType: + [invocation setArgument:&value.shortValue atIndex:i]; + break; + case ObjcIntType: + case ObjcUnsignedIntType: + [invocation setArgument:&value.intValue atIndex:i]; + break; + case ObjcLongType: + case ObjcUnsignedLongType: + [invocation setArgument:&value.longValue atIndex:i]; + break; + case ObjcLongLongType: + case ObjcUnsignedLongLongType: + [invocation setArgument:&value.longLongValue atIndex:i]; + break; + case ObjcFloatType: + [invocation setArgument:&value.floatValue atIndex:i]; + break; + case ObjcDoubleType: + [invocation setArgument:&value.doubleValue atIndex:i]; + break; + default: + // Should never get here. Argument types are filtered (and + // the assert above should have fired in the impossible case + // of an invalid type anyway). + fprintf(stderr, "%s: invalid type (%d)\n", __PRETTY_FUNCTION__, (int)objcValueType); + ASSERT(false); + } + } + } + + [invocation invoke]; + + // Get the return value type. + const char* type = [signature methodReturnType]; + ObjcValueType objcValueType = objcValueTypeForType(type); + + // Must have a valid return type. This method signature should have + // been filtered already to ensure that it have an acceptable return + // type. + ASSERT(objcValueType != ObjcInvalidType); + + // Get the return value and convert it to a JavaScript value. Length + // of return value will never exceed the size of largest scalar + // or a pointer. + char buffer[1024]; + ASSERT([signature methodReturnLength] < 1024); + + if (*type != 'v') { + [invocation getReturnValue:buffer]; + result = convertObjcValueToValue(exec, buffer, objcValueType, _rootObject.get()); + } +} @catch(NSException* localException) { +} + return result; +} + +JSValue* ObjcInstance::invokeDefaultMethod(ExecState* exec, const List &args) +{ + JSValue* result = jsUndefined(); + + JSLock::DropAllLocks dropAllLocks; // Can't put this inside the @try scope because it unwinds incorrectly. + +@try { + if (![_instance.get() respondsToSelector:@selector(invokeDefaultMethodWithArguments:)]) + return result; + + NSMethodSignature* signature = [_instance.get() methodSignatureForSelector:@selector(invokeDefaultMethodWithArguments:)]; + NSInvocation* invocation = [NSInvocation invocationWithMethodSignature:signature]; + [invocation setSelector:@selector(invokeDefaultMethodWithArguments:)]; + [invocation setTarget:_instance.get()]; + + if (objcValueTypeForType([signature methodReturnType]) != ObjcObjectType) { + NSLog(@"Incorrect signature for invokeDefaultMethodWithArguments: -- return type must be object."); + return result; + } + + NSMutableArray* objcArgs = [NSMutableArray array]; + unsigned count = args.size(); + for (unsigned i = 0; i < count; i++) { + ObjcValue value = convertValueToObjcValue(exec, args.at(i), ObjcObjectType); + [objcArgs addObject:value.objectValue]; + } + [invocation setArgument:&objcArgs atIndex:2]; + + [invocation invoke]; + + // Get the return value type, should always be "@" because of + // check above. + const char* type = [signature methodReturnType]; + ObjcValueType objcValueType = objcValueTypeForType(type); + + // Get the return value and convert it to a JavaScript value. Length + // of return value will never exceed the size of a pointer, so we're + // OK with 32 here. + char buffer[32]; + [invocation getReturnValue:buffer]; + result = convertObjcValueToValue(exec, buffer, objcValueType, _rootObject.get()); +} @catch(NSException* localException) { +} + + return result; +} + +bool ObjcInstance::supportsSetValueOfUndefinedField() +{ + id targetObject = getObject(); + if ([targetObject respondsToSelector:@selector(setValue:forUndefinedKey:)]) + return true; + return false; +} + +void ObjcInstance::setValueOfUndefinedField(ExecState* exec, const Identifier &property, JSValue* aValue) +{ + id targetObject = getObject(); + + JSLock::DropAllLocks dropAllLocks; // Can't put this inside the @try scope because it unwinds incorrectly. + + // This check is not really necessary because NSObject implements + // setValue:forUndefinedKey:, and unfortnately the default implementation + // throws an exception. + if ([targetObject respondsToSelector:@selector(setValue:forUndefinedKey:)]){ + ObjcValue objcValue = convertValueToObjcValue(exec, aValue, ObjcObjectType); + + @try { + [targetObject setValue:objcValue.objectValue forUndefinedKey:[NSString stringWithCString:property.ascii() encoding:NSASCIIStringEncoding]]; + } @catch(NSException* localException) { + // Do nothing. Class did not override valueForUndefinedKey:. + } + } +} + +JSValue* ObjcInstance::getValueOfUndefinedField(ExecState* exec, const Identifier& property, JSType) const +{ + JSValue* result = jsUndefined(); + + id targetObject = getObject(); + + JSLock::DropAllLocks dropAllLocks; // Can't put this inside the @try scope because it unwinds incorrectly. + + // This check is not really necessary because NSObject implements + // valueForUndefinedKey:, and unfortnately the default implementation + // throws an exception. + if ([targetObject respondsToSelector:@selector(valueForUndefinedKey:)]){ + @try { + id objcValue = [targetObject valueForUndefinedKey:[NSString stringWithCString:property.ascii() encoding:NSASCIIStringEncoding]]; + result = convertObjcValueToValue(exec, &objcValue, ObjcObjectType, _rootObject.get()); + } @catch(NSException* localException) { + // Do nothing. Class did not override valueForUndefinedKey:. + } + } + + return result; +} + +JSValue* ObjcInstance::defaultValue(JSType hint) const +{ + switch (hint) { + case StringType: + return stringValue(); + case NumberType: + return numberValue(); + case BooleanType: + return booleanValue(); + case UnspecifiedType: + if ([_instance.get() isKindOfClass:[NSString class]]) + return stringValue(); + if ([_instance.get() isKindOfClass:[NSNumber class]]) + return numberValue(); + default: + return valueOf(); + } +} + +JSValue* ObjcInstance::stringValue() const +{ + return convertNSStringToString([getObject() description]); +} + +JSValue* ObjcInstance::numberValue() const +{ + // FIXME: Implement something sensible + return jsNumber(0); +} + +JSValue* ObjcInstance::booleanValue() const +{ + // FIXME: Implement something sensible + return jsBoolean(false); +} + +JSValue* ObjcInstance::valueOf() const +{ + return stringValue(); +} diff --git a/JavaScriptCore/bindings/objc/objc_runtime.h b/JavaScriptCore/bindings/objc/objc_runtime.h new file mode 100644 index 0000000..74bc49e --- /dev/null +++ b/JavaScriptCore/bindings/objc/objc_runtime.h @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2004 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 COMPUTER, INC. OR + * 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. + */ + +#ifndef KJS_BINDINGS_OBJC_RUNTIME_H +#define KJS_BINDINGS_OBJC_RUNTIME_H + +#include <CoreFoundation/CoreFoundation.h> +#include <JavaScriptCore/objc_header.h> +#include <JavaScriptCore/object.h> +#include <JavaScriptCore/runtime.h> + +#include <wtf/RetainPtr.h> + +namespace KJS { +namespace Bindings { + +extern ClassStructPtr webScriptObjectClass(); +extern ClassStructPtr webUndefinedClass(); + +class ObjcInstance; + +class ObjcField : public Field +{ +public: + ObjcField(IvarStructPtr ivar); + ObjcField(CFStringRef name); + + virtual JSValue *valueFromInstance(ExecState *exec, const Instance *instance) const; + virtual void setValueToInstance(ExecState *exec, const Instance *instance, JSValue *aValue) const; + + virtual const char *name() const; + +private: + IvarStructPtr _ivar; + RetainPtr<CFStringRef> _name; +}; + +class ObjcMethod : public Method +{ +public: + ObjcMethod() : _objcClass(0), _selector(0), _javaScriptName(0) {} + ObjcMethod(ClassStructPtr aClass, const char *_selector); + + virtual const char *name() const; + + virtual int numParameters() const; + + NSMethodSignature *getMethodSignature() const; + + bool isFallbackMethod() const { return strcmp(_selector, "invokeUndefinedMethodFromWebScript:withArguments:") == 0; } + void setJavaScriptName(CFStringRef n) { _javaScriptName = n; } + CFStringRef javaScriptName() const { return _javaScriptName.get(); } + +private: + ClassStructPtr _objcClass; + const char *_selector; + RetainPtr<CFStringRef> _javaScriptName; +}; + +class ObjcArray : public Array +{ +public: + ObjcArray(ObjectStructPtr, PassRefPtr<RootObject>); + + virtual void setValueAt(ExecState *exec, unsigned int index, JSValue *aValue) const; + virtual JSValue *valueAt(ExecState *exec, unsigned int index) const; + virtual unsigned int getLength() const; + + ObjectStructPtr getObjcArray() const { return _array.get(); } + + static JSValue *convertObjcArrayToArray(ExecState *exec, ObjectStructPtr anObject); + +private: + RetainPtr<ObjectStructPtr> _array; +}; + +class ObjcFallbackObjectImp : public JSObject { +public: + ObjcFallbackObjectImp(ObjcInstance *i, const Identifier propertyName); + + const ClassInfo *classInfo() const { return &info; } + + virtual bool getOwnPropertySlot(ExecState *, const Identifier&, PropertySlot&); + virtual void put(ExecState*, const Identifier& propertyName, JSValue*); + virtual bool implementsCall() const; + virtual JSValue *callAsFunction(ExecState *exec, JSObject *thisObj, const List &args); + virtual bool deleteProperty(ExecState *exec, const Identifier &propertyName); + virtual JSValue *defaultValue(ExecState *exec, JSType hint) const; + + virtual JSType type() const; + virtual bool toBoolean(ExecState *exec) const; + +private: + ObjcFallbackObjectImp(); // prevent default construction + + static const ClassInfo info; + + RefPtr<ObjcInstance> _instance; + Identifier _item; +}; + +} // namespace Bindings +} // namespace KJS + +#endif diff --git a/JavaScriptCore/bindings/objc/objc_runtime.mm b/JavaScriptCore/bindings/objc/objc_runtime.mm new file mode 100644 index 0000000..14877b4 --- /dev/null +++ b/JavaScriptCore/bindings/objc/objc_runtime.mm @@ -0,0 +1,289 @@ +/* + * Copyright (C) 2004 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 COMPUTER, INC. OR + * 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 "objc_runtime.h" + +#include "objc_instance.h" +#include "runtime_array.h" +#include "runtime_object.h" +#include "WebScriptObject.h" + +using namespace KJS; +using namespace KJS::Bindings; + +extern ClassStructPtr KJS::Bindings::webScriptObjectClass() +{ + static ClassStructPtr<WebScriptObject> webScriptObjectClass = NSClassFromString(@"WebScriptObject"); + return webScriptObjectClass; +} + +extern ClassStructPtr KJS::Bindings::webUndefinedClass() +{ + static ClassStructPtr<WebUndefined> webUndefinedClass = NSClassFromString(@"WebUndefined"); + return webUndefinedClass; +} + +// ---------------------- ObjcMethod ---------------------- + +ObjcMethod::ObjcMethod(ClassStructPtr aClass, const char* name) +{ + _objcClass = aClass; + _selector = name; // Assume ObjC runtime keeps these around forever. + _javaScriptName = 0; +} + +const char* ObjcMethod::name() const +{ + return _selector; +} + +int ObjcMethod::numParameters() const +{ + return [getMethodSignature() numberOfArguments] - 2; +} + +NSMethodSignature* ObjcMethod::getMethodSignature() const +{ +#if defined(OBJC_API_VERSION) && OBJC_API_VERSION >= 2 + return [_objcClass instanceMethodSignatureForSelector:sel_registerName(_selector)]; +#else + return [_objcClass instanceMethodSignatureForSelector:(SEL)_selector]; +#endif +} + +// ---------------------- ObjcField ---------------------- + +ObjcField::ObjcField(Ivar ivar) +{ + _ivar = ivar; // Assume ObjectiveC runtime will keep this alive forever + _name = 0; +} + +ObjcField::ObjcField(CFStringRef name) +{ + _ivar = 0; + _name = (CFStringRef)CFRetain(name); +} + +const char* ObjcField::name() const +{ +#if defined(OBJC_API_VERSION) && OBJC_API_VERSION >= 2 + if (_ivar) + return ivar_getName(_ivar); +#else + if (_ivar) + return _ivar->ivar_name; +#endif + return [(NSString*)_name.get() UTF8String]; +} + +JSValue* ObjcField::valueFromInstance(ExecState* exec, const Instance* instance) const +{ + JSValue* result = jsUndefined(); + + id targetObject = (static_cast<const ObjcInstance*>(instance))->getObject(); + + JSLock::DropAllLocks dropAllLocks; // Can't put this inside the @try scope because it unwinds incorrectly. + + @try { + NSString* key = [NSString stringWithCString:name() encoding:NSASCIIStringEncoding]; + if (id objcValue = [targetObject valueForKey:key]) + result = convertObjcValueToValue(exec, &objcValue, ObjcObjectType, instance->rootObject()); + } @catch(NSException* localException) { + JSLock::lock(); + throwError(exec, GeneralError, [localException reason]); + JSLock::unlock(); + } + + return result; +} + +static id convertValueToObjcObject(ExecState* exec, JSValue* value) +{ + RefPtr<RootObject> rootObject = findRootObject(exec->dynamicGlobalObject()); + if (!rootObject) + return nil; + return [webScriptObjectClass() _convertValueToObjcValue:value originRootObject:rootObject.get() rootObject:rootObject.get()]; +} + +void ObjcField::setValueToInstance(ExecState* exec, const Instance* instance, JSValue* aValue) const +{ + id targetObject = (static_cast<const ObjcInstance*>(instance))->getObject(); + id value = convertValueToObjcObject(exec, aValue); + + JSLock::DropAllLocks dropAllLocks; // Can't put this inside the @try scope because it unwinds incorrectly. + + @try { + NSString* key = [NSString stringWithCString:name() encoding:NSASCIIStringEncoding]; + [targetObject setValue:value forKey:key]; + } @catch(NSException* localException) { + JSLock::lock(); + throwError(exec, GeneralError, [localException reason]); + JSLock::unlock(); + } +} + +// ---------------------- ObjcArray ---------------------- + +ObjcArray::ObjcArray(ObjectStructPtr a, PassRefPtr<RootObject> rootObject) + : Array(rootObject) + , _array(a) +{ +} + +void ObjcArray::setValueAt(ExecState* exec, unsigned int index, JSValue* aValue) const +{ + if (![_array.get() respondsToSelector:@selector(insertObject:atIndex:)]) { + throwError(exec, TypeError, "Array is not mutable."); + return; + } + + if (index > [_array.get() count]) { + throwError(exec, RangeError, "Index exceeds array size."); + return; + } + + // Always try to convert the value to an ObjC object, so it can be placed in the + // array. + ObjcValue oValue = convertValueToObjcValue (exec, aValue, ObjcObjectType); + + @try { + [_array.get() insertObject:oValue.objectValue atIndex:index]; + } @catch(NSException* localException) { + throwError(exec, GeneralError, "Objective-C exception."); + } +} + +JSValue* ObjcArray::valueAt(ExecState* exec, unsigned int index) const +{ + if (index > [_array.get() count]) + return throwError(exec, RangeError, "Index exceeds array size."); + @try { + id obj = [_array.get() objectAtIndex:index]; + if (obj) + return convertObjcValueToValue (exec, &obj, ObjcObjectType, _rootObject.get()); + } @catch(NSException* localException) { + return throwError(exec, GeneralError, "Objective-C exception."); + } + return jsUndefined(); +} + +unsigned int ObjcArray::getLength() const +{ + return [_array.get() count]; +} + +const ClassInfo ObjcFallbackObjectImp::info = { "ObjcFallbackObject", 0, 0 }; + +ObjcFallbackObjectImp::ObjcFallbackObjectImp(ObjcInstance* i, const KJS::Identifier propertyName) +: _instance(i) +, _item(propertyName) +{ +} + +bool ObjcFallbackObjectImp::getOwnPropertySlot(ExecState*, const Identifier&, PropertySlot& slot) +{ + // keep the prototype from getting called instead of just returning false + slot.setUndefined(this); + return true; +} + +void ObjcFallbackObjectImp::put(ExecState*, const Identifier&, JSValue*) +{ +} + +JSType ObjcFallbackObjectImp::type() const +{ + id targetObject = _instance->getObject(); + + if ([targetObject respondsToSelector:@selector(invokeUndefinedMethodFromWebScript:withArguments:)]) + return ObjectType; + + return UndefinedType; +} + +bool ObjcFallbackObjectImp::implementsCall() const +{ + id targetObject = _instance->getObject(); + + if ([targetObject respondsToSelector:@selector(invokeUndefinedMethodFromWebScript:withArguments:)]) + return true; + + return false; +} + +JSValue* ObjcFallbackObjectImp::callAsFunction(ExecState* exec, JSObject* thisObj, const List &args) +{ + if (thisObj->classInfo() != &KJS::RuntimeObjectImp::info) + return throwError(exec, TypeError); + + JSValue* result = jsUndefined(); + + RuntimeObjectImp* imp = static_cast<RuntimeObjectImp*>(thisObj); + Instance* instance = imp->getInternalInstance(); + + if (!instance) + return RuntimeObjectImp::throwInvalidAccessError(exec); + + instance->begin(); + + ObjcInstance* objcInstance = static_cast<ObjcInstance*>(instance); + id targetObject = objcInstance->getObject(); + + if ([targetObject respondsToSelector:@selector(invokeUndefinedMethodFromWebScript:withArguments:)]){ + MethodList methodList; + ObjcClass* objcClass = static_cast<ObjcClass*>(instance->getClass()); + ObjcMethod* fallbackMethod = new ObjcMethod (objcClass->isa(), sel_getName(@selector(invokeUndefinedMethodFromWebScript:withArguments:))); + fallbackMethod->setJavaScriptName((CFStringRef)[NSString stringWithCString:_item.ascii() encoding:NSASCIIStringEncoding]); + methodList.append(fallbackMethod); + result = instance->invokeMethod(exec, methodList, args); + delete fallbackMethod; + } + + instance->end(); + + return result; +} + +bool ObjcFallbackObjectImp::deleteProperty(ExecState*, const Identifier&) +{ + return false; +} + +JSValue* ObjcFallbackObjectImp::defaultValue(ExecState* exec, JSType hint) const +{ + return _instance->getValueOfUndefinedField(exec, _item, hint); +} + +bool ObjcFallbackObjectImp::toBoolean(ExecState *) const +{ + id targetObject = _instance->getObject(); + + if ([targetObject respondsToSelector:@selector(invokeUndefinedMethodFromWebScript:withArguments:)]) + return true; + + return false; +} diff --git a/JavaScriptCore/bindings/objc/objc_utility.h b/JavaScriptCore/bindings/objc/objc_utility.h new file mode 100644 index 0000000..e674fcc --- /dev/null +++ b/JavaScriptCore/bindings/objc/objc_utility.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2004 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 COMPUTER, INC. OR + * 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. + */ + +#ifndef KJS_BINDINGS_OBJC_UTILITY_H +#define KJS_BINDINGS_OBJC_UTILITY_H + +#include <CoreFoundation/CoreFoundation.h> + +#include "object.h" +#include "objc_header.h" + +#ifdef __OBJC__ +@class NSString; +#else +class NSString; +#endif + +namespace KJS { +namespace Bindings { + +typedef union { + ObjectStructPtr objectValue; + bool booleanValue; + char charValue; + short shortValue; + int intValue; + long longValue; + long long longLongValue; + float floatValue; + double doubleValue; +} ObjcValue; + +typedef enum { + ObjcVoidType, + ObjcObjectType, + ObjcCharType, + ObjcUnsignedCharType, + ObjcShortType, + ObjcUnsignedShortType, + ObjcIntType, + ObjcUnsignedIntType, + ObjcLongType, + ObjcUnsignedLongType, + ObjcLongLongType, + ObjcUnsignedLongLongType, + ObjcFloatType, + ObjcDoubleType, + ObjcInvalidType +} ObjcValueType; + +class RootObject; + +ObjcValue convertValueToObjcValue(ExecState *exec, JSValue *value, ObjcValueType type); +JSValue *convertNSStringToString(NSString *nsstring); +JSValue *convertObjcValueToValue(ExecState *exec, void *buffer, ObjcValueType type, RootObject*); +ObjcValueType objcValueTypeForType(const char *type); + +bool convertJSMethodNameToObjc(const char *JSName, char *buffer, size_t bufferSize); + +JSObject *throwError(ExecState *, ErrorType, NSString *message); + +} // namespace Bindings +} // namespace KJS + +#endif diff --git a/JavaScriptCore/bindings/objc/objc_utility.mm b/JavaScriptCore/bindings/objc/objc_utility.mm new file mode 100644 index 0000000..62a0ad0 --- /dev/null +++ b/JavaScriptCore/bindings/objc/objc_utility.mm @@ -0,0 +1,372 @@ +/* + * Copyright (C) 2004 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 COMPUTER, INC. OR + * 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 "objc_utility.h" + +#include "objc_instance.h" +#include "JSGlobalObject.h" +#include "runtime_array.h" +#include "runtime_object.h" +#include "WebScriptObject.h" +#include <wtf/Assertions.h> + +#if !defined(_C_LNG_LNG) +#define _C_LNG_LNG 'q' +#endif + +#if !defined(_C_ULNG_LNG) +#define _C_ULNG_LNG 'Q' +#endif + +#if !defined(_C_CONST) +#define _C_CONST 'r' +#endif + +#if !defined(_C_BYCOPY) +#define _C_BYCOPY 'O' +#endif + +#if !defined(_C_BYREF) +#define _C_BYREF 'R' +#endif + +#if !defined(_C_ONEWAY) +#define _C_ONEWAY 'V' +#endif + +#if !defined(_C_GCINVISIBLE) +#define _C_GCINVISIBLE '!' +#endif + +namespace KJS { +namespace Bindings { + +/* + By default, a JavaScript method name is produced by concatenating the + components of an ObjectiveC method name, replacing ':' with '_', and + escaping '_' and '$' with a leading '$', such that '_' becomes "$_" and + '$' becomes "$$". For example: + + ObjectiveC name Default JavaScript name + moveTo:: moveTo__ + moveTo_ moveTo$_ + moveTo$_ moveTo$$$_ + + This function performs the inverse of that operation. + + @result Fills 'buffer' with the ObjectiveC method name that corresponds to 'JSName'. + Returns true for success, false for failure. (Failure occurs when 'buffer' + is not big enough to hold the result.) +*/ +bool convertJSMethodNameToObjc(const char *JSName, char *buffer, size_t bufferSize) +{ + ASSERT(JSName && buffer); + + const char *sp = JSName; // source pointer + char *dp = buffer; // destination pointer + + char *end = buffer + bufferSize; + while (dp < end) { + if (*sp == '$') { + ++sp; + *dp = *sp; + } else if (*sp == '_') + *dp = ':'; + else + *dp = *sp; + + // If a future coder puts funny ++ operators above, we might write off the end + // of the buffer in the middle of this loop. Let's make sure to check for that. + ASSERT(dp < end); + + if (*sp == 0) { // We finished converting JSName + ASSERT(strlen(JSName) < bufferSize); + return true; + } + + ++sp; + ++dp; + } + + return false; // We ran out of buffer before converting JSName +} + +/* + + JavaScript to ObjC + Number coerced to char, short, int, long, float, double, or NSNumber, as appropriate + String NSString + wrapper id + Object WebScriptObject + null NSNull + [], other exception + +*/ +ObjcValue convertValueToObjcValue(ExecState *exec, JSValue *value, ObjcValueType type) +{ + ObjcValue result; + double d = 0; + + if (value->isNumber() || value->isString() || value->isBoolean()) + d = value->toNumber(exec); + + switch (type) { + case ObjcObjectType: { + JSLock lock; + + JSGlobalObject *originGlobalObject = exec->dynamicGlobalObject(); + RootObject* originRootObject = findRootObject(originGlobalObject); + + JSGlobalObject* globalObject = 0; + if (value->isObject() && static_cast<JSObject*>(value)->isGlobalObject()) + globalObject = static_cast<JSGlobalObject*>(value); + + if (!globalObject) + globalObject = originGlobalObject; + + RootObject* rootObject = findRootObject(globalObject); + result.objectValue = rootObject + ? [webScriptObjectClass() _convertValueToObjcValue:value originRootObject:originRootObject rootObject:rootObject] + : nil; + } + break; + + case ObjcCharType: + case ObjcUnsignedCharType: + result.charValue = (char)d; + break; + case ObjcShortType: + case ObjcUnsignedShortType: + result.shortValue = (short)d; + break; + case ObjcIntType: + case ObjcUnsignedIntType: + result.intValue = (int)d; + break; + case ObjcLongType: + case ObjcUnsignedLongType: + result.longValue = (long)d; + break; + case ObjcLongLongType: + case ObjcUnsignedLongLongType: + result.longLongValue = (long long)d; + break; + case ObjcFloatType: + result.floatValue = (float)d; + break; + case ObjcDoubleType: + result.doubleValue = (double)d; + break; + case ObjcVoidType: + bzero(&result, sizeof(ObjcValue)); + break; + + case ObjcInvalidType: + default: + // FIXME: throw an exception? + break; + } + + return result; +} + +JSValue *convertNSStringToString(NSString *nsstring) +{ + JSLock lock; + + unichar *chars; + unsigned int length = [nsstring length]; + chars = (unichar *)malloc(sizeof(unichar)*length); + [nsstring getCharacters:chars]; + UString u((const UChar*)chars, length); + JSValue *aValue = jsString(u); + free((void *)chars); + return aValue; +} + +/* + ObjC to JavaScript + ---- ---------- + char number + short number + int number + long number + float number + double number + NSNumber boolean or number + NSString string + NSArray array + NSNull null + WebScriptObject underlying JavaScript object + WebUndefined undefined + id object wrapper + other should not happen +*/ +JSValue* convertObjcValueToValue(ExecState* exec, void* buffer, ObjcValueType type, RootObject* rootObject) +{ + JSLock lock; + + switch (type) { + case ObjcObjectType: { + id obj = *(id*)buffer; + if ([obj isKindOfClass:[NSString class]]) + return convertNSStringToString((NSString *)obj); + if ([obj isKindOfClass:webUndefinedClass()]) + return jsUndefined(); + if ((CFBooleanRef)obj == kCFBooleanTrue) + return jsBoolean(true); + if ((CFBooleanRef)obj == kCFBooleanFalse) + return jsBoolean(false); + if ([obj isKindOfClass:[NSNumber class]]) + return jsNumber([obj doubleValue]); + if ([obj isKindOfClass:[NSArray class]]) + return new RuntimeArray(exec, new ObjcArray(obj, rootObject)); + if ([obj isKindOfClass:webScriptObjectClass()]) { + JSObject* imp = [obj _imp]; + return imp ? imp : jsUndefined(); + } + if ([obj isKindOfClass:[NSNull class]]) + return jsNull(); + if (obj == 0) + return jsUndefined(); + return Instance::createRuntimeObject(Instance::ObjectiveCLanguage, obj, rootObject); + } + case ObjcCharType: + return jsNumber(*(char *)buffer); + case ObjcUnsignedCharType: + return jsNumber(*(unsigned char *)buffer); + case ObjcShortType: + return jsNumber(*(short *)buffer); + case ObjcUnsignedShortType: + return jsNumber(*(unsigned short *)buffer); + case ObjcIntType: + return jsNumber(*(int *)buffer); + case ObjcUnsignedIntType: + return jsNumber(*(unsigned int *)buffer); + case ObjcLongType: + return jsNumber(*(long *)buffer); + case ObjcUnsignedLongType: + return jsNumber(*(unsigned long *)buffer); + case ObjcLongLongType: + return jsNumber(*(long long *)buffer); + case ObjcUnsignedLongLongType: + return jsNumber(*(unsigned long long *)buffer); + case ObjcFloatType: + return jsNumber(*(float *)buffer); + case ObjcDoubleType: + return jsNumber(*(double *)buffer); + default: + // Should never get here. Argument types are filtered. + fprintf(stderr, "%s: invalid type (%d)\n", __PRETTY_FUNCTION__, (int)type); + ASSERT(false); + } + + return 0; +} + +ObjcValueType objcValueTypeForType(const char *type) +{ + int typeLength = strlen(type); + ObjcValueType objcValueType = ObjcInvalidType; + + for (int i = 0; i < typeLength; ++i) { + char typeChar = type[i]; + switch (typeChar) { + case _C_CONST: + case _C_BYCOPY: + case _C_BYREF: + case _C_ONEWAY: + case _C_GCINVISIBLE: + // skip these type modifiers + break; + case _C_ID: + objcValueType = ObjcObjectType; + break; + case _C_CHR: + objcValueType = ObjcCharType; + break; + case _C_UCHR: + objcValueType = ObjcUnsignedCharType; + break; + case _C_SHT: + objcValueType = ObjcShortType; + break; + case _C_USHT: + objcValueType = ObjcUnsignedShortType; + break; + case _C_INT: + objcValueType = ObjcIntType; + break; + case _C_UINT: + objcValueType = ObjcUnsignedIntType; + break; + case _C_LNG: + objcValueType = ObjcLongType; + break; + case _C_ULNG: + objcValueType = ObjcUnsignedLongType; + break; + case _C_LNG_LNG: + objcValueType = ObjcLongLongType; + break; + case _C_ULNG_LNG: + objcValueType = ObjcUnsignedLongLongType; + break; + case _C_FLT: + objcValueType = ObjcFloatType; + break; + case _C_DBL: + objcValueType = ObjcDoubleType; + break; + case _C_VOID: + objcValueType = ObjcVoidType; + break; + default: + // Unhandled type. We don't handle C structs, unions, etc. + // FIXME: throw an exception? + ASSERT(false); + } + + if (objcValueType != ObjcInvalidType) + break; + } + + return objcValueType; +} + +JSObject *throwError(ExecState *exec, ErrorType type, NSString *message) +{ + ASSERT(message); + size_t length = [message length]; + unichar *buffer = new unichar[length]; + [message getCharacters:buffer]; + JSObject *error = throwError(exec, type, UString(reinterpret_cast<UChar *>(buffer), length)); + delete [] buffer; + return error; +} + +} +} diff --git a/JavaScriptCore/bindings/qt/qt_class.cpp b/JavaScriptCore/bindings/qt/qt_class.cpp new file mode 100644 index 0000000..59730b8 --- /dev/null +++ b/JavaScriptCore/bindings/qt/qt_class.cpp @@ -0,0 +1,223 @@ +/* + * Copyright (C) 2006 Trolltech ASA + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "config.h" +#include "identifier.h" + +#include "qt_class.h" +#include "qt_instance.h" +#include "qt_runtime.h" + +#include <qmetaobject.h> +#include <qdebug.h> + +namespace KJS { +namespace Bindings { + +QtClass::QtClass(const QMetaObject* mo) + : m_metaObject(mo) +{ +} + +QtClass::~QtClass() +{ +} + +typedef HashMap<const QMetaObject*, QtClass*> ClassesByMetaObject; +static ClassesByMetaObject* classesByMetaObject = 0; + +QtClass* QtClass::classForObject(QObject* o) +{ + if (!classesByMetaObject) + classesByMetaObject = new ClassesByMetaObject; + + const QMetaObject* mo = o->metaObject(); + QtClass* aClass = classesByMetaObject->get(mo); + if (!aClass) { + aClass = new QtClass(mo); + classesByMetaObject->set(mo, aClass); + } + + return aClass; +} + +const char* QtClass::name() const +{ + return m_metaObject->className(); +} + +// We use this to get at signals (so we can return a proper function object, +// and not get wrapped in RuntimeMethod). Also, use this for methods, +// so we can cache the JSValue* and return the same JSValue for the same +// identifier... +// +// Unfortunately... we need to gcProtect our JSValues, since we don't have +// access to an actual JS class that can mark() our JSValues. +// +JSValue* QtClass::fallbackObject(ExecState *exec, Instance *inst, const Identifier &identifier) +{ + QtInstance* qtinst = static_cast<QtInstance*>(inst); + + QByteArray name(identifier.ascii()); + + // First see if we have a cache hit + JSValue* val = qtinst->m_methods.value(name); + if (val) + return val; + + // Nope, create an entry + QByteArray normal = QMetaObject::normalizedSignature(name.constData()); + + // See if there is an exact match + int index = -1; + if (normal.contains('(') && (index = m_metaObject->indexOfMethod(normal)) != -1) { + QMetaMethod m = m_metaObject->method(index); + if (m.access() != QMetaMethod::Private) { + JSValue *val = new QtRuntimeMetaMethod(exec, identifier, static_cast<QtInstance*>(inst), index, normal, false); + gcProtect(val); + qtinst->m_methods.insert(name, val); + return val; + } + } + + // Nope.. try a basename match + int count = m_metaObject->methodCount(); + for (index = count - 1; index >= 0; --index) { + const QMetaMethod m = m_metaObject->method(index); + if (m.access() == QMetaMethod::Private) + continue; + + QByteArray signature = m.signature(); + signature.truncate(signature.indexOf('(')); + + if (normal == signature) { + JSValue* val = new QtRuntimeMetaMethod(exec, identifier, static_cast<QtInstance*>(inst), index, normal, false); + gcProtect(val); + qtinst->m_methods.insert(name, val); + return val; + } + } + + return jsUndefined(); +} + +// This functionality is handled by the fallback case above... +MethodList QtClass::methodsNamed(const Identifier&, Instance*) const +{ + return MethodList(); +} + +// ### we may end up with a different search order than QtScript by not +// folding this code into the fallbackMethod above, but Fields propagate out +// of the binding code +Field* QtClass::fieldNamed(const Identifier& identifier, Instance* instance) const +{ + // Check static properties first + QtInstance* qtinst = static_cast<QtInstance*>(instance); + + QObject* obj = qtinst->getObject(); + UString ustring = identifier.ustring(); + QString objName(QString::fromUtf16((const ushort*)ustring.rep()->data(),ustring.size())); + QByteArray ba = objName.toAscii(); + + // First check for a cached field + QtField* f = qtinst->m_fields.value(objName); + + if (obj) { + if (f) { + // We only cache real metaproperties, but we do store the + // other types so we can delete them later + if (f->fieldType() == QtField::MetaProperty) + return f; + else if (f->fieldType() == QtField::DynamicProperty) { + if (obj->dynamicPropertyNames().indexOf(ba) >= 0) + return f; + else { + // Dynamic property that disappeared + qtinst->m_fields.remove(objName); + delete f; + } + } else { + QList<QObject*> children = obj->children(); + for (int index = 0; index < children.count(); ++index) { + QObject *child = children.at(index); + if (child->objectName() == objName) + return f; + } + + // Didn't find it, delete it from the cache + qtinst->m_fields.remove(objName); + delete f; + } + } + + int index = m_metaObject->indexOfProperty(identifier.ascii()); + if (index >= 0) { + QMetaProperty prop = m_metaObject->property(index); + + if (prop.isScriptable(obj)) { + f = new QtField(prop); + qtinst->m_fields.insert(objName, f); + return f; + } + } + + // Dynamic properties + index = obj->dynamicPropertyNames().indexOf(ba); + if (index >= 0) { + f = new QtField(ba); + qtinst->m_fields.insert(objName, f); + return f; + } + + // Child objects + + QList<QObject*> children = obj->children(); + for (index = 0; index < children.count(); ++index) { + QObject *child = children.at(index); + if (child->objectName() == objName) { + f = new QtField(child); + qtinst->m_fields.insert(objName, f); + return f; + } + } + + // Nothing named this + return 0; + } else { + QByteArray ba(identifier.ascii()); + // For compatibility with qtscript, cached methods don't cause + // errors until they are accessed, so don't blindly create an error + // here. + if (qtinst->m_methods.contains(ba)) + return 0; + + // deleted qobject, but can't throw an error from here (no exec) + // create a fake QtField that will throw upon access + if (!f) { + f = new QtField(ba); + qtinst->m_fields.insert(objName, f); + } + return f; + } +} + +} +} + diff --git a/JavaScriptCore/bindings/qt/qt_class.h b/JavaScriptCore/bindings/qt/qt_class.h new file mode 100644 index 0000000..c3e59ec --- /dev/null +++ b/JavaScriptCore/bindings/qt/qt_class.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2006 Trolltech ASA + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef BINDINGS_QT_CLASS_H_ +#define BINDINGS_QT_CLASS_H_ + +#include "runtime.h" +class QObject; +class QMetaObject; + +namespace KJS { +namespace Bindings { + + +class QtClass : public Class { +protected: + QtClass(const QMetaObject*); + +public: + static QtClass* classForObject(QObject*); + virtual ~QtClass(); + + virtual const char* name() const; + virtual MethodList methodsNamed(const Identifier&, Instance*) const; + virtual Field* fieldNamed(const Identifier&, Instance*) const; + + virtual JSValue* fallbackObject(ExecState*, Instance*, const Identifier&); + +private: + QtClass(const QtClass&); // prohibit copying + QtClass& operator=(const QtClass&); // prohibit assignment + + const QMetaObject* m_metaObject; +}; + +} // namespace Bindings +} // namespace KJS + +#endif diff --git a/JavaScriptCore/bindings/qt/qt_instance.cpp b/JavaScriptCore/bindings/qt/qt_instance.cpp new file mode 100644 index 0000000..0197b84 --- /dev/null +++ b/JavaScriptCore/bindings/qt/qt_instance.cpp @@ -0,0 +1,394 @@ +/* + * Copyright (C) 2006 Trolltech ASA + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "config.h" +#include "qt_instance.h" + +#include "JSGlobalObject.h" +#include "list.h" +#include "qt_class.h" +#include "qt_runtime.h" +#include "PropertyNameArray.h" +#include "runtime_object.h" +#include "object_object.h" + +#include <qmetaobject.h> +#include <qdebug.h> +#include <qmetatype.h> +#include <qhash.h> + +namespace KJS { +namespace Bindings { + +// Cache QtInstances +typedef QMultiHash<void*, QtInstance*> QObjectInstanceMap; +static QObjectInstanceMap cachedInstances; + +// Cache JSObjects +typedef QHash<QtInstance*, JSObject*> InstanceJSObjectMap; +static InstanceJSObjectMap cachedObjects; + +// Derived RuntimeObject +class QtRuntimeObjectImp : public RuntimeObjectImp { + public: + QtRuntimeObjectImp(Instance*); + ~QtRuntimeObjectImp(); + virtual void invalidate(); + + // Additions + virtual bool implementsConstruct() const {return implementsCall();} + virtual JSObject* construct(ExecState* exec, const List& args); + protected: + void removeFromCache(); +}; + +QtRuntimeObjectImp::QtRuntimeObjectImp(Instance* instance) + : RuntimeObjectImp(instance) +{ +} + +QtRuntimeObjectImp::~QtRuntimeObjectImp() +{ + removeFromCache(); +} + +void QtRuntimeObjectImp::invalidate() +{ + removeFromCache(); + RuntimeObjectImp::invalidate(); +} + +void QtRuntimeObjectImp::removeFromCache() +{ + JSLock lock; + QtInstance* key = cachedObjects.key(this); + if (key) + cachedObjects.remove(key); +} + +JSObject* QtRuntimeObjectImp::construct(ExecState* exec, const List& args) +{ + // ECMA 15.2.2.1 (?) + JSValue *val = callAsFunction(exec, this, args); + + if (!val || val->type() == NullType || val->type() == UndefinedType) + return new JSObject(exec->lexicalGlobalObject()->objectPrototype()); + else + return val->toObject(exec); +} + +// QtInstance +QtInstance::QtInstance(QObject* o, PassRefPtr<RootObject> rootObject) + : Instance(rootObject) + , m_class(0) + , m_object(o) + , m_hashkey(o) + , m_defaultMethod(0) + , m_defaultMethodIndex(-2) +{ +} + +QtInstance::~QtInstance() +{ + JSLock lock; + + cachedObjects.remove(this); + cachedInstances.remove(m_hashkey); + + // clean up (unprotect from gc) the JSValues we've created + foreach(JSValue* val, m_methods.values()) { + gcUnprotect(val); + } + m_methods.clear(); + + foreach(QtField* f, m_fields.values()) { + delete f; + } + m_fields.clear(); + + if (m_defaultMethod) + gcUnprotect(m_defaultMethod); +} + +QtInstance* QtInstance::getQtInstance(QObject* o, PassRefPtr<RootObject> rootObject) +{ + JSLock lock; + + foreach(QtInstance* instance, cachedInstances.values(o)) { + if (instance->rootObject() == rootObject) + return instance; + } + + QtInstance* ret = new QtInstance(o, rootObject); + cachedInstances.insert(o, ret); + + return ret; +} + +JSObject* QtInstance::getRuntimeObject(QtInstance* instance) +{ + JSLock lock; + JSObject* ret = cachedObjects.value(instance); + if (!ret) { + ret = new QtRuntimeObjectImp(instance); + cachedObjects.insert(instance, ret); + } + return ret; +} + +Class* QtInstance::getClass() const +{ + if (!m_class) + m_class = QtClass::classForObject(m_object); + return m_class; +} + +void QtInstance::begin() +{ + // Do nothing. +} + +void QtInstance::end() +{ + // Do nothing. +} + +void QtInstance::getPropertyNames(ExecState* , PropertyNameArray& array) +{ + // This is the enumerable properties, so put: + // properties + // dynamic properties + // slots + QObject* obj = getObject(); + if (obj) { + const QMetaObject* meta = obj->metaObject(); + + int i; + for (i=0; i < meta->propertyCount(); i++) { + QMetaProperty prop = meta->property(i); + if (prop.isScriptable()) { + array.add(Identifier(prop.name())); + } + } + + QList<QByteArray> dynProps = obj->dynamicPropertyNames(); + foreach(QByteArray ba, dynProps) { + array.add(Identifier(ba.constData())); + } + + for (i=0; i < meta->methodCount(); i++) { + QMetaMethod method = meta->method(i); + if (method.access() != QMetaMethod::Private) { + array.add(Identifier(method.signature())); + } + } + } +} + +JSValue* QtInstance::invokeMethod(ExecState*, const MethodList&, const List&) +{ + // Implemented via fallbackMethod & QtRuntimeMetaMethod::callAsFunction + return jsUndefined(); +} + +bool QtInstance::implementsCall() const +{ + // See if we have qscript_call + if (m_defaultMethodIndex == -2) { + if (m_object) { + const QMetaObject* meta = m_object->metaObject(); + int count = meta->methodCount(); + const QByteArray defsig("qscript_call"); + for (int index = count - 1; index >= 0; --index) { + const QMetaMethod m = meta->method(index); + + QByteArray signature = m.signature(); + signature.truncate(signature.indexOf('(')); + + if (defsig == signature) { + m_defaultMethodIndex = index; + break; + } + } + } + + if (m_defaultMethodIndex == -2) // Not checked + m_defaultMethodIndex = -1; // No qscript_call + } + + // typeof object that implements call == function + return (m_defaultMethodIndex >= 0); +} + +JSValue* QtInstance::invokeDefaultMethod(ExecState* exec, const List& args) +{ + // QtScript tries to invoke a meta method qscript_call + if (!getObject()) + return throwError(exec, GeneralError, "cannot call function of deleted QObject"); + + // implementsCall will update our default method cache, if possible + if (implementsCall()) { + if (!m_defaultMethod) { + m_defaultMethod = new QtRuntimeMetaMethod(exec, Identifier("[[Call]]"),this, m_defaultMethodIndex, QByteArray("qscript_call"), true); + gcProtect(m_defaultMethod); + } + + return m_defaultMethod->callAsFunction(exec, 0, args); // Luckily QtRuntimeMetaMethod ignores the obj parameter + } else + return throwError(exec, TypeError, "not a function"); +} + +JSValue* QtInstance::defaultValue(JSType hint) const +{ + if (hint == StringType) + return stringValue(); + if (hint == NumberType) + return numberValue(); + if (hint == BooleanType) + return booleanValue(); + return valueOf(); +} + +JSValue* QtInstance::stringValue() const +{ + // Hmm.. see if there is a toString defined + QByteArray buf; + bool useDefault = true; + getClass(); + QObject* obj = getObject(); + if (m_class && obj) { + // Cheat and don't use the full name resolution + int index = obj->metaObject()->indexOfMethod("toString()"); + if (index >= 0) { + QMetaMethod m = obj->metaObject()->method(index); + // Check to see how much we can call it + if (m.access() != QMetaMethod::Private + && m.methodType() != QMetaMethod::Signal + && m.parameterTypes().count() == 0) { + const char* retsig = m.typeName(); + if (retsig && *retsig) { + QVariant ret(QMetaType::type(retsig), (void*)0); + void * qargs[1]; + qargs[0] = ret.data(); + + if (obj->qt_metacall(QMetaObject::InvokeMetaMethod, index, qargs) < 0) { + if (ret.isValid() && ret.canConvert(QVariant::String)) { + buf = ret.toString().toLatin1().constData(); // ### Latin 1? Ascii? + useDefault = false; + } + } + } + } + } + } + + if (useDefault) { + const QMetaObject* meta = obj ? obj->metaObject() : &QObject::staticMetaObject; + QString name = obj ? obj->objectName() : QString::fromUtf8("unnamed"); + QString str = QString::fromUtf8("%0(name = \"%1\")") + .arg(QLatin1String(meta->className())).arg(name); + + buf = str.toLatin1(); + } + return jsString(buf.constData()); +} + +JSValue* QtInstance::numberValue() const +{ + return jsNumber(0); +} + +JSValue* QtInstance::booleanValue() const +{ + // ECMA 9.2 + return jsBoolean(true); +} + +JSValue* QtInstance::valueOf() const +{ + return stringValue(); +} + +// In qt_runtime.cpp +JSValue* convertQVariantToValue(ExecState* exec, PassRefPtr<RootObject> root, const QVariant& variant); +QVariant convertValueToQVariant(ExecState* exec, JSValue* value, QMetaType::Type hint, int *distance); + +const char* QtField::name() const +{ + if (m_type == MetaProperty) + return m_property.name(); + else if (m_type == ChildObject && m_childObject) + return m_childObject->objectName().toLatin1(); + else if (m_type == DynamicProperty) + return m_dynamicProperty.constData(); + return ""; // deleted child object +} + +JSValue* QtField::valueFromInstance(ExecState* exec, const Instance* inst) const +{ + const QtInstance* instance = static_cast<const QtInstance*>(inst); + QObject* obj = instance->getObject(); + + if (obj) { + QVariant val; + if (m_type == MetaProperty) { + if (m_property.isReadable()) + val = m_property.read(obj); + else + return jsUndefined(); + } else if (m_type == ChildObject) + val = QVariant::fromValue((QObject*) m_childObject); + else if (m_type == DynamicProperty) + val = obj->property(m_dynamicProperty); + + return convertQVariantToValue(exec, inst->rootObject(), val); + } else { + QString msg = QString("cannot access member `%1' of deleted QObject").arg(name()); + return throwError(exec, GeneralError, msg.toLatin1().constData()); + } +} + +void QtField::setValueToInstance(ExecState* exec, const Instance* inst, JSValue* aValue) const +{ + if (m_type == ChildObject) // QtScript doesn't allow setting to a named child + return; + + const QtInstance* instance = static_cast<const QtInstance*>(inst); + QObject* obj = instance->getObject(); + if (obj) { + QMetaType::Type argtype = QMetaType::Void; + if (m_type == MetaProperty) + argtype = (QMetaType::Type) QMetaType::type(m_property.typeName()); + + // dynamic properties just get any QVariant + QVariant val = convertValueToQVariant(exec, aValue, argtype, 0); + if (m_type == MetaProperty) { + if (m_property.isWritable()) + m_property.write(obj, val); + } else if (m_type == DynamicProperty) + obj->setProperty(m_dynamicProperty.constData(), val); + } else { + QString msg = QString("cannot access member `%1' of deleted QObject").arg(name()); + throwError(exec, GeneralError, msg.toLatin1().constData()); + } +} + + +} +} diff --git a/JavaScriptCore/bindings/qt/qt_instance.h b/JavaScriptCore/bindings/qt/qt_instance.h new file mode 100644 index 0000000..2304ac5 --- /dev/null +++ b/JavaScriptCore/bindings/qt/qt_instance.h @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2006 Trolltech ASA + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef BINDINGS_QT_INSTANCE_H_ +#define BINDINGS_QT_INSTANCE_H_ + +#include "runtime.h" +#include "runtime_root.h" +#include <qpointer.h> +#include <qhash.h> + +class QObject; + +namespace KJS { + +namespace Bindings { + +class QtClass; +class QtField; +class QtRuntimeMetaMethod; + +class QtInstance : public Instance +{ +public: + ~QtInstance (); + + virtual Class* getClass() const; + + virtual void begin(); + virtual void end(); + + virtual JSValue* valueOf() const; + virtual JSValue* defaultValue (JSType hint) const; + + virtual bool implementsCall() const; + + virtual JSValue* invokeMethod (ExecState *exec, const MethodList &method, const List &args); + virtual JSValue* invokeDefaultMethod (ExecState *exec, const List &args); + + virtual void getPropertyNames(ExecState*, PropertyNameArray&); + + virtual BindingLanguage getBindingLanguage() const { return QtLanguage; } + + JSValue* stringValue() const; + JSValue* numberValue() const; + JSValue* booleanValue() const; + + QObject* getObject() const { return m_object; } + + static QtInstance* getQtInstance(QObject*, PassRefPtr<RootObject>); + static JSObject* getRuntimeObject(QtInstance*); + +private: + friend class QtClass; + QtInstance(QObject*, PassRefPtr<RootObject>); // Factory produced only.. + mutable QtClass* m_class; + QPointer<QObject> m_object; + QObject* m_hashkey; + mutable QHash<QByteArray,JSValue*> m_methods; + mutable QHash<QString,QtField*> m_fields; + mutable QtRuntimeMetaMethod* m_defaultMethod; + mutable int m_defaultMethodIndex; +}; + +} // namespace Bindings + +} // namespace KJS + +#endif diff --git a/JavaScriptCore/bindings/qt/qt_runtime.cpp b/JavaScriptCore/bindings/qt/qt_runtime.cpp new file mode 100644 index 0000000..113f0c5 --- /dev/null +++ b/JavaScriptCore/bindings/qt/qt_runtime.cpp @@ -0,0 +1,1590 @@ +/* + * Copyright (C) 2006 Trolltech ASA + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "config.h" +#include "qt_runtime.h" +#include "qt_instance.h" +#include "object.h" +#include "array_instance.h" +#include "date_object.h" +#include "DateMath.h" +#include "regexp_object.h" +#include <runtime_object.h> +#include <runtime_array.h> +#include <function.h> +#include "PropertyNameArray.h" +#include "qmetatype.h" +#include "qmetaobject.h" +#include "qobject.h" +#include "qstringlist.h" +#include "qdebug.h" +#include "qvarlengtharray.h" +#include "qdatetime.h" +#include <limits.h> + +// QtScript has these +Q_DECLARE_METATYPE(QObjectList); +Q_DECLARE_METATYPE(QList<int>); +Q_DECLARE_METATYPE(QVariant); + + +namespace KJS { +namespace Bindings { + +// Debugging +//#define QTWK_RUNTIME_CONVERSION_DEBUG +//#define QTWK_RUNTIME_MATCH_DEBUG + +class QWKNoDebug +{ +public: + inline QWKNoDebug(){} + inline ~QWKNoDebug(){} + + template<typename T> + inline QWKNoDebug &operator<<(const T &) { return *this; } +}; + +#ifdef QTWK_RUNTIME_CONVERSION_DEBUG +#define qConvDebug() qDebug() +#else +#define qConvDebug() QWKNoDebug() +#endif + +#ifdef QTWK_RUNTIME_MATCH_DEBUG +#define qMatchDebug() qDebug() +#else +#define qMatchDebug() QWKNoDebug() +#endif + +typedef enum { + Variant, + Number, + Boolean, + String, + Date, + RegExp, + Array, + QObj, + Object, + Null +} JSRealType; + +static JSRealType valueRealType(ExecState* exec, JSValue* val) +{ + if (val->isNumber()) + return Number; + else if (val->isString()) + return String; + else if (val->isBoolean()) + return Boolean; + else if (val->isNull()) + return Null; + else if (val->isObject()) { + JSObject *object = val->toObject(exec); + if (object->inherits(&ArrayInstance::info)) + return Array; + else if (object->inherits(&DateInstance::info)) + return Date; + else if (object->inherits(&RegExpImp::info)) + return RegExp; + else if (object->inherits(&RuntimeObjectImp::info)) + return QObj; + return Object; + } + + return String; // I don't know. +} + +QVariant convertValueToQVariant(ExecState* exec, JSValue* value, QMetaType::Type hint, int *distance) +{ + // check magic pointer values before dereferencing value + if (value == jsNaN() || value == jsUndefined()) { + if (distance) + *distance = -1; + return QVariant(); + } + + JSLock lock; + JSRealType type = valueRealType(exec, value); + if (hint == QMetaType::Void) { + switch(type) { + case Number: + hint = QMetaType::Double; + break; + case Boolean: + hint = QMetaType::Bool; + break; + case String: + default: + hint = QMetaType::QString; + break; + case Date: + hint = QMetaType::QDateTime; + break; + case RegExp: + hint = QMetaType::QRegExp; + break; + case QObj: + hint = QMetaType::QObjectStar; + break; + case Array: + hint = QMetaType::QVariantList; + break; + } + } + + if (value == jsNull() + && hint != QMetaType::QObjectStar + && hint != QMetaType::VoidStar) { + if (distance) + *distance = -1; + return QVariant(); + } + + QVariant ret; + int dist = -1; + switch (hint) { + case QMetaType::Bool: + ret = QVariant(value->toBoolean(exec)); + if (type == Boolean) + dist = 0; + else + dist = 10; + break; + + case QMetaType::Int: + case QMetaType::UInt: + case QMetaType::Long: + case QMetaType::ULong: + case QMetaType::LongLong: + case QMetaType::ULongLong: + case QMetaType::Short: + case QMetaType::UShort: + case QMetaType::Float: + case QMetaType::Double: + ret = QVariant(value->toNumber(exec)); + ret.convert((QVariant::Type)hint); + if (type == Number) { + switch (hint) { + case QMetaType::Double: + dist = 0; + break; + case QMetaType::Float: + dist = 1; + break; + case QMetaType::LongLong: + case QMetaType::ULongLong: + dist = 2; + break; + case QMetaType::Long: + case QMetaType::ULong: + dist = 3; + break; + case QMetaType::Int: + case QMetaType::UInt: + dist = 4; + break; + case QMetaType::Short: + case QMetaType::UShort: + dist = 5; + break; + break; + default: + dist = 10; + break; + } + } else { + dist = 10; + } + break; + + case QMetaType::QChar: + if (type == Number || type == Boolean) { + ret = QVariant(QChar((ushort)value->toNumber(exec))); + if (type == Boolean) + dist = 3; + else + dist = 6; + } else { + UString str = value->toString(exec); + ret = QVariant(QChar(str.size() ? *(const ushort*)str.rep()->data() : 0)); + if (type == String) + dist = 3; + else + dist = 10; + } + break; + + case QMetaType::QString: { + UString ustring = value->toString(exec); + ret = QVariant(QString::fromUtf16((const ushort*)ustring.rep()->data(),ustring.size())); + if (type == String) + dist = 0; + else + dist = 10; + break; + } + + case QMetaType::QVariantMap: + if (type == Object || type == Array) { + // Enumerate the contents of the object + JSObject* object = value->toObject(exec); + + PropertyNameArray properties; + object->getPropertyNames(exec, properties); + PropertyNameArray::const_iterator it = properties.begin(); + + QVariantMap result; + int objdist = 0; + while(it != properties.end()) { + if (object->propertyIsEnumerable(exec, *it)) { + JSValue* val = object->get(exec, *it); + QVariant v = convertValueToQVariant(exec, val, QMetaType::Void, &objdist); + if (objdist >= 0) { + UString ustring = (*it).ustring(); + QString id = QString::fromUtf16((const ushort*)ustring.rep()->data(),ustring.size()); + result.insert(id, v); + } + } + ++it; + } + dist = 1; + ret = QVariant(result); + } + break; + + case QMetaType::QVariantList: + if (type == Array) { + JSObject* object = value->toObject(exec); + ArrayInstance* array = static_cast<ArrayInstance*>(object); + + QVariantList result; + int len = array->getLength(); + int objdist = 0; + for (int i = 0; i < len; ++i) { + JSValue *val = array->getItem(i); + result.append(convertValueToQVariant(exec, val, QMetaType::Void, &objdist)); + if (objdist == -1) + break; // Failed converting a list entry, so fail the array + } + if (objdist != -1) { + dist = 5; + ret = QVariant(result); + } + } else { + // Make a single length array + QVariantList result; + int objdist; + result.append(convertValueToQVariant(exec, value, QMetaType::Void, &objdist)); + if (objdist != -1) { + ret = QVariant(result); + dist = 10; + } + } + break; + + case QMetaType::QStringList: { + if (type == Array) { + JSObject* object = value->toObject(exec); + ArrayInstance* array = static_cast<ArrayInstance*>(object); + + QStringList result; + int len = array->getLength(); + for (int i = 0; i < len; ++i) { + JSValue* val = array->getItem(i); + UString ustring = val->toString(exec); + QString qstring = QString::fromUtf16((const ushort*)ustring.rep()->data(),ustring.size()); + + result.append(qstring); + } + dist = 5; + ret = QVariant(result); + } else { + // Make a single length array + UString ustring = value->toString(exec); + QString qstring = QString::fromUtf16((const ushort*)ustring.rep()->data(),ustring.size()); + QStringList result; + result.append(qstring); + ret = QVariant(result); + dist = 10; + } + break; + } + + case QMetaType::QByteArray: { + UString ustring = value->toString(exec); + ret = QVariant(QString::fromUtf16((const ushort*)ustring.rep()->data(),ustring.size()).toLatin1()); + if (type == String) + dist = 5; + else + dist = 10; + break; + } + + case QMetaType::QDateTime: + case QMetaType::QDate: + case QMetaType::QTime: + if (type == Date) { + JSObject* object = value->toObject(exec); + DateInstance* date = static_cast<DateInstance*>(object); + GregorianDateTime gdt; + date->getUTCTime(gdt); + if (hint == QMetaType::QDateTime) { + ret = QDateTime(QDate(gdt.year + 1900, gdt.month + 1, gdt.monthDay), QTime(gdt.hour, gdt.minute, gdt.second), Qt::UTC); + dist = 0; + } else if (hint == QMetaType::QDate) { + ret = QDate(gdt.year + 1900, gdt.month + 1, gdt.monthDay); + dist = 1; + } else { + ret = QTime(gdt.hour + 1900, gdt.minute, gdt.second); + dist = 2; + } + } else if (type == Number) { + double b = value->toNumber(exec); + GregorianDateTime gdt; + msToGregorianDateTime(b, true, gdt); + if (hint == QMetaType::QDateTime) { + ret = QDateTime(QDate(gdt.year + 1900, gdt.month + 1, gdt.monthDay), QTime(gdt.hour, gdt.minute, gdt.second), Qt::UTC); + dist = 6; + } else if (hint == QMetaType::QDate) { + ret = QDate(gdt.year + 1900, gdt.month + 1, gdt.monthDay); + dist = 8; + } else { + ret = QTime(gdt.hour, gdt.minute, gdt.second); + dist = 10; + } + } else if (type == String) { + UString ustring = value->toString(exec); + QString qstring = QString::fromUtf16((const ushort*)ustring.rep()->data(),ustring.size()); + + if (hint == QMetaType::QDateTime) { + QDateTime dt = QDateTime::fromString(qstring, Qt::ISODate); + if (!dt.isValid()) + dt = QDateTime::fromString(qstring, Qt::TextDate); + if (!dt.isValid()) + dt = QDateTime::fromString(qstring, Qt::SystemLocaleDate); + if (!dt.isValid()) + dt = QDateTime::fromString(qstring, Qt::LocaleDate); + if (dt.isValid()) { + ret = dt; + dist = 2; + } + } else if (hint == QMetaType::QDate) { + QDate dt = QDate::fromString(qstring, Qt::ISODate); + if (!dt.isValid()) + dt = QDate::fromString(qstring, Qt::TextDate); + if (!dt.isValid()) + dt = QDate::fromString(qstring, Qt::SystemLocaleDate); + if (!dt.isValid()) + dt = QDate::fromString(qstring, Qt::LocaleDate); + if (dt.isValid()) { + ret = dt; + dist = 3; + } + } else { + QTime dt = QTime::fromString(qstring, Qt::ISODate); + if (!dt.isValid()) + dt = QTime::fromString(qstring, Qt::TextDate); + if (!dt.isValid()) + dt = QTime::fromString(qstring, Qt::SystemLocaleDate); + if (!dt.isValid()) + dt = QTime::fromString(qstring, Qt::LocaleDate); + if (dt.isValid()) { + ret = dt; + dist = 3; + } + } + } + break; + + case QMetaType::QRegExp: + if (type == RegExp) { +/* JSObject *object = value->toObject(exec); + RegExpImp *re = static_cast<RegExpImp*>(object); +*/ + // Attempt to convert.. a bit risky + UString ustring = value->toString(exec); + QString qstring = QString::fromUtf16((const ushort*)ustring.rep()->data(),ustring.size()); + + // this is of the form '/xxxxxx/i' + int firstSlash = qstring.indexOf('/'); + int lastSlash = qstring.lastIndexOf('/'); + if (firstSlash >=0 && lastSlash > firstSlash) { + QRegExp realRe; + + realRe.setPattern(qstring.mid(firstSlash + 1, lastSlash - firstSlash - 1)); + + if (qstring.mid(lastSlash + 1).contains('i')) + realRe.setCaseSensitivity(Qt::CaseInsensitive); + + ret = qVariantFromValue(realRe); + dist = 0; + } else { + qConvDebug() << "couldn't parse a JS regexp"; + } + } else if (type == String) { + UString ustring = value->toString(exec); + QString qstring = QString::fromUtf16((const ushort*)ustring.rep()->data(),ustring.size()); + + QRegExp re(qstring); + if (re.isValid()) { + ret = qVariantFromValue(re); + dist = 10; + } + } + break; + + case QMetaType::QObjectStar: + if (type == QObj) { + JSObject* object = value->toObject(exec); + QtInstance* qtinst = static_cast<QtInstance*>(Instance::getInstance(object, Instance::QtLanguage)); + if (qtinst) { + if (qtinst->getObject()) { + qConvDebug() << "found instance, with object:" << (void*) qtinst->getObject(); + ret = qVariantFromValue(qtinst->getObject()); + qConvDebug() << ret; + dist = 0; + } else { + qConvDebug() << "can't convert deleted qobject"; + } + } else { + qConvDebug() << "wasn't a qtinstance"; + } + } else if (type == Null) { + QObject* nullobj = 0; + ret = qVariantFromValue(nullobj); + dist = 0; + } else { + qConvDebug() << "previous type was not an object:" << type; + } + break; + + case QMetaType::VoidStar: + if (type == QObj) { + JSObject* object = value->toObject(exec); + QtInstance* qtinst = static_cast<QtInstance*>(Instance::getInstance(object, Instance::QtLanguage)); + if (qtinst) { + if (qtinst->getObject()) { + qConvDebug() << "found instance, with object:" << (void*) qtinst->getObject(); + ret = qVariantFromValue((void *)qtinst->getObject()); + qConvDebug() << ret; + dist = 0; + } else { + qConvDebug() << "can't convert deleted qobject"; + } + } else { + qConvDebug() << "wasn't a qtinstance"; + } + } else if (type == Null) { + ret = qVariantFromValue((void*)0); + dist = 0; + } else if (type == Number) { + // I don't think that converting a double to a pointer is a wise + // move. Except maybe 0. + qConvDebug() << "got number for void * - not converting, seems unsafe:" << value->toNumber(exec); + } else { + qConvDebug() << "void* - unhandled type" << type; + } + break; + + default: + // Non const type ids + if (hint == (QMetaType::Type) qMetaTypeId<QObjectList>()) + { + if (type == Array) { + JSObject* object = value->toObject(exec); + ArrayInstance* array = static_cast<ArrayInstance *>(object); + + QObjectList result; + int len = array->getLength(); + for (int i = 0; i < len; ++i) { + JSValue *val = array->getItem(i); + int itemdist = -1; + QVariant item = convertValueToQVariant(exec, val, QMetaType::QObjectStar, &itemdist); + if (itemdist >= 0) + result.append(item.value<QObject*>()); + else + break; + } + // If we didn't fail conversion + if (result.count() == len) { + dist = 5; + ret = QVariant::fromValue(result); + } else { + qConvDebug() << "type conversion failed (wanted" << len << ", got " << result.count() << ")"; + } + } else { + // Make a single length array + QObjectList result; + int itemdist = -1; + QVariant item = convertValueToQVariant(exec, value, QMetaType::QObjectStar, &itemdist); + if (itemdist >= 0) { + result.append(item.value<QObject*>()); + dist = 10; + ret = QVariant::fromValue(result); + } + } + break; + } else if (hint == (QMetaType::Type) qMetaTypeId<QList<int> >()) { + if (type == Array) { + JSObject* object = value->toObject(exec); + ArrayInstance* array = static_cast<ArrayInstance *>(object); + + QList<int> result; + int len = array->getLength(); + for (int i = 0; i < len; ++i) { + JSValue* val = array->getItem(i); + int itemdist = -1; + QVariant item = convertValueToQVariant(exec, val, QMetaType::Int, &itemdist); + if (itemdist >= 0) + result.append(item.value<int>()); + else + break; + } + // If we didn't fail conversion + if (result.count() == len) { + dist = 5; + ret = QVariant::fromValue(result); + } else { + qConvDebug() << "type conversion failed (wanted" << len << ", got " << result.count() << ")"; + } + } else { + // Make a single length array + QList<int> result; + int itemdist = -1; + QVariant item = convertValueToQVariant(exec, value, QMetaType::Int, &itemdist); + if (itemdist >= 0) { + result.append(item.value<int>()); + dist = 10; + ret = QVariant::fromValue(result); + } + } + break; + } else if (hint == (QMetaType::Type) qMetaTypeId<QVariant>()) { + // Well.. we can do anything... just recurse with the autodetect flag + ret = convertValueToQVariant(exec, value, QMetaType::Void, distance); + dist = 10; + break; + } + + dist = 10; + break; + } + + if (!ret.isValid()) + dist = -1; + if (distance) + *distance = dist; + + return ret; +} + +JSValue* convertQVariantToValue(ExecState* exec, PassRefPtr<RootObject> root, const QVariant& variant) +{ + // Variants with QObject * can be isNull but not a null pointer + // An empty QString variant is also null + QMetaType::Type type = (QMetaType::Type) variant.userType(); + if (variant.isNull() && + type != QMetaType::QObjectStar && + type != QMetaType::VoidStar && + type != QMetaType::QWidgetStar && + type != QMetaType::QString) { + return jsNull(); + } + + JSLock lock; + + if (type == QMetaType::Bool) + return jsBoolean(variant.toBool()); + + if (type == QMetaType::Int || + type == QMetaType::UInt || + type == QMetaType::Long || + type == QMetaType::ULong || + type == QMetaType::LongLong || + type == QMetaType::ULongLong || + type == QMetaType::Short || + type == QMetaType::UShort || + type == QMetaType::Float || + type == QMetaType::Double) + return jsNumber(variant.toDouble()); + + if (type == QMetaType::QRegExp) { + QRegExp re = variant.value<QRegExp>(); + + if (re.isValid()) { + RegExpObjectImp* regExpObj = static_cast<RegExpObjectImp*>(exec->lexicalGlobalObject()->regExpConstructor()); + List args; + UString uflags; + + if (re.caseSensitivity() == Qt::CaseInsensitive) + uflags = "i"; // ### Can't do g or m + UString ustring((KJS::UChar*)re.pattern().utf16(), re.pattern().length()); + args.append(jsString(ustring)); + args.append(jsString(uflags)); + return regExpObj->construct(exec, args); + } + } + + if (type == QMetaType::QDateTime || + type == QMetaType::QDate || + type == QMetaType::QTime) { + DateObjectImp *dateObj = static_cast<DateObjectImp*>(exec->lexicalGlobalObject()->dateConstructor()); + List args; + + QDate date = QDate::currentDate(); + QTime time(0,0,0); // midnight + + if (type == QMetaType::QDate) + date = variant.value<QDate>(); + else if (type == QMetaType::QTime) + time = variant.value<QTime>(); + else { + QDateTime dt = variant.value<QDateTime>().toLocalTime(); + date = dt.date(); + time = dt.time(); + } + + // Dates specified this way are in local time (we convert DateTimes above) + args.append(jsNumber(date.year())); + args.append(jsNumber(date.month() - 1)); + args.append(jsNumber(date.day())); + args.append(jsNumber(time.hour())); + args.append(jsNumber(time.minute())); + args.append(jsNumber(time.second())); + args.append(jsNumber(time.msec())); + return dateObj->construct(exec, args); + } + + if (type == QMetaType::QByteArray) { + QByteArray ba = variant.value<QByteArray>(); + UString ustring(ba.constData()); + return jsString(ustring); + } + + if (type == QMetaType::QObjectStar || type == QMetaType::QWidgetStar) { + QObject* obj = variant.value<QObject*>(); + return Instance::createRuntimeObject(Instance::QtLanguage, obj, root); + } + + if (type == QMetaType::QVariantMap) { + // create a new object, and stuff properties into it + JSObject* ret = new JSObject(exec->lexicalGlobalObject()->objectPrototype()); + QVariantMap map = variant.value<QVariantMap>(); + QVariantMap::const_iterator i = map.constBegin(); + while (i != map.constEnd()) { + QString s = i.key(); + JSValue* val = convertQVariantToValue(exec, root, i.value()); + if (val) + ret->put(exec, Identifier((const UChar *)s.constData(), s.length()), val); + // ### error case? + ++i; + } + + return ret; + } + + // List types + if (type == QMetaType::QVariantList) { + QVariantList vl = variant.toList(); + return new RuntimeArray(exec, new QtArray<QVariant>(vl, QMetaType::Void, root)); + } else if (type == QMetaType::QStringList) { + QStringList sl = variant.value<QStringList>(); + return new RuntimeArray(exec, new QtArray<QString>(sl, QMetaType::QString, root)); + } else if (type == (QMetaType::Type) qMetaTypeId<QObjectList>()) { + QObjectList ol= variant.value<QObjectList>(); + return new RuntimeArray(exec, new QtArray<QObject*>(ol, QMetaType::QObjectStar, root)); + } else if (type == (QMetaType::Type)qMetaTypeId<QList<int> >()) { + QList<int> il= variant.value<QList<int> >(); + return new RuntimeArray(exec, new QtArray<int>(il, QMetaType::Int, root)); + } + + if (type == (QMetaType::Type)qMetaTypeId<QVariant>()) { + QVariant real = variant.value<QVariant>(); + qConvDebug() << "real variant is:" << real; + return convertQVariantToValue(exec, root, real); + } + + qConvDebug() << "fallback path for" << variant << variant.userType(); + + QString string = variant.toString(); + UString ustring((KJS::UChar*)string.utf16(), string.length()); + return jsString(ustring); +} + +// =============== + +// Qt-like macros +#define QW_D(Class) Class##Data* d = d_func() +#define QW_DS(Class,Instance) Class##Data* d = Instance->d_func() + +QtRuntimeMethod::QtRuntimeMethod(QtRuntimeMethodData* dd, ExecState *exec, const Identifier &ident, PassRefPtr<QtInstance> inst) + : InternalFunctionImp (static_cast<FunctionPrototype*>(exec->lexicalGlobalObject()->functionPrototype()), ident) + , d_ptr(dd) +{ + QW_D(QtRuntimeMethod); + d->m_instance = inst; +} + +QtRuntimeMethod::~QtRuntimeMethod() +{ + delete d_ptr; +} + +CodeType QtRuntimeMethod::codeType() const +{ + return FunctionCode; +} + +Completion QtRuntimeMethod::execute(ExecState*) +{ + return Completion(Normal, jsUndefined()); +} + +// =============== + +QtRuntimeMethodData::~QtRuntimeMethodData() +{ +} + +QtRuntimeMetaMethodData::~QtRuntimeMetaMethodData() +{ + +} + +QtRuntimeConnectionMethodData::~QtRuntimeConnectionMethodData() +{ + +} + +// =============== + +// Type conversion metadata (from QtScript originally) +class QtMethodMatchType +{ +public: + enum Kind { + Invalid, + Variant, + MetaType, + Unresolved, + MetaEnum + }; + + + QtMethodMatchType() + : m_kind(Invalid) { } + + Kind kind() const + { return m_kind; } + + QMetaType::Type typeId() const; + + bool isValid() const + { return (m_kind != Invalid); } + + bool isVariant() const + { return (m_kind == Variant); } + + bool isMetaType() const + { return (m_kind == MetaType); } + + bool isUnresolved() const + { return (m_kind == Unresolved); } + + bool isMetaEnum() const + { return (m_kind == MetaEnum); } + + QByteArray name() const; + + int enumeratorIndex() const + { Q_ASSERT(isMetaEnum()); return m_typeId; } + + static QtMethodMatchType variant() + { return QtMethodMatchType(Variant); } + + static QtMethodMatchType metaType(int typeId, const QByteArray &name) + { return QtMethodMatchType(MetaType, typeId, name); } + + static QtMethodMatchType metaEnum(int enumIndex, const QByteArray &name) + { return QtMethodMatchType(MetaEnum, enumIndex, name); } + + static QtMethodMatchType unresolved(const QByteArray &name) + { return QtMethodMatchType(Unresolved, /*typeId=*/0, name); } + +private: + QtMethodMatchType(Kind kind, int typeId = 0, const QByteArray &name = QByteArray()) + : m_kind(kind), m_typeId(typeId), m_name(name) { } + + Kind m_kind; + int m_typeId; + QByteArray m_name; +}; + +QMetaType::Type QtMethodMatchType::typeId() const +{ + if (isVariant()) + return (QMetaType::Type) QMetaType::type("QVariant"); + return (QMetaType::Type) (isMetaEnum() ? QMetaType::Int : m_typeId); +} + +QByteArray QtMethodMatchType::name() const +{ + if (!m_name.isEmpty()) + return m_name; + else if (m_kind == Variant) + return "QVariant"; + return QByteArray(); +} + +struct QtMethodMatchData +{ + int matchDistance; + int index; + QVector<QtMethodMatchType> types; + QVarLengthArray<QVariant, 10> args; + + QtMethodMatchData(int dist, int idx, QVector<QtMethodMatchType> typs, + const QVarLengthArray<QVariant, 10> &as) + : matchDistance(dist), index(idx), types(typs), args(as) { } + QtMethodMatchData() + : index(-1) { } + + bool isValid() const + { return (index != -1); } + + int firstUnresolvedIndex() const + { + for (int i=0; i < types.count(); i++) { + if (types.at(i).isUnresolved()) + return i; + } + return -1; + } +}; + +static int indexOfMetaEnum(const QMetaObject *meta, const QByteArray &str) +{ + QByteArray scope; + QByteArray name; + int scopeIdx = str.indexOf("::"); + if (scopeIdx != -1) { + scope = str.left(scopeIdx); + name = str.mid(scopeIdx + 2); + } else { + name = str; + } + for (int i = meta->enumeratorCount() - 1; i >= 0; --i) { + QMetaEnum m = meta->enumerator(i); + if ((m.name() == name)/* && (scope.isEmpty() || (m.scope() == scope))*/) + return i; + } + return -1; +} + +// Helper function for resolving methods +// Largely based on code in QtScript for compatibility reasons +static int findMethodIndex(ExecState* exec, + const QMetaObject* meta, + const QByteArray& signature, + bool allowPrivate, + const List& jsArgs, + QVarLengthArray<QVariant, 10> &vars, + void** vvars, + JSObject **pError) +{ + QList<int> matchingIndices; + + bool overloads = !signature.contains('('); + + int count = meta->methodCount(); + for (int i = count - 1; i >= 0; --i) { + const QMetaMethod m = meta->method(i); + + // Don't choose private methods + if (m.access() == QMetaMethod::Private && !allowPrivate) + continue; + + // try and find all matching named methods + if (m.signature() == signature) + matchingIndices.append(i); + else if (overloads) { + QByteArray rawsignature = m.signature(); + rawsignature.truncate(rawsignature.indexOf('(')); + if (rawsignature == signature) + matchingIndices.append(i); + } + } + + int chosenIndex = -1; + *pError = 0; + QVector<QtMethodMatchType> chosenTypes; + + QVarLengthArray<QVariant, 10> args; + QVector<QtMethodMatchData> candidates; + QVector<QtMethodMatchData> unresolved; + QVector<int> tooFewArgs; + QVector<int> conversionFailed; + + foreach(int index, matchingIndices) { + QMetaMethod method = meta->method(index); + + QVector<QtMethodMatchType> types; + bool unresolvedTypes = false; + + // resolve return type + QByteArray returnTypeName = method.typeName(); + int rtype = QMetaType::type(returnTypeName); + if ((rtype == 0) && !returnTypeName.isEmpty()) { + if (returnTypeName == "QVariant") { + types.append(QtMethodMatchType::variant()); + } else if (returnTypeName.endsWith('*')) { + types.append(QtMethodMatchType::metaType(QMetaType::VoidStar, returnTypeName)); + } else { + int enumIndex = indexOfMetaEnum(meta, returnTypeName); + if (enumIndex != -1) + types.append(QtMethodMatchType::metaEnum(enumIndex, returnTypeName)); + else { + unresolvedTypes = true; + types.append(QtMethodMatchType::unresolved(returnTypeName)); + } + } + } else { + if (returnTypeName == "QVariant") + types.append(QtMethodMatchType::variant()); + else + types.append(QtMethodMatchType::metaType(rtype, returnTypeName)); + } + + // resolve argument types + QList<QByteArray> parameterTypeNames = method.parameterTypes(); + for (int i = 0; i < parameterTypeNames.count(); ++i) { + QByteArray argTypeName = parameterTypeNames.at(i); + int atype = QMetaType::type(argTypeName); + if (atype == 0) { + if (argTypeName == "QVariant") { + types.append(QtMethodMatchType::variant()); + } else { + int enumIndex = indexOfMetaEnum(meta, argTypeName); + if (enumIndex != -1) + types.append(QtMethodMatchType::metaEnum(enumIndex, argTypeName)); + else { + unresolvedTypes = true; + types.append(QtMethodMatchType::unresolved(argTypeName)); + } + } + } else { + if (argTypeName == "QVariant") + types.append(QtMethodMatchType::variant()); + else + types.append(QtMethodMatchType::metaType(atype, argTypeName)); + } + } + + if (jsArgs.size() < (types.count() - 1)) { + qMatchDebug() << "Match:too few args for" << method.signature(); + tooFewArgs.append(index); + continue; + } + + if (unresolvedTypes) { + qMatchDebug() << "Match:unresolved arg types for" << method.signature(); + // remember it so we can give an error message later, if necessary + unresolved.append(QtMethodMatchData(/*matchDistance=*/INT_MAX, index, + types, QVarLengthArray<QVariant, 10>())); + continue; + } + + // Now convert arguments + if (args.count() != types.count()) + args.resize(types.count()); + + QtMethodMatchType retType = types[0]; + args[0] = QVariant(retType.typeId(), (void *)0); // the return value + + bool converted = true; + int matchDistance = 0; + for (int i = 0; converted && i < types.count() - 1; ++i) { + JSValue* arg = i < jsArgs.size() ? jsArgs[i] : jsUndefined(); + + int argdistance = -1; + QVariant v = convertValueToQVariant(exec, arg, types.at(i+1).typeId(), &argdistance); + if (argdistance >= 0) { + matchDistance += argdistance; + args[i+1] = v; + } else { + qMatchDebug() << "failed to convert argument " << i << "type" << types.at(i+1).typeId() << QMetaType::typeName(types.at(i+1).typeId()); + converted = false; + } + } + + qMatchDebug() << "Match: " << method.signature() << (converted ? "converted":"failed to convert") << "distance " << matchDistance; + + if (converted) { + if ((jsArgs.size() == types.count() - 1) + && (matchDistance == 0)) { + // perfect match, use this one + chosenIndex = index; + break; + } else { + QtMethodMatchData metaArgs(matchDistance, index, types, args); + if (candidates.isEmpty()) { + candidates.append(metaArgs); + } else { + QtMethodMatchData otherArgs = candidates.at(0); + if ((args.count() > otherArgs.args.count()) + || ((args.count() == otherArgs.args.count()) + && (matchDistance <= otherArgs.matchDistance))) { + candidates.prepend(metaArgs); + } else { + candidates.append(metaArgs); + } + } + } + } else { + conversionFailed.append(index); + } + + if (!overloads) + break; + } + + if (chosenIndex == -1 && candidates.count() == 0) { + // No valid functions at all - format an error message + if (!conversionFailed.isEmpty()) { + QString message = QString::fromLatin1("incompatible type of argument(s) in call to %0(); candidates were\n") + .arg(QLatin1String(signature)); + for (int i = 0; i < conversionFailed.size(); ++i) { + if (i > 0) + message += QLatin1String("\n"); + QMetaMethod mtd = meta->method(conversionFailed.at(i)); + message += QString::fromLatin1(" %0").arg(QString::fromLatin1(mtd.signature())); + } + *pError = throwError(exec, TypeError, message.toLatin1().constData()); + } else if (!unresolved.isEmpty()) { + QtMethodMatchData argsInstance = unresolved.first(); + int unresolvedIndex = argsInstance.firstUnresolvedIndex(); + Q_ASSERT(unresolvedIndex != -1); + QtMethodMatchType unresolvedType = argsInstance.types.at(unresolvedIndex); + QString message = QString::fromLatin1("cannot call %0(): unknown type `%1'") + .arg(QString::fromLatin1(signature)) + .arg(QLatin1String(unresolvedType.name())); + *pError = throwError(exec, TypeError, message.toLatin1().constData()); + } else { + QString message = QString::fromLatin1("too few arguments in call to %0(); candidates are\n") + .arg(QLatin1String(signature)); + for (int i = 0; i < tooFewArgs.size(); ++i) { + if (i > 0) + message += QLatin1String("\n"); + QMetaMethod mtd = meta->method(tooFewArgs.at(i)); + message += QString::fromLatin1(" %0").arg(QString::fromLatin1(mtd.signature())); + } + *pError = throwError(exec, SyntaxError, message.toLatin1().constData()); + } + } + + if (chosenIndex == -1 && candidates.count() > 0) { + QtMethodMatchData metaArgs = candidates.at(0); + if ((candidates.size() > 1) + && (metaArgs.args.count() == candidates.at(1).args.count()) + && (metaArgs.matchDistance == candidates.at(1).matchDistance)) { + // ambiguous call + QString message = QString::fromLatin1("ambiguous call of overloaded function %0(); candidates were\n") + .arg(QLatin1String(signature)); + for (int i = 0; i < candidates.size(); ++i) { + if (i > 0) + message += QLatin1String("\n"); + QMetaMethod mtd = meta->method(candidates.at(i).index); + message += QString::fromLatin1(" %0").arg(QString::fromLatin1(mtd.signature())); + } + *pError = throwError(exec, TypeError, message.toLatin1().constData()); + } else { + chosenIndex = metaArgs.index; + args = metaArgs.args; + } + } + + if (chosenIndex != -1) { + /* Copy the stuff over */ + int i; + vars.resize(args.count()); + for (i=0; i < args.count(); i++) { + vars[i] = args[i]; + vvars[i] = vars[i].data(); + } + } + + return chosenIndex; +} + +// Signals are not fuzzy matched as much as methods +static int findSignalIndex(const QMetaObject* meta, int initialIndex, QByteArray signature) +{ + int index = initialIndex; + QMetaMethod method = meta->method(index); + bool overloads = !signature.contains('('); + if (overloads && (method.attributes() & QMetaMethod::Cloned)) { + // find the most general method + do { + method = meta->method(--index); + } while (method.attributes() & QMetaMethod::Cloned); + } + return index; +} + +QtRuntimeMetaMethod::QtRuntimeMetaMethod(ExecState* exec, const Identifier& ident, PassRefPtr<QtInstance> inst, int index, const QByteArray& signature, bool allowPrivate) + : QtRuntimeMethod (new QtRuntimeMetaMethodData(), exec, ident, inst) +{ + QW_D(QtRuntimeMetaMethod); + d->m_signature = signature; + d->m_index = index; + d->m_connect = 0; + d->m_disconnect = 0; + d->m_allowPrivate = allowPrivate; +} + +void QtRuntimeMetaMethod::mark() +{ + QtRuntimeMethod::mark(); + QW_D(QtRuntimeMetaMethod); + if (d->m_connect) + d->m_connect->mark(); + if (d->m_disconnect) + d->m_disconnect->mark(); +} + +JSValue* QtRuntimeMetaMethod::callAsFunction(ExecState* exec, JSObject*, const List& args) +{ + QW_D(QtRuntimeMetaMethod); + + // We're limited to 10 args + if (args.size() > 10) + return jsUndefined(); + + // We have to pick a method that matches.. + JSLock lock; + + QObject *obj = d->m_instance->getObject(); + if (obj) { + QVarLengthArray<QVariant, 10> vargs; + void *qargs[11]; + + int methodIndex; + JSObject* errorObj = 0; + if ((methodIndex = findMethodIndex(exec, obj->metaObject(), d->m_signature, d->m_allowPrivate, args, vargs, (void **)qargs, &errorObj)) != -1) { + if (obj->qt_metacall(QMetaObject::InvokeMetaMethod, methodIndex, qargs) >= 0) + return jsUndefined(); + + if (vargs[0].isValid()) + return convertQVariantToValue(exec, d->m_instance->rootObject(), vargs[0]); + } + + if (errorObj) + return errorObj; + } else { + return throwError(exec, GeneralError, "cannot call function of deleted QObject"); + } + + // void functions return undefined + return jsUndefined(); +} + +bool QtRuntimeMetaMethod::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) +{ + if (propertyName == "connect") { + slot.setCustom(this, connectGetter); + return true; + } else if (propertyName == "disconnect") { + slot.setCustom(this, disconnectGetter); + return true; + } else if (propertyName == exec->propertyNames().length) { + slot.setCustom(this, lengthGetter); + return true; + } + + return QtRuntimeMethod::getOwnPropertySlot(exec, propertyName, slot); +} + +JSValue *QtRuntimeMetaMethod::lengthGetter(ExecState*, JSObject*, const Identifier&, const PropertySlot&) +{ + // QtScript always returns 0 + return jsNumber(0); +} + +JSValue *QtRuntimeMetaMethod::connectGetter(ExecState* exec, JSObject*, const Identifier& ident, const PropertySlot& slot) +{ + QtRuntimeMetaMethod* thisObj = static_cast<QtRuntimeMetaMethod*>(slot.slotBase()); + QW_DS(QtRuntimeMetaMethod, thisObj); + + if (!d->m_connect) + d->m_connect = new QtRuntimeConnectionMethod(exec, ident, true, d->m_instance, d->m_index, d->m_signature); + return d->m_connect; +} + +JSValue* QtRuntimeMetaMethod::disconnectGetter(ExecState* exec, JSObject*, const Identifier& ident, const PropertySlot& slot) +{ + QtRuntimeMetaMethod* thisObj = static_cast<QtRuntimeMetaMethod*>(slot.slotBase()); + QW_DS(QtRuntimeMetaMethod, thisObj); + + if (!d->m_disconnect) + d->m_disconnect = new QtRuntimeConnectionMethod(exec, ident, false, d->m_instance, d->m_index, d->m_signature); + return d->m_disconnect; +} + +// =============== + +QMultiMap<QObject*, QtConnectionObject*> QtRuntimeConnectionMethod::connections; + +QtRuntimeConnectionMethod::QtRuntimeConnectionMethod(ExecState* exec, const Identifier& ident, bool isConnect, PassRefPtr<QtInstance> inst, int index, const QByteArray& signature) + : QtRuntimeMethod (new QtRuntimeConnectionMethodData(), exec, ident, inst) +{ + QW_D(QtRuntimeConnectionMethod); + + d->m_signature = signature; + d->m_index = index; + d->m_isConnect = isConnect; +} + +JSValue *QtRuntimeConnectionMethod::callAsFunction(ExecState* exec, JSObject*, const List& args) +{ + QW_D(QtRuntimeConnectionMethod); + + JSLock lock; + + QObject* sender = d->m_instance->getObject(); + + if (sender) { + + JSObject* thisObject = exec->lexicalGlobalObject(); + JSObject* funcObject = 0; + + // QtScript checks signalness first, arguments second + int signalIndex = -1; + + // Make sure the initial index is a signal + QMetaMethod m = sender->metaObject()->method(d->m_index); + if (m.methodType() == QMetaMethod::Signal) + signalIndex = findSignalIndex(sender->metaObject(), d->m_index, d->m_signature); + + if (signalIndex != -1) { + if (args.size() == 1) { + funcObject = args[0]->toObject(exec); + if (!funcObject->implementsCall()) { + if (d->m_isConnect) + return throwError(exec, TypeError, "QtMetaMethod.connect: target is not a function"); + else + return throwError(exec, TypeError, "QtMetaMethod.disconnect: target is not a function"); + } + } else if (args.size() >= 2) { + if (args[0]->type() == ObjectType) { + thisObject = args[0]->toObject(exec); + + // Get the actual function to call + JSObject *asObj = args[1]->toObject(exec); + if (asObj->implementsCall()) { + // Function version + funcObject = asObj; + } else { + // Convert it to a string + UString funcName = args[1]->toString(exec); + Identifier funcIdent(funcName); + + // ### DropAllLocks + // This is resolved at this point in QtScript + JSValue* val = thisObject->get(exec, funcIdent); + JSObject* asFuncObj = val->toObject(exec); + + if (asFuncObj->implementsCall()) { + funcObject = asFuncObj; + } else { + if (d->m_isConnect) + return throwError(exec, TypeError, "QtMetaMethod.connect: target is not a function"); + else + return throwError(exec, TypeError, "QtMetaMethod.disconnect: target is not a function"); + } + } + } else { + if (d->m_isConnect) + return throwError(exec, TypeError, "QtMetaMethod.connect: thisObject is not an object"); + else + return throwError(exec, TypeError, "QtMetaMethod.disconnect: thisObject is not an object"); + } + } else { + if (d->m_isConnect) + return throwError(exec, GeneralError, "QtMetaMethod.connect: no arguments given"); + else + return throwError(exec, GeneralError, "QtMetaMethod.disconnect: no arguments given"); + } + + if (d->m_isConnect) { + // to connect, we need: + // target object [from ctor] + // target signal index etc. [from ctor] + // receiver function [from arguments] + // receiver this object [from arguments] + + QtConnectionObject* conn = new QtConnectionObject(d->m_instance, signalIndex, thisObject, funcObject); + bool ok = QMetaObject::connect(sender, signalIndex, conn, conn->metaObject()->methodOffset()); + if (!ok) { + delete conn; + QString msg = QString("QtMetaMethod.connect: failed to connect to %1::%2()") + .arg(sender->metaObject()->className()) + .arg(QLatin1String(d->m_signature)); + return throwError(exec, GeneralError, msg.toLatin1().constData()); + } + else { + // Store connection + connections.insert(sender, conn); + } + } else { + // Now to find our previous connection object. Hmm. + QList<QtConnectionObject*> conns = connections.values(sender); + bool ret = false; + + foreach(QtConnectionObject* conn, conns) { + // Is this the right connection? + if (conn->match(sender, signalIndex, thisObject, funcObject)) { + // Yep, disconnect it + QMetaObject::disconnect(sender, signalIndex, conn, conn->metaObject()->methodOffset()); + delete conn; // this will also remove it from the map + ret = true; + break; + } + } + + if (!ret) { + QString msg = QString("QtMetaMethod.disconnect: failed to disconnect from %1::%2()") + .arg(sender->metaObject()->className()) + .arg(QLatin1String(d->m_signature)); + return throwError(exec, GeneralError, msg.toLatin1().constData()); + } + } + } else { + QString msg = QString("QtMetaMethod.%1: %2::%3() is not a signal") + .arg(d->m_isConnect ? "connect": "disconnect") + .arg(sender->metaObject()->className()) + .arg(QLatin1String(d->m_signature)); + return throwError(exec, TypeError, msg.toLatin1().constData()); + } + } else { + return throwError(exec, GeneralError, "cannot call function of deleted QObject"); + } + + return jsUndefined(); +} + +bool QtRuntimeConnectionMethod::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) +{ + if (propertyName == exec->propertyNames().length) { + slot.setCustom(this, lengthGetter); + return true; + } + + return QtRuntimeMethod::getOwnPropertySlot(exec, propertyName, slot); +} + +JSValue *QtRuntimeConnectionMethod::lengthGetter(ExecState*, JSObject*, const Identifier&, const PropertySlot&) +{ + // we have one formal argument, and one optional + return jsNumber(1); +} + +// =============== + +QtConnectionObject::QtConnectionObject(PassRefPtr<QtInstance> instance, int signalIndex, JSObject* thisObject, JSObject* funcObject) + : m_instance(instance) + , m_signalIndex(signalIndex) + , m_originalObject(m_instance->getObject()) + , m_thisObject(thisObject) + , m_funcObject(funcObject) +{ + setParent(m_originalObject); + ASSERT(JSLock::currentThreadIsHoldingLock()); // so our ProtectedPtrs are safe +} + +QtConnectionObject::~QtConnectionObject() +{ + // Remove us from the map of active connections + QtRuntimeConnectionMethod::connections.remove(m_originalObject, this); +} + +static const uint qt_meta_data_QtConnectionObject[] = { + + // content: + 1, // revision + 0, // classname + 0, 0, // classinfo + 1, 10, // methods + 0, 0, // properties + 0, 0, // enums/sets + + // slots: signature, parameters, type, tag, flags + 28, 27, 27, 27, 0x0a, + + 0 // eod +}; + +static const char qt_meta_stringdata_QtConnectionObject[] = { + "KJS::Bindings::QtConnectionObject\0\0execute()\0" +}; + +const QMetaObject QtConnectionObject::staticMetaObject = { + { &QObject::staticMetaObject, qt_meta_stringdata_QtConnectionObject, + qt_meta_data_QtConnectionObject, 0 } +}; + +const QMetaObject *QtConnectionObject::metaObject() const +{ + return &staticMetaObject; +} + +void *QtConnectionObject::qt_metacast(const char *_clname) +{ + if (!_clname) return 0; + if (!strcmp(_clname, qt_meta_stringdata_QtConnectionObject)) + return static_cast<void*>(const_cast<QtConnectionObject*>(this)); + return QObject::qt_metacast(_clname); +} + +int QtConnectionObject::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = QObject::qt_metacall(_c, _id, _a); + if (_id < 0) + return _id; + if (_c == QMetaObject::InvokeMetaMethod) { + switch (_id) { + case 0: execute(_a); break; + } + _id -= 1; + } + return _id; +} + +void QtConnectionObject::execute(void **argv) +{ + QObject* obj = m_instance->getObject(); + if (obj) { + const QMetaObject* meta = obj->metaObject(); + const QMetaMethod method = meta->method(m_signalIndex); + + QList<QByteArray> parameterTypes = method.parameterTypes(); + + int argc = parameterTypes.count(); + + JSLock lock; + + // ### Should the Interpreter/ExecState come from somewhere else? + RefPtr<RootObject> ro = m_instance->rootObject(); + if (ro) { + JSGlobalObject* globalobj = ro->globalObject(); + if (globalobj) { + ExecState* exec = globalobj->globalExec(); + if (exec) { + // Build the argument list (up to the formal argument length of the slot) + List l; + // ### DropAllLocks? + int funcArgC = m_funcObject->get(exec, exec->propertyNames().length)->toInt32(exec); + int argTotal = qMax(funcArgC, argc); + for(int i=0; i < argTotal; i++) { + if (i < argc) { + int argType = QMetaType::type(parameterTypes.at(i)); + l.append(convertQVariantToValue(exec, ro, QVariant(argType, argv[i+1]))); + } else { + l.append(jsUndefined()); + } + } + // Stuff in the __qt_sender property, if we can + if (m_funcObject->inherits(&FunctionImp::info)) { + FunctionImp* fimp = static_cast<FunctionImp*>(m_funcObject.get()); + + JSObject* qt_sender = Instance::createRuntimeObject(Instance::QtLanguage, sender(), ro); + JSObject* wrapper = new JSObject(); + wrapper->put(exec, "__qt_sender__", qt_sender); + ScopeChain oldsc = fimp->scope(); + ScopeChain sc = oldsc; + sc.push(wrapper); + fimp->setScope(sc); + fimp->call(exec, m_thisObject, l); + fimp->setScope(oldsc); + } else + m_funcObject->call(exec, m_thisObject, l); + } + } + } + } else { + // A strange place to be - a deleted object emitted a signal here. + qWarning() << "sender deleted, cannot deliver signal"; + } +} + +bool QtConnectionObject::match(QObject* sender, int signalIndex, JSObject* thisObject, JSObject *funcObject) +{ + if (m_originalObject == sender && m_signalIndex == signalIndex + && thisObject == (JSObject*)m_thisObject && funcObject == (JSObject*)m_funcObject) + return true; + return false; +} + +// =============== + +template <typename T> QtArray<T>::QtArray(QList<T> list, QMetaType::Type type, PassRefPtr<RootObject> rootObject) + : Array(rootObject) + , m_list(list) + , m_type(type) +{ + m_length = m_list.count(); +} + +template <typename T> QtArray<T>::~QtArray () +{ +} + +template <typename T> RootObject* QtArray<T>::rootObject() const +{ + return _rootObject && _rootObject->isValid() ? _rootObject.get() : 0; +} + +template <typename T> void QtArray<T>::setValueAt(ExecState *exec, unsigned int index, JSValue *aValue) const +{ + // QtScript sets the value, but doesn't forward it to the original source + // (e.g. if you do 'object.intList[5] = 6', the object is not updated, but the + // copy of the list is). + int dist = -1; + QVariant val = convertValueToQVariant(exec, aValue, m_type, &dist); + + if (dist >= 0) { + m_list[index] = val.value<T>(); + } +} + + +template <typename T> JSValue* QtArray<T>::valueAt(ExecState *exec, unsigned int index) const +{ + if (index < m_length) { + T val = m_list.at(index); + return convertQVariantToValue(exec, rootObject(), QVariant::fromValue(val)); + } + + return jsUndefined(); +} + +// =============== + +} } diff --git a/JavaScriptCore/bindings/qt/qt_runtime.h b/JavaScriptCore/bindings/qt/qt_runtime.h new file mode 100644 index 0000000..e44af7b --- /dev/null +++ b/JavaScriptCore/bindings/qt/qt_runtime.h @@ -0,0 +1,217 @@ +/* + * Copyright (C) 2006 Trolltech ASA + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef BINDINGS_QT_RUNTIME_H_ +#define BINDINGS_QT_RUNTIME_H_ + +#include "runtime.h" +#include "runtime_method.h" +#include "protect.h" + +#include <qbytearray.h> +#include <qmetaobject.h> +#include <qpointer.h> + +namespace KJS { +namespace Bindings { + +class QtInstance; + +class QtField : public Field { +public: + + typedef enum { + MetaProperty, + DynamicProperty, + ChildObject + } QtFieldType; + + QtField(const QMetaProperty &p) + : m_type(MetaProperty), m_property(p) + {} + + QtField(const QByteArray &b) + : m_type(DynamicProperty), m_dynamicProperty(b) + {} + + QtField(QObject *child) + : m_type(ChildObject), m_childObject(child) + {} + + virtual JSValue* valueFromInstance(ExecState*, const Instance*) const; + virtual void setValueToInstance(ExecState*, const Instance*, JSValue*) const; + virtual const char* name() const; + QtFieldType fieldType() const {return m_type;} +private: + QtFieldType m_type; + QByteArray m_dynamicProperty; + QMetaProperty m_property; + QPointer<QObject> m_childObject; +}; + + +class QtMethod : public Method +{ +public: + QtMethod(const QMetaObject *mo, int i, const QByteArray &ident, int numParameters) + : m_metaObject(mo), + m_index(i), + m_identifier(ident), + m_nParams(numParameters) + { } + + virtual const char* name() const { return m_identifier.constData(); } + virtual int numParameters() const { return m_nParams; } + +private: + friend class QtInstance; + const QMetaObject *m_metaObject; + int m_index; + QByteArray m_identifier; + int m_nParams; +}; + + +template <typename T> class QtArray : public Array +{ +public: + QtArray(QList<T> list, QMetaType::Type type, PassRefPtr<RootObject>); + virtual ~QtArray(); + + RootObject* rootObject() const; + + virtual void setValueAt(ExecState *exec, unsigned int index, JSValue *aValue) const; + virtual JSValue *valueAt(ExecState *exec, unsigned int index) const; + virtual unsigned int getLength() const {return m_length;} + +private: + mutable QList<T> m_list; // setValueAt is const! + unsigned int m_length; + QMetaType::Type m_type; +}; + +// Based on RuntimeMethod + +// Extra data classes (to avoid the CELL_SIZE limit on JS objects) + +class QtRuntimeMethodData { + public: + virtual ~QtRuntimeMethodData(); + RefPtr<QtInstance> m_instance; +}; + +class QtRuntimeConnectionMethod; +class QtRuntimeMetaMethodData : public QtRuntimeMethodData { + public: + ~QtRuntimeMetaMethodData(); + QByteArray m_signature; + bool m_allowPrivate; + int m_index; + QtRuntimeConnectionMethod *m_connect; + QtRuntimeConnectionMethod *m_disconnect; +}; + +class QtRuntimeConnectionMethodData : public QtRuntimeMethodData { + public: + ~QtRuntimeConnectionMethodData(); + QByteArray m_signature; + int m_index; + bool m_isConnect; +}; + +// Common base class (doesn't really do anything interesting) +class QtRuntimeMethod : public InternalFunctionImp +{ +public: + virtual ~QtRuntimeMethod(); + + virtual CodeType codeType() const; + virtual Completion execute(ExecState *exec); + +protected: + QtRuntimeMethodData *d_func() const {return d_ptr;} + QtRuntimeMethod(QtRuntimeMethodData *dd, ExecState *exec, const Identifier &n, PassRefPtr<QtInstance> inst); + QtRuntimeMethodData *d_ptr; +}; + +class QtRuntimeMetaMethod : public QtRuntimeMethod +{ +public: + QtRuntimeMetaMethod(ExecState *exec, const Identifier &n, PassRefPtr<QtInstance> inst, int index, const QByteArray& signature, bool allowPrivate); + + virtual bool getOwnPropertySlot(ExecState *, const Identifier&, PropertySlot&); + virtual JSValue *callAsFunction(ExecState *exec, JSObject *thisObj, const List &args); + + virtual void mark(); + +protected: + QtRuntimeMetaMethodData* d_func() const {return reinterpret_cast<QtRuntimeMetaMethodData*>(d_ptr);} + +private: + static JSValue *lengthGetter(ExecState *, JSObject *, const Identifier&, const PropertySlot&); + static JSValue *connectGetter(ExecState *, JSObject *, const Identifier&, const PropertySlot&); + static JSValue *disconnectGetter(ExecState *, JSObject *, const Identifier&, const PropertySlot&); +}; + +class QtConnectionObject; +class QtRuntimeConnectionMethod : public QtRuntimeMethod +{ +public: + QtRuntimeConnectionMethod(ExecState *exec, const Identifier &n, bool isConnect, PassRefPtr<QtInstance> inst, int index, const QByteArray& signature ); + + virtual bool getOwnPropertySlot(ExecState *, const Identifier&, PropertySlot&); + virtual JSValue *callAsFunction(ExecState *exec, JSObject *thisObj, const List &args); + +protected: + QtRuntimeConnectionMethodData* d_func() const {return reinterpret_cast<QtRuntimeConnectionMethodData*>(d_ptr);} + +private: + static JSValue *lengthGetter(ExecState *, JSObject *, const Identifier&, const PropertySlot&); + static QMultiMap<QObject *, QtConnectionObject *> connections; + friend class QtConnectionObject; +}; + +class QtConnectionObject: public QObject +{ +public: + QtConnectionObject(PassRefPtr<QtInstance> instance, int signalIndex, JSObject* thisObject, JSObject* funcObject); + ~QtConnectionObject(); + + static const QMetaObject staticMetaObject; + virtual const QMetaObject *metaObject() const; + virtual void *qt_metacast(const char *); + virtual int qt_metacall(QMetaObject::Call, int, void **argv); + + bool match(QObject *sender, int signalIndex, JSObject* thisObject, JSObject *funcObject); + + // actual slot: + void execute(void **argv); + +private: + RefPtr<QtInstance> m_instance; + int m_signalIndex; + QObject* m_originalObject; // only used as a key, not dereferenced + ProtectedPtr<JSObject> m_thisObject; + ProtectedPtr<JSObject> m_funcObject; +}; + +} // namespace Bindings +} // namespace KJS + +#endif diff --git a/JavaScriptCore/bindings/runtime.cpp b/JavaScriptCore/bindings/runtime.cpp new file mode 100644 index 0000000..cc68fa6 --- /dev/null +++ b/JavaScriptCore/bindings/runtime.cpp @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2003, 2006 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 COMPUTER, INC. OR + * 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 "runtime.h" + +#include "JSLock.h" +#include "NP_jsobject.h" +#include "c_instance.h" +#include "runtime_object.h" +#include "runtime_root.h" + +#if HAVE(JNI) +#include "jni_instance.h" +#endif +#if PLATFORM(MAC) +#include "objc_instance.h" +#endif +#if PLATFORM(QT) +#include "qt_instance.h" +#endif + +namespace KJS { namespace Bindings { + +Array::Array(PassRefPtr<RootObject> rootObject) + : _rootObject(rootObject) +{ + ASSERT(_rootObject); +} + +Array::~Array() +{ +} + +Instance::Instance(PassRefPtr<RootObject> rootObject) + : _rootObject(rootObject) + , _refCount(0) +{ + ASSERT(_rootObject); +} + +Instance::~Instance() +{ +} + +static KJSDidExecuteFunctionPtr _DidExecuteFunction; + +void Instance::setDidExecuteFunction(KJSDidExecuteFunctionPtr func) { _DidExecuteFunction = func; } +KJSDidExecuteFunctionPtr Instance::didExecuteFunction() { return _DidExecuteFunction; } + +JSValue *Instance::getValueOfField(ExecState *exec, const Field *aField) const +{ + return aField->valueFromInstance(exec, this); +} + +void Instance::setValueOfField(ExecState *exec, const Field *aField, JSValue *aValue) const +{ + aField->setValueToInstance(exec, this, aValue); +} + +Instance* Instance::createBindingForLanguageInstance(BindingLanguage language, void* nativeInstance, PassRefPtr<RootObject> rootObject) +{ + Instance *newInstance = 0; + + switch (language) { +#if HAVE(JNI) + case Instance::JavaLanguage: { + newInstance = new Bindings::JavaInstance((jobject)nativeInstance, rootObject); + break; + } +#endif +#if PLATFORM(MAC) + case Instance::ObjectiveCLanguage: { + newInstance = new Bindings::ObjcInstance((ObjectStructPtr)nativeInstance, rootObject); + break; + } +#endif +#if !PLATFORM(DARWIN) || !defined(__LP64__) + case Instance::CLanguage: { + newInstance = new Bindings::CInstance((NPObject *)nativeInstance, rootObject); + break; + } +#endif +#if PLATFORM(QT) + case Instance::QtLanguage: { + newInstance = Bindings::QtInstance::getQtInstance((QObject *)nativeInstance, rootObject); + break; + } +#endif + default: + break; + } + + return newInstance; +} + +JSObject* Instance::createRuntimeObject(BindingLanguage language, void* nativeInstance, PassRefPtr<RootObject> rootObject) +{ + Instance* instance = Instance::createBindingForLanguageInstance(language, nativeInstance, rootObject); + + return createRuntimeObject(instance); +} + +JSObject* Instance::createRuntimeObject(Instance* instance) +{ +#if PLATFORM(QT) + if (instance->getBindingLanguage() == QtLanguage) + return QtInstance::getRuntimeObject(static_cast<QtInstance*>(instance)); +#endif + JSLock lock; + + return new RuntimeObjectImp(instance); +} + +Instance* Instance::getInstance(JSObject* object, BindingLanguage language) +{ + if (!object) + return 0; + if (!object->inherits(&RuntimeObjectImp::info)) + return 0; + Instance* instance = (static_cast<RuntimeObjectImp*>(object))->getInternalInstance(); + if (!instance) + return 0; + if (instance->getBindingLanguage() != language) + return 0; + return instance; +} + +RootObject* Instance::rootObject() const +{ + return _rootObject && _rootObject->isValid() ? _rootObject.get() : 0; +} + +} } // namespace KJS::Bindings diff --git a/JavaScriptCore/bindings/runtime.h b/JavaScriptCore/bindings/runtime.h new file mode 100644 index 0000000..dbb324c --- /dev/null +++ b/JavaScriptCore/bindings/runtime.h @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2003 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 COMPUTER, INC. OR + * 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. + */ + +#ifndef JAVASCRIPTCORE_BINDINGS_RUNTIME_H +#define JAVASCRIPTCORE_BINDINGS_RUNTIME_H + +#include "value.h" + +#include <wtf/Noncopyable.h> +#include <wtf/HashMap.h> +#include <wtf/Vector.h> + +namespace KJS { + +class Identifier; +class List; +class PropertyNameArray; + +namespace Bindings { + +class Instance; +class Method; +class RootObject; + +typedef Vector<Method*> MethodList; + +class Field +{ +public: + virtual const char* name() const = 0; + + virtual JSValue* valueFromInstance(ExecState*, const Instance*) const = 0; + virtual void setValueToInstance(ExecState*, const Instance*, JSValue*) const = 0; + + virtual ~Field() {} +}; + +class Method : Noncopyable +{ +public: + virtual const char *name() const = 0; + + virtual int numParameters() const = 0; + + virtual ~Method() {} +}; + +class Class : Noncopyable +{ +public: + virtual const char *name() const = 0; + + virtual MethodList methodsNamed(const Identifier&, Instance*) const = 0; + + virtual Field *fieldNamed(const Identifier&, Instance*) const = 0; + + virtual JSValue* fallbackObject(ExecState*, Instance*, const Identifier&) { return jsUndefined(); } + + virtual ~Class() {} +}; + +typedef void (*KJSDidExecuteFunctionPtr)(ExecState*, JSObject* rootObject); + +class Instance : Noncopyable { +public: + typedef enum { + JavaLanguage, + ObjectiveCLanguage, + CLanguage +#if PLATFORM(QT) + , QtLanguage +#endif + } BindingLanguage; + + Instance(PassRefPtr<RootObject>); + + static void setDidExecuteFunction(KJSDidExecuteFunctionPtr func); + static KJSDidExecuteFunctionPtr didExecuteFunction(); + + static Instance* createBindingForLanguageInstance(BindingLanguage, void* nativeInstance, PassRefPtr<RootObject>); + static JSObject* createRuntimeObject(BindingLanguage, void* nativeInstance, PassRefPtr<RootObject>); + static JSObject* createRuntimeObject(Instance*); + + static Instance* getInstance(JSObject*, BindingLanguage); + + void ref() { _refCount++; } + void deref() + { + if (--_refCount == 0) + delete this; + } + + // These functions are called before and after the main entry points into + // the native implementations. They can be used to establish and cleanup + // any needed state. + virtual void begin() {} + virtual void end() {} + + virtual Class *getClass() const = 0; + + virtual JSValue* getValueOfField(ExecState*, const Field*) const; + virtual JSValue* getValueOfUndefinedField(ExecState*, const Identifier&, JSType) const { return jsUndefined(); } + virtual void setValueOfField(ExecState*, const Field*, JSValue*) const; + virtual bool supportsSetValueOfUndefinedField() { return false; } + virtual void setValueOfUndefinedField(ExecState*, const Identifier&, JSValue*) {} + + virtual bool implementsCall() const { return false; } + + virtual JSValue* invokeMethod(ExecState*, const MethodList&, const List& args) = 0; + virtual JSValue* invokeDefaultMethod(ExecState*, const List&) { return jsUndefined(); } + + virtual void getPropertyNames(ExecState*, PropertyNameArray&) { } + + virtual JSValue* defaultValue(JSType hint) const = 0; + + virtual JSValue* valueOf() const { return jsString(getClass()->name()); } + + RootObject* rootObject() const; + + virtual ~Instance(); + + virtual BindingLanguage getBindingLanguage() const = 0; + +protected: + RefPtr<RootObject> _rootObject; + unsigned _refCount; +}; + +class Array : Noncopyable +{ +public: + Array(PassRefPtr<RootObject>); + virtual ~Array(); + + virtual void setValueAt(ExecState *, unsigned index, JSValue*) const = 0; + virtual JSValue* valueAt(ExecState *, unsigned index) const = 0; + virtual unsigned int getLength() const = 0; +protected: + RefPtr<RootObject> _rootObject; +}; + +const char *signatureForParameters(const List&); + +typedef HashMap<RefPtr<UString::Rep>, MethodList*> MethodListMap; +typedef HashMap<RefPtr<UString::Rep>, Method*> MethodMap; +typedef HashMap<RefPtr<UString::Rep>, Field*> FieldMap; + +} // namespace Bindings + +} // namespace KJS + +#endif diff --git a/JavaScriptCore/bindings/runtime_array.cpp b/JavaScriptCore/bindings/runtime_array.cpp new file mode 100644 index 0000000..005bae2 --- /dev/null +++ b/JavaScriptCore/bindings/runtime_array.cpp @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2003 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 COMPUTER, INC. OR + * 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 "runtime_array.h" + +#include "JSGlobalObject.h" +#include "array_object.h" + +using namespace KJS; + +const ClassInfo RuntimeArray::info = { "RuntimeArray", &ArrayInstance::info, 0 }; + +RuntimeArray::RuntimeArray(ExecState *exec, Bindings::Array *a) + : JSObject(exec->lexicalGlobalObject()->arrayPrototype()) + , _array(a) +{ +} + +JSValue *RuntimeArray::lengthGetter(ExecState*, JSObject*, const Identifier&, const PropertySlot& slot) +{ + RuntimeArray *thisObj = static_cast<RuntimeArray *>(slot.slotBase()); + return jsNumber(thisObj->getLength()); +} + +JSValue *RuntimeArray::indexGetter(ExecState* exec, JSObject*, const Identifier&, const PropertySlot& slot) +{ + RuntimeArray *thisObj = static_cast<RuntimeArray *>(slot.slotBase()); + return thisObj->getConcreteArray()->valueAt(exec, slot.index()); +} + +bool RuntimeArray::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) +{ + if (propertyName == exec->propertyNames().length) { + slot.setCustom(this, lengthGetter); + return true; + } + + bool ok; + unsigned index = propertyName.toArrayIndex(&ok); + if (ok) { + if (index < getLength()) { + slot.setCustomIndex(this, index, indexGetter); + return true; + } + } + + return JSObject::getOwnPropertySlot(exec, propertyName, slot); +} + +bool RuntimeArray::getOwnPropertySlot(ExecState *exec, unsigned index, PropertySlot& slot) +{ + if (index < getLength()) { + slot.setCustomIndex(this, index, indexGetter); + return true; + } + + return JSObject::getOwnPropertySlot(exec, index, slot); +} + +void RuntimeArray::put(ExecState* exec, const Identifier& propertyName, JSValue* value) +{ + if (propertyName == exec->propertyNames().length) { + throwError(exec, RangeError); + return; + } + + bool ok; + unsigned index = propertyName.toArrayIndex(&ok); + if (ok) { + getConcreteArray()->setValueAt(exec, index, value); + return; + } + + JSObject::put(exec, propertyName, value); +} + +void RuntimeArray::put(ExecState* exec, unsigned index, JSValue* value) +{ + if (index >= getLength()) { + throwError(exec, RangeError); + return; + } + + getConcreteArray()->setValueAt(exec, index, value); +} + +bool RuntimeArray::deleteProperty(ExecState*, const Identifier&) +{ + return false; +} + +bool RuntimeArray::deleteProperty(ExecState*, unsigned) +{ + return false; +} diff --git a/JavaScriptCore/bindings/runtime_array.h b/JavaScriptCore/bindings/runtime_array.h new file mode 100644 index 0000000..74b540f --- /dev/null +++ b/JavaScriptCore/bindings/runtime_array.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2003 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 COMPUTER, INC. OR + * 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. + */ + +#ifndef RUNTIME_ARRAY_H_ +#define RUNTIME_ARRAY_H_ + +#include <wtf/OwnPtr.h> + +#include "array_instance.h" +#include "runtime.h" + +namespace KJS { + +class RuntimeArray : public JSObject { +public: + RuntimeArray(ExecState *exec, Bindings::Array *i); + + virtual bool getOwnPropertySlot(ExecState *, const Identifier&, PropertySlot&); + virtual bool getOwnPropertySlot(ExecState *, unsigned, PropertySlot&); + virtual void put(ExecState *exec, const Identifier &propertyName, JSValue *value); + virtual void put(ExecState *exec, unsigned propertyName, JSValue *value); + + virtual bool deleteProperty(ExecState *exec, const Identifier &propertyName); + virtual bool deleteProperty(ExecState *exec, unsigned propertyName); + + virtual const ClassInfo *classInfo() const { return &info; } + + unsigned getLength() const { return getConcreteArray()->getLength(); } + + Bindings::Array *getConcreteArray() const { return _array.get(); } + + static const ClassInfo info; + +private: + static JSValue *lengthGetter(ExecState *, JSObject *, const Identifier&, const PropertySlot&); + static JSValue *indexGetter(ExecState *, JSObject *, const Identifier&, const PropertySlot&); + + OwnPtr<Bindings::Array> _array; +}; + +} // namespace KJS + +#endif // RUNTIME_ARRAY_H_ diff --git a/JavaScriptCore/bindings/runtime_method.cpp b/JavaScriptCore/bindings/runtime_method.cpp new file mode 100644 index 0000000..4430f25 --- /dev/null +++ b/JavaScriptCore/bindings/runtime_method.cpp @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2003 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 COMPUTER, INC. OR + * 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 "runtime_method.h" + +#include "ExecState.h" +#include "JSGlobalObject.h" +#include "runtime_object.h" +#include "function_object.h" + +using namespace KJS::Bindings; +using namespace KJS; + +RuntimeMethod::RuntimeMethod(ExecState *exec, const Identifier &ident, Bindings::MethodList &m) + : InternalFunctionImp(exec->lexicalGlobalObject()->functionPrototype(), ident) + , _methodList(new MethodList(m)) +{ +} + +JSValue *RuntimeMethod::lengthGetter(ExecState*, JSObject*, const Identifier&, const PropertySlot& slot) +{ + RuntimeMethod *thisObj = static_cast<RuntimeMethod *>(slot.slotBase()); + + // Ick! There may be more than one method with this name. Arbitrarily + // just pick the first method. The fundamental problem here is that + // JavaScript doesn't have the notion of method overloading and + // Java does. + // FIXME: a better solution might be to give the maximum number of parameters + // of any method + return jsNumber(thisObj->_methodList->at(0)->numParameters()); +} + +bool RuntimeMethod::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot &slot) +{ + if (propertyName == exec->propertyNames().length) { + slot.setCustom(this, lengthGetter); + return true; + } + + return InternalFunctionImp::getOwnPropertySlot(exec, propertyName, slot); +} + +JSValue *RuntimeMethod::callAsFunction(ExecState *exec, JSObject *thisObj, const List &args) +{ + if (_methodList->isEmpty()) + return jsUndefined(); + + RuntimeObjectImp *imp = 0; + + if (thisObj->classInfo() == &KJS::RuntimeObjectImp::info) { + imp = static_cast<RuntimeObjectImp*>(thisObj); + } else { + // If thisObj is the DOM object for a plugin, get the corresponding + // runtime object from the DOM object. + JSValue* value = thisObj->get(exec, "__apple_runtime_object"); + if (value->isObject(&KJS::RuntimeObjectImp::info)) + imp = static_cast<RuntimeObjectImp*>(value); + } + + if (!imp) + return throwError(exec, TypeError); + + RefPtr<Instance> instance = imp->getInternalInstance(); + if (!instance) + return RuntimeObjectImp::throwInvalidAccessError(exec); + + instance->begin(); + JSValue *aValue = instance->invokeMethod(exec, *_methodList, args); + instance->end(); + return aValue; +} diff --git a/JavaScriptCore/bindings/runtime_method.h b/JavaScriptCore/bindings/runtime_method.h new file mode 100644 index 0000000..7fbe32a --- /dev/null +++ b/JavaScriptCore/bindings/runtime_method.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2003 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 COMPUTER, INC. OR + * 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. + */ + +#ifndef RUNTIME_FUNCTION_H_ +#define RUNTIME_FUNCTION_H_ + +#include "function.h" +#include "runtime.h" +#include "object.h" + +namespace KJS { + +class RuntimeMethod : public InternalFunctionImp { +public: + RuntimeMethod(ExecState *exec, const Identifier &n, Bindings::MethodList &methodList); + + virtual bool getOwnPropertySlot(ExecState *, const Identifier&, PropertySlot&); + + virtual JSValue *callAsFunction(ExecState *exec, JSObject *thisObj, const List &args); + +private: + static JSValue *lengthGetter(ExecState *, JSObject *, const Identifier&, const PropertySlot&); + + OwnPtr<Bindings::MethodList> _methodList; +}; + +} // namespace KJS + +#endif diff --git a/JavaScriptCore/bindings/runtime_object.cpp b/JavaScriptCore/bindings/runtime_object.cpp new file mode 100644 index 0000000..30ab488 --- /dev/null +++ b/JavaScriptCore/bindings/runtime_object.cpp @@ -0,0 +1,238 @@ +/* + * Copyright (C) 2003 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 COMPUTER, INC. OR + * 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 "runtime_object.h" + +#include "error_object.h" +#include "operations.h" +#include "runtime_method.h" +#include "runtime_root.h" + +using namespace KJS; +using namespace Bindings; + +const ClassInfo RuntimeObjectImp::info = { "RuntimeObject", 0, 0 }; + +RuntimeObjectImp::RuntimeObjectImp(Bindings::Instance *i) +: instance(i) +{ + instance->rootObject()->addRuntimeObject(this); +} + +RuntimeObjectImp::~RuntimeObjectImp() +{ + if (instance) + instance->rootObject()->removeRuntimeObject(this); +} + +void RuntimeObjectImp::invalidate() +{ + ASSERT(instance); + instance = 0; +} + +JSValue *RuntimeObjectImp::fallbackObjectGetter(ExecState* exec, JSObject*, const Identifier& propertyName, const PropertySlot& slot) +{ + RuntimeObjectImp *thisObj = static_cast<RuntimeObjectImp *>(slot.slotBase()); + RefPtr<Bindings::Instance> instance = thisObj->instance; + + if (!instance) + return throwInvalidAccessError(exec); + + instance->begin(); + + Class *aClass = instance->getClass(); + JSValue* result = aClass->fallbackObject(exec, instance.get(), propertyName); + + instance->end(); + + return result; +} + +JSValue *RuntimeObjectImp::fieldGetter(ExecState* exec, JSObject*, const Identifier& propertyName, const PropertySlot& slot) +{ + RuntimeObjectImp *thisObj = static_cast<RuntimeObjectImp *>(slot.slotBase()); + RefPtr<Bindings::Instance> instance = thisObj->instance; + + if (!instance) + return throwInvalidAccessError(exec); + + instance->begin(); + + Class *aClass = instance->getClass(); + Field* aField = aClass->fieldNamed(propertyName, instance.get()); + JSValue *result = instance->getValueOfField(exec, aField); + + instance->end(); + + return result; +} + +JSValue *RuntimeObjectImp::methodGetter(ExecState* exec, JSObject*, const Identifier& propertyName, const PropertySlot& slot) +{ + RuntimeObjectImp *thisObj = static_cast<RuntimeObjectImp *>(slot.slotBase()); + RefPtr<Bindings::Instance> instance = thisObj->instance; + + if (!instance) + return throwInvalidAccessError(exec); + + instance->begin(); + + Class *aClass = instance->getClass(); + MethodList methodList = aClass->methodsNamed(propertyName, instance.get()); + JSValue *result = new RuntimeMethod(exec, propertyName, methodList); + + instance->end(); + + return result; +} + +bool RuntimeObjectImp::getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot& slot) +{ + if (!instance) { + throwInvalidAccessError(exec); + return false; + } + + instance->begin(); + + Class *aClass = instance->getClass(); + + if (aClass) { + // See if the instance has a field with the specified name. + Field *aField = aClass->fieldNamed(propertyName, instance.get()); + if (aField) { + slot.setCustom(this, fieldGetter); + instance->end(); + return true; + } else { + // Now check if a method with specified name exists, if so return a function object for + // that method. + MethodList methodList = aClass->methodsNamed(propertyName, instance.get()); + if (methodList.size() > 0) { + slot.setCustom(this, methodGetter); + + instance->end(); + return true; + } + } + + // Try a fallback object. + if (!aClass->fallbackObject(exec, instance.get(), propertyName)->isUndefined()) { + slot.setCustom(this, fallbackObjectGetter); + instance->end(); + return true; + } + } + + instance->end(); + + // don't call superclass, because runtime objects can't have custom properties or a prototype + return false; +} + +void RuntimeObjectImp::put(ExecState* exec, const Identifier& propertyName, JSValue* value) +{ + if (!instance) { + throwInvalidAccessError(exec); + return; + } + + RefPtr<Bindings::Instance> protector(instance); + instance->begin(); + + // Set the value of the property. + Field *aField = instance->getClass()->fieldNamed(propertyName, instance.get()); + if (aField) + instance->setValueOfField(exec, aField, value); + else if (instance->supportsSetValueOfUndefinedField()) + instance->setValueOfUndefinedField(exec, propertyName, value); + + instance->end(); +} + +bool RuntimeObjectImp::deleteProperty(ExecState*, const Identifier&) +{ + // Can never remove a property of a RuntimeObject. + return false; +} + +JSValue *RuntimeObjectImp::defaultValue(ExecState* exec, JSType hint) const +{ + if (!instance) + return throwInvalidAccessError(exec); + + JSValue *result; + + RefPtr<Bindings::Instance> protector(instance); + instance->begin(); + + result = instance->defaultValue(hint); + + instance->end(); + + return result; +} + +bool RuntimeObjectImp::implementsCall() const +{ + if (!instance) + return false; + + return instance->implementsCall(); +} + +JSValue *RuntimeObjectImp::callAsFunction(ExecState* exec, JSObject*, const List& args) +{ + if (!instance) + return throwInvalidAccessError(exec); + + RefPtr<Bindings::Instance> protector(instance); + instance->begin(); + + JSValue *aValue = instance->invokeDefaultMethod(exec, args); + + instance->end(); + + return aValue; +} + +void RuntimeObjectImp::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames) +{ + if (!instance) { + throwInvalidAccessError(exec); + return; + } + + instance->begin(); + instance->getPropertyNames(exec, propertyNames); + instance->end(); +} + +JSObject* RuntimeObjectImp::throwInvalidAccessError(ExecState* exec) +{ + return throwError(exec, ReferenceError, "Trying to access object from destroyed plug-in."); +} diff --git a/JavaScriptCore/bindings/runtime_object.h b/JavaScriptCore/bindings/runtime_object.h new file mode 100644 index 0000000..e2387e3 --- /dev/null +++ b/JavaScriptCore/bindings/runtime_object.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2003 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 COMPUTER, INC. OR + * 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. + */ + +#ifndef KJS_RUNTIME_OBJECT_H +#define KJS_RUNTIME_OBJECT_H + +#include "runtime.h" +#include "object.h" + +#include <wtf/Noncopyable.h> + +namespace KJS { + +class RuntimeObjectImp : public JSObject { +public: + virtual ~RuntimeObjectImp(); + + const ClassInfo *classInfo() const { return &info; } + + virtual bool getOwnPropertySlot(ExecState *, const Identifier&, PropertySlot&); + virtual void put(ExecState *exec, const Identifier &propertyName, JSValue *value); + virtual bool deleteProperty(ExecState *exec, const Identifier &propertyName); + virtual JSValue *defaultValue(ExecState *exec, JSType hint) const; + virtual bool implementsCall() const; + virtual JSValue *callAsFunction(ExecState *exec, JSObject *thisObj, const List &args); + virtual void getPropertyNames(ExecState*, PropertyNameArray&); + + virtual void invalidate(); + Bindings::Instance *getInternalInstance() const { return instance.get(); } + + static JSObject* throwInvalidAccessError(ExecState*); + + static const ClassInfo info; + +protected: + friend class Bindings::Instance; + RuntimeObjectImp(Bindings::Instance*); // Only allow Instances and derived classes to create us + +private: + RuntimeObjectImp(); // prevent default construction + + static JSValue *fallbackObjectGetter(ExecState *, JSObject *, const Identifier&, const PropertySlot&); + static JSValue *fieldGetter(ExecState *, JSObject *, const Identifier&, const PropertySlot&); + static JSValue *methodGetter(ExecState *, JSObject *, const Identifier&, const PropertySlot&); + + RefPtr<Bindings::Instance> instance; +}; + +} // namespace + +#endif diff --git a/JavaScriptCore/bindings/runtime_root.cpp b/JavaScriptCore/bindings/runtime_root.cpp new file mode 100644 index 0000000..e7ae28f --- /dev/null +++ b/JavaScriptCore/bindings/runtime_root.cpp @@ -0,0 +1,305 @@ +/* + * Copyright (C) 2004 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 COMPUTER, INC. OR + * 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 "runtime_root.h" + +#include "JSGlobalObject.h" +#include "object.h" +#include "runtime.h" +#include "runtime_object.h" +#include <wtf/HashCountedSet.h> +#include <wtf/HashSet.h> + +namespace KJS { namespace Bindings { + +// This code attempts to solve two problems: (1) plug-ins leaking references to +// JS and the DOM; (2) plug-ins holding stale references to JS and the DOM. Previous +// comments in this file claimed that problem #1 was an issue in Java, in particular, +// because Java, allegedly, didn't always call finalize when collecting an object. + +typedef HashSet<RootObject*> RootObjectSet; + +static RootObjectSet* rootObjectSet() +{ + static RootObjectSet staticRootObjectSet; + return &staticRootObjectSet; +} + +// FIXME: These two functions are a potential performance problem. We could +// fix them by adding a JSObject to RootObject dictionary. + +RootObject* findProtectingRootObject(JSObject* jsObject) +{ + RootObjectSet::const_iterator end = rootObjectSet()->end(); + for (RootObjectSet::const_iterator it = rootObjectSet()->begin(); it != end; ++it) { + if ((*it)->gcIsProtected(jsObject)) + return *it; + } + return 0; +} + +RootObject* findRootObject(JSGlobalObject* globalObject) +{ + RootObjectSet::const_iterator end = rootObjectSet()->end(); + for (RootObjectSet::const_iterator it = rootObjectSet()->begin(); it != end; ++it) { + if ((*it)->globalObject() == globalObject) + return *it; + } + return 0; +} + +#if PLATFORM(MAC) +// May only be set by dispatchToJavaScriptThread(). +static CFRunLoopSourceRef completionSource; + +static void completedJavaScriptAccess (void *i) +{ + assert (CFRunLoopGetCurrent() != RootObject::runLoop()); + + JSObjectCallContext *callContext = (JSObjectCallContext *)i; + CFRunLoopRef runLoop = (CFRunLoopRef)callContext->originatingLoop; + + assert (CFRunLoopGetCurrent() == runLoop); + + CFRunLoopStop(runLoop); +} + +static pthread_once_t javaScriptAccessLockOnce = PTHREAD_ONCE_INIT; +static pthread_mutex_t javaScriptAccessLock; +static int javaScriptAccessLockCount = 0; + +static void initializeJavaScriptAccessLock() +{ + pthread_mutexattr_t attr; + + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE); + + pthread_mutex_init(&javaScriptAccessLock, &attr); +} + +static inline void lockJavaScriptAccess() +{ + // Perhaps add deadlock detection? + pthread_once(&javaScriptAccessLockOnce, initializeJavaScriptAccessLock); + pthread_mutex_lock(&javaScriptAccessLock); + javaScriptAccessLockCount++; +} + +static inline void unlockJavaScriptAccess() +{ + javaScriptAccessLockCount--; + pthread_mutex_unlock(&javaScriptAccessLock); +} + + +void RootObject::dispatchToJavaScriptThread(JSObjectCallContext *context) +{ + // This lock guarantees that only one thread can invoke + // at a time, and also guarantees that completionSource; + // won't get clobbered. + lockJavaScriptAccess(); + + CFRunLoopRef currentRunLoop = CFRunLoopGetCurrent(); + + assert (currentRunLoop != RootObject::runLoop()); + + // Setup a source to signal once the invocation of the JavaScript + // call completes. + // + // FIXME: This could be a potential performance issue. Creating and + // adding run loop sources is expensive. We could create one source + // per thread, as needed, instead. + context->originatingLoop = currentRunLoop; + CFRunLoopSourceContext sourceContext = {0, context, NULL, NULL, NULL, NULL, NULL, NULL, NULL, completedJavaScriptAccess}; + completionSource = CFRunLoopSourceCreate(NULL, 0, &sourceContext); + CFRunLoopAddSource(currentRunLoop, completionSource, kCFRunLoopDefaultMode); + + // Wakeup JavaScript access thread and make it do it's work. + CFRunLoopSourceSignal(RootObject::performJavaScriptSource()); + if (CFRunLoopIsWaiting(RootObject::runLoop())) { + CFRunLoopWakeUp(RootObject::runLoop()); + } + + // Wait until the JavaScript access thread is done. + CFRunLoopRun (); + + CFRunLoopRemoveSource(currentRunLoop, completionSource, kCFRunLoopDefaultMode); + CFRelease (completionSource); + + unlockJavaScriptAccess(); +} + +static void performJavaScriptAccess(void*) +{ + assert (CFRunLoopGetCurrent() == RootObject::runLoop()); + + // Dispatch JavaScript calls here. + CFRunLoopSourceContext sourceContext; + CFRunLoopSourceGetContext (completionSource, &sourceContext); + JSObjectCallContext *callContext = (JSObjectCallContext *)sourceContext.info; + CFRunLoopRef originatingLoop = callContext->originatingLoop; + + JavaJSObject::invoke (callContext); + + // Signal the originating thread that we're done. + CFRunLoopSourceSignal (completionSource); + if (CFRunLoopIsWaiting(originatingLoop)) { + CFRunLoopWakeUp(originatingLoop); + } +} + +CreateRootObjectFunction RootObject::_createRootObject = 0; +CFRunLoopRef RootObject::_runLoop = 0; +CFRunLoopSourceRef RootObject::_performJavaScriptSource = 0; + +// Must be called from the thread that will be used to access JavaScript. +void RootObject::setCreateRootObject(CreateRootObjectFunction createRootObject) { + // Should only be called once. + ASSERT(!_createRootObject); + + _createRootObject = createRootObject; + + // Assume that we can retain this run loop forever. It'll most + // likely (always?) be the main loop. + _runLoop = (CFRunLoopRef)CFRetain (CFRunLoopGetCurrent ()); + + // Setup a source the other threads can use to signal the _runLoop + // thread that a JavaScript call needs to be invoked. + CFRunLoopSourceContext sourceContext = {0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, performJavaScriptAccess}; + RootObject::_performJavaScriptSource = CFRunLoopSourceCreate(NULL, 0, &sourceContext); + CFRunLoopAddSource(RootObject::_runLoop, RootObject::_performJavaScriptSource, kCFRunLoopDefaultMode); +} + +#endif + +PassRefPtr<RootObject> RootObject::create(const void* nativeHandle, JSGlobalObject* globalObject) +{ + return adoptRef(new RootObject(nativeHandle, globalObject)); +} + +RootObject::RootObject(const void* nativeHandle, JSGlobalObject* globalObject) + : m_isValid(true) + , m_nativeHandle(nativeHandle) + , m_globalObject(globalObject) +{ + ASSERT(globalObject); + rootObjectSet()->add(this); +} + +RootObject::~RootObject() +{ + if (m_isValid) + invalidate(); +} + +void RootObject::invalidate() +{ + if (!m_isValid) + return; + + { + HashSet<RuntimeObjectImp*>::iterator end = m_runtimeObjects.end(); + for (HashSet<RuntimeObjectImp*>::iterator it = m_runtimeObjects.begin(); it != end; ++it) + (*it)->invalidate(); + + m_runtimeObjects.clear(); + } + + m_isValid = false; + + m_nativeHandle = 0; + m_globalObject = 0; + + ProtectCountSet::iterator end = m_protectCountSet.end(); + for (ProtectCountSet::iterator it = m_protectCountSet.begin(); it != end; ++it) { + JSLock lock; + KJS::gcUnprotect(it->first); + } + m_protectCountSet.clear(); + + rootObjectSet()->remove(this); +} + +void RootObject::gcProtect(JSObject* jsObject) +{ + ASSERT(m_isValid); + + if (!m_protectCountSet.contains(jsObject)) { + JSLock lock; + KJS::gcProtect(jsObject); + } + m_protectCountSet.add(jsObject); +} + +void RootObject::gcUnprotect(JSObject* jsObject) +{ + ASSERT(m_isValid); + + if (!jsObject) + return; + + if (m_protectCountSet.count(jsObject) == 1) { + JSLock lock; + KJS::gcUnprotect(jsObject); + } + m_protectCountSet.remove(jsObject); +} + +bool RootObject::gcIsProtected(JSObject* jsObject) +{ + ASSERT(m_isValid); + return m_protectCountSet.contains(jsObject); +} + +const void* RootObject::nativeHandle() const +{ + ASSERT(m_isValid); + return m_nativeHandle; +} + +JSGlobalObject* RootObject::globalObject() const +{ + ASSERT(m_isValid); + return m_globalObject; +} + +void RootObject::addRuntimeObject(RuntimeObjectImp* object) +{ + ASSERT(m_isValid); + ASSERT(!m_runtimeObjects.contains(object)); + + m_runtimeObjects.add(object); +} + +void RootObject::removeRuntimeObject(RuntimeObjectImp* object) +{ + ASSERT(m_isValid); + ASSERT(m_runtimeObjects.contains(object)); + + m_runtimeObjects.remove(object); +} + +} } // namespace KJS::Bindings diff --git a/JavaScriptCore/bindings/runtime_root.h b/JavaScriptCore/bindings/runtime_root.h new file mode 100644 index 0000000..4db02d4 --- /dev/null +++ b/JavaScriptCore/bindings/runtime_root.h @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2004 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 COMPUTER, INC. OR + * 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. + */ + +#ifndef RUNTIME_ROOT_H_ +#define RUNTIME_ROOT_H_ + +#if PLATFORM(MAC) +#include "jni_jsobject.h" +#endif +#include "protect.h" + +#include <wtf/HashSet.h> +#include <wtf/Noncopyable.h> +#include <wtf/RefCounted.h> + +namespace KJS { + +class Interpreter; +class JSGlobalObject; +class RuntimeObjectImp; + +namespace Bindings { + +class RootObject; + +typedef PassRefPtr<RootObject> (*CreateRootObjectFunction)(void* nativeHandle); +typedef HashCountedSet<JSObject*> ProtectCountSet; + +extern RootObject* findProtectingRootObject(JSObject*); +extern RootObject* findRootObject(JSGlobalObject*); + +class RootObject : public RefCounted<RootObject> { + friend class JavaJSObject; + +public: + ~RootObject(); + + static PassRefPtr<RootObject> create(const void* nativeHandle, JSGlobalObject*); + + bool isValid() { return m_isValid; } + void invalidate(); + + void gcProtect(JSObject*); + void gcUnprotect(JSObject*); + bool gcIsProtected(JSObject*); + + const void* nativeHandle() const; + JSGlobalObject* globalObject() const; + +#if PLATFORM(MAC) + // Must be called from the thread that will be used to access JavaScript. + static void setCreateRootObject(CreateRootObjectFunction); + static CreateRootObjectFunction createRootObject() { + return _createRootObject; + } + + static CFRunLoopRef runLoop() { return _runLoop; } + static CFRunLoopSourceRef performJavaScriptSource() { return _performJavaScriptSource; } + + static void dispatchToJavaScriptThread(JSObjectCallContext *context); +#endif + + void addRuntimeObject(RuntimeObjectImp*); + void removeRuntimeObject(RuntimeObjectImp*); +private: + RootObject(const void* nativeHandle, JSGlobalObject*); + + bool m_isValid; + + const void* m_nativeHandle; + ProtectedPtr<JSGlobalObject> m_globalObject; + ProtectCountSet m_protectCountSet; + + HashSet<RuntimeObjectImp*> m_runtimeObjects; + +#if PLATFORM(MAC) + static CreateRootObjectFunction _createRootObject; + static CFRunLoopRef _runLoop; + static CFRunLoopSourceRef _performJavaScriptSource; +#endif +}; + +} // namespace Bindings + +} // namespace KJS + +#endif diff --git a/JavaScriptCore/bindings/test.js b/JavaScriptCore/bindings/test.js new file mode 100644 index 0000000..5d4f79f --- /dev/null +++ b/JavaScriptCore/bindings/test.js @@ -0,0 +1,19 @@ +myInterface.logMessage ("Starting test"); + +myInterface.logMessage ("Testing properties:"); +myInterface.logMessage ("myInterface.doubleValue = " + myInterface.doubleValue); +myInterface.logMessage ("myInterface.intValue = " + myInterface.intValue); +myInterface.logMessage ("myInterface.stringValue = " + myInterface.stringValue); +myInterface.logMessage ("myInterface.booleanValue = " + myInterface.booleanValue); +myInterface.logMessage ("myInterface.nullValue = " + myInterface.nullValue); +myInterface.logMessage ("myInterface.undefinedValue = " + myInterface.undefinedValue); + +myInterface.logMessage ("myInterface.setInt_(666) = " + myInterface.setInt_(666)); +myInterface.logMessage ("myInterface.getInt() = " + myInterface.getInt()); +myInterface.logMessage ("myInterface.getString() = " + myInterface.getString()); +myInterface.logMessage ("myInterface.myInt = " + myInterface.myInt); +myInterface.logMessage ("setting myInterface.myInt = 777"); +myInterface.myInt = 777; +myInterface.logMessage ("myInterface.myInt = " + myInterface.myInt); +myInterface.logMessage ("myInterface.getMySecondInterface().doubleValue = " + myInterface.getMySecondInterface().doubleValue); +myInterface.logMessage ("myInterface.getMySecondInterface() = " + myInterface.getMySecondInterface()); diff --git a/JavaScriptCore/bindings/testC.js b/JavaScriptCore/bindings/testC.js new file mode 100644 index 0000000..44677c7 --- /dev/null +++ b/JavaScriptCore/bindings/testC.js @@ -0,0 +1,21 @@ +myInterface.logMessage ("Starting test"); + +myInterface.logMessage ("Testing properties:"); +myInterface.logMessage (" myInterface.doubleValue = " + myInterface.doubleValue); +myInterface.logMessage (" myInterface.intValue = " + myInterface.intValue); +myInterface.logMessage (" myInterface.stringValue = " + myInterface.stringValue); +myInterface.logMessage (" myInterface.booleanValue = " + myInterface.booleanValue); +myInterface.logMessage (" myInterface.nullValue = " + myInterface.nullValue); +myInterface.logMessage (" myInterface.undefinedValue = " + myInterface.undefinedValue); + +myInterface.logMessage ("Testing methods:"); +myInterface.logMessage (" myInterface.setDoubleValue(1234.1234) = " + myInterface.setDoubleValue(1234.1234)); +myInterface.logMessage (" myInterface.setIntValue(5678) = " + myInterface.setIntValue(5678)); +myInterface.logMessage (" myInterface.setStringValue(Goodbye) = " + myInterface.setStringValue('Goodbye')); +myInterface.logMessage (" myInterface.setBooleanValue(false) = " + myInterface.setBooleanValue(false)); + +myInterface.logMessage ("Value of properties after calling setters:"); +myInterface.logMessage (" myInterface.getDoubleValue() = " + myInterface.getDoubleValue()); +myInterface.logMessage (" myInterface.getIntValue() = " + myInterface.getIntValue()); +myInterface.logMessage (" myInterface.getStringValue() = " + myInterface.getStringValue()); +myInterface.logMessage (" myInterface.getBooleanValue() = " + myInterface.getBooleanValue()); diff --git a/JavaScriptCore/bindings/testM.js b/JavaScriptCore/bindings/testM.js new file mode 100644 index 0000000..7985d21 --- /dev/null +++ b/JavaScriptCore/bindings/testM.js @@ -0,0 +1,29 @@ +myInterface.logMessage ("Starting test"); + +myInterface.logMessage ("Testing properties:"); + +myInterface.jsobject = new Function ("arg1","arg2","return arg1 + arg2;"); +myInterface.logMessage ("myInterface.jsobject =" + myInterface.jsobject); + +var functionBody = 'return arg1*arg2;' + +myInterface.setJSObject_(new Function ("arg1","arg2",functionBody)); +myInterface.logMessage ("myInterface.jsobject =" + myInterface.jsobject); +myInterface.callJSObject__(5,6); +myInterface.callJSObject__(8,9); + +myInterface.logMessage ("myInterface.setInt_(666) = " + myInterface.setInt_(666)); +myInterface.logMessage ("myInterface.getInt() = " + myInterface.getInt()); +myInterface.logMessage ("myInterface.getString().foo() = " + myInterface.getString().foo()); +myInterface.logMessage ("myInterface.myInt = " + myInterface.myInt); +myInterface.logMessage ("setting myInterface.myInt = 777"); +myInterface.myInt = 777; +myInterface.logMessage ("myInterface.myInt = " + myInterface.myInt); +myInterface.logMessage ("myInterface.getMySecondInterface().doubleValue = " + myInterface.getMySecondInterface().doubleValue); +myInterface.logMessage ("myInterface.getMySecondInterface() = " + myInterface.getMySecondInterface()); + +myInterface.logMessageWithPrefix ("msg", "prefix"); + +var strings = [ "one", "two", "three" ]; + +myInterface.logMessages (strings);
\ No newline at end of file diff --git a/JavaScriptCore/bindings/testbindings.cpp b/JavaScriptCore/bindings/testbindings.cpp new file mode 100644 index 0000000..25f1f99 --- /dev/null +++ b/JavaScriptCore/bindings/testbindings.cpp @@ -0,0 +1,422 @@ +// -*- c-basic-offset: 2 -*- +/* + * This file is part of the KDE libraries + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ +#include "config.h" +#include <assert.h> +#include <stdio.h> +#include <string.h> + +#include "value.h" +#include "object.h" +#include "types.h" +#include "interpreter.h" + +#include "npruntime_internal.h" + +#include "runtime.h" +#include "runtime_object.h" + + +#define LOG(formatAndArgs...) { \ + fprintf (stderr, "%s: ", __PRETTY_FUNCTION__); \ + fprintf(stderr, formatAndArgs); \ +} + + +// ------------------ NP Interface definition -------------------- +typedef struct +{ + NPObject object; + double doubleValue; + int intValue; + NPVariant stringValue; + bool boolValue; +} MyObject; + + +static bool identifiersInitialized = false; + +#define ID_DOUBLE_VALUE 0 +#define ID_INT_VALUE 1 +#define ID_STRING_VALUE 2 +#define ID_BOOLEAN_VALUE 3 +#define ID_NULL_VALUE 4 +#define ID_UNDEFINED_VALUE 5 +#define NUM_PROPERTY_IDENTIFIERS 6 + +static NPIdentifier myPropertyIdentifiers[NUM_PROPERTY_IDENTIFIERS]; +static const NPUTF8 *myPropertyIdentifierNames[NUM_PROPERTY_IDENTIFIERS] = { + "doubleValue", + "intValue", + "stringValue", + "booleanValue", + "nullValue", + "undefinedValue" +}; + +#define ID_LOG_MESSAGE 0 +#define ID_SET_DOUBLE_VALUE 1 +#define ID_SET_INT_VALUE 2 +#define ID_SET_STRING_VALUE 3 +#define ID_SET_BOOLEAN_VALUE 4 +#define ID_GET_DOUBLE_VALUE 5 +#define ID_GET_INT_VALUE 6 +#define ID_GET_STRING_VALUE 7 +#define ID_GET_BOOLEAN_VALUE 8 +#define NUM_METHOD_IDENTIFIERS 9 + +static NPIdentifier myMethodIdentifiers[NUM_METHOD_IDENTIFIERS]; +static const NPUTF8 *myMethodIdentifierNames[NUM_METHOD_IDENTIFIERS] = { + "logMessage", + "setDoubleValue", + "setIntValue", + "setStringValue", + "setBooleanValue", + "getDoubleValue", + "getIntValue", + "getStringValue", + "getBooleanValue" +}; + +static void initializeIdentifiers() +{ + NPN_GetStringIdentifiers (myPropertyIdentifierNames, NUM_PROPERTY_IDENTIFIERS, myPropertyIdentifiers); + NPN_GetStringIdentifiers (myMethodIdentifierNames, NUM_METHOD_IDENTIFIERS, myMethodIdentifiers); +}; + +bool myHasProperty (NPClass *theClass, NPIdentifier name) +{ + int i; + for (i = 0; i < NUM_PROPERTY_IDENTIFIERS; i++) { + if (name == myPropertyIdentifiers[i]){ + return true; + } + } + return false; +} + +bool myHasMethod (NPClass *theClass, NPIdentifier name) +{ + int i; + for (i = 0; i < NUM_METHOD_IDENTIFIERS; i++) { + if (name == myMethodIdentifiers[i]){ + return true; + } + } + return false; +} + + +void logMessage (const NPVariant *message) +{ + if (message->type == NPVariantStringType) { + char msgBuf[1024]; + strncpy (msgBuf, message->value.stringValue.UTF8Characters, message->value.stringValue.UTF8Length); + msgBuf[message->value.stringValue.UTF8Length] = 0; + printf ("%s\n", msgBuf); + } + else if (message->type == NPVariantDoubleType) + printf ("%f\n", (float)message->value.doubleValue); + else if (message->type == NPVariantInt32Type) + printf ("%d\n", message->value.intValue); + else if (message->type == NPVariantObjectType) + printf ("%p\n", message->value.objectValue); +} + +void setDoubleValue (MyObject *obj, const NPVariant *variant) +{ + if (!NPN_VariantToDouble (variant, &obj->doubleValue)) { + NPUTF8 *msg = "Attempt to set double value with invalid type."; + NPString aString; + aString.UTF8Characters = msg; + aString.UTF8Length = strlen (msg); + NPN_SetException ((NPObject *)obj, &aString); + } +} + +void setIntValue (MyObject *obj, const NPVariant *variant) +{ + if (!NPN_VariantToInt32 (variant, &obj->intValue)) { + NPUTF8 *msg = "Attempt to set int value with invalid type."; + NPString aString; + aString.UTF8Characters = msg; + aString.UTF8Length = strlen (msg); + NPN_SetException ((NPObject *)obj, &aString); + } +} + +void setStringValue (MyObject *obj, const NPVariant *variant) +{ + NPN_ReleaseVariantValue (&obj->stringValue); + NPN_InitializeVariantWithVariant (&obj->stringValue, variant); +} + +void setBooleanValue (MyObject *obj, const NPVariant *variant) +{ + if (!NPN_VariantToBool (variant, (NPBool *)&obj->boolValue)) { + NPUTF8 *msg = "Attempt to set bool value with invalid type."; + NPString aString; + aString.UTF8Characters = msg; + aString.UTF8Length = strlen (msg); + NPN_SetException ((NPObject *)obj, &aString); + } +} + +void getDoubleValue (MyObject *obj, NPVariant *variant) +{ + NPN_InitializeVariantWithDouble (variant, obj->doubleValue); +} + +void getIntValue (MyObject *obj, NPVariant *variant) +{ + NPN_InitializeVariantWithInt32 (variant, obj->intValue); +} + +void getStringValue (MyObject *obj, NPVariant *variant) +{ + NPN_InitializeVariantWithVariant (variant, &obj->stringValue); +} + +void getBooleanValue (MyObject *obj, NPVariant *variant) +{ + NPN_InitializeVariantWithBool (variant, obj->boolValue); +} + +void myGetProperty (MyObject *obj, NPIdentifier name, NPVariant *variant) +{ + if (name == myPropertyIdentifiers[ID_DOUBLE_VALUE]){ + getDoubleValue (obj, variant); + } + else if (name == myPropertyIdentifiers[ID_INT_VALUE]){ + getIntValue (obj, variant); + } + else if (name == myPropertyIdentifiers[ID_STRING_VALUE]){ + getStringValue (obj, variant); + } + else if (name == myPropertyIdentifiers[ID_BOOLEAN_VALUE]){ + getBooleanValue (obj, variant); + } + else if (name == myPropertyIdentifiers[ID_NULL_VALUE]){ + return NPN_InitializeVariantAsNull (variant); + } + else if (name == myPropertyIdentifiers[ID_UNDEFINED_VALUE]){ + return NPN_InitializeVariantAsUndefined (variant); + } + else + NPN_InitializeVariantAsUndefined(variant); +} + +void mySetProperty (MyObject *obj, NPIdentifier name, const NPVariant *variant) +{ + if (name == myPropertyIdentifiers[ID_DOUBLE_VALUE]) { + setDoubleValue (obj, variant); + } + else if (name == myPropertyIdentifiers[ID_INT_VALUE]) { + setIntValue (obj, variant); + } + else if (name == myPropertyIdentifiers[ID_STRING_VALUE]) { + setStringValue (obj, variant); + } + else if (name == myPropertyIdentifiers[ID_BOOLEAN_VALUE]) { + setBooleanValue (obj, variant); + } + else if (name == myPropertyIdentifiers[ID_NULL_VALUE]) { + // Do nothing! + } + else if (name == myPropertyIdentifiers[ID_UNDEFINED_VALUE]) { + // Do nothing! + } +} + +void myInvoke (MyObject *obj, NPIdentifier name, NPVariant *args, unsigned argCount, NPVariant *result) +{ + if (name == myMethodIdentifiers[ID_LOG_MESSAGE]) { + if (argCount == 1 && NPN_VariantIsString(&args[0])) + logMessage (&args[0]); + NPN_InitializeVariantAsVoid (result); + } + else if (name == myMethodIdentifiers[ID_SET_DOUBLE_VALUE]) { + if (argCount == 1 && NPN_VariantIsDouble (&args[0])) + setDoubleValue (obj, &args[0]); + NPN_InitializeVariantAsVoid (result); + } + else if (name == myMethodIdentifiers[ID_SET_INT_VALUE]) { + if (argCount == 1 && (NPN_VariantIsDouble (&args[0]) || NPN_VariantIsInt32 (&args[0]))) + setIntValue (obj, &args[0]); + NPN_InitializeVariantAsVoid (result); + } + else if (name == myMethodIdentifiers[ID_SET_STRING_VALUE]) { + if (argCount == 1 && NPN_VariantIsString (&args[0])) + setStringValue (obj, &args[0]); + NPN_InitializeVariantAsVoid (result); + } + else if (name == myMethodIdentifiers[ID_SET_BOOLEAN_VALUE]) { + if (argCount == 1 && NPN_VariantIsBool (&args[0])) + setBooleanValue (obj, &args[0]); + NPN_InitializeVariantAsVoid (result); + } + else if (name == myMethodIdentifiers[ID_GET_DOUBLE_VALUE]) { + getDoubleValue (obj, result); + } + else if (name == myMethodIdentifiers[ID_GET_INT_VALUE]) { + getIntValue (obj, result); + } + else if (name == myMethodIdentifiers[ID_GET_STRING_VALUE]) { + getStringValue (obj, result); + } + else if (name == myMethodIdentifiers[ID_GET_BOOLEAN_VALUE]) { + getBooleanValue (obj, result); + } + else + NPN_InitializeVariantAsUndefined (result); +} + +NPObject *myAllocate () +{ + MyObject *newInstance = (MyObject *)malloc (sizeof(MyObject)); + + if (!identifiersInitialized) { + identifiersInitialized = true; + initializeIdentifiers(); + } + + + newInstance->doubleValue = 666.666; + newInstance->intValue = 1234; + newInstance->boolValue = true; + newInstance->stringValue.type = NPVariantType_String; + newInstance->stringValue.value.stringValue.UTF8Length = strlen ("Hello world"); + newInstance->stringValue.value.stringValue.UTF8Characters = strdup ("Hello world"); + + return (NPObject *)newInstance; +} + +void myInvalidate () +{ + // Make sure we've released any remaining references to JavaScript objects. +} + +void myDeallocate (MyObject *obj) +{ + free ((void *)obj); +} + +static NPClass _myFunctionPtrs = { + kNPClassStructVersionCurrent, + (NPAllocateFunctionPtr) myAllocate, + (NPDeallocateFunctionPtr) myDeallocate, + (NPInvalidateFunctionPtr) myInvalidate, + (NPHasMethodFunctionPtr) myHasMethod, + (NPInvokeFunctionPtr) myInvoke, + (NPHasPropertyFunctionPtr) myHasProperty, + (NPGetPropertyFunctionPtr) myGetProperty, + (NPSetPropertyFunctionPtr) mySetProperty, +}; +static NPClass *myFunctionPtrs = &_myFunctionPtrs; + +// -------------------------------------------------------- + +using namespace KJS; +using namespace KJS::Bindings; + +class GlobalImp : public ObjectImp { +public: + virtual UString className() const { return "global"; } +}; + +#define BufferSize 200000 +static char code[BufferSize]; + +const char *readJavaScriptFromFile (const char *file) +{ + FILE *f = fopen(file, "r"); + if (!f) { + fprintf(stderr, "Error opening %s.\n", file); + return 0; + } + + int num = fread(code, 1, BufferSize, f); + code[num] = '\0'; + if(num >= BufferSize) + fprintf(stderr, "Warning: File may have been too long.\n"); + + fclose(f); + + return code; +} + +int main(int argc, char **argv) +{ + // expecting a filename + if (argc < 2) { + fprintf(stderr, "You have to specify at least one filename\n"); + return -1; + } + + bool ret = true; + { + JSLock lock; + + // create interpreter w/ global object + Object global(new GlobalImp()); + Interpreter interp; + interp.setGlobalObject(global); + ExecState *exec = interp.globalExec(); + + MyObject *myObject = (MyObject *)NPN_CreateObject (myFunctionPtrs); + + global.put(exec, Identifier("myInterface"), Instance::createRuntimeObject(Instance::CLanguage, (void *)myObject)); + + for (int i = 1; i < argc; i++) { + const char *code = readJavaScriptFromFile(argv[i]); + + if (code) { + // run + Completion comp(interp.evaluate(code)); + + if (comp.complType() == Throw) { + Value exVal = comp.value(); + char *msg = exVal.toString(exec).ascii(); + int lineno = -1; + if (exVal.type() == ObjectType) { + Value lineVal = Object::dynamicCast(exVal).get(exec,Identifier("line")); + if (lineVal.type() == NumberType) + lineno = int(lineVal.toNumber(exec)); + } + if (lineno != -1) + fprintf(stderr,"Exception, line %d: %s\n",lineno,msg); + else + fprintf(stderr,"Exception: %s\n",msg); + ret = false; + } + else if (comp.complType() == ReturnValue) { + char *msg = comp.value().toString(interp.globalExec()).ascii(); + fprintf(stderr,"Return value: %s\n",msg); + } + } + } + + NPN_ReleaseObject ((NPObject *)myObject); + + } // end block, so that Interpreter and global get deleted + + return ret ? 0 : 3; +} diff --git a/JavaScriptCore/bindings/testbindings.mm b/JavaScriptCore/bindings/testbindings.mm new file mode 100644 index 0000000..7061503 --- /dev/null +++ b/JavaScriptCore/bindings/testbindings.mm @@ -0,0 +1,289 @@ +/* + * Copyright (C) 2004 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 COMPUTER, INC. OR + * 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 <Foundation/Foundation.h> + +#import <WebKit/WebScriptObject.h> + +#include <stdio.h> +#include <string.h> + +#include "value.h" +#include "object.h" +#include "types.h" +#include "interpreter.h" + +#include "runtime.h" +#include "runtime_object.h" + +#define LOG(formatAndArgs...) { \ + fprintf (stderr, "%s: ", __PRETTY_FUNCTION__); \ + fprintf(stderr, formatAndArgs); \ +} + +@interface MySecondInterface : NSObject +{ + double doubleValue; +} + +- init; + +@end + +@implementation MySecondInterface + +- init +{ + LOG ("\n"); + doubleValue = 666.666; + return self; +} + +@end + +@interface MyFirstInterface : NSObject +{ + int myInt; + MySecondInterface *mySecondInterface; + id jsobject; + NSString *string; +} + +- (int)getInt; +- (void)setInt: (int)anInt; +- (MySecondInterface *)getMySecondInterface; +- (void)logMessage:(NSString *)message; +- (void)setJSObject:(id)jsobject; +@end + +@implementation MyFirstInterface + ++ (NSString *)webScriptNameForSelector:(SEL)aSelector +{ + if (aSelector == @selector(logMessage:)) + return @"logMessage"; + if (aSelector == @selector(logMessages:)) + return @"logMessages"; + if (aSelector == @selector(logMessage:prefix:)) + return @"logMessageWithPrefix"; + return nil; +} + ++ (BOOL)isSelectorExcludedFromWebScript:(SEL)aSelector +{ + return NO; +} + ++ (BOOL)isKeyExcludedFromWebScript:(const char *)name +{ + return NO; +} + +/* +- (id)invokeUndefinedMethodFromWebScript:(NSString *)name withArguments:(NSArray *)args; +{ + NSLog (@"Call to undefined method %@", name); + NSLog (@"%d args\n", [args count]); + int i; + for (i = 0; i < [args count]; i++) { + NSLog (@"%d: %@\n", i, [args objectAtIndex:i]); + } + return @"success"; +} +*/ + +/* +- (id)valueForUndefinedKey:(NSString *)key +{ + NSLog (@"%s: key = %@", __PRETTY_FUNCTION__, key); + return @"aValue"; +} +*/ + +- (void)setValue:(id)value forUndefinedKey:(NSString *)key +{ + NSLog (@"%s: key = %@", __PRETTY_FUNCTION__, key); +} + +- init +{ + LOG ("\n"); + mySecondInterface = [[MySecondInterface alloc] init]; + return self; +} + +- (void)dealloc +{ + LOG ("\n"); + [mySecondInterface release]; + [super dealloc]; +} + +- (int)getInt +{ + LOG ("myInt = %d\n", myInt); + return myInt; +} + +- (void)setInt: (int)anInt +{ + LOG ("anInt = %d\n", anInt); + myInt = anInt; +} + +- (NSString *)getString +{ + return string; +} + +- (MySecondInterface *)getMySecondInterface +{ + LOG ("\n"); + return mySecondInterface; +} + +- (void)logMessage:(NSString *)message +{ + printf ("%s\n", [message lossyCString]); +} + +- (void)logMessages:(id)messages +{ + int i, count = [[messages valueForKey:@"length"] intValue]; + for (i = 0; i < count; i++) + printf ("%s\n", [[messages webScriptValueAtIndex:i] lossyCString]); +} + +- (void)logMessage:(NSString *)message prefix:(NSString *)prefix +{ + printf ("%s:%s\n", [prefix lossyCString], [message lossyCString]); +} + +- (void)setJSObject:(id)jso +{ + [jsobject autorelease]; + jsobject = [jso retain]; +} + +- (void)callJSObject:(int)arg1 :(int)arg2 +{ + id foo1 = [jsobject callWebScriptMethod:@"call" withArguments:[NSArray arrayWithObjects:jsobject, [NSNumber numberWithInt:arg1], [NSNumber numberWithInt:arg2], nil]]; + printf ("foo (via call) = %s\n", [[foo1 description] lossyCString] ); + id foo2 = [jsobject callWebScriptMethod:@"apply" withArguments:[NSArray arrayWithObjects:jsobject, [NSArray arrayWithObjects:[NSNumber numberWithInt:arg1], [NSNumber numberWithInt:arg2], nil], nil]]; + printf ("foo (via apply) = %s\n", [[foo2 description] lossyCString] ); +} + +@end + + +using namespace KJS; +using namespace KJS::Bindings; + +class GlobalImp : public ObjectImp { +public: + virtual UString className() const { return "global"; } +}; + +#define BufferSize 200000 +static char code[BufferSize]; + +const char *readJavaScriptFromFile (const char *file) +{ + FILE *f = fopen(file, "r"); + if (!f) { + fprintf(stderr, "Error opening %s.\n", file); + return 0; + } + + int num = fread(code, 1, BufferSize, f); + code[num] = '\0'; + if(num >= BufferSize) + fprintf(stderr, "Warning: File may have been too long.\n"); + + fclose(f); + + return code; +} + +int main(int argc, char **argv) +{ + // expecting a filename + if (argc < 2) { + fprintf(stderr, "You have to specify at least one filename\n"); + return -1; + } + + bool ret = true; + { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + JSLock lock; + + // create interpreter w/ global object + Object global(new GlobalImp()); + Interpreter interp; + interp.setGlobalObject(global); + ExecState *exec = interp.globalExec(); + + MyFirstInterface *myInterface = [[MyFirstInterface alloc] init]; + + global.put(exec, Identifier("myInterface"), Instance::createRuntimeObject(Instance::ObjectiveCLanguage, (void *)myInterface)); + + for (int i = 1; i < argc; i++) { + const char *code = readJavaScriptFromFile(argv[i]); + + if (code) { + // run + Completion comp(interp.evaluate(code)); + + if (comp.complType() == Throw) { + Value exVal = comp.value(); + char *msg = exVal.toString(exec).ascii(); + int lineno = -1; + if (exVal.type() == ObjectType) { + Value lineVal = Object::dynamicCast(exVal).get(exec,Identifier("line")); + if (lineVal.type() == NumberType) + lineno = int(lineVal.toNumber(exec)); + } + if (lineno != -1) + fprintf(stderr,"Exception, line %d: %s\n",lineno,msg); + else + fprintf(stderr,"Exception: %s\n",msg); + ret = false; + } + else if (comp.complType() == ReturnValue) { + char *msg = comp.value().toString(interp.globalExec()).ascii(); + fprintf(stderr,"Return value: %s\n",msg); + } + } + } + + [myInterface release]; + [pool drain]; + } // end block, so that Interpreter and global get deleted + + return ret ? 0 : 3; +} diff --git a/JavaScriptCore/bindings/testbindings.pro b/JavaScriptCore/bindings/testbindings.pro new file mode 100644 index 0000000..e6e0b9d --- /dev/null +++ b/JavaScriptCore/bindings/testbindings.pro @@ -0,0 +1,8 @@ +QT -= gui + +include(../../WebKit.pri) +INCLUDEPATH += .. ../kjs . +qt-port:INCLUDEPATH += bindings/qt + +SOURCES += testqtbindings.cpp + diff --git a/JavaScriptCore/bindings/testqtbindings.cpp b/JavaScriptCore/bindings/testqtbindings.cpp new file mode 100644 index 0000000..490b3ff --- /dev/null +++ b/JavaScriptCore/bindings/testqtbindings.cpp @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2006 Trolltech ASA + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 COMPUTER, INC. OR + * 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 <assert.h> +#include <stdio.h> +#include <string.h> + +#include "value.h" +#include "object.h" +#include "types.h" +#include "interpreter.h" + +#include "qobject.h" +#include "qdebug.h" + +#include "runtime.h" +#include "runtime_object.h" + + + +class MyObject : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString testString READ testString WRITE setTestString) + Q_PROPERTY(int testInt READ testInt WRITE setTestInt) + +public: + MyObject() : QObject(0), integer(0){} + + void setTestString(const QString &str) { + qDebug() << "called setTestString" << str; + string = str; + } + void setTestInt(int i) { + qDebug() << "called setTestInt" << i; + integer = i; + } + QString testString() const { + qDebug() << "called testString" << string; + return string; + } + int testInt() const { + qDebug() << "called testInt" << integer; + return integer; + } + QString string; + int integer; + +public slots: + void foo() { qDebug() << "foo invoked"; } +}; + +// -------------------------------------------------------- + +using namespace KJS; +using namespace KJS::Bindings; + +class Global : public JSObject { +public: + virtual UString className() const { return "global"; } +}; + +static char code[] = + "myInterface.foo();\n" + "myInterface.testString = \"Hello\";\n" + "str = myInterface.testString;\n" + "myInterface.testInt = 10;\n" + "i = myInterface.testInt;\n"; + +int main(int argc, char** argv) +{ + // expecting a filename + bool ret = true; + { + JSLock lock; + + // create interpreter w/ global object + Global* global = new Global(); + + // create interpreter + RefPtr<Interpreter> interp = new Interpreter(global); + ExecState* exec = interp->globalExec(); + + MyObject* myObject = new MyObject; + + global->put(exec, Identifier("myInterface"), Instance::createRuntimeObject(Instance::QtLanguage, (void*)myObject)); + + + if (code) { + // run + Completion comp(interp->evaluate("", 0, code)); + + if (comp.complType() == Throw) { + qDebug() << "exception thrown"; + JSValue* exVal = comp.value(); + char* msg = exVal->toString(exec).ascii(); + int lineno = -1; + if (exVal->type() == ObjectType) { + JSValue* lineVal = exVal->getObject()->get(exec, Identifier("line")); + if (lineVal->type() == NumberType) + lineno = int(lineVal->toNumber(exec)); + } + if (lineno != -1) + fprintf(stderr,"Exception, line %d: %s\n",lineno,msg); + else + fprintf(stderr,"Exception: %s\n",msg); + ret = false; + } + else if (comp.complType() == ReturnValue) { + char* msg = comp.value()->toString(interp->globalExec()).ascii(); + fprintf(stderr,"Return value: %s\n",msg); + } + } + + } // end block, so that Interpreter and global get deleted + + return ret ? 0 : 1; +} + +#include "testqtbindings.moc" |