/**************************************************************************** | |
** | |
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). | |
** All rights reserved. | |
** Contact: Nokia Corporation (qt-info@nokia.com) | |
** | |
** This file is part of the QtScript module of the Qt Toolkit. | |
** | |
** $QT_BEGIN_LICENSE:LGPL-ONLY$ | |
** GNU Lesser General Public License Usage | |
** This file may be used under the terms of the GNU Lesser | |
** General Public License version 2.1 as published by the Free Software | |
** Foundation and appearing in the file LICENSE.LGPL included in the | |
** packaging of this file. Please review the following information to | |
** ensure the GNU Lesser General Public License version 2.1 requirements | |
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. | |
** | |
** If you have questions regarding the use of this file, please contact | |
** Nokia at qt-info@nokia.com. | |
** $QT_END_LICENSE$ | |
** | |
****************************************************************************/ | |
#include "config.h" | |
#include "qscriptengine.h" | |
#include "qscriptsyntaxchecker_p.h" | |
#include "qscriptengine_p.h" | |
#include "qscriptengineagent_p.h" | |
#include "qscriptcontext_p.h" | |
#include "qscriptstring_p.h" | |
#include "qscriptvalue_p.h" | |
#include "qscriptvalueiterator.h" | |
#include "qscriptclass.h" | |
#include "qscriptcontextinfo.h" | |
#include "qscriptprogram.h" | |
#include "qscriptprogram_p.h" | |
#include "qdebug.h" | |
#include <QtCore/qstringlist.h> | |
#include <QtCore/qmetaobject.h> | |
#include <math.h> | |
#include "CodeBlock.h" | |
#include "Error.h" | |
#include "Interpreter.h" | |
#include "ExceptionHelpers.h" | |
#include "PrototypeFunction.h" | |
#include "InitializeThreading.h" | |
#include "ObjectPrototype.h" | |
#include "SourceCode.h" | |
#include "FunctionPrototype.h" | |
#include "TimeoutChecker.h" | |
#include "JSFunction.h" | |
#include "Parser.h" | |
#include "PropertyNameArray.h" | |
#include "Operations.h" | |
#include "bridge/qscriptfunction_p.h" | |
#include "bridge/qscriptclassobject_p.h" | |
#include "bridge/qscriptvariant_p.h" | |
#include "bridge/qscriptqobject_p.h" | |
#include "bridge/qscriptglobalobject_p.h" | |
#include "bridge/qscriptactivationobject_p.h" | |
#include "bridge/qscriptstaticscopeobject_p.h" | |
#ifndef QT_NO_QOBJECT | |
#include <QtCore/qcoreapplication.h> | |
#include <QtCore/qdir.h> | |
#include <QtCore/qfile.h> | |
#include <QtCore/qfileinfo.h> | |
#include <QtCore/qpluginloader.h> | |
#include <QtCore/qset.h> | |
#include <QtCore/qtextstream.h> | |
#include "qscriptextensioninterface.h" | |
#endif | |
Q_DECLARE_METATYPE(QScriptValue) | |
#ifndef QT_NO_QOBJECT | |
Q_DECLARE_METATYPE(QObjectList) | |
#endif | |
Q_DECLARE_METATYPE(QList<int>) | |
QT_BEGIN_NAMESPACE | |
/*! | |
\since 4.3 | |
\class QScriptEngine | |
\reentrant | |
\brief The QScriptEngine class provides an environment for evaluating Qt Script code. | |
\ingroup script | |
\mainclass | |
See the \l{QtScript} documentation for information about the Qt Script language, | |
and how to get started with scripting your C++ application. | |
\section1 Evaluating Scripts | |
Use evaluate() to evaluate script code; this is the C++ equivalent | |
of the built-in script function \c{eval()}. | |
\snippet doc/src/snippets/code/src_script_qscriptengine.cpp 0 | |
evaluate() returns a QScriptValue that holds the result of the | |
evaluation. The QScriptValue class provides functions for converting | |
the result to various C++ types (e.g. QScriptValue::toString() | |
and QScriptValue::toNumber()). | |
The following code snippet shows how a script function can be | |
defined and then invoked from C++ using QScriptValue::call(): | |
\snippet doc/src/snippets/code/src_script_qscriptengine.cpp 1 | |
As can be seen from the above snippets, a script is provided to the | |
engine in the form of a string. One common way of loading scripts is | |
by reading the contents of a file and passing it to evaluate(): | |
\snippet doc/src/snippets/code/src_script_qscriptengine.cpp 2 | |
Here we pass the name of the file as the second argument to | |
evaluate(). This does not affect evaluation in any way; the second | |
argument is a general-purpose string that is used to identify the | |
script for debugging purposes (for example, our filename will now | |
show up in any uncaughtExceptionBacktrace() involving the script). | |
\section1 Engine Configuration | |
The globalObject() function returns the \bold {Global Object} | |
associated with the script engine. Properties of the Global Object | |
are accessible from any script code (i.e. they are global | |
variables). Typically, before evaluating "user" scripts, you will | |
want to configure a script engine by adding one or more properties | |
to the Global Object: | |
\snippet doc/src/snippets/code/src_script_qscriptengine.cpp 3 | |
Adding custom properties to the scripting environment is one of the | |
standard means of providing a scripting API that is specific to your | |
application. Usually these custom properties are objects created by | |
the newQObject() or newObject() functions, or constructor functions | |
created by newFunction(). | |
\section1 Script Exceptions | |
evaluate() can throw a script exception (e.g. due to a syntax | |
error); in that case, the return value is the value that was thrown | |
(typically an \c{Error} object). You can check whether the | |
evaluation caused an exception by calling hasUncaughtException(). In | |
that case, you can call toString() on the error object to obtain an | |
error message. The current uncaught exception is also available | |
through uncaughtException(). | |
Calling clearExceptions() will cause any uncaught exceptions to be | |
cleared. | |
\snippet doc/src/snippets/code/src_script_qscriptengine.cpp 4 | |
The checkSyntax() function can be used to determine whether code can be | |
usefully passed to evaluate(). | |
\section1 Script Object Creation | |
Use newObject() to create a standard Qt Script object; this is the | |
C++ equivalent of the script statement \c{new Object()}. You can use | |
the object-specific functionality in QScriptValue to manipulate the | |
script object (e.g. QScriptValue::setProperty()). Similarly, use | |
newArray() to create a Qt Script array object. Use newDate() to | |
create a \c{Date} object, and newRegExp() to create a \c{RegExp} | |
object. | |
\section1 QObject Integration | |
Use newQObject() to wrap a QObject (or subclass) | |
pointer. newQObject() returns a proxy script object; properties, | |
children, and signals and slots of the QObject are available as | |
properties of the proxy object. No binding code is needed because it | |
is done dynamically using the Qt meta object system. | |
\snippet doc/src/snippets/code/src_script_qscriptengine.cpp 5 | |
Use qScriptConnect() to connect a C++ signal to a script function; | |
this is the Qt Script equivalent of QObject::connect(). When a | |
script function is invoked in response to a C++ signal, it can cause | |
a script exception; you can connect to the signalHandlerException() | |
signal to catch such an exception. | |
Use newQMetaObject() to wrap a QMetaObject; this gives you a "script | |
representation" of a QObject-based class. newQMetaObject() returns a | |
proxy script object; enum values of the class are available as | |
properties of the proxy object. You can also specify a function that | |
will be used to construct objects of the class (e.g. when the | |
constructor is invoked from a script). For classes that have a | |
"standard" Qt constructor, Qt Script can provide a default script | |
constructor for you; see scriptValueFromQMetaObject(). | |
See the \l{QtScript} documentation for more information on | |
the QObject integration. | |
\section1 Support for Custom C++ Types | |
Use newVariant() to wrap a QVariant. This can be used to store | |
values of custom (non-QObject) C++ types that have been registered | |
with the Qt meta-type system. To make such types scriptable, you | |
typically associate a prototype (delegate) object with the C++ type | |
by calling setDefaultPrototype(); the prototype object defines the | |
scripting API for the C++ type. Unlike the QObject integration, | |
there is no automatic binding possible here; i.e. you have to create | |
the scripting API yourself, for example by using the QScriptable | |
class. | |
Use fromScriptValue() to cast from a QScriptValue to another type, | |
and toScriptValue() to create a QScriptValue from another value. | |
You can specify how the conversion of C++ types is to be performed | |
with qScriptRegisterMetaType() and qScriptRegisterSequenceMetaType(). | |
By default, Qt Script will use QVariant to store values of custom | |
types. | |
\section1 Importing Extensions | |
Use importExtension() to import plugin-based extensions into the | |
engine. Call availableExtensions() to obtain a list naming all the | |
available extensions, and importedExtensions() to obtain a list | |
naming only those extensions that have been imported. | |
Call pushContext() to open up a new variable scope, and popContext() | |
to close the current scope. This is useful if you are implementing | |
an extension that evaluates script code containing temporary | |
variable definitions (e.g. \c{var foo = 123;}) that are safe to | |
discard when evaluation has completed. | |
\section1 Native Functions | |
Use newFunction() to wrap native (C++) functions, including | |
constructors for your own custom types, so that these can be invoked | |
from script code. Such functions must have the signature | |
QScriptEngine::FunctionSignature. You may then pass the function as | |
argument to newFunction(). Here is an example of a function that | |
returns the sum of its first two arguments: | |
\snippet doc/src/snippets/code/src_script_qscriptengine.cpp 6 | |
To expose this function to script code, you can set it as a property | |
of the Global Object: | |
\snippet doc/src/snippets/code/src_script_qscriptengine.cpp 7 | |
Once this is done, script code can call your function in the exact | |
same manner as a "normal" script function: | |
\snippet doc/src/snippets/code/src_script_qscriptengine.cpp 8 | |
\section1 Long-running Scripts | |
If you need to evaluate possibly long-running scripts from the main | |
(GUI) thread, you should first call setProcessEventsInterval() to | |
make sure that the GUI stays responsive. You can abort a currently | |
running script by calling abortEvaluation(). You can determine | |
whether an engine is currently running a script by calling | |
isEvaluating(). | |
\section1 Garbage Collection | |
Qt Script objects may be garbage collected when they are no longer | |
referenced. There is no guarantee as to when automatic garbage | |
collection will take place. | |
The collectGarbage() function can be called to explicitly request | |
garbage collection. | |
The reportAdditionalMemoryCost() function can be called to indicate | |
that a Qt Script object occupies memory that isn't managed by the | |
scripting environment. Reporting the additional cost makes it more | |
likely that the garbage collector will be triggered. This can be | |
useful, for example, when many custom, native Qt Script objects are | |
allocated. | |
\section1 Core Debugging/Tracing Facilities | |
Since Qt 4.4, you can be notified of events pertaining to script | |
execution (e.g. script function calls and statement execution) | |
through the QScriptEngineAgent interface; see the setAgent() | |
function. This can be used to implement debugging and profiling of a | |
QScriptEngine. | |
\sa QScriptValue, QScriptContext, QScriptEngineAgent | |
*/ | |
/*! | |
\enum QScriptEngine::ValueOwnership | |
This enum specifies the ownership when wrapping a C++ value, e.g. by using newQObject(). | |
\value QtOwnership The standard Qt ownership rules apply, i.e. the | |
associated object will never be explicitly deleted by the script | |
engine. This is the default. (QObject ownership is explained in | |
\l{Object Trees & Ownership}.) | |
\value ScriptOwnership The value is owned by the script | |
environment. The associated data will be deleted when appropriate | |
(i.e. after the garbage collector has discovered that there are no | |
more live references to the value). | |
\value AutoOwnership If the associated object has a parent, the Qt | |
ownership rules apply (QtOwnership); otherwise, the object is | |
owned by the script environment (ScriptOwnership). | |
*/ | |
/*! | |
\enum QScriptEngine::QObjectWrapOption | |
These flags specify options when wrapping a QObject pointer with newQObject(). | |
\value ExcludeChildObjects The script object will not expose child objects as properties. | |
\value ExcludeSuperClassMethods The script object will not expose signals and slots inherited from the superclass. | |
\value ExcludeSuperClassProperties The script object will not expose properties inherited from the superclass. | |
\value ExcludeSuperClassContents Shorthand form for ExcludeSuperClassMethods | ExcludeSuperClassProperties | |
\value ExcludeDeleteLater The script object will not expose the QObject::deleteLater() slot. | |
\value ExcludeSlots The script object will not expose the QObject's slots. | |
\value AutoCreateDynamicProperties Properties that don't already exist in the QObject will be created as dynamic properties of that object, rather than as properties of the script object. | |
\value PreferExistingWrapperObject If a wrapper object with the requested configuration already exists, return that object. | |
\value SkipMethodsInEnumeration Don't include methods (signals and slots) when enumerating the object's properties. | |
*/ | |
class QScriptSyntaxCheckResultPrivate | |
{ | |
public: | |
QScriptSyntaxCheckResultPrivate() { ref = 0; } | |
~QScriptSyntaxCheckResultPrivate() {} | |
QScriptSyntaxCheckResult::State state; | |
int errorColumnNumber; | |
int errorLineNumber; | |
QString errorMessage; | |
QBasicAtomicInt ref; | |
}; | |
class QScriptTypeInfo | |
{ | |
public: | |
QScriptTypeInfo() : signature(0, '\0'), marshal(0), demarshal(0) | |
{ } | |
QByteArray signature; | |
QScriptEngine::MarshalFunction marshal; | |
QScriptEngine::DemarshalFunction demarshal; | |
JSC::JSValue prototype; | |
}; | |
namespace QScript | |
{ | |
static const qsreal D32 = 4294967296.0; | |
qint32 ToInt32(qsreal n) | |
{ | |
if (qIsNaN(n) || qIsInf(n) || (n == 0)) | |
return 0; | |
qsreal sign = (n < 0) ? -1.0 : 1.0; | |
qsreal abs_n = fabs(n); | |
n = ::fmod(sign * ::floor(abs_n), D32); | |
const double D31 = D32 / 2.0; | |
if (sign == -1 && n < -D31) | |
n += D32; | |
else if (sign != -1 && n >= D31) | |
n -= D32; | |
return qint32 (n); | |
} | |
quint32 ToUInt32(qsreal n) | |
{ | |
if (qIsNaN(n) || qIsInf(n) || (n == 0)) | |
return 0; | |
qsreal sign = (n < 0) ? -1.0 : 1.0; | |
qsreal abs_n = fabs(n); | |
n = ::fmod(sign * ::floor(abs_n), D32); | |
if (n < 0) | |
n += D32; | |
return quint32 (n); | |
} | |
quint16 ToUInt16(qsreal n) | |
{ | |
static const qsreal D16 = 65536.0; | |
if (qIsNaN(n) || qIsInf(n) || (n == 0)) | |
return 0; | |
qsreal sign = (n < 0) ? -1.0 : 1.0; | |
qsreal abs_n = fabs(n); | |
n = ::fmod(sign * ::floor(abs_n), D16); | |
if (n < 0) | |
n += D16; | |
return quint16 (n); | |
} | |
qsreal ToInteger(qsreal n) | |
{ | |
if (qIsNaN(n)) | |
return 0; | |
if (n == 0 || qIsInf(n)) | |
return n; | |
int sign = n < 0 ? -1 : 1; | |
return sign * ::floor(::fabs(n)); | |
} | |
#ifdef Q_CC_MSVC | |
// MSVC2008 crashes if these are inlined. | |
QString ToString(qsreal value) | |
{ | |
return JSC::UString::from(value); | |
} | |
qsreal ToNumber(const QString &value) | |
{ | |
return ((JSC::UString)value).toDouble(); | |
} | |
#endif | |
static const qsreal MsPerSecond = 1000.0; | |
static inline int MsFromTime(qsreal t) | |
{ | |
int r = int(::fmod(t, MsPerSecond)); | |
return (r >= 0) ? r : r + int(MsPerSecond); | |
} | |
/*! | |
\internal | |
Converts a JS date value (milliseconds) to a QDateTime (local time). | |
*/ | |
QDateTime MsToDateTime(JSC::ExecState *exec, qsreal t) | |
{ | |
if (qIsNaN(t)) | |
return QDateTime(); | |
JSC::GregorianDateTime tm; | |
JSC::msToGregorianDateTime(exec, t, /*output UTC=*/true, tm); | |
int ms = MsFromTime(t); | |
QDateTime convertedUTC = QDateTime(QDate(tm.year + 1900, tm.month + 1, tm.monthDay), | |
QTime(tm.hour, tm.minute, tm.second, ms), Qt::UTC); | |
return convertedUTC.toLocalTime(); | |
} | |
/*! | |
\internal | |
Converts a QDateTime to a JS date value (milliseconds). | |
*/ | |
qsreal DateTimeToMs(JSC::ExecState *exec, const QDateTime &dt) | |
{ | |
if (!dt.isValid()) | |
return qSNaN(); | |
QDateTime utc = dt.toUTC(); | |
QDate date = utc.date(); | |
QTime time = utc.time(); | |
JSC::GregorianDateTime tm; | |
tm.year = date.year() - 1900; | |
tm.month = date.month() - 1; | |
tm.monthDay = date.day(); | |
tm.weekDay = date.dayOfWeek(); | |
tm.yearDay = date.dayOfYear(); | |
tm.hour = time.hour(); | |
tm.minute = time.minute(); | |
tm.second = time.second(); | |
return JSC::gregorianDateTimeToMS(exec, tm, time.msec(), /*inputIsUTC=*/true); | |
} | |
void GlobalClientData::mark(JSC::MarkStack& markStack) | |
{ | |
engine->mark(markStack); | |
} | |
class TimeoutCheckerProxy : public JSC::TimeoutChecker | |
{ | |
public: | |
TimeoutCheckerProxy(const JSC::TimeoutChecker& originalChecker) | |
: JSC::TimeoutChecker(originalChecker) | |
, m_shouldProcessEvents(false) | |
, m_shouldAbortEvaluation(false) | |
{} | |
void setShouldProcessEvents(bool shouldProcess) { m_shouldProcessEvents = shouldProcess; } | |
void setShouldAbort(bool shouldAbort) { m_shouldAbortEvaluation = shouldAbort; } | |
bool shouldAbort() { return m_shouldAbortEvaluation; } | |
virtual bool didTimeOut(JSC::ExecState* exec) | |
{ | |
if (JSC::TimeoutChecker::didTimeOut(exec)) | |
return true; | |
if (m_shouldProcessEvents) | |
QCoreApplication::processEvents(); | |
return m_shouldAbortEvaluation; | |
} | |
private: | |
bool m_shouldProcessEvents; | |
bool m_shouldAbortEvaluation; | |
}; | |
static int toDigit(char c) | |
{ | |
if ((c >= '0') && (c <= '9')) | |
return c - '0'; | |
else if ((c >= 'a') && (c <= 'z')) | |
return 10 + c - 'a'; | |
else if ((c >= 'A') && (c <= 'Z')) | |
return 10 + c - 'A'; | |
return -1; | |
} | |
qsreal integerFromString(const char *buf, int size, int radix) | |
{ | |
if (size == 0) | |
return qSNaN(); | |
qsreal sign = 1.0; | |
int i = 0; | |
if (buf[0] == '+') { | |
++i; | |
} else if (buf[0] == '-') { | |
sign = -1.0; | |
++i; | |
} | |
if (((size-i) >= 2) && (buf[i] == '0')) { | |
if (((buf[i+1] == 'x') || (buf[i+1] == 'X')) | |
&& (radix < 34)) { | |
if ((radix != 0) && (radix != 16)) | |
return 0; | |
radix = 16; | |
i += 2; | |
} else { | |
if (radix == 0) { | |
radix = 8; | |
++i; | |
} | |
} | |
} else if (radix == 0) { | |
radix = 10; | |
} | |
int j = i; | |
for ( ; i < size; ++i) { | |
int d = toDigit(buf[i]); | |
if ((d == -1) || (d >= radix)) | |
break; | |
} | |
qsreal result; | |
if (j == i) { | |
if (!qstrcmp(buf, "Infinity")) | |
result = qInf(); | |
else | |
result = qSNaN(); | |
} else { | |
result = 0; | |
qsreal multiplier = 1; | |
for (--i ; i >= j; --i, multiplier *= radix) | |
result += toDigit(buf[i]) * multiplier; | |
} | |
result *= sign; | |
return result; | |
} | |
qsreal integerFromString(const QString &str, int radix) | |
{ | |
QByteArray ba = str.trimmed().toUtf8(); | |
return integerFromString(ba.constData(), ba.size(), radix); | |
} | |
bool isFunction(JSC::JSValue value) | |
{ | |
if (!value || !value.isObject()) | |
return false; | |
JSC::CallData callData; | |
return (JSC::asObject(value)->getCallData(callData) != JSC::CallTypeNone); | |
} | |
static JSC::JSValue JSC_HOST_CALL functionConnect(JSC::ExecState*, JSC::JSObject*, JSC::JSValue, const JSC::ArgList&); | |
static JSC::JSValue JSC_HOST_CALL functionDisconnect(JSC::ExecState*, JSC::JSObject*, JSC::JSValue, const JSC::ArgList&); | |
JSC::JSValue JSC_HOST_CALL functionDisconnect(JSC::ExecState *exec, JSC::JSObject * /*callee*/, JSC::JSValue thisObject, const JSC::ArgList &args) | |
{ | |
#ifndef QT_NO_QOBJECT | |
if (args.size() == 0) { | |
return JSC::throwError(exec, JSC::GeneralError, "Function.prototype.disconnect: no arguments given"); | |
} | |
if (!JSC::asObject(thisObject)->inherits(&QScript::QtFunction::info)) { | |
return JSC::throwError(exec, JSC::TypeError, "Function.prototype.disconnect: this object is not a signal"); | |
} | |
QScript::QtFunction *qtSignal = static_cast<QScript::QtFunction*>(JSC::asObject(thisObject)); | |
const QMetaObject *meta = qtSignal->metaObject(); | |
if (!meta) { | |
return JSC::throwError(exec, JSC::TypeError, "Function.prototype.discconnect: cannot disconnect from deleted QObject"); | |
} | |
QMetaMethod sig = meta->method(qtSignal->initialIndex()); | |
if (sig.methodType() != QMetaMethod::Signal) { | |
QString message = QString::fromLatin1("Function.prototype.disconnect: %0::%1 is not a signal") | |
.arg(QLatin1String(qtSignal->metaObject()->className())) | |
.arg(QLatin1String(sig.signature())); | |
return JSC::throwError(exec, JSC::TypeError, message); | |
} | |
QScriptEnginePrivate *engine = scriptEngineFromExec(exec); | |
JSC::JSValue receiver; | |
JSC::JSValue slot; | |
JSC::JSValue arg0 = args.at(0); | |
if (args.size() < 2) { | |
slot = arg0; | |
} else { | |
receiver = arg0; | |
JSC::JSValue arg1 = args.at(1); | |
if (isFunction(arg1)) | |
slot = arg1; | |
else { | |
QScript::SaveFrameHelper saveFrame(engine, exec); | |
JSC::UString propertyName = QScriptEnginePrivate::toString(exec, arg1); | |
slot = QScriptEnginePrivate::property(exec, arg0, propertyName, QScriptValue::ResolvePrototype); | |
} | |
} | |
if (!isFunction(slot)) { | |
return JSC::throwError(exec, JSC::TypeError, "Function.prototype.disconnect: target is not a function"); | |
} | |
bool ok = engine->scriptDisconnect(thisObject, receiver, slot); | |
if (!ok) { | |
QString message = QString::fromLatin1("Function.prototype.disconnect: failed to disconnect from %0::%1") | |
.arg(QLatin1String(qtSignal->metaObject()->className())) | |
.arg(QLatin1String(sig.signature())); | |
return JSC::throwError(exec, JSC::GeneralError, message); | |
} | |
return JSC::jsUndefined(); | |
#else | |
Q_UNUSED(eng); | |
return context->throwError(QScriptContext::TypeError, | |
QLatin1String("Function.prototype.disconnect")); | |
#endif // QT_NO_QOBJECT | |
} | |
JSC::JSValue JSC_HOST_CALL functionConnect(JSC::ExecState *exec, JSC::JSObject * /*callee*/, JSC::JSValue thisObject, const JSC::ArgList &args) | |
{ | |
#ifndef QT_NO_QOBJECT | |
if (args.size() == 0) { | |
return JSC::throwError(exec, JSC::GeneralError,"Function.prototype.connect: no arguments given"); | |
} | |
if (!JSC::asObject(thisObject)->inherits(&QScript::QtFunction::info)) { | |
return JSC::throwError(exec, JSC::TypeError, "Function.prototype.connect: this object is not a signal"); | |
} | |
QScript::QtFunction *qtSignal = static_cast<QScript::QtFunction*>(JSC::asObject(thisObject)); | |
const QMetaObject *meta = qtSignal->metaObject(); | |
if (!meta) { | |
return JSC::throwError(exec, JSC::TypeError, "Function.prototype.connect: cannot connect to deleted QObject"); | |
} | |
QMetaMethod sig = meta->method(qtSignal->initialIndex()); | |
if (sig.methodType() != QMetaMethod::Signal) { | |
QString message = QString::fromLatin1("Function.prototype.connect: %0::%1 is not a signal") | |
.arg(QLatin1String(qtSignal->metaObject()->className())) | |
.arg(QLatin1String(sig.signature())); | |
return JSC::throwError(exec, JSC::TypeError, message); | |
} | |
{ | |
QList<int> overloads = qtSignal->overloadedIndexes(); | |
if (!overloads.isEmpty()) { | |
overloads.append(qtSignal->initialIndex()); | |
QByteArray signature = sig.signature(); | |
QString message = QString::fromLatin1("Function.prototype.connect: ambiguous connect to %0::%1(); candidates are\n") | |
.arg(QLatin1String(qtSignal->metaObject()->className())) | |
.arg(QLatin1String(signature.left(signature.indexOf('(')))); | |
for (int i = 0; i < overloads.size(); ++i) { | |
QMetaMethod mtd = meta->method(overloads.at(i)); | |
message.append(QString::fromLatin1(" %0\n").arg(QString::fromLatin1(mtd.signature()))); | |
} | |
message.append(QString::fromLatin1("Use e.g. object['%0'].connect() to connect to a particular overload") | |
.arg(QLatin1String(signature))); | |
return JSC::throwError(exec, JSC::GeneralError, message); | |
} | |
} | |
QScriptEnginePrivate *engine = scriptEngineFromExec(exec); | |
JSC::JSValue receiver; | |
JSC::JSValue slot; | |
JSC::JSValue arg0 = args.at(0); | |
if (args.size() < 2) { | |
slot = arg0; | |
} else { | |
receiver = arg0; | |
JSC::JSValue arg1 = args.at(1); | |
if (isFunction(arg1)) | |
slot = arg1; | |
else { | |
QScript::SaveFrameHelper saveFrame(engine, exec); | |
JSC::UString propertyName = QScriptEnginePrivate::toString(exec, arg1); | |
slot = QScriptEnginePrivate::property(exec, arg0, propertyName, QScriptValue::ResolvePrototype); | |
} | |
} | |
if (!isFunction(slot)) { | |
return JSC::throwError(exec, JSC::TypeError, "Function.prototype.connect: target is not a function"); | |
} | |
bool ok = engine->scriptConnect(thisObject, receiver, slot, Qt::AutoConnection); | |
if (!ok) { | |
QString message = QString::fromLatin1("Function.prototype.connect: failed to connect to %0::%1") | |
.arg(QLatin1String(qtSignal->metaObject()->className())) | |
.arg(QLatin1String(sig.signature())); | |
return JSC::throwError(exec, JSC::GeneralError, message); | |
} | |
return JSC::jsUndefined(); | |
#else | |
Q_UNUSED(eng); | |
Q_UNUSED(classInfo); | |
return context->throwError(QScriptContext::TypeError, | |
QLatin1String("Function.prototype.connect")); | |
#endif // QT_NO_QOBJECT | |
} | |
static JSC::JSValue JSC_HOST_CALL functionPrint(JSC::ExecState*, JSC::JSObject*, JSC::JSValue, const JSC::ArgList&); | |
static JSC::JSValue JSC_HOST_CALL functionGC(JSC::ExecState*, JSC::JSObject*, JSC::JSValue, const JSC::ArgList&); | |
static JSC::JSValue JSC_HOST_CALL functionVersion(JSC::ExecState*, JSC::JSObject*, JSC::JSValue, const JSC::ArgList&); | |
JSC::JSValue JSC_HOST_CALL functionPrint(JSC::ExecState* exec, JSC::JSObject*, JSC::JSValue, const JSC::ArgList& args) | |
{ | |
QString result; | |
for (unsigned i = 0; i < args.size(); ++i) { | |
if (i != 0) | |
result.append(QLatin1Char(' ')); | |
QString s(args.at(i).toString(exec)); | |
if (exec->hadException()) | |
break; | |
result.append(s); | |
} | |
if (exec->hadException()) | |
return exec->exception(); | |
qDebug("%s", qPrintable(result)); | |
return JSC::jsUndefined(); | |
} | |
JSC::JSValue JSC_HOST_CALL functionGC(JSC::ExecState* exec, JSC::JSObject*, JSC::JSValue, const JSC::ArgList&) | |
{ | |
QScriptEnginePrivate *engine = scriptEngineFromExec(exec); | |
engine->collectGarbage(); | |
return JSC::jsUndefined(); | |
} | |
JSC::JSValue JSC_HOST_CALL functionVersion(JSC::ExecState *exec, JSC::JSObject*, JSC::JSValue, const JSC::ArgList&) | |
{ | |
return JSC::JSValue(exec, 1); | |
} | |
static JSC::JSValue JSC_HOST_CALL functionQsTranslate(JSC::ExecState*, JSC::JSObject*, JSC::JSValue, const JSC::ArgList&); | |
static JSC::JSValue JSC_HOST_CALL functionQsTranslateNoOp(JSC::ExecState*, JSC::JSObject*, JSC::JSValue, const JSC::ArgList&); | |
static JSC::JSValue JSC_HOST_CALL functionQsTr(JSC::ExecState*, JSC::JSObject*, JSC::JSValue, const JSC::ArgList&); | |
static JSC::JSValue JSC_HOST_CALL functionQsTrNoOp(JSC::ExecState*, JSC::JSObject*, JSC::JSValue, const JSC::ArgList&); | |
static JSC::JSValue JSC_HOST_CALL functionQsTrId(JSC::ExecState*, JSC::JSObject*, JSC::JSValue, const JSC::ArgList&); | |
static JSC::JSValue JSC_HOST_CALL functionQsTrIdNoOp(JSC::ExecState*, JSC::JSObject*, JSC::JSValue, const JSC::ArgList&); | |
JSC::JSValue JSC_HOST_CALL functionQsTranslate(JSC::ExecState *exec, JSC::JSObject*, JSC::JSValue, const JSC::ArgList &args) | |
{ | |
if (args.size() < 2) | |
return JSC::throwError(exec, JSC::GeneralError, "qsTranslate() requires at least two arguments"); | |
if (!args.at(0).isString()) | |
return JSC::throwError(exec, JSC::GeneralError, "qsTranslate(): first argument (context) must be a string"); | |
if (!args.at(1).isString()) | |
return JSC::throwError(exec, JSC::GeneralError, "qsTranslate(): second argument (text) must be a string"); | |
if ((args.size() > 2) && !args.at(2).isString()) | |
return JSC::throwError(exec, JSC::GeneralError, "qsTranslate(): third argument (comment) must be a string"); | |
if ((args.size() > 3) && !args.at(3).isString()) | |
return JSC::throwError(exec, JSC::GeneralError, "qsTranslate(): fourth argument (encoding) must be a string"); | |
if ((args.size() > 4) && !args.at(4).isNumber()) | |
return JSC::throwError(exec, JSC::GeneralError, "qsTranslate(): fifth argument (n) must be a number"); | |
#ifndef QT_NO_QOBJECT | |
JSC::UString context = args.at(0).toString(exec); | |
#endif | |
JSC::UString text = args.at(1).toString(exec); | |
#ifndef QT_NO_QOBJECT | |
JSC::UString comment; | |
if (args.size() > 2) | |
comment = args.at(2).toString(exec); | |
QCoreApplication::Encoding encoding = QCoreApplication::CodecForTr; | |
if (args.size() > 3) { | |
JSC::UString encStr = args.at(3).toString(exec); | |
if (encStr == "CodecForTr") | |
encoding = QCoreApplication::CodecForTr; | |
else if (encStr == "UnicodeUTF8") | |
encoding = QCoreApplication::UnicodeUTF8; | |
else | |
return JSC::throwError(exec, JSC::GeneralError, QString::fromLatin1("qsTranslate(): invalid encoding '%0'").arg(encStr)); | |
} | |
int n = -1; | |
if (args.size() > 4) | |
n = args.at(4).toInt32(exec); | |
#endif | |
JSC::UString result; | |
#ifndef QT_NO_QOBJECT | |
result = QCoreApplication::translate(QScript::convertToLatin1(context).constData(), | |
QScript::convertToLatin1(text).constData(), | |
QScript::convertToLatin1(comment).constData(), | |
encoding, n); | |
#else | |
result = text; | |
#endif | |
return JSC::jsString(exec, result); | |
} | |
JSC::JSValue JSC_HOST_CALL functionQsTranslateNoOp(JSC::ExecState *, JSC::JSObject*, JSC::JSValue, const JSC::ArgList &args) | |
{ | |
if (args.size() < 2) | |
return JSC::jsUndefined(); | |
return args.at(1); | |
} | |
JSC::JSValue JSC_HOST_CALL functionQsTr(JSC::ExecState *exec, JSC::JSObject*, JSC::JSValue, const JSC::ArgList &args) | |
{ | |
if (args.size() < 1) | |
return JSC::throwError(exec, JSC::GeneralError, "qsTr() requires at least one argument"); | |
if (!args.at(0).isString()) | |
return JSC::throwError(exec, JSC::GeneralError, "qsTr(): first argument (text) must be a string"); | |
if ((args.size() > 1) && !args.at(1).isString()) | |
return JSC::throwError(exec, JSC::GeneralError, "qsTr(): second argument (comment) must be a string"); | |
if ((args.size() > 2) && !args.at(2).isNumber()) | |
return JSC::throwError(exec, JSC::GeneralError, "qsTr(): third argument (n) must be a number"); | |
#ifndef QT_NO_QOBJECT | |
QScriptEnginePrivate *engine = scriptEngineFromExec(exec); | |
JSC::UString context; | |
// The first non-empty source URL in the call stack determines the translation context. | |
{ | |
JSC::ExecState *frame = exec->callerFrame()->removeHostCallFrameFlag(); | |
while (frame) { | |
if (frame->codeBlock() && QScriptEnginePrivate::hasValidCodeBlockRegister(frame) | |
&& frame->codeBlock()->source() | |
&& !frame->codeBlock()->source()->url().isEmpty()) { | |
context = engine->translationContextFromUrl(frame->codeBlock()->source()->url()); | |
break; | |
} | |
frame = frame->callerFrame()->removeHostCallFrameFlag(); | |
} | |
} | |
#endif | |
JSC::UString text = args.at(0).toString(exec); | |
#ifndef QT_NO_QOBJECT | |
JSC::UString comment; | |
if (args.size() > 1) | |
comment = args.at(1).toString(exec); | |
int n = -1; | |
if (args.size() > 2) | |
n = args.at(2).toInt32(exec); | |
#endif | |
JSC::UString result; | |
#ifndef QT_NO_QOBJECT | |
result = QCoreApplication::translate(QScript::convertToLatin1(context).constData(), | |
QScript::convertToLatin1(text).constData(), | |
QScript::convertToLatin1(comment).constData(), | |
QCoreApplication::CodecForTr, n); | |
#else | |
result = text; | |
#endif | |
return JSC::jsString(exec, result); | |
} | |
JSC::JSValue JSC_HOST_CALL functionQsTrNoOp(JSC::ExecState *, JSC::JSObject*, JSC::JSValue, const JSC::ArgList &args) | |
{ | |
if (args.size() < 1) | |
return JSC::jsUndefined(); | |
return args.at(0); | |
} | |
JSC::JSValue JSC_HOST_CALL functionQsTrId(JSC::ExecState *exec, JSC::JSObject*, JSC::JSValue, const JSC::ArgList &args) | |
{ | |
if (args.size() < 1) | |
return JSC::throwError(exec, JSC::GeneralError, "qsTrId() requires at least one argument"); | |
if (!args.at(0).isString()) | |
return JSC::throwError(exec, JSC::TypeError, "qsTrId(): first argument (id) must be a string"); | |
if ((args.size() > 1) && !args.at(1).isNumber()) | |
return JSC::throwError(exec, JSC::TypeError, "qsTrId(): second argument (n) must be a number"); | |
JSC::UString id = args.at(0).toString(exec); | |
int n = -1; | |
if (args.size() > 1) | |
n = args.at(1).toInt32(exec); | |
return JSC::jsString(exec, qtTrId(QScript::convertToLatin1(id).constData(), n)); | |
} | |
JSC::JSValue JSC_HOST_CALL functionQsTrIdNoOp(JSC::ExecState *, JSC::JSObject*, JSC::JSValue, const JSC::ArgList &args) | |
{ | |
if (args.size() < 1) | |
return JSC::jsUndefined(); | |
return args.at(0); | |
} | |
static JSC::JSValue JSC_HOST_CALL stringProtoFuncArg(JSC::ExecState*, JSC::JSObject*, JSC::JSValue, const JSC::ArgList&); | |
JSC::JSValue JSC_HOST_CALL stringProtoFuncArg(JSC::ExecState *exec, JSC::JSObject*, JSC::JSValue thisObject, const JSC::ArgList &args) | |
{ | |
QString value(thisObject.toString(exec)); | |
JSC::JSValue arg = (args.size() != 0) ? args.at(0) : JSC::jsUndefined(); | |
QString result; | |
if (arg.isString()) | |
result = value.arg(arg.toString(exec)); | |
else if (arg.isNumber()) | |
result = value.arg(arg.toNumber(exec)); | |
return JSC::jsString(exec, result); | |
} | |
#if !defined(QT_NO_QOBJECT) && !defined(QT_NO_LIBRARY) | |
static QScriptValue __setupPackage__(QScriptContext *ctx, QScriptEngine *eng) | |
{ | |
QString path = ctx->argument(0).toString(); | |
QStringList components = path.split(QLatin1Char('.')); | |
QScriptValue o = eng->globalObject(); | |
for (int i = 0; i < components.count(); ++i) { | |
QString name = components.at(i); | |
QScriptValue oo = o.property(name); | |
if (!oo.isValid()) { | |
oo = eng->newObject(); | |
o.setProperty(name, oo); | |
} | |
o = oo; | |
} | |
return o; | |
} | |
#endif | |
} // namespace QScript | |
QScriptEnginePrivate::QScriptEnginePrivate() | |
: originalGlobalObjectProxy(0), currentFrame(0), | |
qobjectPrototype(0), qmetaobjectPrototype(0), variantPrototype(0), | |
activeAgent(0), agentLineNumber(-1), | |
registeredScriptValues(0), freeScriptValues(0), freeScriptValuesCount(0), | |
registeredScriptStrings(0), processEventsInterval(-1), inEval(false) | |
{ | |
qMetaTypeId<QScriptValue>(); | |
qMetaTypeId<QList<int> >(); | |
#ifndef QT_NO_QOBJECT | |
qMetaTypeId<QObjectList>(); | |
#endif | |
if (!QCoreApplication::instance()) { | |
qFatal("QScriptEngine: Must construct a Q(Core)Application before a QScriptEngine"); | |
return; | |
} | |
JSC::initializeThreading(); | |
JSC::IdentifierTable *oldTable = JSC::currentIdentifierTable(); | |
globalData = JSC::JSGlobalData::create().releaseRef(); | |
globalData->clientData = new QScript::GlobalClientData(this); | |
JSC::JSGlobalObject *globalObject = new (globalData)QScript::GlobalObject(); | |
JSC::ExecState* exec = globalObject->globalExec(); | |
scriptObjectStructure = QScriptObject::createStructure(globalObject->objectPrototype()); | |
staticScopeObjectStructure = QScriptStaticScopeObject::createStructure(JSC::jsNull()); | |
qobjectPrototype = new (exec) QScript::QObjectPrototype(exec, QScript::QObjectPrototype::createStructure(globalObject->objectPrototype()), globalObject->prototypeFunctionStructure()); | |
qobjectWrapperObjectStructure = QScriptObject::createStructure(qobjectPrototype); | |
qmetaobjectPrototype = new (exec) QScript::QMetaObjectPrototype(exec, QScript::QMetaObjectPrototype::createStructure(globalObject->objectPrototype()), globalObject->prototypeFunctionStructure()); | |
qmetaobjectWrapperObjectStructure = QScript::QMetaObjectWrapperObject::createStructure(qmetaobjectPrototype); | |
variantPrototype = new (exec) QScript::QVariantPrototype(exec, QScript::QVariantPrototype::createStructure(globalObject->objectPrototype()), globalObject->prototypeFunctionStructure()); | |
variantWrapperObjectStructure = QScriptObject::createStructure(variantPrototype); | |
globalObject->putDirectFunction(exec, new (exec)JSC::NativeFunctionWrapper(exec, globalObject->prototypeFunctionStructure(), 1, JSC::Identifier(exec, "print"), QScript::functionPrint)); | |
globalObject->putDirectFunction(exec, new (exec)JSC::NativeFunctionWrapper(exec, globalObject->prototypeFunctionStructure(), 0, JSC::Identifier(exec, "gc"), QScript::functionGC)); | |
globalObject->putDirectFunction(exec, new (exec)JSC::NativeFunctionWrapper(exec, globalObject->prototypeFunctionStructure(), 0, JSC::Identifier(exec, "version"), QScript::functionVersion)); | |
// ### rather than extending Function.prototype, consider creating a QtSignal.prototype | |
globalObject->functionPrototype()->putDirectFunction(exec, new (exec)JSC::NativeFunctionWrapper(exec, globalObject->prototypeFunctionStructure(), 1, JSC::Identifier(exec, "disconnect"), QScript::functionDisconnect)); | |
globalObject->functionPrototype()->putDirectFunction(exec, new (exec)JSC::NativeFunctionWrapper(exec, globalObject->prototypeFunctionStructure(), 1, JSC::Identifier(exec, "connect"), QScript::functionConnect)); | |
JSC::TimeoutChecker* originalChecker = globalData->timeoutChecker; | |
globalData->timeoutChecker = new QScript::TimeoutCheckerProxy(*originalChecker); | |
delete originalChecker; | |
currentFrame = exec; | |
cachedTranslationUrl = JSC::UString(); | |
cachedTranslationContext = JSC::UString(); | |
JSC::setCurrentIdentifierTable(oldTable); | |
} | |
QScriptEnginePrivate::~QScriptEnginePrivate() | |
{ | |
QScript::APIShim shim(this); | |
//disconnect all loadedScripts and generate all jsc::debugger::scriptUnload events | |
QHash<intptr_t,QScript::UStringSourceProviderWithFeedback*>::const_iterator it; | |
for (it = loadedScripts.constBegin(); it != loadedScripts.constEnd(); ++it) | |
it.value()->disconnectFromEngine(); | |
while (!ownedAgents.isEmpty()) | |
delete ownedAgents.takeFirst(); | |
detachAllRegisteredScriptPrograms(); | |
detachAllRegisteredScriptValues(); | |
detachAllRegisteredScriptStrings(); | |
qDeleteAll(m_qobjectData); | |
qDeleteAll(m_typeInfos); | |
globalData->heap.destroy(); | |
globalData->deref(); | |
while (freeScriptValues) { | |
QScriptValuePrivate *p = freeScriptValues; | |
freeScriptValues = p->next; | |
qFree(p); | |
} | |
} | |
QVariant QScriptEnginePrivate::jscValueToVariant(JSC::ExecState *exec, JSC::JSValue value, int targetType) | |
{ | |
QVariant v(targetType, (void *)0); | |
if (convertValue(exec, value, targetType, v.data())) | |
return v; | |
if (uint(targetType) == QVariant::LastType) | |
return toVariant(exec, value); | |
if (isVariant(value)) { | |
v = variantValue(value); | |
if (v.canConvert(QVariant::Type(targetType))) { | |
v.convert(QVariant::Type(targetType)); | |
return v; | |
} | |
QByteArray typeName = v.typeName(); | |
if (typeName.endsWith('*') | |
&& (QMetaType::type(typeName.left(typeName.size()-1)) == targetType)) { | |
return QVariant(targetType, *reinterpret_cast<void* *>(v.data())); | |
} | |
} | |
return QVariant(); | |
} | |
JSC::JSValue QScriptEnginePrivate::arrayFromStringList(JSC::ExecState *exec, const QStringList &lst) | |
{ | |
JSC::JSValue arr = newArray(exec, lst.size()); | |
for (int i = 0; i < lst.size(); ++i) | |
setProperty(exec, arr, i, JSC::jsString(exec, lst.at(i))); | |
return arr; | |
} | |
QStringList QScriptEnginePrivate::stringListFromArray(JSC::ExecState *exec, JSC::JSValue arr) | |
{ | |
QStringList lst; | |
uint len = toUInt32(exec, property(exec, arr, exec->propertyNames().length)); | |
for (uint i = 0; i < len; ++i) | |
lst.append(toString(exec, property(exec, arr, i))); | |
return lst; | |
} | |
JSC::JSValue QScriptEnginePrivate::arrayFromVariantList(JSC::ExecState *exec, const QVariantList &lst) | |
{ | |
JSC::JSValue arr = newArray(exec, lst.size()); | |
for (int i = 0; i < lst.size(); ++i) | |
setProperty(exec, arr, i, jscValueFromVariant(exec, lst.at(i))); | |
return arr; | |
} | |
QVariantList QScriptEnginePrivate::variantListFromArray(JSC::ExecState *exec, JSC::JSArray *arr) | |
{ | |
QScriptEnginePrivate *eng = QScript::scriptEngineFromExec(exec); | |
if (eng->visitedConversionObjects.contains(arr)) | |
return QVariantList(); // Avoid recursion. | |
eng->visitedConversionObjects.insert(arr); | |
QVariantList lst; | |
uint len = toUInt32(exec, property(exec, arr, exec->propertyNames().length)); | |
for (uint i = 0; i < len; ++i) | |
lst.append(toVariant(exec, property(exec, arr, i))); | |
eng->visitedConversionObjects.remove(arr); | |
return lst; | |
} | |
JSC::JSValue QScriptEnginePrivate::objectFromVariantMap(JSC::ExecState *exec, const QVariantMap &vmap) | |
{ | |
JSC::JSValue obj = JSC::constructEmptyObject(exec); | |
QVariantMap::const_iterator it; | |
for (it = vmap.constBegin(); it != vmap.constEnd(); ++it) | |
setProperty(exec, obj, it.key(), jscValueFromVariant(exec, it.value())); | |
return obj; | |
} | |
QVariantMap QScriptEnginePrivate::variantMapFromObject(JSC::ExecState *exec, JSC::JSObject *obj) | |
{ | |
QScriptEnginePrivate *eng = QScript::scriptEngineFromExec(exec); | |
if (eng->visitedConversionObjects.contains(obj)) | |
return QVariantMap(); // Avoid recursion. | |
eng->visitedConversionObjects.insert(obj); | |
JSC::PropertyNameArray propertyNames(exec); | |
obj->getOwnPropertyNames(exec, propertyNames, JSC::IncludeDontEnumProperties); | |
QVariantMap vmap; | |
JSC::PropertyNameArray::const_iterator it = propertyNames.begin(); | |
for( ; it != propertyNames.end(); ++it) | |
vmap.insert(it->ustring(), toVariant(exec, property(exec, obj, *it))); | |
eng->visitedConversionObjects.remove(obj); | |
return vmap; | |
} | |
JSC::JSValue QScriptEnginePrivate::defaultPrototype(int metaTypeId) const | |
{ | |
QScriptTypeInfo *info = m_typeInfos.value(metaTypeId); | |
if (!info) | |
return JSC::JSValue(); | |
return info->prototype; | |
} | |
void QScriptEnginePrivate::setDefaultPrototype(int metaTypeId, JSC::JSValue prototype) | |
{ | |
QScriptTypeInfo *info = m_typeInfos.value(metaTypeId); | |
if (!info) { | |
info = new QScriptTypeInfo(); | |
m_typeInfos.insert(metaTypeId, info); | |
} | |
info->prototype = prototype; | |
} | |
JSC::JSGlobalObject *QScriptEnginePrivate::originalGlobalObject() const | |
{ | |
return globalData->head; | |
} | |
JSC::JSObject *QScriptEnginePrivate::customGlobalObject() const | |
{ | |
QScript::GlobalObject *glob = static_cast<QScript::GlobalObject*>(originalGlobalObject()); | |
return glob->customGlobalObject; | |
} | |
JSC::JSObject *QScriptEnginePrivate::getOriginalGlobalObjectProxy() | |
{ | |
if (!originalGlobalObjectProxy) { | |
JSC::ExecState* exec = currentFrame; | |
originalGlobalObjectProxy = new (exec)QScript::OriginalGlobalObjectProxy(scriptObjectStructure, originalGlobalObject()); | |
} | |
return originalGlobalObjectProxy; | |
} | |
JSC::JSObject *QScriptEnginePrivate::globalObject() const | |
{ | |
QScript::GlobalObject *glob = static_cast<QScript::GlobalObject*>(originalGlobalObject()); | |
if (glob->customGlobalObject) | |
return glob->customGlobalObject; | |
return glob; | |
} | |
void QScriptEnginePrivate::setGlobalObject(JSC::JSObject *object) | |
{ | |
if (object == globalObject()) | |
return; | |
QScript::GlobalObject *glob = static_cast<QScript::GlobalObject*>(originalGlobalObject()); | |
if (object == originalGlobalObjectProxy) { | |
glob->customGlobalObject = 0; | |
// Sync the internal prototype, since JSObject::prototype() is not virtual. | |
glob->setPrototype(originalGlobalObjectProxy->prototype()); | |
} else { | |
Q_ASSERT(object != originalGlobalObject()); | |
glob->customGlobalObject = object; | |
// Sync the internal prototype, since JSObject::prototype() is not virtual. | |
glob->setPrototype(object->prototype()); | |
} | |
} | |
/*! | |
\internal | |
If the given \a value is the original global object, returns the custom | |
global object or a proxy to the original global object; otherwise returns \a | |
value. | |
*/ | |
JSC::JSValue QScriptEnginePrivate::toUsableValue(JSC::JSValue value) | |
{ | |
if (!value || !value.isObject() || !JSC::asObject(value)->isGlobalObject()) | |
return value; | |
Q_ASSERT(JSC::asObject(value) == originalGlobalObject()); | |
if (customGlobalObject()) | |
return customGlobalObject(); | |
if (!originalGlobalObjectProxy) | |
originalGlobalObjectProxy = new (currentFrame)QScript::OriginalGlobalObjectProxy(scriptObjectStructure, originalGlobalObject()); | |
return originalGlobalObjectProxy; | |
} | |
/*! | |
\internal | |
Return the 'this' value for a given context | |
*/ | |
JSC::JSValue QScriptEnginePrivate::thisForContext(JSC::ExecState *frame) | |
{ | |
if (frame->codeBlock() != 0) { | |
return frame->thisValue(); | |
} else if(frame == frame->lexicalGlobalObject()->globalExec()) { | |
return frame->globalThisValue(); | |
} else { | |
JSC::Register *thisRegister = thisRegisterForFrame(frame); | |
return thisRegister->jsValue(); | |
} | |
} | |
JSC::Register* QScriptEnginePrivate::thisRegisterForFrame(JSC::ExecState *frame) | |
{ | |
Q_ASSERT(frame->codeBlock() == 0); // only for native calls | |
return frame->registers() - JSC::RegisterFile::CallFrameHeaderSize - frame->argumentCount(); | |
} | |
/*! \internal | |
For native context, we use the ReturnValueRegister entry in the stackframe header to store flags. | |
We can do that because this header is not used as the native function return their value thought C++ | |
when setting flags, NativeContext should always be set | |
contextFlags returns 0 for non native context | |
*/ | |
uint QScriptEnginePrivate::contextFlags(JSC::ExecState *exec) | |
{ | |
if (exec->codeBlock()) | |
return 0; //js function doesn't have flags | |
return exec->returnValueRegister(); | |
} | |
void QScriptEnginePrivate::setContextFlags(JSC::ExecState *exec, uint flags) | |
{ | |
Q_ASSERT(!exec->codeBlock()); | |
exec->registers()[JSC::RegisterFile::ReturnValueRegister] = JSC::Register::withInt(flags); | |
} | |
void QScriptEnginePrivate::mark(JSC::MarkStack& markStack) | |
{ | |
Q_Q(QScriptEngine); | |
if (originalGlobalObject()) { | |
markStack.append(originalGlobalObject()); | |
markStack.append(globalObject()); | |
if (originalGlobalObjectProxy) | |
markStack.append(originalGlobalObjectProxy); | |
} | |
if (qobjectPrototype) | |
markStack.append(qobjectPrototype); | |
if (qmetaobjectPrototype) | |
markStack.append(qmetaobjectPrototype); | |
if (variantPrototype) | |
markStack.append(variantPrototype); | |
{ | |
QScriptValuePrivate *it; | |
for (it = registeredScriptValues; it != 0; it = it->next) { | |
if (it->isJSC()) | |
markStack.append(it->jscValue); | |
} | |
} | |
{ | |
QHash<int, QScriptTypeInfo*>::const_iterator it; | |
for (it = m_typeInfos.constBegin(); it != m_typeInfos.constEnd(); ++it) { | |
if ((*it)->prototype) | |
markStack.append((*it)->prototype); | |
} | |
} | |
if (q) { | |
QScriptContext *context = q->currentContext(); | |
while (context) { | |
JSC::ScopeChainNode *node = frameForContext(context)->scopeChain(); | |
JSC::ScopeChainIterator it(node); | |
for (it = node->begin(); it != node->end(); ++it) { | |
JSC::JSObject *object = *it; | |
if (object) | |
markStack.append(object); | |
} | |
context = context->parentContext(); | |
} | |
} | |
#ifndef QT_NO_QOBJECT | |
markStack.drain(); // make sure everything is marked before marking qobject data | |
{ | |
QHash<QObject*, QScript::QObjectData*>::const_iterator it; | |
for (it = m_qobjectData.constBegin(); it != m_qobjectData.constEnd(); ++it) { | |
QScript::QObjectData *qdata = it.value(); | |
qdata->mark(markStack); | |
} | |
} | |
#endif | |
} | |
bool QScriptEnginePrivate::isCollecting() const | |
{ | |
return globalData->heap.isBusy(); | |
} | |
void QScriptEnginePrivate::collectGarbage() | |
{ | |
QScript::APIShim shim(this); | |
globalData->heap.collectAllGarbage(); | |
} | |
void QScriptEnginePrivate::reportAdditionalMemoryCost(int size) | |
{ | |
if (size > 0) | |
globalData->heap.reportExtraMemoryCost(size); | |
} | |
QScript::TimeoutCheckerProxy *QScriptEnginePrivate::timeoutChecker() const | |
{ | |
return static_cast<QScript::TimeoutCheckerProxy*>(globalData->timeoutChecker); | |
} | |
void QScriptEnginePrivate::agentDeleted(QScriptEngineAgent *agent) | |
{ | |
ownedAgents.removeOne(agent); | |
if (activeAgent == agent) { | |
QScriptEngineAgentPrivate::get(agent)->detach(); | |
activeAgent = 0; | |
} | |
} | |
JSC::JSValue QScriptEnginePrivate::evaluateHelper(JSC::ExecState *exec, intptr_t sourceId, | |
JSC::EvalExecutable *executable, | |
bool &compile) | |
{ | |
Q_Q(QScriptEngine); | |
QBoolBlocker inEvalBlocker(inEval, true); | |
q->currentContext()->activationObject(); //force the creation of a context for native function; | |
JSC::Debugger* debugger = originalGlobalObject()->debugger(); | |
if (debugger) | |
debugger->evaluateStart(sourceId); | |
q->clearExceptions(); | |
JSC::DynamicGlobalObjectScope dynamicGlobalObjectScope(exec, exec->scopeChain()->globalObject); | |
if (compile) { | |
JSC::JSObject* error = executable->compile(exec, exec->scopeChain()); | |
if (error) { | |
compile = false; | |
exec->setException(error); | |
if (debugger) { | |
debugger->exceptionThrow(JSC::DebuggerCallFrame(exec, error), sourceId, false); | |
debugger->evaluateStop(error, sourceId); | |
} | |
return error; | |
} | |
} | |
JSC::JSValue thisValue = thisForContext(exec); | |
JSC::JSObject* thisObject = (!thisValue || thisValue.isUndefinedOrNull()) | |
? exec->dynamicGlobalObject() : thisValue.toObject(exec); | |
JSC::JSValue exceptionValue; | |
timeoutChecker()->setShouldAbort(false); | |
if (processEventsInterval > 0) | |
timeoutChecker()->reset(); | |
JSC::JSValue result = exec->interpreter()->execute(executable, exec, thisObject, exec->scopeChain(), &exceptionValue); | |
if (timeoutChecker()->shouldAbort()) { | |
if (abortResult.isError()) | |
exec->setException(scriptValueToJSCValue(abortResult)); | |
if (debugger) | |
debugger->evaluateStop(scriptValueToJSCValue(abortResult), sourceId); | |
return scriptValueToJSCValue(abortResult); | |
} | |
if (exceptionValue) { | |
exec->setException(exceptionValue); | |
if (debugger) | |
debugger->evaluateStop(exceptionValue, sourceId); | |
return exceptionValue; | |
} | |
if (debugger) | |
debugger->evaluateStop(result, sourceId); | |
Q_ASSERT(!exec->hadException()); | |
return result; | |
} | |
#ifndef QT_NO_QOBJECT | |
JSC::JSValue QScriptEnginePrivate::newQObject( | |
QObject *object, QScriptEngine::ValueOwnership ownership, | |
const QScriptEngine::QObjectWrapOptions &options) | |
{ | |
if (!object) | |
return JSC::jsNull(); | |
JSC::ExecState* exec = currentFrame; | |
QScript::QObjectData *data = qobjectData(object); | |
bool preferExisting = (options & QScriptEngine::PreferExistingWrapperObject) != 0; | |
QScriptEngine::QObjectWrapOptions opt = options & ~QScriptEngine::PreferExistingWrapperObject; | |
QScriptObject *result = 0; | |
if (preferExisting) { | |
result = data->findWrapper(ownership, opt); | |
if (result) | |
return result; | |
} | |
result = new (exec) QScriptObject(qobjectWrapperObjectStructure); | |
if (preferExisting) | |
data->registerWrapper(result, ownership, opt); | |
result->setDelegate(new QScript::QObjectDelegate(object, ownership, options)); | |
/*if (setDefaultPrototype)*/ { | |
const QMetaObject *meta = object->metaObject(); | |
while (meta) { | |
QByteArray typeString = meta->className(); | |
typeString.append('*'); | |
int typeId = QMetaType::type(typeString); | |
if (typeId != 0) { | |
JSC::JSValue proto = defaultPrototype(typeId); | |
if (proto) { | |
result->setPrototype(proto); | |
break; | |
} | |
} | |
meta = meta->superClass(); | |
} | |
} | |
return result; | |
} | |
JSC::JSValue QScriptEnginePrivate::newQMetaObject( | |
const QMetaObject *metaObject, JSC::JSValue ctor) | |
{ | |
if (!metaObject) | |
return JSC::jsNull(); | |
JSC::ExecState* exec = currentFrame; | |
QScript::QMetaObjectWrapperObject *result = new (exec) QScript::QMetaObjectWrapperObject(exec, metaObject, ctor, qmetaobjectWrapperObjectStructure); | |
return result; | |
} | |
bool QScriptEnginePrivate::convertToNativeQObject(JSC::ExecState *exec, JSC::JSValue value, | |
const QByteArray &targetType, | |
void **result) | |
{ | |
if (!targetType.endsWith('*')) | |
return false; | |
if (QObject *qobject = toQObject(exec, value)) { | |
int start = targetType.startsWith("const ") ? 6 : 0; | |
QByteArray className = targetType.mid(start, targetType.size()-start-1); | |
if (void *instance = qobject->qt_metacast(className)) { | |
*result = instance; | |
return true; | |
} | |
} | |
return false; | |
} | |
QScript::QObjectData *QScriptEnginePrivate::qobjectData(QObject *object) | |
{ | |
QHash<QObject*, QScript::QObjectData*>::const_iterator it; | |
it = m_qobjectData.constFind(object); | |
if (it != m_qobjectData.constEnd()) | |
return it.value(); | |
QScript::QObjectData *data = new QScript::QObjectData(this); | |
m_qobjectData.insert(object, data); | |
QObject::connect(object, SIGNAL(destroyed(QObject*)), | |
q_func(), SLOT(_q_objectDestroyed(QObject*))); | |
return data; | |
} | |
void QScriptEnginePrivate::_q_objectDestroyed(QObject *object) | |
{ | |
QHash<QObject*, QScript::QObjectData*>::iterator it; | |
it = m_qobjectData.find(object); | |
Q_ASSERT(it != m_qobjectData.end()); | |
QScript::QObjectData *data = it.value(); | |
m_qobjectData.erase(it); | |
delete data; | |
} | |
void QScriptEnginePrivate::disposeQObject(QObject *object) | |
{ | |
// TODO | |
/* if (isCollecting()) { | |
// wait until we're done with GC before deleting it | |
int index = m_qobjectsToBeDeleted.indexOf(object); | |
if (index == -1) | |
m_qobjectsToBeDeleted.append(object); | |
} else*/ { | |
delete object; | |
} | |
} | |
void QScriptEnginePrivate::emitSignalHandlerException() | |
{ | |
Q_Q(QScriptEngine); | |
emit q->signalHandlerException(q->uncaughtException()); | |
} | |
bool QScriptEnginePrivate::scriptConnect(QObject *sender, const char *signal, | |
JSC::JSValue receiver, JSC::JSValue function, | |
Qt::ConnectionType type) | |
{ | |
Q_ASSERT(sender); | |
Q_ASSERT(signal); | |
const QMetaObject *meta = sender->metaObject(); | |
int index = meta->indexOfSignal(QMetaObject::normalizedSignature(signal+1)); | |
if (index == -1) | |
return false; | |
return scriptConnect(sender, index, receiver, function, /*wrapper=*/JSC::JSValue(), type); | |
} | |
bool QScriptEnginePrivate::scriptDisconnect(QObject *sender, const char *signal, | |
JSC::JSValue receiver, JSC::JSValue function) | |
{ | |
Q_ASSERT(sender); | |
Q_ASSERT(signal); | |
const QMetaObject *meta = sender->metaObject(); | |
int index = meta->indexOfSignal(QMetaObject::normalizedSignature(signal+1)); | |
if (index == -1) | |
return false; | |
return scriptDisconnect(sender, index, receiver, function); | |
} | |
bool QScriptEnginePrivate::scriptConnect(QObject *sender, int signalIndex, | |
JSC::JSValue receiver, JSC::JSValue function, | |
JSC::JSValue senderWrapper, | |
Qt::ConnectionType type) | |
{ | |
QScript::QObjectData *data = qobjectData(sender); | |
return data->addSignalHandler(sender, signalIndex, receiver, function, senderWrapper, type); | |
} | |
bool QScriptEnginePrivate::scriptDisconnect(QObject *sender, int signalIndex, | |
JSC::JSValue receiver, JSC::JSValue function) | |
{ | |
QScript::QObjectData *data = qobjectData(sender); | |
if (!data) | |
return false; | |
return data->removeSignalHandler(sender, signalIndex, receiver, function); | |
} | |
bool QScriptEnginePrivate::scriptConnect(JSC::JSValue signal, JSC::JSValue receiver, | |
JSC::JSValue function, Qt::ConnectionType type) | |
{ | |
QScript::QtFunction *fun = static_cast<QScript::QtFunction*>(JSC::asObject(signal)); | |
int index = fun->mostGeneralMethod(); | |
return scriptConnect(fun->qobject(), index, receiver, function, fun->wrapperObject(), type); | |
} | |
bool QScriptEnginePrivate::scriptDisconnect(JSC::JSValue signal, JSC::JSValue receiver, | |
JSC::JSValue function) | |
{ | |
QScript::QtFunction *fun = static_cast<QScript::QtFunction*>(JSC::asObject(signal)); | |
int index = fun->mostGeneralMethod(); | |
return scriptDisconnect(fun->qobject(), index, receiver, function); | |
} | |
#endif | |
void QScriptEnginePrivate::detachAllRegisteredScriptPrograms() | |
{ | |
QSet<QScriptProgramPrivate*>::const_iterator it; | |
for (it = registeredScriptPrograms.constBegin(); it != registeredScriptPrograms.constEnd(); ++it) | |
(*it)->detachFromEngine(); | |
registeredScriptPrograms.clear(); | |
} | |
void QScriptEnginePrivate::detachAllRegisteredScriptValues() | |
{ | |
QScriptValuePrivate *it; | |
QScriptValuePrivate *next; | |
for (it = registeredScriptValues; it != 0; it = next) { | |
it->detachFromEngine(); | |
next = it->next; | |
it->prev = 0; | |
it->next = 0; | |
} | |
registeredScriptValues = 0; | |
} | |
void QScriptEnginePrivate::detachAllRegisteredScriptStrings() | |
{ | |
QScriptStringPrivate *it; | |
QScriptStringPrivate *next; | |
for (it = registeredScriptStrings; it != 0; it = next) { | |
it->detachFromEngine(); | |
next = it->next; | |
it->prev = 0; | |
it->next = 0; | |
} | |
registeredScriptStrings = 0; | |
} | |
#ifndef QT_NO_REGEXP | |
Q_CORE_EXPORT QString qt_regexp_toCanonical(const QString &, QRegExp::PatternSyntax); | |
JSC::JSValue QScriptEnginePrivate::newRegExp(JSC::ExecState *exec, const QRegExp ®exp) | |
{ | |
JSC::JSValue buf[2]; | |
JSC::ArgList args(buf, sizeof(buf)); | |
//convert the pattern to a ECMAScript pattern | |
QString pattern = qt_regexp_toCanonical(regexp.pattern(), regexp.patternSyntax()); | |
if (regexp.isMinimal()) { | |
QString ecmaPattern; | |
int len = pattern.length(); | |
ecmaPattern.reserve(len); | |
int i = 0; | |
const QChar *wc = pattern.unicode(); | |
bool inBracket = false; | |
while (i < len) { | |
QChar c = wc[i++]; | |
ecmaPattern += c; | |
switch (c.unicode()) { | |
case '?': | |
case '+': | |
case '*': | |
case '}': | |
if (!inBracket) | |
ecmaPattern += QLatin1Char('?'); | |
break; | |
case '\\': | |
if (i < len) | |
ecmaPattern += wc[i++]; | |
break; | |
case '[': | |
inBracket = true; | |
break; | |
case ']': | |
inBracket = false; | |
break; | |
default: | |
break; | |
} | |
} | |
pattern = ecmaPattern; | |
} | |
JSC::UString jscPattern = pattern; | |
QString flags; | |
if (regexp.caseSensitivity() == Qt::CaseInsensitive) | |
flags.append(QLatin1Char('i')); | |
JSC::UString jscFlags = flags; | |
buf[0] = JSC::jsString(exec, jscPattern); | |
buf[1] = JSC::jsString(exec, jscFlags); | |
return JSC::constructRegExp(exec, args); | |
} | |
#endif | |
JSC::JSValue QScriptEnginePrivate::newRegExp(JSC::ExecState *exec, const QString &pattern, const QString &flags) | |
{ | |
JSC::JSValue buf[2]; | |
JSC::ArgList args(buf, sizeof(buf)); | |
JSC::UString jscPattern = pattern; | |
QString strippedFlags; | |
if (flags.contains(QLatin1Char('i'))) | |
strippedFlags += QLatin1Char('i'); | |
if (flags.contains(QLatin1Char('m'))) | |
strippedFlags += QLatin1Char('m'); | |
if (flags.contains(QLatin1Char('g'))) | |
strippedFlags += QLatin1Char('g'); | |
JSC::UString jscFlags = strippedFlags; | |
buf[0] = JSC::jsString(exec, jscPattern); | |
buf[1] = JSC::jsString(exec, jscFlags); | |
return JSC::constructRegExp(exec, args); | |
} | |
JSC::JSValue QScriptEnginePrivate::newVariant(const QVariant &value) | |
{ | |
QScriptObject *obj = new (currentFrame) QScriptObject(variantWrapperObjectStructure); | |
obj->setDelegate(new QScript::QVariantDelegate(value)); | |
JSC::JSValue proto = defaultPrototype(value.userType()); | |
if (proto) | |
obj->setPrototype(proto); | |
return obj; | |
} | |
JSC::JSValue QScriptEnginePrivate::newVariant(JSC::JSValue objectValue, | |
const QVariant &value) | |
{ | |
if (!isObject(objectValue)) | |
return newVariant(value); | |
JSC::JSObject *jscObject = JSC::asObject(objectValue); | |
if (!jscObject->inherits(&QScriptObject::info)) { | |
qWarning("QScriptEngine::newVariant(): changing class of non-QScriptObject not supported"); | |
return JSC::JSValue(); | |
} | |
QScriptObject *jscScriptObject = static_cast<QScriptObject*>(jscObject); | |
if (!isVariant(objectValue)) { | |
jscScriptObject->setDelegate(new QScript::QVariantDelegate(value)); | |
} else { | |
setVariantValue(objectValue, value); | |
} | |
return objectValue; | |
} | |
#ifndef QT_NO_REGEXP | |
QRegExp QScriptEnginePrivate::toRegExp(JSC::ExecState *exec, JSC::JSValue value) | |
{ | |
if (!isRegExp(value)) | |
return QRegExp(); | |
QString pattern = toString(exec, property(exec, value, "source", QScriptValue::ResolvePrototype)); | |
Qt::CaseSensitivity kase = Qt::CaseSensitive; | |
if (toBool(exec, property(exec, value, "ignoreCase", QScriptValue::ResolvePrototype))) | |
kase = Qt::CaseInsensitive; | |
return QRegExp(pattern, kase, QRegExp::RegExp2); | |
} | |
#endif | |
QVariant QScriptEnginePrivate::toVariant(JSC::ExecState *exec, JSC::JSValue value) | |
{ | |
if (!value) { | |
return QVariant(); | |
} else if (isObject(value)) { | |
if (isVariant(value)) | |
return variantValue(value); | |
#ifndef QT_NO_QOBJECT | |
else if (isQObject(value)) | |
return qVariantFromValue(toQObject(exec, value)); | |
#endif | |
else if (isDate(value)) | |
return QVariant(toDateTime(exec, value)); | |
#ifndef QT_NO_REGEXP | |
else if (isRegExp(value)) | |
return QVariant(toRegExp(exec, value)); | |
#endif | |
else if (isArray(value)) | |
return variantListFromArray(exec, JSC::asArray(value)); | |
else if (QScriptDeclarativeClass *dc = declarativeClass(value)) | |
return dc->toVariant(declarativeObject(value)); | |
return variantMapFromObject(exec, JSC::asObject(value)); | |
} else if (value.isNumber()) { | |
return QVariant(toNumber(exec, value)); | |
} else if (value.isString()) { | |
return QVariant(toString(exec, value)); | |
} else if (value.isBoolean()) { | |
return QVariant(toBool(exec, value)); | |
} | |
return QVariant(); | |
} | |
JSC::JSValue QScriptEnginePrivate::propertyHelper(JSC::ExecState *exec, JSC::JSValue value, const JSC::Identifier &id, int resolveMode) | |
{ | |
JSC::JSValue result; | |
if (!(resolveMode & QScriptValue::ResolvePrototype)) { | |
// Look in the object's own properties | |
JSC::JSObject *object = JSC::asObject(value); | |
JSC::PropertySlot slot(object); | |
if (object->getOwnPropertySlot(exec, id, slot)) | |
result = slot.getValue(exec, id); | |
} | |
if (!result && (resolveMode & QScriptValue::ResolveScope)) { | |
// ### check if it's a function object and look in the scope chain | |
JSC::JSValue scope = property(exec, value, "__qt_scope__", QScriptValue::ResolveLocal); | |
if (isObject(scope)) | |
result = property(exec, scope, id, resolveMode); | |
} | |
return result; | |
} | |
JSC::JSValue QScriptEnginePrivate::propertyHelper(JSC::ExecState *exec, JSC::JSValue value, quint32 index, int resolveMode) | |
{ | |
JSC::JSValue result; | |
if (!(resolveMode & QScriptValue::ResolvePrototype)) { | |
// Look in the object's own properties | |
JSC::JSObject *object = JSC::asObject(value); | |
JSC::PropertySlot slot(object); | |
if (object->getOwnPropertySlot(exec, index, slot)) | |
result = slot.getValue(exec, index); | |
} | |
return result; | |
} | |
void QScriptEnginePrivate::setProperty(JSC::ExecState *exec, JSC::JSValue objectValue, const JSC::Identifier &id, | |
JSC::JSValue value, const QScriptValue::PropertyFlags &flags) | |
{ | |
JSC::JSObject *thisObject = JSC::asObject(objectValue); | |
JSC::JSValue setter = thisObject->lookupSetter(exec, id); | |
JSC::JSValue getter = thisObject->lookupGetter(exec, id); | |
if ((flags & QScriptValue::PropertyGetter) || (flags & QScriptValue::PropertySetter)) { | |
if (!value) { | |
// deleting getter/setter | |
if ((flags & QScriptValue::PropertyGetter) && (flags & QScriptValue::PropertySetter)) { | |
// deleting both: just delete the property | |
thisObject->deleteProperty(exec, id); | |
} else if (flags & QScriptValue::PropertyGetter) { | |
// preserve setter, if there is one | |
thisObject->deleteProperty(exec, id); | |
if (setter && setter.isObject()) | |
thisObject->defineSetter(exec, id, JSC::asObject(setter)); | |
} else { // flags & QScriptValue::PropertySetter | |
// preserve getter, if there is one | |
thisObject->deleteProperty(exec, id); | |
if (getter && getter.isObject()) | |
thisObject->defineGetter(exec, id, JSC::asObject(getter)); | |
} | |
} else { | |
if (value.isObject()) { // ### should check if it has callData() | |
// defining getter/setter | |
if (id == exec->propertyNames().underscoreProto) { | |
qWarning("QScriptValue::setProperty() failed: " | |
"cannot set getter or setter of native property `__proto__'"); | |
} else { | |
if (flags & QScriptValue::PropertyGetter) | |
thisObject->defineGetter(exec, id, JSC::asObject(value)); | |
if (flags & QScriptValue::PropertySetter) | |
thisObject->defineSetter(exec, id, JSC::asObject(value)); | |
} | |
} else { | |
qWarning("QScriptValue::setProperty(): getter/setter must be a function"); | |
} | |
} | |
} else { | |
// setting the value | |
if (getter && getter.isObject() && !(setter && setter.isObject())) { | |
qWarning("QScriptValue::setProperty() failed: " | |
"property '%s' has a getter but no setter", | |
qPrintable(QString(id.ustring()))); | |
return; | |
} | |
if (!value) { | |
// ### check if it's a getter/setter property | |
thisObject->deleteProperty(exec, id); | |
} else if (flags != QScriptValue::KeepExistingFlags) { | |
if (thisObject->hasOwnProperty(exec, id)) | |
thisObject->deleteProperty(exec, id); // ### hmmm - can't we just update the attributes? | |
thisObject->putWithAttributes(exec, id, value, propertyFlagsToJSCAttributes(flags)); | |
} else { | |
JSC::PutPropertySlot slot; | |
thisObject->put(exec, id, value, slot); | |
} | |
} | |
} | |
void QScriptEnginePrivate::setProperty(JSC::ExecState *exec, JSC::JSValue objectValue, quint32 index, | |
JSC::JSValue value, const QScriptValue::PropertyFlags &flags) | |
{ | |
if (!value) { | |
JSC::asObject(objectValue)->deleteProperty(exec, index); | |
} else { | |
if ((flags & QScriptValue::PropertyGetter) || (flags & QScriptValue::PropertySetter)) { | |
// fall back to string-based setProperty(), since there is no | |
// JSC::JSObject::defineGetter(unsigned) | |
setProperty(exec, objectValue, JSC::Identifier::from(exec, index), value, flags); | |
} else { | |
if (flags != QScriptValue::KeepExistingFlags) { | |
// if (JSC::asObject(d->jscValue)->hasOwnProperty(exec, arrayIndex)) | |
// JSC::asObject(d->jscValue)->deleteProperty(exec, arrayIndex); | |
unsigned attribs = 0; | |
if (flags & QScriptValue::ReadOnly) | |
attribs |= JSC::ReadOnly; | |
if (flags & QScriptValue::SkipInEnumeration) | |
attribs |= JSC::DontEnum; | |
if (flags & QScriptValue::Undeletable) | |
attribs |= JSC::DontDelete; | |
attribs |= flags & QScriptValue::UserRange; | |
JSC::asObject(objectValue)->putWithAttributes(exec, index, value, attribs); | |
} else { | |
JSC::asObject(objectValue)->put(exec, index, value); | |
} | |
} | |
} | |
} | |
QScriptValue::PropertyFlags QScriptEnginePrivate::propertyFlags(JSC::ExecState *exec, JSC::JSValue value, const JSC::Identifier &id, | |
const QScriptValue::ResolveFlags &mode) | |
{ | |
JSC::JSObject *object = JSC::asObject(value); | |
unsigned attribs = 0; | |
JSC::PropertyDescriptor descriptor; | |
if (object->getOwnPropertyDescriptor(exec, id, descriptor)) | |
attribs = descriptor.attributes(); | |
else { | |
if ((mode & QScriptValue::ResolvePrototype) && object->prototype() && object->prototype().isObject()) { | |
JSC::JSValue proto = object->prototype(); | |
return propertyFlags(exec, proto, id, mode); | |
} | |
return 0; | |
} | |
QScriptValue::PropertyFlags result = 0; | |
if (attribs & JSC::ReadOnly) | |
result |= QScriptValue::ReadOnly; | |
if (attribs & JSC::DontEnum) | |
result |= QScriptValue::SkipInEnumeration; | |
if (attribs & JSC::DontDelete) | |
result |= QScriptValue::Undeletable; | |
//We cannot rely on attribs JSC::Setter/Getter because they are not necesserly set by JSC (bug?) | |
if (attribs & JSC::Getter || !object->lookupGetter(exec, id).isUndefinedOrNull()) | |
result |= QScriptValue::PropertyGetter; | |
if (attribs & JSC::Setter || !object->lookupSetter(exec, id).isUndefinedOrNull()) | |
result |= QScriptValue::PropertySetter; | |
#ifndef QT_NO_QOBJECT | |
if (attribs & QScript::QObjectMemberAttribute) | |
result |= QScriptValue::QObjectMember; | |
#endif | |
result |= QScriptValue::PropertyFlag(attribs & QScriptValue::UserRange); | |
return result; | |
} | |
QScriptString QScriptEnginePrivate::toStringHandle(const JSC::Identifier &name) | |
{ | |
QScriptString result; | |
QScriptStringPrivate *p = new QScriptStringPrivate(this, name, QScriptStringPrivate::HeapAllocated); | |
QScriptStringPrivate::init(result, p); | |
registerScriptString(p); | |
return result; | |
} | |
#ifdef QT_NO_QOBJECT | |
QScriptEngine::QScriptEngine() | |
: d_ptr(new QScriptEnginePrivate) | |
{ | |
d_ptr->q_ptr = this; | |
} | |
/*! \internal | |
*/ | |
QScriptEngine::QScriptEngine(QScriptEnginePrivate &dd) | |
: d_ptr(&dd) | |
{ | |
d_ptr->q_ptr = this; | |
} | |
#else | |
/*! | |
Constructs a QScriptEngine object. | |
The globalObject() is initialized to have properties as described in | |
\l{ECMA-262}, Section 15.1. | |
*/ | |
QScriptEngine::QScriptEngine() | |
: QObject(*new QScriptEnginePrivate, 0) | |
{ | |
} | |
/*! | |
Constructs a QScriptEngine object with the given \a parent. | |
The globalObject() is initialized to have properties as described in | |
\l{ECMA-262}, Section 15.1. | |
*/ | |
QScriptEngine::QScriptEngine(QObject *parent) | |
: QObject(*new QScriptEnginePrivate, parent) | |
{ | |
} | |
/*! \internal | |
*/ | |
QScriptEngine::QScriptEngine(QScriptEnginePrivate &dd, QObject *parent) | |
: QObject(dd, parent) | |
{ | |
} | |
#endif | |
/*! | |
Destroys this QScriptEngine. | |
*/ | |
QScriptEngine::~QScriptEngine() | |
{ | |
#ifdef QT_NO_QOBJECT | |
delete d_ptr; | |
d_ptr = 0; | |
#endif | |
} | |
/*! | |
Returns this engine's Global Object. | |
By default, the Global Object contains the built-in objects that are | |
part of \l{ECMA-262}, such as Math, Date and String. Additionally, | |
you can set properties of the Global Object to make your own | |
extensions available to all script code. Non-local variables in | |
script code will be created as properties of the Global Object, as | |
well as local variables in global code. | |
*/ | |
QScriptValue QScriptEngine::globalObject() const | |
{ | |
Q_D(const QScriptEngine); | |
QScript::APIShim shim(const_cast<QScriptEnginePrivate*>(d)); | |
JSC::JSObject *result = d->globalObject(); | |
return const_cast<QScriptEnginePrivate*>(d)->scriptValueFromJSCValue(result); | |
} | |
/*! | |
\since 4.5 | |
Sets this engine's Global Object to be the given \a object. | |
If \a object is not a valid script object, this function does | |
nothing. | |
When setting a custom global object, you may want to use | |
QScriptValueIterator to copy the properties of the standard Global | |
Object; alternatively, you can set the internal prototype of your | |
custom object to be the original Global Object. | |
*/ | |
void QScriptEngine::setGlobalObject(const QScriptValue &object) | |
{ | |
Q_D(QScriptEngine); | |
if (!object.isObject()) | |
return; | |
QScript::APIShim shim(d); | |
JSC::JSObject *jscObject = JSC::asObject(d->scriptValueToJSCValue(object)); | |
d->setGlobalObject(jscObject); | |
} | |
/*! | |
Returns a QScriptValue of the primitive type Null. | |
\sa undefinedValue() | |
*/ | |
QScriptValue QScriptEngine::nullValue() | |
{ | |
Q_D(QScriptEngine); | |
return d->scriptValueFromJSCValue(JSC::jsNull()); | |
} | |
/*! | |
Returns a QScriptValue of the primitive type Undefined. | |
\sa nullValue() | |
*/ | |
QScriptValue QScriptEngine::undefinedValue() | |
{ | |
Q_D(QScriptEngine); | |
return d->scriptValueFromJSCValue(JSC::jsUndefined()); | |
} | |
/*! | |
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) | |
{ | |
Q_D(QScriptEngine); | |
QScript::APIShim shim(d); | |
JSC::ExecState* exec = d->currentFrame; | |
JSC::JSValue function = new (exec)QScript::FunctionWrapper(exec, length, JSC::Identifier(exec, ""), fun); | |
QScriptValue result = d->scriptValueFromJSCValue(function); | |
result.setProperty(QLatin1String("prototype"), prototype, QScriptValue::Undeletable); | |
const_cast<QScriptValue&>(prototype) | |
.setProperty(QLatin1String("constructor"), result, | |
QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); | |
return result; | |
} | |
#ifndef QT_NO_REGEXP | |
/*! | |
Creates a QtScript object of class RegExp with the given | |
\a regexp. | |
\sa QScriptValue::toRegExp() | |
*/ | |
QScriptValue QScriptEngine::newRegExp(const QRegExp ®exp) | |
{ | |
Q_D(QScriptEngine); | |
QScript::APIShim shim(d); | |
return d->scriptValueFromJSCValue(d->newRegExp(d->currentFrame, regexp)); | |
} | |
#endif // QT_NO_REGEXP | |
/*! | |
Creates a QtScript object holding the given variant \a value. | |
If a default prototype has been registered with the meta type id of | |
\a value, then the prototype of the created object will be that | |
prototype; otherwise, the prototype will be the Object prototype | |
object. | |
\sa setDefaultPrototype(), QScriptValue::toVariant(), reportAdditionalMemoryCost() | |
*/ | |
QScriptValue QScriptEngine::newVariant(const QVariant &value) | |
{ | |
Q_D(QScriptEngine); | |
QScript::APIShim shim(d); | |
return d->scriptValueFromJSCValue(d->newVariant(value)); | |
} | |
/*! | |
\since 4.4 | |
\overload | |
Initializes the given Qt Script \a object to hold the given variant | |
\a value, and returns the \a object. | |
This function enables you to "promote" a plain Qt Script object | |
(created by the newObject() function) to a variant, or to replace | |
the variant contained inside an object previously created by the | |
newVariant() function. | |
The prototype() of the \a object will remain unchanged. | |
If \a object is not an object, this function behaves like the normal | |
newVariant(), i.e. it creates a new script object and returns it. | |
This function is useful when you want to provide a script | |
constructor for a C++ type. If your constructor is invoked in a | |
\c{new} expression (QScriptContext::isCalledAsConstructor() returns | |
true), you can pass QScriptContext::thisObject() (the default | |
constructed script object) to this function to initialize the new | |
object. | |
\sa reportAdditionalMemoryCost() | |
*/ | |
QScriptValue QScriptEngine::newVariant(const QScriptValue &object, | |
const QVariant &value) | |
{ | |
Q_D(QScriptEngine); | |
QScript::APIShim shim(d); | |
JSC::JSValue jsObject = d->scriptValueToJSCValue(object); | |
return d->scriptValueFromJSCValue(d->newVariant(jsObject, value)); | |
} | |
#ifndef QT_NO_QOBJECT | |
/*! | |
Creates a QtScript object that wraps the given QObject \a | |
object, using the given \a ownership. The given \a options control | |
various aspects of the interaction with the resulting script object. | |
Signals and slots, properties and children of \a object are | |
available as properties of the created QScriptValue. For more | |
information, see the \l{QtScript} documentation. | |
If \a object is a null pointer, this function returns nullValue(). | |
If a default prototype has been registered for the \a object's class | |
(or its superclass, recursively), the prototype of the new script | |
object will be set to be that default prototype. | |
If the given \a object is deleted outside of QtScript's control, any | |
attempt to access the deleted QObject's members through the QtScript | |
wrapper object (either by script code or C++) will result in a | |
script exception. | |
\sa QScriptValue::toQObject(), reportAdditionalMemoryCost() | |
*/ | |
QScriptValue QScriptEngine::newQObject(QObject *object, ValueOwnership ownership, | |
const QObjectWrapOptions &options) | |
{ | |
Q_D(QScriptEngine); | |
QScript::APIShim shim(d); | |
JSC::JSValue jscQObject = d->newQObject(object, ownership, options); | |
return d->scriptValueFromJSCValue(jscQObject); | |
} | |
/*! | |
\since 4.4 | |
\overload | |
Initializes the given \a scriptObject to hold the given \a qtObject, | |
and returns the \a scriptObject. | |
This function enables you to "promote" a plain Qt Script object | |
(created by the newObject() function) to a QObject proxy, or to | |
replace the QObject contained inside an object previously created by | |
the newQObject() function. | |
The prototype() of the \a scriptObject will remain unchanged. | |
If \a scriptObject is not an object, this function behaves like the | |
normal newQObject(), i.e. it creates a new script object and returns | |
it. | |
This function is useful when you want to provide a script | |
constructor for a QObject-based class. If your constructor is | |
invoked in a \c{new} expression | |
(QScriptContext::isCalledAsConstructor() returns true), you can pass | |
QScriptContext::thisObject() (the default constructed script object) | |
to this function to initialize the new object. | |
\sa reportAdditionalMemoryCost() | |
*/ | |
QScriptValue QScriptEngine::newQObject(const QScriptValue &scriptObject, | |
QObject *qtObject, | |
ValueOwnership ownership, | |
const QObjectWrapOptions &options) | |
{ | |
Q_D(QScriptEngine); | |
if (!scriptObject.isObject()) | |
return newQObject(qtObject, ownership, options); | |
QScript::APIShim shim(d); | |
JSC::JSObject *jscObject = JSC::asObject(QScriptValuePrivate::get(scriptObject)->jscValue); | |
if (!jscObject->inherits(&QScriptObject::info)) { | |
qWarning("QScriptEngine::newQObject(): changing class of non-QScriptObject not supported"); | |
return QScriptValue(); | |
} | |
QScriptObject *jscScriptObject = static_cast<QScriptObject*>(jscObject); | |
if (!scriptObject.isQObject()) { | |
jscScriptObject->setDelegate(new QScript::QObjectDelegate(qtObject, ownership, options)); | |
} else { | |
QScript::QObjectDelegate *delegate = static_cast<QScript::QObjectDelegate*>(jscScriptObject->delegate()); | |
delegate->setValue(qtObject); | |
delegate->setOwnership(ownership); | |
delegate->setOptions(options); | |
} | |
return scriptObject; | |
} | |
#endif // QT_NO_QOBJECT | |
/*! | |
Creates a QtScript object of class Object. | |
The prototype of the created object will be the Object | |
prototype object. | |
\sa newArray(), QScriptValue::setProperty() | |
*/ | |
QScriptValue QScriptEngine::newObject() | |
{ | |
Q_D(QScriptEngine); | |
QScript::APIShim shim(d); | |
return d->scriptValueFromJSCValue(d->newObject()); | |
} | |
/*! | |
\since 4.4 | |
\overload | |
Creates a QtScript Object of the given class, \a scriptClass. | |
The prototype of the created object will be the Object | |
prototype object. | |
\a data, if specified, is set as the internal data of the | |
new object (using QScriptValue::setData()). | |
\sa QScriptValue::scriptClass(), reportAdditionalMemoryCost() | |
*/ | |
QScriptValue QScriptEngine::newObject(QScriptClass *scriptClass, | |
const QScriptValue &data) | |
{ | |
Q_D(QScriptEngine); | |
QScript::APIShim shim(d); | |
JSC::ExecState* exec = d->currentFrame; | |
QScriptObject *result = new (exec) QScriptObject(d->scriptObjectStructure); | |
result->setDelegate(new QScript::ClassObjectDelegate(scriptClass)); | |
QScriptValue scriptObject = d->scriptValueFromJSCValue(result); | |
scriptObject.setData(data); | |
QScriptValue proto = scriptClass->prototype(); | |
if (proto.isValid()) | |
scriptObject.setPrototype(proto); | |
return scriptObject; | |
} | |
/*! | |
\internal | |
*/ | |
QScriptValue QScriptEngine::newActivationObject() | |
{ | |
qWarning("QScriptEngine::newActivationObject() not implemented"); | |
// ### JSActivation or JSVariableObject? | |
return QScriptValue(); | |
} | |
/*! | |
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) | |
{ | |
Q_D(QScriptEngine); | |
QScript::APIShim shim(d); | |
JSC::ExecState* exec = d->currentFrame; | |
JSC::JSValue function = new (exec)QScript::FunctionWrapper(exec, length, JSC::Identifier(exec, ""), fun); | |
QScriptValue result = d->scriptValueFromJSCValue(function); | |
QScriptValue proto = newObject(); | |
result.setProperty(QLatin1String("prototype"), proto, QScriptValue::Undeletable); | |
proto.setProperty(QLatin1String("constructor"), result, | |
QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); | |
return result; | |
} | |
/*! | |
\internal | |
\since 4.4 | |
*/ | |
QScriptValue QScriptEngine::newFunction(QScriptEngine::FunctionWithArgSignature fun, void *arg) | |
{ | |
Q_D(QScriptEngine); | |
QScript::APIShim shim(d); | |
JSC::ExecState* exec = d->currentFrame; | |
JSC::JSValue function = new (exec)QScript::FunctionWithArgWrapper(exec, /*length=*/0, JSC::Identifier(exec, ""), fun, arg); | |
QScriptValue result = d->scriptValueFromJSCValue(function); | |
QScriptValue proto = newObject(); | |
result.setProperty(QLatin1String("prototype"), proto, QScriptValue::Undeletable); | |
proto.setProperty(QLatin1String("constructor"), result, | |
QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); | |
return result; | |
} | |
/*! | |
Creates a QtScript object of class Array with the given \a length. | |
\sa newObject() | |
*/ | |
QScriptValue QScriptEngine::newArray(uint length) | |
{ | |
Q_D(QScriptEngine); | |
QScript::APIShim shim(d); | |
return d->scriptValueFromJSCValue(d->newArray(d->currentFrame, length)); | |
} | |
/*! | |
Creates a QtScript object of class RegExp with the given | |
\a pattern and \a flags. | |
The legal flags are 'g' (global), 'i' (ignore case), and 'm' | |
(multiline). | |
*/ | |
QScriptValue QScriptEngine::newRegExp(const QString &pattern, const QString &flags) | |
{ | |
Q_D(QScriptEngine); | |
QScript::APIShim shim(d); | |
return d->scriptValueFromJSCValue(d->newRegExp(d->currentFrame, pattern, flags)); | |
} | |
/*! | |
Creates a QtScript object of class Date with the given | |
\a value (the number of milliseconds since 01 January 1970, | |
UTC). | |
*/ | |
QScriptValue QScriptEngine::newDate(qsreal value) | |
{ | |
Q_D(QScriptEngine); | |
QScript::APIShim shim(d); | |
return d->scriptValueFromJSCValue(d->newDate(d->currentFrame, value)); | |
} | |
/*! | |
Creates a QtScript object of class Date from the given \a value. | |
\sa QScriptValue::toDateTime() | |
*/ | |
QScriptValue QScriptEngine::newDate(const QDateTime &value) | |
{ | |
Q_D(QScriptEngine); | |
QScript::APIShim shim(d); | |
return d->scriptValueFromJSCValue(d->newDate(d->currentFrame, value)); | |
} | |
#ifndef QT_NO_QOBJECT | |
/*! | |
Creates a QtScript object that represents a QObject class, using the | |
the given \a metaObject and constructor \a ctor. | |
Enums of \a metaObject (declared with Q_ENUMS) are available as | |
properties of the created QScriptValue. When the class is called as | |
a function, \a ctor will be called to create a new instance of the | |
class. | |
Example: | |
\snippet doc/src/snippets/code/src_script_qscriptengine.cpp 27 | |
\sa newQObject(), scriptValueFromQMetaObject() | |
*/ | |
QScriptValue QScriptEngine::newQMetaObject( | |
const QMetaObject *metaObject, const QScriptValue &ctor) | |
{ | |
Q_D(QScriptEngine); | |
QScript::APIShim shim(d); | |
JSC::JSValue jscCtor = d->scriptValueToJSCValue(ctor); | |
JSC::JSValue jscQMetaObject = d->newQMetaObject(metaObject, jscCtor); | |
return d->scriptValueFromJSCValue(jscQMetaObject); | |
} | |
/*! | |
\fn QScriptValue QScriptEngine::scriptValueFromQMetaObject() | |
Creates a QScriptValue that represents the Qt class \c{T}. | |
This function is used in combination with one of the | |
Q_SCRIPT_DECLARE_QMETAOBJECT() macro. Example: | |
\snippet doc/src/snippets/code/src_script_qscriptengine.cpp 13 | |
\warning This function is not available with MSVC 6. Use | |
qScriptValueFromQMetaObject() instead if you need to support that version | |
of the compiler. | |
\sa QScriptEngine::newQMetaObject() | |
*/ | |
/*! | |
\fn QScriptValue qScriptValueFromQMetaObject(QScriptEngine *engine) | |
\since 4.3 | |
\relates QScriptEngine | |
Uses \a engine to create a QScriptValue that represents the Qt class | |
\c{T}. | |
This function is equivalent to | |
QScriptEngine::scriptValueFromQMetaObject(). It is provided as a | |
work-around for MSVC 6, which doesn't support member template | |
functions. | |
\sa QScriptEngine::newQMetaObject() | |
*/ | |
#endif // QT_NO_QOBJECT | |
/*! | |
\obsolete | |
Returns true if \a program can be evaluated; i.e. the code is | |
sufficient to determine whether it appears to be a syntactically | |
correct program, or contains a syntax error. | |
This function returns false if \a program is incomplete; i.e. the | |
input is syntactically correct up to the point where the input is | |
terminated. | |
Note that this function only does a static check of \a program; | |
e.g. it does not check whether references to variables are | |
valid, and so on. | |
A typical usage of canEvaluate() is to implement an interactive | |
interpreter for QtScript. The user is repeatedly queried for | |
individual lines of code; the lines are concatened internally, and | |
only when canEvaluate() returns true for the resulting program is it | |
passed to evaluate(). | |
The following are some examples to illustrate the behavior of | |
canEvaluate(). (Note that all example inputs are assumed to have an | |
explicit newline as their last character, since otherwise the | |
QtScript parser would automatically insert a semi-colon character at | |
the end of the input, and this could cause canEvaluate() to produce | |
different results.) | |
Given the input | |
\snippet doc/src/snippets/code/src_script_qscriptengine.cpp 14 | |
canEvaluate() will return true, since the program appears to be complete. | |
Given the input | |
\snippet doc/src/snippets/code/src_script_qscriptengine.cpp 15 | |
canEvaluate() will return false, since the if-statement is not complete, | |
but is syntactically correct so far. | |
Given the input | |
\snippet doc/src/snippets/code/src_script_qscriptengine.cpp 16 | |
canEvaluate() will return true, but evaluate() will throw a | |
SyntaxError given the same input. | |
Given the input | |
\snippet doc/src/snippets/code/src_script_qscriptengine.cpp 17 | |
canEvaluate() will return true, even though the code is clearly not | |
syntactically valid QtScript code. evaluate() will throw a | |
SyntaxError when this code is evaluated. | |
Given the input | |
\snippet doc/src/snippets/code/src_script_qscriptengine.cpp 18 | |
canEvaluate() will return true, but evaluate() will throw a | |
ReferenceError if \c{foo} is not defined in the script | |
environment. | |
\sa evaluate(), checkSyntax() | |
*/ | |
bool QScriptEngine::canEvaluate(const QString &program) const | |
{ | |
return QScriptEnginePrivate::canEvaluate(program); | |
} | |
bool QScriptEnginePrivate::canEvaluate(const QString &program) | |
{ | |
QScript::SyntaxChecker checker; | |
QScript::SyntaxChecker::Result result = checker.checkSyntax(program); | |
return (result.state != QScript::SyntaxChecker::Intermediate); | |
} | |
/*! | |
\since 4.5 | |
Checks the syntax of the given \a program. Returns a | |
QScriptSyntaxCheckResult object that contains the result of the check. | |
*/ | |
QScriptSyntaxCheckResult QScriptEngine::checkSyntax(const QString &program) | |
{ | |
return QScriptEnginePrivate::checkSyntax(program); | |
} | |
QScriptSyntaxCheckResult QScriptEnginePrivate::checkSyntax(const QString &program) | |
{ | |
QScript::SyntaxChecker checker; | |
QScript::SyntaxChecker::Result result = checker.checkSyntax(program); | |
QScriptSyntaxCheckResultPrivate *p = new QScriptSyntaxCheckResultPrivate(); | |
switch (result.state) { | |
case QScript::SyntaxChecker::Error: | |
p->state = QScriptSyntaxCheckResult::Error; | |
break; | |
case QScript::SyntaxChecker::Intermediate: | |
p->state = QScriptSyntaxCheckResult::Intermediate; | |
break; | |
case QScript::SyntaxChecker::Valid: | |
p->state = QScriptSyntaxCheckResult::Valid; | |
break; | |
} | |
p->errorLineNumber = result.errorLineNumber; | |
p->errorColumnNumber = result.errorColumnNumber; | |
p->errorMessage = result.errorMessage; | |
return QScriptSyntaxCheckResult(p); | |
} | |
/*! | |
Evaluates \a program, using \a lineNumber as the base line number, | |
and returns the result of the evaluation. | |
The script code will be evaluated in the current context. | |
The evaluation of \a program can cause an exception in the | |
engine; in this case the return value will be the exception | |
that was thrown (typically an \c{Error} object). You can call | |
hasUncaughtException() to determine if an exception occurred in | |
the last call to evaluate(). | |
\a lineNumber is used to specify a starting line number for \a | |
program; line number information reported by the engine that pertain | |
to this evaluation (e.g. uncaughtExceptionLineNumber()) will be | |
based on this argument. For example, if \a program consists of two | |
lines of code, and the statement on the second line causes a script | |
exception, uncaughtExceptionLineNumber() would return the given \a | |
lineNumber plus one. When no starting line number is specified, line | |
numbers will be 1-based. | |
\a fileName is used for error reporting. For example in error objects | |
the file name is accessible through the "fileName" property if it's | |
provided with this function. | |
\sa canEvaluate(), hasUncaughtException(), isEvaluating(), abortEvaluation() | |
*/ | |
QScriptValue QScriptEngine::evaluate(const QString &program, const QString &fileName, int lineNumber) | |
{ | |
Q_D(QScriptEngine); | |
QScript::APIShim shim(d); | |
WTF::PassRefPtr<QScript::UStringSourceProviderWithFeedback> provider | |
= QScript::UStringSourceProviderWithFeedback::create(program, fileName, lineNumber, d); | |
intptr_t sourceId = provider->asID(); | |
JSC::SourceCode source(provider, lineNumber); //after construction of SourceCode provider variable will be null. | |
JSC::ExecState* exec = d->currentFrame; | |
WTF::RefPtr<JSC::EvalExecutable> executable = JSC::EvalExecutable::create(exec, source); | |
bool compile = true; | |
return d->scriptValueFromJSCValue(d->evaluateHelper(exec, sourceId, executable.get(), compile)); | |
} | |
/*! | |
\since 4.7 | |
Evaluates the given \a program and returns the result of the | |
evaluation. | |
*/ | |
QScriptValue QScriptEngine::evaluate(const QScriptProgram &program) | |
{ | |
Q_D(QScriptEngine); | |
QScriptProgramPrivate *program_d = QScriptProgramPrivate::get(program); | |
if (!program_d) | |
return QScriptValue(); | |
QScript::APIShim shim(d); | |
JSC::ExecState* exec = d->currentFrame; | |
JSC::EvalExecutable *executable = program_d->executable(exec, d); | |
bool compile = !program_d->isCompiled; | |
JSC::JSValue result = d->evaluateHelper(exec, program_d->sourceId, | |
executable, compile); | |
if (compile) | |
program_d->isCompiled = true; | |
return d->scriptValueFromJSCValue(result); | |
} | |
/*! | |
Returns the current context. | |
The current context is typically accessed to retrieve the arguments | |
and `this' object in native functions; for convenience, it is | |
available as the first argument in QScriptEngine::FunctionSignature. | |
*/ | |
QScriptContext *QScriptEngine::currentContext() const | |
{ | |
Q_D(const QScriptEngine); | |
return const_cast<QScriptEnginePrivate*>(d)->contextForFrame(d->currentFrame); | |
} | |
/*! | |
Enters a new execution context and returns the associated | |
QScriptContext object. | |
Once you are done with the context, you should call popContext() to | |
restore the old context. | |
By default, the `this' object of the new context is the Global Object. | |
The context's \l{QScriptContext::callee()}{callee}() will be invalid. | |
This function is useful when you want to evaluate script code | |
as if it were the body of a function. You can use the context's | |
\l{QScriptContext::activationObject()}{activationObject}() to initialize | |
local variables that will be available to scripts. Example: | |
\snippet doc/src/snippets/code/src_script_qscriptengine.cpp 19 | |
In the above example, the new variable "tmp" defined in the script | |
will be local to the context; in other words, the script doesn't | |
have any effect on the global environment. | |
Returns 0 in case of stack overflow | |
\sa popContext() | |
*/ | |
QScriptContext *QScriptEngine::pushContext() | |
{ | |
Q_D(QScriptEngine); | |
QScript::APIShim shim(d); | |
JSC::CallFrame* newFrame = d->pushContext(d->currentFrame, d->currentFrame->globalData().dynamicGlobalObject, | |
JSC::ArgList(), /*callee = */0); | |
if (agent()) | |
agent()->contextPush(); | |
return d->contextForFrame(newFrame); | |
} | |
/*! \internal | |
push a context for a native function. | |
JSC native function doesn't have different stackframe or context. so we need to create one. | |
use popContext right after to go back to the previous context the context if no stack overflow has hapenned | |
exec is the current top frame. | |
return the new top frame. (might be the same as exec if a new stackframe was not needed) or 0 if stack overflow | |
*/ | |
JSC::CallFrame *QScriptEnginePrivate::pushContext(JSC::CallFrame *exec, JSC::JSValue _thisObject, | |
const JSC::ArgList& args, JSC::JSObject *callee, bool calledAsConstructor, | |
bool clearScopeChain) | |
{ | |
JSC::JSValue thisObject = _thisObject; | |
if (!callee) { | |
// callee can't be zero, as this can cause JSC to crash during GC | |
// marking phase if the context's Arguments object has been created. | |
// Fake it by using the global object. Note that this is also handled | |
// in QScriptContext::callee(), as that function should still return | |
// an invalid value. | |
callee = originalGlobalObject(); | |
} | |
if (calledAsConstructor) { | |
//JSC doesn't create default created object for native functions. so we do it | |
JSC::JSValue prototype = callee->get(exec, exec->propertyNames().prototype); | |
JSC::Structure *structure = prototype.isObject() ? JSC::asObject(prototype)->inheritorID() | |
: originalGlobalObject()->emptyObjectStructure(); | |
thisObject = new (exec) QScriptObject(structure); | |
} | |
int flags = NativeContext; | |
if (calledAsConstructor) | |
flags |= CalledAsConstructorContext; | |
//build a frame | |
JSC::CallFrame *newCallFrame = exec; | |
if (callee == 0 //called from public QScriptEngine::pushContext | |
|| exec->returnPC() == 0 || (contextFlags(exec) & NativeContext) //called from native-native call | |
|| (exec->codeBlock() && exec->callee() != callee)) { //the interpreter did not build a frame for us. | |
//We need to check if the Interpreter might have already created a frame for function called from JS. | |
JSC::Interpreter *interp = exec->interpreter(); | |
JSC::Register *oldEnd = interp->registerFile().end(); | |
int argc = args.size() + 1; //add "this" | |
JSC::Register *newEnd = oldEnd + argc + JSC::RegisterFile::CallFrameHeaderSize; | |
if (!interp->registerFile().grow(newEnd)) | |
return 0; //### Stack overflow | |
newCallFrame = JSC::CallFrame::create(oldEnd); | |
newCallFrame[0] = thisObject; | |
int dst = 0; | |
JSC::ArgList::const_iterator it; | |
for (it = args.begin(); it != args.end(); ++it) | |
newCallFrame[++dst] = *it; | |
newCallFrame += argc + JSC::RegisterFile::CallFrameHeaderSize; | |
if (!clearScopeChain) { | |
newCallFrame->init(0, /*vPC=*/0, exec->scopeChain(), exec, flags | ShouldRestoreCallFrame, argc, callee); | |
} else { | |
newCallFrame->init(0, /*vPC=*/0, globalExec()->scopeChain(), exec, flags | ShouldRestoreCallFrame, argc, callee); | |
} | |
} else { | |
setContextFlags(newCallFrame, flags); | |
#if ENABLE(JIT) | |
exec->registers()[JSC::RegisterFile::Callee] = JSC::JSValue(callee); //JIT let the callee set the 'callee' | |
#endif | |
if (calledAsConstructor) { | |
//update the new created this | |
JSC::Register* thisRegister = thisRegisterForFrame(newCallFrame); | |
*thisRegister = thisObject; | |
} | |
} | |
currentFrame = newCallFrame; | |
return newCallFrame; | |
} | |
/*! | |
Pops the current execution context and restores the previous one. | |
This function must be used in conjunction with pushContext(). | |
\sa pushContext() | |
*/ | |
void QScriptEngine::popContext() | |
{ | |
if (agent()) | |
agent()->contextPop(); | |
Q_D(QScriptEngine); | |
QScript::APIShim shim(d); | |
if (d->currentFrame->returnPC() != 0 || d->currentFrame->codeBlock() != 0 | |
|| !currentContext()->parentContext()) { | |
qWarning("QScriptEngine::popContext() doesn't match with pushContext()"); | |
return; | |
} | |
d->popContext(); | |
} | |
/*! \internal | |
counter part of QScriptEnginePrivate::pushContext | |
*/ | |
void QScriptEnginePrivate::popContext() | |
{ | |
uint flags = contextFlags(currentFrame); | |
bool hasScope = flags & HasScopeContext; | |
if (flags & ShouldRestoreCallFrame) { //normal case | |
JSC::RegisterFile ®isterFile = currentFrame->interpreter()->registerFile(); | |
JSC::Register *const newEnd = currentFrame->registers() - JSC::RegisterFile::CallFrameHeaderSize - currentFrame->argumentCount(); | |
if (hasScope) | |
currentFrame->scopeChain()->pop()->deref(); | |
registerFile.shrink(newEnd); | |
} else if(hasScope) { //the stack frame was created by the Interpreter, we don't need to rewind it. | |
currentFrame->setScopeChain(currentFrame->scopeChain()->pop()); | |
currentFrame->scopeChain()->deref(); | |
} | |
currentFrame = currentFrame->callerFrame(); | |
} | |
/*! | |
Returns true if the last script evaluation resulted in an uncaught | |
exception; otherwise returns false. | |
The exception state is cleared when evaluate() is called. | |
\sa uncaughtException(), uncaughtExceptionLineNumber() | |
*/ | |
bool QScriptEngine::hasUncaughtException() const | |
{ | |
Q_D(const QScriptEngine); | |
JSC::ExecState* exec = d->globalExec(); | |
return exec->hadException() || d->currentException().isValid(); | |
} | |
/*! | |
Returns the current uncaught exception, or an invalid QScriptValue | |
if there is no uncaught exception. | |
The exception value is typically an \c{Error} object; in that case, | |
you can call toString() on the return value to obtain an error | |
message. | |
\sa hasUncaughtException(), uncaughtExceptionLineNumber(), | |
*/ | |
QScriptValue QScriptEngine::uncaughtException() const | |
{ | |
Q_D(const QScriptEngine); | |
QScriptValue result; | |
JSC::ExecState* exec = d->globalExec(); | |
if (exec->hadException()) | |
result = const_cast<QScriptEnginePrivate*>(d)->scriptValueFromJSCValue(exec->exception()); | |
else | |
result = d->currentException(); | |
return result; | |
} | |
/*! | |
Returns the line number where the last uncaught exception occurred. | |
Line numbers are 1-based, unless a different base was specified as | |
the second argument to evaluate(). | |
\sa hasUncaughtException() | |
*/ | |
int QScriptEngine::uncaughtExceptionLineNumber() const | |
{ | |
if (!hasUncaughtException()) | |
return -1; | |
return uncaughtException().property(QLatin1String("lineNumber")).toInt32(); | |
} | |
/*! | |
Returns a human-readable backtrace of the last uncaught exception. | |
It is in the form \c{<function-name>()@<file-name>:<line-number>}. | |
\sa uncaughtException() | |
*/ | |
QStringList QScriptEngine::uncaughtExceptionBacktrace() const | |
{ | |
if (!hasUncaughtException()) | |
return QStringList(); | |
// ### currently no way to get a full backtrace from JSC without installing a | |
// debugger that reimplements exception() and store the backtrace there. | |
QScriptValue value = uncaughtException(); | |
if (!value.isError()) | |
return QStringList(); | |
QStringList result; | |
result.append(QString::fromLatin1("<anonymous>()@%0:%1") | |
.arg(value.property(QLatin1String("fileName")).toString()) | |
.arg(value.property(QLatin1String("lineNumber")).toInt32())); | |
return result; | |
} | |
/*! | |
\since 4.4 | |
Clears any uncaught exceptions in this engine. | |
\sa hasUncaughtException() | |
*/ | |
void QScriptEngine::clearExceptions() | |
{ | |
Q_D(QScriptEngine); | |
JSC::ExecState* exec = d->currentFrame; | |
exec->clearException(); | |
d->clearCurrentException(); | |
} | |
/*! | |
Returns the default prototype associated with the given \a metaTypeId, | |
or an invalid QScriptValue if no default prototype has been set. | |
\sa setDefaultPrototype() | |
*/ | |
QScriptValue QScriptEngine::defaultPrototype(int metaTypeId) const | |
{ | |
Q_D(const QScriptEngine); | |
return const_cast<QScriptEnginePrivate*>(d)->scriptValueFromJSCValue(d->defaultPrototype(metaTypeId)); | |
} | |
/*! | |
Sets the default prototype of the C++ type identified by the given | |
\a metaTypeId to \a prototype. | |
The default prototype provides a script interface for values of | |
type \a metaTypeId when a value of that type is accessed from script | |
code. Whenever the script engine (implicitly or explicitly) creates | |
a QScriptValue from a value of type \a metaTypeId, the default | |
prototype will be set as the QScriptValue's prototype. | |
The \a prototype object itself may be constructed using one of two | |
principal techniques; the simplest is to subclass QScriptable, which | |
enables you to define the scripting API of the type through QObject | |
properties and slots. Another possibility is to create a script | |
object by calling newObject(), and populate the object with the | |
desired properties (e.g. native functions wrapped with | |
newFunction()). | |
\sa defaultPrototype(), qScriptRegisterMetaType(), QScriptable, {Default Prototypes Example} | |
*/ | |
void QScriptEngine::setDefaultPrototype(int metaTypeId, const QScriptValue &prototype) | |
{ | |
Q_D(QScriptEngine); | |
d->setDefaultPrototype(metaTypeId, d->scriptValueToJSCValue(prototype)); | |
} | |
/*! | |
\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. | |
*/ | |
/*! | |
\typedef QScriptEngine::MarshalFunction | |
\internal | |
*/ | |
/*! | |
\typedef QScriptEngine::DemarshalFunction | |
\internal | |
*/ | |
/*! | |
\internal | |
*/ | |
QScriptValue QScriptEngine::create(int type, const void *ptr) | |
{ | |
Q_D(QScriptEngine); | |
QScript::APIShim shim(d); | |
return d->scriptValueFromJSCValue(d->create(d->currentFrame, type, ptr)); | |
} | |
JSC::JSValue QScriptEnginePrivate::create(JSC::ExecState *exec, int type, const void *ptr) | |
{ | |
Q_ASSERT(ptr != 0); | |
JSC::JSValue result; | |
QScriptEnginePrivate *eng = exec ? QScript::scriptEngineFromExec(exec) : 0; | |
QScriptTypeInfo *info = eng ? eng->m_typeInfos.value(type) : 0; | |
if (info && info->marshal) { | |
result = eng->scriptValueToJSCValue(info->marshal(eng->q_func(), ptr)); | |
} else { | |
// check if it's one of the types we know | |
switch (QMetaType::Type(type)) { | |
case QMetaType::Void: | |
return JSC::jsUndefined(); | |
case QMetaType::Bool: | |
return JSC::jsBoolean(*reinterpret_cast<const bool*>(ptr)); | |
case QMetaType::Int: | |
return JSC::jsNumber(exec, *reinterpret_cast<const int*>(ptr)); | |
case QMetaType::UInt: | |
return JSC::jsNumber(exec, *reinterpret_cast<const uint*>(ptr)); | |
case QMetaType::LongLong: | |
return JSC::jsNumber(exec, qsreal(*reinterpret_cast<const qlonglong*>(ptr))); | |
case QMetaType::ULongLong: | |
#if defined(Q_OS_WIN) && defined(_MSC_FULL_VER) && _MSC_FULL_VER <= 12008804 | |
#pragma message("** NOTE: You need the Visual Studio Processor Pack to compile support for 64bit unsigned integers.") | |
return JSC::jsNumber(exec, qsreal((qlonglong)*reinterpret_cast<const qulonglong*>(ptr))); | |
#elif defined(Q_CC_MSVC) && !defined(Q_CC_MSVC_NET) | |
return JSC::jsNumber(exec, qsreal((qlonglong)*reinterpret_cast<const qulonglong*>(ptr))); | |
#else | |
return JSC::jsNumber(exec, qsreal(*reinterpret_cast<const qulonglong*>(ptr))); | |
#endif | |
case QMetaType::Double: | |
return JSC::jsNumber(exec, qsreal(*reinterpret_cast<const double*>(ptr))); | |
case QMetaType::QString: | |
return JSC::jsString(exec, *reinterpret_cast<const QString*>(ptr)); | |
case QMetaType::Float: | |
return JSC::jsNumber(exec, *reinterpret_cast<const float*>(ptr)); | |
case QMetaType::Short: | |
return JSC::jsNumber(exec, *reinterpret_cast<const short*>(ptr)); | |
case QMetaType::UShort: | |
return JSC::jsNumber(exec, *reinterpret_cast<const unsigned short*>(ptr)); | |
case QMetaType::Char: | |
return JSC::jsNumber(exec, *reinterpret_cast<const char*>(ptr)); | |
case QMetaType::UChar: | |
return JSC::jsNumber(exec, *reinterpret_cast<const unsigned char*>(ptr)); | |
case QMetaType::QChar: | |
return JSC::jsNumber(exec, (*reinterpret_cast<const QChar*>(ptr)).unicode()); | |
case QMetaType::QStringList: | |
result = arrayFromStringList(exec, *reinterpret_cast<const QStringList *>(ptr)); | |
break; | |
case QMetaType::QVariantList: | |
result = arrayFromVariantList(exec, *reinterpret_cast<const QVariantList *>(ptr)); | |
break; | |
case QMetaType::QVariantMap: | |
result = objectFromVariantMap(exec, *reinterpret_cast<const QVariantMap *>(ptr)); | |
break; | |
case QMetaType::QDateTime: | |
result = newDate(exec, *reinterpret_cast<const QDateTime *>(ptr)); | |
break; | |
case QMetaType::QDate: | |
result = newDate(exec, QDateTime(*reinterpret_cast<const QDate *>(ptr))); | |
break; | |
#ifndef QT_NO_REGEXP | |
case QMetaType::QRegExp: | |
result = newRegExp(exec, *reinterpret_cast<const QRegExp *>(ptr)); | |
break; | |
#endif | |
#ifndef QT_NO_QOBJECT | |
case QMetaType::QObjectStar: | |
case QMetaType::QWidgetStar: | |
result = eng->newQObject(*reinterpret_cast<QObject* const *>(ptr)); | |
break; | |
#endif | |
case QMetaType::QVariant: | |
result = jscValueFromVariant(exec, *reinterpret_cast<const QVariant*>(ptr)); | |
break; | |
default: | |
if (type == qMetaTypeId<QScriptValue>()) { | |
result = eng->scriptValueToJSCValue(*reinterpret_cast<const QScriptValue*>(ptr)); | |
if (!result) | |
return JSC::jsUndefined(); | |
} | |
#ifndef QT_NO_QOBJECT | |
// lazy registration of some common list types | |
else if (type == qMetaTypeId<QObjectList>()) { | |
qScriptRegisterSequenceMetaType<QObjectList>(eng->q_func()); | |
return create(exec, type, ptr); | |
} | |
#endif | |
else if (type == qMetaTypeId<QList<int> >()) { | |
qScriptRegisterSequenceMetaType<QList<int> >(eng->q_func()); | |
return create(exec, type, ptr); | |
} | |
else { | |
QByteArray typeName = QMetaType::typeName(type); | |
if (typeName.endsWith('*') && !*reinterpret_cast<void* const *>(ptr)) | |
return JSC::jsNull(); | |
else | |
result = eng->newVariant(QVariant(type, ptr)); | |
} | |
} | |
} | |
if (result && result.isObject() && info && info->prototype | |
&& JSC::JSValue::strictEqual(exec, JSC::asObject(result)->prototype(), eng->originalGlobalObject()->objectPrototype())) { | |
JSC::asObject(result)->setPrototype(info->prototype); | |
} | |
return result; | |
} | |
bool QScriptEnginePrivate::convertValue(JSC::ExecState *exec, JSC::JSValue value, | |
int type, void *ptr) | |
{ | |
QScriptEnginePrivate *eng = exec ? QScript::scriptEngineFromExec(exec) : 0; | |
if (eng) { | |
QScriptTypeInfo *info = eng->m_typeInfos.value(type); | |
if (info && info->demarshal) { | |
info->demarshal(eng->scriptValueFromJSCValue(value), ptr); | |
return true; | |
} | |
} | |
// check if it's one of the types we know | |
switch (QMetaType::Type(type)) { | |
case QMetaType::Bool: | |
*reinterpret_cast<bool*>(ptr) = toBool(exec, value); | |
return true; | |
case QMetaType::Int: | |
*reinterpret_cast<int*>(ptr) = toInt32(exec, value); | |
return true; | |
case QMetaType::UInt: | |
*reinterpret_cast<uint*>(ptr) = toUInt32(exec, value); | |
return true; | |
case QMetaType::LongLong: | |
*reinterpret_cast<qlonglong*>(ptr) = qlonglong(toInteger(exec, value)); | |
return true; | |
case QMetaType::ULongLong: | |
*reinterpret_cast<qulonglong*>(ptr) = qulonglong(toInteger(exec, value)); | |
return true; | |
case QMetaType::Double: | |
*reinterpret_cast<double*>(ptr) = toNumber(exec, value); | |
return true; | |
case QMetaType::QString: | |
if (value.isUndefined() || value.isNull()) | |
*reinterpret_cast<QString*>(ptr) = QString(); | |
else | |
*reinterpret_cast<QString*>(ptr) = toString(exec, value); | |
return true; | |
case QMetaType::Float: | |
*reinterpret_cast<float*>(ptr) = toNumber(exec, value); | |
return true; | |
case QMetaType::Short: | |
*reinterpret_cast<short*>(ptr) = short(toInt32(exec, value)); | |
return true; | |
case QMetaType::UShort: | |
*reinterpret_cast<unsigned short*>(ptr) = QScript::ToUInt16(toNumber(exec, value)); | |
return true; | |
case QMetaType::Char: | |
*reinterpret_cast<char*>(ptr) = char(toInt32(exec, value)); | |
return true; | |
case QMetaType::UChar: | |
*reinterpret_cast<unsigned char*>(ptr) = (unsigned char)(toInt32(exec, value)); | |
return true; | |
case QMetaType::QChar: | |
if (value.isString()) { | |
QString str = toString(exec, value); | |
*reinterpret_cast<QChar*>(ptr) = str.isEmpty() ? QChar() : str.at(0); | |
} else { | |
*reinterpret_cast<QChar*>(ptr) = QChar(QScript::ToUInt16(toNumber(exec, value))); | |
} | |
return true; | |
case QMetaType::QDateTime: | |
if (isDate(value)) { | |
*reinterpret_cast<QDateTime *>(ptr) = toDateTime(exec, value); | |
return true; | |
} break; | |
case QMetaType::QDate: | |
if (isDate(value)) { | |
*reinterpret_cast<QDate *>(ptr) = toDateTime(exec, value).date(); | |
return true; | |
} break; | |
#ifndef QT_NO_REGEXP | |
case QMetaType::QRegExp: | |
if (isRegExp(value)) { | |
*reinterpret_cast<QRegExp *>(ptr) = toRegExp(exec, value); | |
return true; | |
} break; | |
#endif | |
#ifndef QT_NO_QOBJECT | |
case QMetaType::QObjectStar: | |
if (isQObject(value) || value.isNull()) { | |
*reinterpret_cast<QObject* *>(ptr) = toQObject(exec, value); | |
return true; | |
} break; | |
case QMetaType::QWidgetStar: | |
if (isQObject(value) || value.isNull()) { | |
QObject *qo = toQObject(exec, value); | |
if (!qo || qo->isWidgetType()) { | |
*reinterpret_cast<QWidget* *>(ptr) = reinterpret_cast<QWidget*>(qo); | |
return true; | |
} | |
} break; | |
#endif | |
case QMetaType::QStringList: | |
if (isArray(value)) { | |
*reinterpret_cast<QStringList *>(ptr) = stringListFromArray(exec, value); | |
return true; | |
} break; | |
case QMetaType::QVariantList: | |
if (isArray(value)) { | |
*reinterpret_cast<QVariantList *>(ptr) = variantListFromArray(exec, JSC::asArray(value)); | |
return true; | |
} break; | |
case QMetaType::QVariantMap: | |
if (isObject(value)) { | |
*reinterpret_cast<QVariantMap *>(ptr) = variantMapFromObject(exec, JSC::asObject(value)); | |
return true; | |
} break; | |
case QMetaType::QVariant: | |
*reinterpret_cast<QVariant*>(ptr) = toVariant(exec, value); | |
return true; | |
default: | |
; | |
} | |
QByteArray name = QMetaType::typeName(type); | |
#ifndef QT_NO_QOBJECT | |
if (convertToNativeQObject(exec, value, name, reinterpret_cast<void* *>(ptr))) | |
return true; | |
#endif | |
if (isVariant(value) && name.endsWith('*')) { | |
int valueType = QMetaType::type(name.left(name.size()-1)); | |
QVariant &var = variantValue(value); | |
if (valueType == var.userType()) { | |
*reinterpret_cast<void* *>(ptr) = var.data(); | |
return true; | |
} else { | |
// look in the prototype chain | |
JSC::JSValue proto = JSC::asObject(value)->prototype(); | |
while (proto.isObject()) { | |
bool canCast = false; | |
if (isVariant(proto)) { | |
canCast = (type == variantValue(proto).userType()) | |
|| (valueType && (valueType == variantValue(proto).userType())); | |
} | |
#ifndef QT_NO_QOBJECT | |
else if (isQObject(proto)) { | |
QByteArray className = name.left(name.size()-1); | |
if (QObject *qobject = toQObject(exec, proto)) | |
canCast = qobject->qt_metacast(className) != 0; | |
} | |
#endif | |
if (canCast) { | |
QByteArray varTypeName = QMetaType::typeName(var.userType()); | |
if (varTypeName.endsWith('*')) | |
*reinterpret_cast<void* *>(ptr) = *reinterpret_cast<void* *>(var.data()); | |
else | |
*reinterpret_cast<void* *>(ptr) = var.data(); | |
return true; | |
} | |
proto = JSC::asObject(proto)->prototype(); | |
} | |
} | |
} else if (value.isNull() && name.endsWith('*')) { | |
*reinterpret_cast<void* *>(ptr) = 0; | |
return true; | |
} else if (type == qMetaTypeId<QScriptValue>()) { | |
if (!eng) | |
return false; | |
*reinterpret_cast<QScriptValue*>(ptr) = eng->scriptValueFromJSCValue(value); | |
return true; | |
} | |
// lazy registration of some common list types | |
#ifndef QT_NO_QOBJECT | |
else if (type == qMetaTypeId<QObjectList>()) { | |
if (!eng) | |
return false; | |
qScriptRegisterSequenceMetaType<QObjectList>(eng->q_func()); | |
return convertValue(exec, value, type, ptr); | |
} | |
#endif | |
else if (type == qMetaTypeId<QList<int> >()) { | |
if (!eng) | |
return false; | |
qScriptRegisterSequenceMetaType<QList<int> >(eng->q_func()); | |
return convertValue(exec, value, type, ptr); | |
} | |
#if 0 | |
if (!name.isEmpty()) { | |
qWarning("QScriptEngine::convert: unable to convert value to type `%s'", | |
name.constData()); | |
} | |
#endif | |
return false; | |
} | |
bool QScriptEnginePrivate::convertNumber(qsreal value, int type, void *ptr) | |
{ | |
switch (QMetaType::Type(type)) { | |
case QMetaType::Bool: | |
*reinterpret_cast<bool*>(ptr) = QScript::ToBool(value); | |
return true; | |
case QMetaType::Int: | |
*reinterpret_cast<int*>(ptr) = QScript::ToInt32(value); | |
return true; | |
case QMetaType::UInt: | |
*reinterpret_cast<uint*>(ptr) = QScript::ToUInt32(value); | |
return true; | |
case QMetaType::LongLong: | |
*reinterpret_cast<qlonglong*>(ptr) = qlonglong(QScript::ToInteger(value)); | |
return true; | |
case QMetaType::ULongLong: | |
*reinterpret_cast<qulonglong*>(ptr) = qulonglong(QScript::ToInteger(value)); | |
return true; | |
case QMetaType::Double: | |
*reinterpret_cast<double*>(ptr) = value; | |
return true; | |
case QMetaType::QString: | |
*reinterpret_cast<QString*>(ptr) = QScript::ToString(value); | |
return true; | |
case QMetaType::Float: | |
*reinterpret_cast<float*>(ptr) = value; | |
return true; | |
case QMetaType::Short: | |
*reinterpret_cast<short*>(ptr) = short(QScript::ToInt32(value)); | |
return true; | |
case QMetaType::UShort: | |
*reinterpret_cast<unsigned short*>(ptr) = QScript::ToUInt16(value); | |
return true; | |
case QMetaType::Char: | |
*reinterpret_cast<char*>(ptr) = char(QScript::ToInt32(value)); | |
return true; | |
case QMetaType::UChar: | |
*reinterpret_cast<unsigned char*>(ptr) = (unsigned char)(QScript::ToInt32(value)); | |
return true; | |
case QMetaType::QChar: | |
*reinterpret_cast<QChar*>(ptr) = QChar(QScript::ToUInt16(value)); | |
return true; | |
default: | |
break; | |
} | |
return false; | |
} | |
bool QScriptEnginePrivate::convertString(const QString &value, int type, void *ptr) | |
{ | |
switch (QMetaType::Type(type)) { | |
case QMetaType::Bool: | |
*reinterpret_cast<bool*>(ptr) = QScript::ToBool(value); | |
return true; | |
case QMetaType::Int: | |
*reinterpret_cast<int*>(ptr) = QScript::ToInt32(value); | |
return true; | |
case QMetaType::UInt: | |
*reinterpret_cast<uint*>(ptr) = QScript::ToUInt32(value); | |
return true; | |
case QMetaType::LongLong: | |
*reinterpret_cast<qlonglong*>(ptr) = qlonglong(QScript::ToInteger(value)); | |
return true; | |
case QMetaType::ULongLong: | |
*reinterpret_cast<qulonglong*>(ptr) = qulonglong(QScript::ToInteger(value)); | |
return true; | |
case QMetaType::Double: | |
*reinterpret_cast<double*>(ptr) = QScript::ToNumber(value); | |
return true; | |
case QMetaType::QString: | |
*reinterpret_cast<QString*>(ptr) = value; | |
return true; | |
case QMetaType::Float: | |
*reinterpret_cast<float*>(ptr) = QScript::ToNumber(value); | |
return true; | |
case QMetaType::Short: | |
*reinterpret_cast<short*>(ptr) = short(QScript::ToInt32(value)); | |
return true; | |
case QMetaType::UShort: | |
*reinterpret_cast<unsigned short*>(ptr) = QScript::ToUInt16(value); | |
return true; | |
case QMetaType::Char: | |
*reinterpret_cast<char*>(ptr) = char(QScript::ToInt32(value)); | |
return true; | |
case QMetaType::UChar: | |
*reinterpret_cast<unsigned char*>(ptr) = (unsigned char)(QScript::ToInt32(value)); | |
return true; | |
case QMetaType::QChar: | |
*reinterpret_cast<QChar*>(ptr) = QChar(QScript::ToUInt16(value)); | |
return true; | |
default: | |
break; | |
} | |
return false; | |
} | |
bool QScriptEnginePrivate::hasDemarshalFunction(int type) const | |
{ | |
QScriptTypeInfo *info = m_typeInfos.value(type); | |
return info && (info->demarshal != 0); | |
} | |
JSC::UString QScriptEnginePrivate::translationContextFromUrl(const JSC::UString &url) | |
{ | |
if (url != cachedTranslationUrl) { | |
cachedTranslationContext = QFileInfo(url).baseName(); | |
cachedTranslationUrl = url; | |
} | |
return cachedTranslationContext; | |
} | |
/*! | |
\internal | |
*/ | |
bool QScriptEngine::convert(const QScriptValue &value, int type, void *ptr) | |
{ | |
Q_D(QScriptEngine); | |
QScript::APIShim shim(d); | |
return QScriptEnginePrivate::convertValue(d->currentFrame, d->scriptValueToJSCValue(value), type, ptr); | |
} | |
/*! | |
\internal | |
*/ | |
bool QScriptEngine::convertV2(const QScriptValue &value, int type, void *ptr) | |
{ | |
QScriptValuePrivate *vp = QScriptValuePrivate::get(value); | |
if (vp) { | |
switch (vp->type) { | |
case QScriptValuePrivate::JavaScriptCore: { | |
if (vp->engine) { | |
QScript::APIShim shim(vp->engine); | |
return QScriptEnginePrivate::convertValue(vp->engine->currentFrame, vp->jscValue, type, ptr); | |
} else { | |
return QScriptEnginePrivate::convertValue(0, vp->jscValue, type, ptr); | |
} | |
} | |
case QScriptValuePrivate::Number: | |
return QScriptEnginePrivate::convertNumber(vp->numberValue, type, ptr); | |
case QScriptValuePrivate::String: | |
return QScriptEnginePrivate::convertString(vp->stringValue, type, ptr); | |
} | |
} | |
return false; | |
} | |
/*! | |
\internal | |
*/ | |
void QScriptEngine::registerCustomType(int type, MarshalFunction mf, | |
DemarshalFunction df, | |
const QScriptValue &prototype) | |
{ | |
Q_D(QScriptEngine); | |
QScript::APIShim shim(d); | |
QScriptTypeInfo *info = d->m_typeInfos.value(type); | |
if (!info) { | |
info = new QScriptTypeInfo(); | |
d->m_typeInfos.insert(type, info); | |
} | |
info->marshal = mf; | |
info->demarshal = df; | |
info->prototype = d->scriptValueToJSCValue(prototype); | |
} | |
/*! | |
\since 4.5 | |
Installs translator functions on the given \a object, or on the Global | |
Object if no object is specified. | |
The relation between Qt Script translator functions and C++ translator | |
functions is described in the following table: | |
\table | |
\header \o Script Function \o Corresponding C++ Function | |
\row \o qsTr() \o QObject::tr() | |
\row \o QT_TR_NOOP() \o QT_TR_NOOP() | |
\row \o qsTranslate() \o QCoreApplication::translate() | |
\row \o QT_TRANSLATE_NOOP() \o QT_TRANSLATE_NOOP() | |
\row \o qsTrId() (since 4.7) \o qtTrId() | |
\row \o QT_TRID_NOOP() (since 4.7) \o QT_TRID_NOOP() | |
\endtable | |
\sa {Internationalization with Qt} | |
*/ | |
void QScriptEngine::installTranslatorFunctions(const QScriptValue &object) | |
{ | |
Q_D(QScriptEngine); | |
QScript::APIShim shim(d); | |
JSC::ExecState* exec = d->currentFrame; | |
JSC::JSValue jscObject = d->scriptValueToJSCValue(object); | |
JSC::JSGlobalObject *glob = d->originalGlobalObject(); | |
if (!jscObject || !jscObject.isObject()) | |
jscObject = d->globalObject(); | |
// unsigned attribs = JSC::DontEnum; | |
JSC::asObject(jscObject)->putDirectFunction(exec, new (exec)JSC::NativeFunctionWrapper(exec, glob->prototypeFunctionStructure(), 5, JSC::Identifier(exec, "qsTranslate"), QScript::functionQsTranslate)); | |
JSC::asObject(jscObject)->putDirectFunction(exec, new (exec)JSC::NativeFunctionWrapper(exec, glob->prototypeFunctionStructure(), 2, JSC::Identifier(exec, "QT_TRANSLATE_NOOP"), QScript::functionQsTranslateNoOp)); | |
JSC::asObject(jscObject)->putDirectFunction(exec, new (exec)JSC::NativeFunctionWrapper(exec, glob->prototypeFunctionStructure(), 3, JSC::Identifier(exec, "qsTr"), QScript::functionQsTr)); | |
JSC::asObject(jscObject)->putDirectFunction(exec, new (exec)JSC::NativeFunctionWrapper(exec, glob->prototypeFunctionStructure(), 1, JSC::Identifier(exec, "QT_TR_NOOP"), QScript::functionQsTrNoOp)); | |
JSC::asObject(jscObject)->putDirectFunction(exec, new (exec)JSC::NativeFunctionWrapper(exec, glob->prototypeFunctionStructure(), 1, JSC::Identifier(exec, "qsTrId"), QScript::functionQsTrId)); | |
JSC::asObject(jscObject)->putDirectFunction(exec, new (exec)JSC::NativeFunctionWrapper(exec, glob->prototypeFunctionStructure(), 1, JSC::Identifier(exec, "QT_TRID_NOOP"), QScript::functionQsTrIdNoOp)); | |
glob->stringPrototype()->putDirectFunction(exec, new (exec)JSC::NativeFunctionWrapper(exec, glob->prototypeFunctionStructure(), 1, JSC::Identifier(exec, "arg"), QScript::stringProtoFuncArg)); | |
} | |
/*! | |
Imports the given \a extension into this QScriptEngine. Returns | |
undefinedValue() if the extension was successfully imported. You | |
can call hasUncaughtException() to check if an error occurred; in | |
that case, the return value is the value that was thrown by the | |
exception (usually an \c{Error} object). | |
QScriptEngine ensures that a particular extension is only imported | |
once; subsequent calls to importExtension() with the same extension | |
name will do nothing and return undefinedValue(). | |
\sa availableExtensions(), QScriptExtensionPlugin, {Creating QtScript Extensions} | |
*/ | |
QScriptValue QScriptEngine::importExtension(const QString &extension) | |
{ | |
#if defined(QT_NO_QOBJECT) || defined(QT_NO_LIBRARY) || defined(QT_NO_SETTINGS) | |
Q_UNUSED(extension); | |
#else | |
Q_D(QScriptEngine); | |
QScript::APIShim shim(d); | |
if (d->importedExtensions.contains(extension)) | |
return undefinedValue(); // already imported | |
QScriptContext *context = currentContext(); | |
QCoreApplication *app = QCoreApplication::instance(); | |
if (!app) | |
return context->throwError(QLatin1String("No application object")); | |
QObjectList staticPlugins = QPluginLoader::staticInstances(); | |
QStringList libraryPaths = app->libraryPaths(); | |
QString dot = QLatin1String("."); | |
QStringList pathComponents = extension.split(dot); | |
QString initDotJs = QLatin1String("__init__.js"); | |
QString ext; | |
for (int i = 0; i < pathComponents.count(); ++i) { | |
if (!ext.isEmpty()) | |
ext.append(dot); | |
ext.append(pathComponents.at(i)); | |
if (d->importedExtensions.contains(ext)) | |
continue; // already imported | |
if (d->extensionsBeingImported.contains(ext)) { | |
return context->throwError(QString::fromLatin1("recursive import of %0") | |
.arg(extension)); | |
} | |
d->extensionsBeingImported.insert(ext); | |
QScriptExtensionInterface *iface = 0; | |
QString initjsContents; | |
QString initjsFileName; | |
// look for the extension in static plugins | |
for (int j = 0; j < staticPlugins.size(); ++j) { | |
iface = qobject_cast<QScriptExtensionInterface*>(staticPlugins.at(j)); | |
if (!iface) | |
continue; | |
if (iface->keys().contains(ext)) | |
break; // use this one | |
else | |
iface = 0; // keep looking | |
} | |
{ | |
// look for __init__.js resource | |
QString path = QString::fromLatin1(":/qtscriptextension"); | |
for (int j = 0; j <= i; ++j) { | |
path.append(QLatin1Char('/')); | |
path.append(pathComponents.at(j)); | |
} | |
path.append(QLatin1Char('/')); | |
path.append(initDotJs); | |
QFile file(path); | |
if (file.open(QIODevice::ReadOnly)) { | |
QTextStream ts(&file); | |
initjsContents = ts.readAll(); | |
initjsFileName = path; | |
file.close(); | |
} | |
} | |
if (!iface && initjsContents.isEmpty()) { | |
// look for the extension in library paths | |
for (int j = 0; j < libraryPaths.count(); ++j) { | |
QString libPath = libraryPaths.at(j) + QDir::separator() + QLatin1String("script"); | |
QDir dir(libPath); | |
if (!dir.exists(dot)) | |
continue; | |
// look for C++ plugin | |
QFileInfoList files = dir.entryInfoList(QDir::Files); | |
for (int k = 0; k < files.count(); ++k) { | |
QFileInfo entry = files.at(k); | |
QString filePath = entry.canonicalFilePath(); | |
QPluginLoader loader(filePath); | |
iface = qobject_cast<QScriptExtensionInterface*>(loader.instance()); | |
if (iface) { | |
if (iface->keys().contains(ext)) | |
break; // use this one | |
else | |
iface = 0; // keep looking | |
} | |
} | |
// look for __init__.js in the corresponding dir | |
QDir dirdir(libPath); | |
bool dirExists = dirdir.exists(); | |
for (int k = 0; dirExists && (k <= i); ++k) | |
dirExists = dirdir.cd(pathComponents.at(k)); | |
if (dirExists && dirdir.exists(initDotJs)) { | |
QFile file(dirdir.canonicalPath() | |
+ QDir::separator() + initDotJs); | |
if (file.open(QIODevice::ReadOnly)) { | |
QTextStream ts(&file); | |
initjsContents = ts.readAll(); | |
initjsFileName = file.fileName(); | |
file.close(); | |
} | |
} | |
if (iface || !initjsContents.isEmpty()) | |
break; | |
} | |
} | |
if (!iface && initjsContents.isEmpty()) { | |
d->extensionsBeingImported.remove(ext); | |
return context->throwError( | |
QString::fromLatin1("Unable to import %0: no such extension") | |
.arg(extension)); | |
} | |
// initialize the extension in a new context | |
QScriptContext *ctx = pushContext(); | |
ctx->setThisObject(globalObject()); | |
ctx->activationObject().setProperty(QLatin1String("__extension__"), ext, | |
QScriptValue::ReadOnly | QScriptValue::Undeletable); | |
ctx->activationObject().setProperty(QLatin1String("__setupPackage__"), | |
newFunction(QScript::__setupPackage__)); | |
ctx->activationObject().setProperty(QLatin1String("__postInit__"), QScriptValue(QScriptValue::UndefinedValue)); | |
// the script is evaluated first | |
if (!initjsContents.isEmpty()) { | |
QScriptValue ret = evaluate(initjsContents, initjsFileName); | |
if (hasUncaughtException()) { | |
popContext(); | |
d->extensionsBeingImported.remove(ext); | |
return ret; | |
} | |
} | |
// next, the C++ plugin is called | |
if (iface) { | |
iface->initialize(ext, this); | |
if (hasUncaughtException()) { | |
QScriptValue ret = uncaughtException(); // ctx_p->returnValue(); | |
popContext(); | |
d->extensionsBeingImported.remove(ext); | |
return ret; | |
} | |
} | |
// if the __postInit__ function has been set, we call it | |
QScriptValue postInit = ctx->activationObject().property(QLatin1String("__postInit__")); | |
if (postInit.isFunction()) { | |
postInit.call(globalObject()); | |
if (hasUncaughtException()) { | |
QScriptValue ret = uncaughtException(); // ctx_p->returnValue(); | |
popContext(); | |
d->extensionsBeingImported.remove(ext); | |
return ret; | |
} | |
} | |
popContext(); | |
d->importedExtensions.insert(ext); | |
d->extensionsBeingImported.remove(ext); | |
} // for (i) | |
#endif // QT_NO_QOBJECT | |
return undefinedValue(); | |
} | |
/*! | |
\since 4.4 | |
Returns a list naming the available extensions that can be | |
imported using the importExtension() function. This list includes | |
extensions that have been imported. | |
\sa importExtension(), importedExtensions() | |
*/ | |
QStringList QScriptEngine::availableExtensions() const | |
{ | |
#if defined(QT_NO_QOBJECT) || defined(QT_NO_LIBRARY) || defined(QT_NO_SETTINGS) | |
return QStringList(); | |
#else | |
QCoreApplication *app = QCoreApplication::instance(); | |
if (!app) | |
return QStringList(); | |
QSet<QString> result; | |
QObjectList staticPlugins = QPluginLoader::staticInstances(); | |
for (int i = 0; i < staticPlugins.size(); ++i) { | |
QScriptExtensionInterface *iface; | |
iface = qobject_cast<QScriptExtensionInterface*>(staticPlugins.at(i)); | |
if (iface) { | |
QStringList keys = iface->keys(); | |
for (int j = 0; j < keys.count(); ++j) | |
result << keys.at(j); | |
} | |
} | |
QStringList libraryPaths = app->libraryPaths(); | |
for (int i = 0; i < libraryPaths.count(); ++i) { | |
QString libPath = libraryPaths.at(i) + QDir::separator() + QLatin1String("script"); | |
QDir dir(libPath); | |
if (!dir.exists()) | |
continue; | |
// look for C++ plugins | |
QFileInfoList files = dir.entryInfoList(QDir::Files); | |
for (int j = 0; j < files.count(); ++j) { | |
QFileInfo entry = files.at(j); | |
QString filePath = entry.canonicalFilePath(); | |
QPluginLoader loader(filePath); | |
QScriptExtensionInterface *iface; | |
iface = qobject_cast<QScriptExtensionInterface*>(loader.instance()); | |
if (iface) { | |
QStringList keys = iface->keys(); | |
for (int k = 0; k < keys.count(); ++k) | |
result << keys.at(k); | |
} | |
} | |
// look for scripts | |
QString initDotJs = QLatin1String("__init__.js"); | |
QList<QFileInfo> stack; | |
stack << dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot); | |
while (!stack.isEmpty()) { | |
QFileInfo entry = stack.takeLast(); | |
QDir dd(entry.canonicalFilePath()); | |
if (dd.exists(initDotJs)) { | |
QString rpath = dir.relativeFilePath(dd.canonicalPath()); | |
QStringList components = rpath.split(QLatin1Char('/')); | |
result << components.join(QLatin1String(".")); | |
stack << dd.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot); | |
} | |
} | |
} | |
QStringList lst = result.toList(); | |
qSort(lst); | |
return lst; | |
#endif | |
} | |
/*! | |
\since 4.4 | |
Returns a list naming the extensions that have been imported | |
using the importExtension() function. | |
\sa availableExtensions() | |
*/ | |
QStringList QScriptEngine::importedExtensions() const | |
{ | |
Q_D(const QScriptEngine); | |
QStringList lst = d->importedExtensions.toList(); | |
qSort(lst); | |
return lst; | |
} | |
/*! \fn QScriptValue QScriptEngine::toScriptValue(const T &value) | |
Creates a QScriptValue with the given \a value. | |
Note that the template type \c{T} must be known to QMetaType. | |
See \l{Conversion Between QtScript and C++ Types} for a | |
description of the built-in type conversion provided by | |
QtScript. By default, the types that are not specially handled by | |
QtScript are represented as QVariants (e.g. the \a value is passed | |
to newVariant()); you can change this behavior by installing your | |
own type conversion functions with qScriptRegisterMetaType(). | |
\warning This function is not available with MSVC 6. Use | |
qScriptValueFromValue() instead if you need to support that | |
version of the compiler. | |
\sa fromScriptValue(), qScriptRegisterMetaType() | |
*/ | |
/*! \fn T QScriptEngine::fromScriptValue(const QScriptValue &value) | |
Returns the given \a value converted to the template type \c{T}. | |
Note that \c{T} must be known to QMetaType. | |
See \l{Conversion Between QtScript and C++ Types} for a | |
description of the built-in type conversion provided by | |
QtScript. | |
\warning This function is not available with MSVC 6. Use | |
qScriptValueToValue() or qscriptvalue_cast() instead if you need | |
to support that version of the compiler. | |
\sa toScriptValue(), qScriptRegisterMetaType() | |
*/ | |
/*! | |
\fn QScriptValue qScriptValueFromValue(QScriptEngine *engine, const T &value) | |
\since 4.3 | |
\relates QScriptEngine | |
Creates a QScriptValue using the given \a engine with the given \a | |
value of template type \c{T}. | |
This function is equivalent to QScriptEngine::toScriptValue(). | |
It is provided as a work-around for MSVC 6, which doesn't support | |
member template functions. | |
\sa qScriptValueToValue() | |
*/ | |
/*! | |
\fn T qScriptValueToValue(const QScriptValue &value) | |
\since 4.3 | |
\relates QScriptEngine | |
Returns the given \a value converted to the template type \c{T}. | |
This function is equivalent to QScriptEngine::fromScriptValue(). | |
It is provided as a work-around for MSVC 6, which doesn't | |
support member template functions. | |
\sa qScriptValueFromValue() | |
*/ | |
/*! | |
\fn QScriptValue qScriptValueFromSequence(QScriptEngine *engine, const Container &container) | |
\since 4.3 | |
\relates QScriptEngine | |
Creates an array in the form of a QScriptValue using the given \a engine | |
with the given \a container of template type \c{Container}. | |
The \c Container type must provide a \c const_iterator class to enable the | |
contents of the container to be copied into the array. | |
Additionally, the type of each element in the sequence should be | |
suitable for conversion to a QScriptValue. See | |
\l{Conversion Between QtScript and C++ Types} for more information | |
about the restrictions on types that can be used with QScriptValue. | |
\sa qScriptValueFromValue() | |
*/ | |
/*! | |
\fn void qScriptValueToSequence(const QScriptValue &value, Container &container) | |
\since 4.3 | |
\relates QScriptEngine | |
Copies the elements in the sequence specified by \a value to the given | |
\a container of template type \c{Container}. | |
The \a value used is typically an array, but any container can be copied | |
as long as it provides a \c length property describing how many elements | |
it contains. | |
Additionally, the type of each element in the sequence must be | |
suitable for conversion to a C++ type from a QScriptValue. See | |
\l{Conversion Between QtScript and C++ Types} for more information | |
about the restrictions on types that can be used with | |
QScriptValue. | |
\sa qscriptvalue_cast() | |
*/ | |
/*! | |
\fn T qscriptvalue_cast(const QScriptValue &value) | |
\since 4.3 | |
\relates QScriptValue | |
Returns the given \a value converted to the template type \c{T}. | |
\sa qScriptRegisterMetaType(), QScriptEngine::toScriptValue() | |
*/ | |
/*! \fn int qScriptRegisterMetaType( | |
QScriptEngine *engine, | |
QScriptValue (*toScriptValue)(QScriptEngine *, const T &t), | |
void (*fromScriptValue)(const QScriptValue &, T &t), | |
const QScriptValue &prototype = QScriptValue()) | |
\relates QScriptEngine | |
Registers the type \c{T} in the given \a engine. \a toScriptValue must | |
be a function that will convert from a value of type \c{T} to a | |
QScriptValue, and \a fromScriptValue a function that does the | |
opposite. \a prototype, if valid, is the prototype that's set on | |
QScriptValues returned by \a toScriptValue. | |
Returns the internal ID used by QMetaType. | |
You only need to call this function if you want to provide custom | |
conversion of values of type \c{T}, i.e. if the default | |
QVariant-based representation and conversion is not | |
appropriate. (Note that custom QObject-derived types also fall in | |
this category; e.g. for a QObject-derived class called MyObject, | |
you probably want to define conversion functions for MyObject* | |
that utilize QScriptEngine::newQObject() and | |
QScriptValue::toQObject().) | |
If you only want to define a common script interface for values of | |
type \c{T}, and don't care how those values are represented | |
(i.e. storing them in QVariants is fine), use | |
\l{QScriptEngine::setDefaultPrototype()}{setDefaultPrototype}() | |
instead; this will minimize conversion costs. | |
You need to declare the custom type first with | |
Q_DECLARE_METATYPE(). | |
After a type has been registered, you can convert from a | |
QScriptValue to that type using | |
\l{QScriptEngine::fromScriptValue()}{fromScriptValue}(), and | |
create a QScriptValue from a value of that type using | |
\l{QScriptEngine::toScriptValue()}{toScriptValue}(). The engine | |
will take care of calling the proper conversion function when | |
calling C++ slots, and when getting or setting a C++ property; | |
i.e. the custom type may be used seamlessly on both the C++ side | |
and the script side. | |
The following is an example of how to use this function. We will | |
specify custom conversion of our type \c{MyStruct}. Here's the C++ | |
type: | |
\snippet doc/src/snippets/code/src_script_qscriptengine.cpp 20 | |
We must declare it so that the type will be known to QMetaType: | |
\snippet doc/src/snippets/code/src_script_qscriptengine.cpp 21 | |
Next, the \c{MyStruct} conversion functions. We represent the | |
\c{MyStruct} value as a script object and just copy the properties: | |
\snippet doc/src/snippets/code/src_script_qscriptengine.cpp 22 | |
Now we can register \c{MyStruct} with the engine: | |
\snippet doc/src/snippets/code/src_script_qscriptengine.cpp 23 | |
Working with \c{MyStruct} values is now easy: | |
\snippet doc/src/snippets/code/src_script_qscriptengine.cpp 24 | |
If you want to be able to construct values of your custom type | |
from script code, you have to register a constructor function for | |
the type. For example: | |
\snippet doc/src/snippets/code/src_script_qscriptengine.cpp 25 | |
\sa qScriptRegisterSequenceMetaType(), qRegisterMetaType() | |
*/ | |
/*! | |
\macro Q_SCRIPT_DECLARE_QMETAOBJECT(QMetaObject, ArgType) | |
\since 4.3 | |
\relates QScriptEngine | |
Declares the given \a QMetaObject. Used in combination with | |
QScriptEngine::scriptValueFromQMetaObject() to make enums and | |
instantiation of \a QMetaObject available to script code. The | |
constructor generated by this macro takes a single argument of | |
type \a ArgType; typically the argument is the parent type of the | |
new instance, in which case \a ArgType is \c{QWidget*} or | |
\c{QObject*}. Objects created by the constructor will have | |
QScriptEngine::AutoOwnership ownership. | |
*/ | |
/*! \fn int qScriptRegisterSequenceMetaType( | |
QScriptEngine *engine, | |
const QScriptValue &prototype = QScriptValue()) | |
\relates QScriptEngine | |
Registers the sequence type \c{T} in the given \a engine. This | |
function provides conversion functions that convert between \c{T} | |
and Qt Script \c{Array} objects. \c{T} must provide a | |
const_iterator class and begin(), end() and push_back() | |
functions. If \a prototype is valid, it will be set as the | |
prototype of \c{Array} objects due to conversion from \c{T}; | |
otherwise, the standard \c{Array} prototype will be used. | |
Returns the internal ID used by QMetaType. | |
You need to declare the container type first with | |
Q_DECLARE_METATYPE(). If the element type isn't a standard Qt/C++ | |
type, it must be declared using Q_DECLARE_METATYPE() as well. | |
Example: | |
\snippet doc/src/snippets/code/src_script_qscriptengine.cpp 26 | |
\sa qScriptRegisterMetaType() | |
*/ | |
/*! | |
Runs the garbage collector. | |
The garbage collector will attempt to reclaim memory by locating and | |
disposing of objects that are no longer reachable in the script | |
environment. | |
Normally you don't need to call this function; the garbage collector | |
will automatically be invoked when the QScriptEngine decides that | |
it's wise to do so (i.e. when a certain number of new objects have | |
been created). However, you can call this function to explicitly | |
request that garbage collection should be performed as soon as | |
possible. | |
\sa reportAdditionalMemoryCost() | |
*/ | |
void QScriptEngine::collectGarbage() | |
{ | |
Q_D(QScriptEngine); | |
d->collectGarbage(); | |
} | |
/*! | |
\since 4.7 | |
Reports an additional memory cost of the given \a size, measured in | |
bytes, to the garbage collector. | |
This function can be called to indicate that a Qt Script object has | |
memory associated with it that isn't managed by Qt Script itself. | |
Reporting the additional cost makes it more likely that the garbage | |
collector will be triggered. | |
Note that if the additional memory is shared with objects outside | |
the scripting environment, the cost should not be reported, since | |
collecting the Qt Script object would not cause the memory to be | |
freed anyway. | |
Negative \a size values are ignored, i.e. this function can't be | |
used to report that the additional memory has been deallocated. | |
\sa collectGarbage() | |
*/ | |
void QScriptEngine::reportAdditionalMemoryCost(int size) | |
{ | |
Q_D(QScriptEngine); | |
d->reportAdditionalMemoryCost(size); | |
} | |
/*! | |
Sets the interval between calls to QCoreApplication::processEvents | |
to \a interval milliseconds. | |
While the interpreter is running, all event processing is by default | |
blocked. This means for instance that the gui will not be updated | |
and timers will not be fired. To allow event processing during | |
interpreter execution one can specify the processing interval to be | |
a positive value, indicating the number of milliseconds between each | |
time QCoreApplication::processEvents() is called. | |
The default value is -1, which disables event processing during | |
interpreter execution. | |
You can use QCoreApplication::postEvent() to post an event that | |
performs custom processing at the next interval. For example, you | |
could keep track of the total running time of the script and call | |
abortEvaluation() when you detect that the script has been running | |
for a long time without completing. | |
\sa processEventsInterval() | |
*/ | |
void QScriptEngine::setProcessEventsInterval(int interval) | |
{ | |
Q_D(QScriptEngine); | |
d->processEventsInterval = interval; | |
if (interval > 0) | |
d->globalData->timeoutChecker->setCheckInterval(interval); | |
d->timeoutChecker()->setShouldProcessEvents(interval > 0); | |
} | |
/*! | |
Returns the interval in milliseconds between calls to | |
QCoreApplication::processEvents() while the interpreter is running. | |
\sa setProcessEventsInterval() | |
*/ | |
int QScriptEngine::processEventsInterval() const | |
{ | |
Q_D(const QScriptEngine); | |
return d->processEventsInterval; | |
} | |
/*! | |
\since 4.4 | |
Returns true if this engine is currently evaluating a script, | |
otherwise returns false. | |
\sa evaluate(), abortEvaluation() | |
*/ | |
bool QScriptEngine::isEvaluating() const | |
{ | |
Q_D(const QScriptEngine); | |
return (d->currentFrame != d->globalExec()) || d->inEval; | |
} | |
/*! | |
\since 4.4 | |
Aborts any script evaluation currently taking place in this engine. | |
The given \a result is passed back as the result of the evaluation | |
(i.e. it is returned from the call to evaluate() being aborted). | |
If the engine isn't evaluating a script (i.e. isEvaluating() returns | |
false), this function does nothing. | |
Call this function if you need to abort a running script for some | |
reason, e.g. when you have detected that the script has been | |
running for several seconds without completing. | |
\sa evaluate(), isEvaluating(), setProcessEventsInterval() | |
*/ | |
void QScriptEngine::abortEvaluation(const QScriptValue &result) | |
{ | |
Q_D(QScriptEngine); | |
if (!isEvaluating()) | |
return; | |
d->abortResult = result; | |
d->timeoutChecker()->setShouldAbort(true); | |
JSC::throwError(d->currentFrame, JSC::createInterruptedExecutionException(&d->currentFrame->globalData()).toObject(d->currentFrame)); | |
} | |
#ifndef QT_NO_QOBJECT | |
/*! | |
\since 4.4 | |
\relates QScriptEngine | |
Creates a connection from the \a signal in the \a sender to the | |
given \a function. If \a receiver is an object, it will act as the | |
`this' object when the signal handler function is invoked. Returns | |
true if the connection succeeds; otherwise returns false. | |
\sa qScriptDisconnect(), QScriptEngine::signalHandlerException() | |
*/ | |
bool qScriptConnect(QObject *sender, const char *signal, | |
const QScriptValue &receiver, const QScriptValue &function) | |
{ | |
if (!sender || !signal) | |
return false; | |
if (!function.isFunction()) | |
return false; | |
if (receiver.isObject() && (receiver.engine() != function.engine())) | |
return false; | |
QScriptEnginePrivate *engine = QScriptEnginePrivate::get(function.engine()); | |
QScript::APIShim shim(engine); | |
JSC::JSValue jscReceiver = engine->scriptValueToJSCValue(receiver); | |
JSC::JSValue jscFunction = engine->scriptValueToJSCValue(function); | |
return engine->scriptConnect(sender, signal, jscReceiver, jscFunction, | |
Qt::AutoConnection); | |
} | |
/*! | |
\since 4.4 | |
\relates QScriptEngine | |
Disconnects the \a signal in the \a sender from the given (\a | |
receiver, \a function) pair. Returns true if the connection is | |
successfully broken; otherwise returns false. | |
\sa qScriptConnect() | |
*/ | |
bool qScriptDisconnect(QObject *sender, const char *signal, | |
const QScriptValue &receiver, const QScriptValue &function) | |
{ | |
if (!sender || !signal) | |
return false; | |
if (!function.isFunction()) | |
return false; | |
if (receiver.isObject() && (receiver.engine() != function.engine())) | |
return false; | |
QScriptEnginePrivate *engine = QScriptEnginePrivate::get(function.engine()); | |
QScript::APIShim shim(engine); | |
JSC::JSValue jscReceiver = engine->scriptValueToJSCValue(receiver); | |
JSC::JSValue jscFunction = engine->scriptValueToJSCValue(function); | |
return engine->scriptDisconnect(sender, signal, jscReceiver, jscFunction); | |
} | |
/*! | |
\since 4.4 | |
\fn void QScriptEngine::signalHandlerException(const QScriptValue &exception) | |
This signal is emitted when a script function connected to a signal causes | |
an \a exception. | |
\sa qScriptConnect() | |
*/ | |
QT_BEGIN_INCLUDE_NAMESPACE | |
#include "moc_qscriptengine.cpp" | |
QT_END_INCLUDE_NAMESPACE | |
#endif // QT_NO_QOBJECT | |
/*! | |
\since 4.4 | |
Installs the given \a agent on this engine. The agent will be | |
notified of various events pertaining to script execution. This is | |
useful when you want to find out exactly what the engine is doing, | |
e.g. when evaluate() is called. The agent interface is the basis of | |
tools like debuggers and profilers. | |
The engine maintains ownership of the \a agent. | |
Calling this function will replace the existing agent, if any. | |
\sa agent() | |
*/ | |
void QScriptEngine::setAgent(QScriptEngineAgent *agent) | |
{ | |
Q_D(QScriptEngine); | |
if (agent && (agent->engine() != this)) { | |
qWarning("QScriptEngine::setAgent(): " | |
"cannot set agent belonging to different engine"); | |
return; | |
} | |
QScript::APIShim shim(d); | |
if (d->activeAgent) | |
QScriptEngineAgentPrivate::get(d->activeAgent)->detach(); | |
d->activeAgent = agent; | |
if (agent) { | |
QScriptEngineAgentPrivate::get(agent)->attach(); | |
} | |
} | |
/*! | |
\since 4.4 | |
Returns the agent currently installed on this engine, or 0 if no | |
agent is installed. | |
\sa setAgent() | |
*/ | |
QScriptEngineAgent *QScriptEngine::agent() const | |
{ | |
Q_D(const QScriptEngine); | |
return d->activeAgent; | |
} | |
/*! | |
\since 4.4 | |
Returns a handle that represents the given string, \a str. | |
QScriptString can be used to quickly look up properties, and | |
compare property names, of script objects. | |
\sa QScriptValue::property() | |
*/ | |
QScriptString QScriptEngine::toStringHandle(const QString &str) | |
{ | |
Q_D(QScriptEngine); | |
QScript::APIShim shim(d); | |
return d->toStringHandle(JSC::Identifier(d->currentFrame, str)); | |
} | |
/*! | |
\since 4.5 | |
Converts the given \a value to an object, if such a conversion is | |
possible; otherwise returns an invalid QScriptValue. The conversion | |
is performed according to the following table: | |
\table | |
\header \o Input Type \o Result | |
\row \o Undefined \o An invalid QScriptValue. | |
\row \o Null \o An invalid QScriptValue. | |
\row \o Boolean \o A new Boolean object whose internal value is set to the value of the boolean. | |
\row \o Number \o A new Number object whose internal value is set to the value of the number. | |
\row \o String \o A new String object whose internal value is set to the value of the string. | |
\row \o Object \o The result is the object itself (no conversion). | |
\endtable | |
\sa newObject() | |
*/ | |
QScriptValue QScriptEngine::toObject(const QScriptValue &value) | |
{ | |
Q_D(QScriptEngine); | |
QScript::APIShim shim(d); | |
JSC::JSValue jscValue = d->scriptValueToJSCValue(value); | |
if (!jscValue || jscValue.isUndefined() || jscValue.isNull()) | |
return QScriptValue(); | |
JSC::ExecState* exec = d->currentFrame; | |
JSC::JSValue result = jscValue.toObject(exec); | |
return d->scriptValueFromJSCValue(result); | |
} | |
/*! | |
\internal | |
Returns the object with the given \a id, or an invalid | |
QScriptValue if there is no object with that id. | |
\sa QScriptValue::objectId() | |
*/ | |
QScriptValue QScriptEngine::objectById(qint64 id) const | |
{ | |
Q_D(const QScriptEngine); | |
// Assumes that the cell was not been garbage collected | |
return const_cast<QScriptEnginePrivate*>(d)->scriptValueFromJSCValue((JSC::JSCell*)id); | |
} | |
/*! | |
\since 4.5 | |
\class QScriptSyntaxCheckResult | |
\brief The QScriptSyntaxCheckResult class provides the result of a script syntax check. | |
\ingroup script | |
\mainclass | |
QScriptSyntaxCheckResult is returned by QScriptEngine::checkSyntax() to | |
provide information about the syntactical (in)correctness of a script. | |
*/ | |
/*! | |
\enum QScriptSyntaxCheckResult::State | |
This enum specifies the state of a syntax check. | |
\value Error The program contains a syntax error. | |
\value Intermediate The program is incomplete. | |
\value Valid The program is a syntactically correct Qt Script program. | |
*/ | |
/*! | |
Constructs a new QScriptSyntaxCheckResult from the \a other result. | |
*/ | |
QScriptSyntaxCheckResult::QScriptSyntaxCheckResult(const QScriptSyntaxCheckResult &other) | |
: d_ptr(other.d_ptr) | |
{ | |
} | |
/*! | |
\internal | |
*/ | |
QScriptSyntaxCheckResult::QScriptSyntaxCheckResult(QScriptSyntaxCheckResultPrivate *d) | |
: d_ptr(d) | |
{ | |
} | |
/*! | |
\internal | |
*/ | |
QScriptSyntaxCheckResult::QScriptSyntaxCheckResult() | |
: d_ptr(0) | |
{ | |
} | |
/*! | |
Destroys this QScriptSyntaxCheckResult. | |
*/ | |
QScriptSyntaxCheckResult::~QScriptSyntaxCheckResult() | |
{ | |
} | |
/*! | |
Returns the state of this QScriptSyntaxCheckResult. | |
*/ | |
QScriptSyntaxCheckResult::State QScriptSyntaxCheckResult::state() const | |
{ | |
Q_D(const QScriptSyntaxCheckResult); | |
if (!d) | |
return Valid; | |
return d->state; | |
} | |
/*! | |
Returns the error line number of this QScriptSyntaxCheckResult, or -1 if | |
there is no error. | |
\sa state(), errorMessage() | |
*/ | |
int QScriptSyntaxCheckResult::errorLineNumber() const | |
{ | |
Q_D(const QScriptSyntaxCheckResult); | |
if (!d) | |
return -1; | |
return d->errorLineNumber; | |
} | |
/*! | |
Returns the error column number of this QScriptSyntaxCheckResult, or -1 if | |
there is no error. | |
\sa state(), errorLineNumber() | |
*/ | |
int QScriptSyntaxCheckResult::errorColumnNumber() const | |
{ | |
Q_D(const QScriptSyntaxCheckResult); | |
if (!d) | |
return -1; | |
return d->errorColumnNumber; | |
} | |
/*! | |
Returns the error message of this QScriptSyntaxCheckResult, or an empty | |
string if there is no error. | |
\sa state(), errorLineNumber() | |
*/ | |
QString QScriptSyntaxCheckResult::errorMessage() const | |
{ | |
Q_D(const QScriptSyntaxCheckResult); | |
if (!d) | |
return QString(); | |
return d->errorMessage; | |
} | |
/*! | |
Assigns the \a other result to this QScriptSyntaxCheckResult, and returns a | |
reference to this QScriptSyntaxCheckResult. | |
*/ | |
QScriptSyntaxCheckResult &QScriptSyntaxCheckResult::operator=(const QScriptSyntaxCheckResult &other) | |
{ | |
d_ptr = other.d_ptr; | |
return *this; | |
} | |
#ifdef QT_BUILD_INTERNAL | |
Q_AUTOTEST_EXPORT bool qt_script_isJITEnabled() | |
{ | |
#if ENABLE(JIT) | |
return true; | |
#else | |
return false; | |
#endif | |
} | |
#endif | |
#ifdef Q_CC_MSVC | |
// Try to prevent compiler from crashing. | |
#pragma optimize("", off) | |
#endif | |
QT_END_NAMESPACE |