diff options
Diffstat (limited to 'JavaScriptCore/qt/api')
-rw-r--r-- | JavaScriptCore/qt/api/QtScript.pro | 3 | ||||
-rw-r--r-- | JavaScriptCore/qt/api/qscriptengine.cpp | 107 | ||||
-rw-r--r-- | JavaScriptCore/qt/api/qscriptengine.h | 11 | ||||
-rw-r--r-- | JavaScriptCore/qt/api/qscriptengine_p.cpp | 36 | ||||
-rw-r--r-- | JavaScriptCore/qt/api/qscriptengine_p.h | 7 | ||||
-rw-r--r-- | JavaScriptCore/qt/api/qscriptfunction.cpp | 145 | ||||
-rw-r--r-- | JavaScriptCore/qt/api/qscriptfunction_p.h | 57 | ||||
-rw-r--r-- | JavaScriptCore/qt/api/qscriptoriginalglobalobject_p.h | 12 | ||||
-rw-r--r-- | JavaScriptCore/qt/api/qscriptvalue_p.h | 48 |
9 files changed, 417 insertions, 9 deletions
diff --git a/JavaScriptCore/qt/api/QtScript.pro b/JavaScriptCore/qt/api/QtScript.pro index c2c6f83..490758c 100644 --- a/JavaScriptCore/qt/api/QtScript.pro +++ b/JavaScriptCore/qt/api/QtScript.pro @@ -28,6 +28,7 @@ SOURCES += $$PWD/qscriptengine.cpp \ $$PWD/qscriptstring.cpp \ $$PWD/qscriptprogram.cpp \ $$PWD/qscriptsyntaxcheckresult.cpp \ + $$PWD/qscriptfunction.cpp HEADERS += $$PWD/qtscriptglobal.h \ $$PWD/qscriptengine.h \ @@ -43,7 +44,7 @@ HEADERS += $$PWD/qtscriptglobal.h \ $$PWD/qscriptprogram_p.h \ $$PWD/qscriptsyntaxcheckresult.h \ $$PWD/qscriptoriginalglobalobject_p.h \ - + $$PWD/qscriptfunction_p.h !static: DEFINES += QT_MAKEDLL diff --git a/JavaScriptCore/qt/api/qscriptengine.cpp b/JavaScriptCore/qt/api/qscriptengine.cpp index 4b2319b..7ef7c8e 100644 --- a/JavaScriptCore/qt/api/qscriptengine.cpp +++ b/JavaScriptCore/qt/api/qscriptengine.cpp @@ -258,6 +258,93 @@ QScriptValue QScriptEngine::undefinedValue() } /*! + Creates a QScriptValue that wraps a native (C++) function. \a fun + must be a C++ function with signature QScriptEngine::FunctionSignature. + \a length is the number of arguments that \a fun expects; this becomes + the \c{length} property of the created QScriptValue. + + Note that \a length only gives an indication of the number of + arguments that the function expects; an actual invocation of a + function can include any number of arguments. You can check the + \l{QScriptContext::argumentCount()}{argumentCount()} of the + QScriptContext associated with the invocation to determine the + actual number of arguments passed. + + A \c{prototype} property is automatically created for the resulting + function object, to provide for the possibility that the function + will be used as a constructor. + + By combining newFunction() and the property flags + QScriptValue::PropertyGetter and QScriptValue::PropertySetter, you + can create script object properties that behave like normal + properties in script code, but are in fact accessed through + functions (analogous to how properties work in \l{Qt's Property + System}). Example: + + \snippet doc/src/snippets/code/src_script_qscriptengine.cpp 11 + + When the property \c{foo} of the script object is subsequently + accessed in script code, \c{getSetFoo()} will be invoked to handle + the access. In this particular case, we chose to store the "real" + value of \c{foo} as a property of the accessor function itself; you + are of course free to do whatever you like in this function. + + In the above example, a single native function was used to handle + both reads and writes to the property; the argument count is used to + determine if we are handling a read or write. You can also use two + separate functions; just specify the relevant flag + (QScriptValue::PropertyGetter or QScriptValue::PropertySetter) when + setting the property, e.g.: + + \snippet doc/src/snippets/code/src_script_qscriptengine.cpp 12 + + \sa QScriptValue::call() +*/ +QScriptValue QScriptEngine::newFunction(QScriptEngine::FunctionSignature fun, int length) +{ + return QScriptValuePrivate::get(d_ptr->newFunction(fun, 0, length)); +} + +/*! + Creates a constructor function from \a fun, with the given \a length. + The \c{prototype} property of the resulting function is set to be the + given \a prototype. The \c{constructor} property of \a prototype is + set to be the resulting function. + + When a function is called as a constructor (e.g. \c{new Foo()}), the + `this' object associated with the function call is the new object + that the function is expected to initialize; the prototype of this + default constructed object will be the function's public + \c{prototype} property. If you always want the function to behave as + a constructor (e.g. \c{Foo()} should also create a new object), or + if you need to create your own object rather than using the default + `this' object, you should make sure that the prototype of your + object is set correctly; either by setting it manually, or, when + wrapping a custom type, by having registered the defaultPrototype() + of that type. Example: + + \snippet doc/src/snippets/code/src_script_qscriptengine.cpp 9 + + To wrap a custom type and provide a constructor for it, you'd typically + do something like this: + + \snippet doc/src/snippets/code/src_script_qscriptengine.cpp 10 +*/ +QScriptValue QScriptEngine::newFunction(QScriptEngine::FunctionSignature fun, const QScriptValue& prototype, int length) +{ + return QScriptValuePrivate::get(d_ptr->newFunction(fun, QScriptValuePrivate::get(prototype), length)); +} + +/*! + \internal + \since 4.4 +*/ +QScriptValue QScriptEngine::newFunction(QScriptEngine::FunctionWithArgSignature fun, void* arg) +{ + return QScriptValuePrivate::get(d_ptr->newFunction(fun, arg)); +} + +/*! Creates a QtScript object of class Object. The prototype of the created object will be the Object @@ -294,3 +381,23 @@ QScriptValue QScriptEngine::globalObject() const { return QScriptValuePrivate::get(d_ptr->globalObject()); } + +/*! + \typedef QScriptEngine::FunctionSignature + \relates QScriptEngine + + The function signature \c{QScriptValue f(QScriptContext *, QScriptEngine *)}. + + A function with such a signature can be passed to + QScriptEngine::newFunction() to wrap the function. +*/ + +/*! + \typedef QScriptEngine::FunctionWithArgSignature + \relates QScriptEngine + + The function signature \c{QScriptValue f(QScriptContext *, QScriptEngine *, void *)}. + + A function with such a signature can be passed to + QScriptEngine::newFunction() to wrap the function. +*/ diff --git a/JavaScriptCore/qt/api/qscriptengine.h b/JavaScriptCore/qt/api/qscriptengine.h index 1a87a37..b85dc52 100644 --- a/JavaScriptCore/qt/api/qscriptengine.h +++ b/JavaScriptCore/qt/api/qscriptengine.h @@ -30,6 +30,9 @@ class QScriptValue; class QScriptEnginePrivate; +// FIXME: Remove this once QScriptContext is properly defined. +typedef void QScriptContext; + // Internal typedef typedef QExplicitlySharedDataPointer<QScriptEnginePrivate> QScriptEnginePtr; @@ -56,6 +59,14 @@ public: QScriptValue nullValue(); QScriptValue undefinedValue(); + + typedef QScriptValue (*FunctionSignature)(QScriptContext *, QScriptEngine *); + typedef QScriptValue (*FunctionWithArgSignature)(QScriptContext *, QScriptEngine *, void *); + + QScriptValue newFunction(FunctionSignature fun, int length = 0); + QScriptValue newFunction(FunctionSignature fun, const QScriptValue& prototype, int length = 0); + QScriptValue newFunction(FunctionWithArgSignature fun, void* arg); + QScriptValue newObject(); QScriptValue newArray(uint length = 0); QScriptValue globalObject() const; diff --git a/JavaScriptCore/qt/api/qscriptengine_p.cpp b/JavaScriptCore/qt/api/qscriptengine_p.cpp index e3311ed..a708a34 100644 --- a/JavaScriptCore/qt/api/qscriptengine_p.cpp +++ b/JavaScriptCore/qt/api/qscriptengine_p.cpp @@ -21,6 +21,7 @@ #include "qscriptengine_p.h" +#include "qscriptfunction_p.h" #include "qscriptprogram_p.h" #include "qscriptvalue_p.h" @@ -33,11 +34,15 @@ QScriptEnginePrivate::QScriptEnginePrivate(const QScriptEngine* engine) , m_context(JSGlobalContextCreate(0)) , m_exception(0) , m_originalGlobalObject(m_context) + , m_nativeFunctionClass(JSClassCreate(&qt_NativeFunctionClass)) + , m_nativeFunctionWithArgClass(JSClassCreate(&qt_NativeFunctionWithArgClass)) { } QScriptEnginePrivate::~QScriptEnginePrivate() { + JSClassRelease(m_nativeFunctionClass); + JSClassRelease(m_nativeFunctionWithArgClass); if (m_exception) JSValueUnprotect(m_context, m_exception); JSGlobalContextRelease(m_context); @@ -86,6 +91,37 @@ QScriptValuePrivate* QScriptEnginePrivate::uncaughtException() const return m_exception ? new QScriptValuePrivate(this, m_exception) : new QScriptValuePrivate(); } +QScriptValuePrivate* QScriptEnginePrivate::newFunction(QScriptEngine::FunctionSignature fun, QScriptValuePrivate* prototype, int length) +{ + // Note that this private data will be deleted in the object finalize function. + QNativeFunctionData* data = new QNativeFunctionData(this, fun); + JSObjectRef funJS = JSObjectMake(m_context, m_nativeFunctionClass, reinterpret_cast<void*>(data)); + QScriptValuePrivate* proto = prototype ? prototype : newObject(); + return newFunction(funJS, proto); +} + +QScriptValuePrivate* QScriptEnginePrivate::newFunction(QScriptEngine::FunctionWithArgSignature fun, void* arg) +{ + // Note that this private data will be deleted in the object finalize function. + QNativeFunctionWithArgData* data = new QNativeFunctionWithArgData(this, fun, arg); + JSObjectRef funJS = JSObjectMake(m_context, m_nativeFunctionWithArgClass, reinterpret_cast<void*>(data)); + QScriptValuePrivate* proto = newObject(); + return newFunction(funJS, proto); +} + +QScriptValuePrivate* QScriptEnginePrivate::newFunction(JSObjectRef funJS, QScriptValuePrivate* prototype) +{ + JSObjectSetPrototype(m_context, funJS, m_originalGlobalObject.functionPrototype()); + + QScriptValuePrivate* result = new QScriptValuePrivate(this, funJS); + static JSStringRef protoName = QScriptConverter::toString("prototype"); + static JSStringRef constructorName = QScriptConverter::toString("constructor"); + result->setProperty(protoName, prototype, QScriptValue::Undeletable); + prototype->setProperty(constructorName, result, QScriptValue::PropertyFlags(QScriptValue::Undeletable | QScriptValue::SkipInEnumeration)); + + return result; +} + QScriptValuePrivate* QScriptEnginePrivate::newObject() const { return new QScriptValuePrivate(this, JSObjectMake(m_context, /* jsClass */ 0, /* userData */ 0)); diff --git a/JavaScriptCore/qt/api/qscriptengine_p.h b/JavaScriptCore/qt/api/qscriptengine_p.h index d54cdcc..eec1929 100644 --- a/JavaScriptCore/qt/api/qscriptengine_p.h +++ b/JavaScriptCore/qt/api/qscriptengine_p.h @@ -71,6 +71,10 @@ public: inline JSValueRef makeJSValue(bool number) const; inline JSValueRef makeJSValue(QScriptValue::SpecialValue value) const; + QScriptValuePrivate* newFunction(QScriptEngine::FunctionSignature fun, QScriptValuePrivate* prototype, int length); + QScriptValuePrivate* newFunction(QScriptEngine::FunctionWithArgSignature fun, void* arg); + QScriptValuePrivate* newFunction(JSObjectRef funObject, QScriptValuePrivate* prototype); + QScriptValuePrivate* newObject() const; QScriptValuePrivate* newArray(uint length); QScriptValuePrivate* globalObject() const; @@ -89,6 +93,9 @@ private: JSValueRef m_exception; QScriptOriginalGlobalObject m_originalGlobalObject; + + JSClassRef m_nativeFunctionClass; + JSClassRef m_nativeFunctionWithArgClass; }; diff --git a/JavaScriptCore/qt/api/qscriptfunction.cpp b/JavaScriptCore/qt/api/qscriptfunction.cpp new file mode 100644 index 0000000..9fe37e6 --- /dev/null +++ b/JavaScriptCore/qt/api/qscriptfunction.cpp @@ -0,0 +1,145 @@ +/* + Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) + + 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. +*/ + +// Contains the necessary helper structures to make possible expose +// C/C++ functions to JavaScript environment. + +#include "config.h" + +#include "qscriptfunction_p.h" + +static void qt_NativeFunction_finalize(JSObjectRef object) +{ + void* priv = JSObjectGetPrivate(object); + delete reinterpret_cast<QNativeFunctionData*>(priv); +} + +static JSValueRef qt_NativeFunction_callAsFunction(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) +{ + QNativeFunctionData* data = reinterpret_cast<QNativeFunctionData*>(JSObjectGetPrivate(object)); + + // TODO: build a QScriptContext and use it in the native call. + QScriptContext* scriptContext = 0; + Q_UNUSED(context); + Q_UNUSED(thisObject); + Q_UNUSED(argumentCount); + Q_UNUSED(arguments); + Q_UNUSED(exception); + + QScriptEnginePrivate* engine = data->engine; + QScriptValuePrivate* result = QScriptValuePrivate::get(data->fun(scriptContext, QScriptEnginePrivate::get(engine))); + if (!result->isValid()) { + qWarning("Invalid value returned from native function, returning undefined value instead."); + return engine->makeJSValue(QScriptValue::UndefinedValue); + } + + // Make sure that the result will be assigned to the correct engine. + if (!result->engine()) { + Q_ASSERT(result->isValid()); + result->assignEngine(engine); + } else if (result->engine() != engine) { + qWarning("Value from different engine returned from native function, returning undefined value instead."); + return engine->makeJSValue(QScriptValue::UndefinedValue); + } + + return *result; +} + +JSClassDefinition qt_NativeFunctionClass = { + 0, // version + kJSClassAttributeNoAutomaticPrototype, // attributes + + "", // className + 0, // parentClass + + 0, // staticValues + 0, // staticFunctions + + 0, // initialize + qt_NativeFunction_finalize, // finalize + 0, // hasProperty + 0, // getProperty + 0, // setProperty + 0, // deleteProperty + 0, // getPropertyNames + qt_NativeFunction_callAsFunction, // callAsFunction + 0, // callAsConstructor + 0, // hasInstance + 0 // convertToType +}; + +static void qt_NativeFunctionWithArg_finalize(JSObjectRef object) +{ + void* priv = JSObjectGetPrivate(object); + delete reinterpret_cast<QNativeFunctionWithArgData*>(priv); +} + +static JSValueRef qt_NativeFunctionWithArg_callAsFunction(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) +{ + QNativeFunctionWithArgData* data = reinterpret_cast<QNativeFunctionWithArgData*>(JSObjectGetPrivate(object)); + + // TODO: build a QScriptContext and use it in the native call. + QScriptContext* scriptContext = 0; + Q_UNUSED(context); + Q_UNUSED(thisObject); + Q_UNUSED(argumentCount); + Q_UNUSED(arguments); + Q_UNUSED(exception); + + QScriptEnginePrivate* engine = data->engine; + QScriptValuePrivate* result = QScriptValuePrivate::get(data->fun(scriptContext, QScriptEnginePrivate::get(engine), data->arg)); + if (!result->isValid()) { + qWarning("Invalid value returned from native function, returning undefined value instead."); + return engine->makeJSValue(QScriptValue::UndefinedValue); + } + + // Make sure that the result will be assigned to the correct engine. + if (!result->engine()) { + Q_ASSERT(result->isValid()); + result->assignEngine(engine); + } else if (result->engine() != engine) { + qWarning("Value from different engine returned from native function, returning undefined value instead."); + return engine->makeJSValue(QScriptValue::UndefinedValue); + } + + return *result; +} + +JSClassDefinition qt_NativeFunctionWithArgClass = { + 0, // version + kJSClassAttributeNoAutomaticPrototype, // attributes + + "", // className + 0, // parentClass + + 0, // staticValues + 0, // staticFunctions + + 0, // initialize + qt_NativeFunctionWithArg_finalize, // finalize + 0, // hasProperty + 0, // getProperty + 0, // setProperty + 0, // deleteProperty + 0, // getPropertyNames + qt_NativeFunctionWithArg_callAsFunction, // callAsFunction + 0, // callAsConstructor + 0, // hasInstance + 0 // convertToType +}; diff --git a/JavaScriptCore/qt/api/qscriptfunction_p.h b/JavaScriptCore/qt/api/qscriptfunction_p.h new file mode 100644 index 0000000..65b6046 --- /dev/null +++ b/JavaScriptCore/qt/api/qscriptfunction_p.h @@ -0,0 +1,57 @@ +/* + Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) + + 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. +*/ + +#ifndef qscriptfunction_p_h +#define qscriptfunction_p_h + +#include "config.h" + +#include "qscriptengine.h" +#include "qscriptvalue_p.h" + +extern JSClassDefinition qt_NativeFunctionClass; +extern JSClassDefinition qt_NativeFunctionWithArgClass; + +struct QNativeFunctionData +{ + QNativeFunctionData(QScriptEnginePrivate* engine, QScriptEngine::FunctionSignature fun) + : engine(engine) + , fun(fun) + { + } + + QScriptEnginePrivate* engine; + QScriptEngine::FunctionSignature fun; +}; + +struct QNativeFunctionWithArgData +{ + QNativeFunctionWithArgData(QScriptEnginePrivate* engine, QScriptEngine::FunctionWithArgSignature fun, void* arg) + : engine(engine) + , fun(fun) + , arg(arg) + { + } + + QScriptEnginePrivate* engine; + QScriptEngine::FunctionWithArgSignature fun; + void* arg; +}; + +#endif diff --git a/JavaScriptCore/qt/api/qscriptoriginalglobalobject_p.h b/JavaScriptCore/qt/api/qscriptoriginalglobalobject_p.h index 8d080fb..31e94f7 100644 --- a/JavaScriptCore/qt/api/qscriptoriginalglobalobject_p.h +++ b/JavaScriptCore/qt/api/qscriptoriginalglobalobject_p.h @@ -45,6 +45,8 @@ public: inline bool isArray(JSValueRef value) const; inline bool isError(JSValueRef value) const; + + inline JSValueRef functionPrototype() const; private: inline bool isType(JSValueRef value, JSObjectRef constructor, JSValueRef prototype) const; inline void initializeMember(JSObjectRef globalObject, JSStringRef prototypeName, const char* type, JSObjectRef& constructor, JSValueRef& prototype); @@ -57,6 +59,8 @@ private: JSValueRef m_arrayPrototype; JSObjectRef m_errorConstructor; JSValueRef m_errorPrototype; + JSObjectRef m_functionConstructor; + JSValueRef m_functionPrototype; // Reference to standard JS functions that are not exposed by JSC C API. JSObjectRef m_hasOwnPropertyFunction; @@ -73,6 +77,7 @@ QScriptOriginalGlobalObject::QScriptOriginalGlobalObject(JSGlobalContextRef cont propertyName.adopt(JSStringCreateWithUTF8CString("prototype")); initializeMember(globalObject, propertyName.get(), "Array", m_arrayConstructor, m_arrayPrototype); initializeMember(globalObject, propertyName.get(), "Error", m_errorConstructor, m_errorPrototype); + initializeMember(globalObject, propertyName.get(), "Function", m_functionConstructor, m_functionPrototype); propertyName.adopt(JSStringCreateWithUTF8CString("hasOwnProperty")); m_hasOwnPropertyFunction = const_cast<JSObjectRef>(JSObjectGetProperty(m_context, globalObject, propertyName.get(), &exception)); @@ -119,6 +124,8 @@ QScriptOriginalGlobalObject::~QScriptOriginalGlobalObject() JSValueUnprotect(m_context, m_arrayPrototype); JSValueUnprotect(m_context, m_errorConstructor); JSValueUnprotect(m_context, m_errorPrototype); + JSValueUnprotect(m_context, m_functionConstructor); + JSValueUnprotect(m_context, m_functionPrototype); JSValueUnprotect(m_context, m_hasOwnPropertyFunction); JSValueUnprotect(m_context, m_getOwnPropertyNamesFunction); JSGlobalContextRelease(m_context); @@ -173,6 +180,11 @@ inline bool QScriptOriginalGlobalObject::isError(JSValueRef value) const return isType(value, m_errorConstructor, m_errorPrototype); } +inline JSValueRef QScriptOriginalGlobalObject::functionPrototype() const +{ + return m_functionPrototype; +} + inline bool QScriptOriginalGlobalObject::isType(JSValueRef value, JSObjectRef constructor, JSValueRef prototype) const { // JSC API doesn't export the [[Class]] information for the builtins. But we know that a value diff --git a/JavaScriptCore/qt/api/qscriptvalue_p.h b/JavaScriptCore/qt/api/qscriptvalue_p.h index 6e93a07..6fbf98b 100644 --- a/JavaScriptCore/qt/api/qscriptvalue_p.h +++ b/JavaScriptCore/qt/api/qscriptvalue_p.h @@ -699,16 +699,48 @@ bool QScriptValuePrivate::equals(QScriptValuePrivate* other) if (!other->isValid()) return false; - if ((m_state == other->m_state) && !isJSBased()) { - if (isNumberBased()) - return u.m_number == other->u.m_number; - Q_ASSERT(isStringBased()); - return *u.m_string == *(other->u.m_string); + if (!isJSBased() && !other->isJSBased()) { + switch (m_state) { + case CNull: + case CUndefined: + return other->isUndefined() || other->isNull(); + case CNumber: + switch (other->m_state) { + case CBool: + case CString: + return u.m_number == other->toNumber(); + case CNumber: + return u.m_number == other->u.m_number; + default: + return false; + } + case CBool: + switch (other->m_state) { + case CBool: + return u.m_bool == other->u.m_bool; + case CNumber: + return toNumber() == other->u.m_number; + case CString: + return toNumber() == other->toNumber(); + default: + return false; + } + case CString: + switch (other->m_state) { + case CBool: + return toNumber() == other->toNumber(); + case CNumber: + return toNumber() == other->u.m_number; + case CString: + return *u.m_string == *other->u.m_string; + default: + return false; + } + default: + Q_ASSERT_X(false, "equals()", "Not all states are included in the previous switch statement."); + } } - if (!isJSBased() && !other->isJSBased()) - return false; - if (isJSBased() && !other->isJSBased()) { if (!other->assignEngine(engine())) { qWarning("equals(): Cannot compare to a value created in a different engine"); |