diff options
Diffstat (limited to 'src/script/qscriptengine_p.cpp')
-rw-r--r-- | src/script/qscriptengine_p.cpp | 2724 |
1 files changed, 2724 insertions, 0 deletions
diff --git a/src/script/qscriptengine_p.cpp b/src/script/qscriptengine_p.cpp new file mode 100644 index 0000000..a2e58de --- /dev/null +++ b/src/script/qscriptengine_p.cpp @@ -0,0 +1,2724 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtScript module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qscriptengine_p.h" + +#ifndef QT_NO_SCRIPT + +#include "qscriptvalueimpl_p.h" +#include "qscriptcontext_p.h" +#include "qscriptmember_p.h" +#include "qscriptobject_p.h" +#include "qscriptlexer_p.h" +#include "qscriptnodepool_p.h" +#include "qscriptparser_p.h" +#include "qscriptcompiler_p.h" +#include "qscriptvalueiteratorimpl_p.h" +#include "qscriptecmaglobal_p.h" +#include "qscriptecmamath_p.h" +#include "qscriptecmaarray_p.h" +#include "qscriptextenumeration_p.h" +#include "qscriptsyntaxchecker_p.h" +#include "qscriptsyntaxcheckresult_p.h" +#include "qscriptclass.h" +#include "qscriptclass_p.h" +#include "qscriptengineagent.h" + +#include <QtCore/QDate> +#include <QtCore/QDateTime> +#include <QtCore/QRegExp> +#include <QtCore/QStringList> +#include <QtCore/QVariant> + +#ifndef QT_NO_QOBJECT +#include "qscriptextensioninterface.h" +#include <QtCore/QDir> +#include <QtCore/QFile> +#include <QtCore/QFileInfo> +#include <QtCore/QTextStream> +#include <QtCore/QCoreApplication> +#include <QtCore/QPluginLoader> +#endif + +Q_DECLARE_METATYPE(QScriptValue) +#ifndef QT_NO_QOBJECT +Q_DECLARE_METATYPE(QObjectList) +#endif +Q_DECLARE_METATYPE(QList<int>) + +QT_BEGIN_NAMESPACE + +extern char *qdtoa(double d, int mode, int ndigits, int *decpt, int *sign, char **rve, char **digits_str); +extern double qstrtod(const char *s00, char const **se, bool *ok); + +namespace QScript { + +QString numberToString(qsreal value) +{ + if (qIsNaN(value)) + return QLatin1String("NaN"); + + else if (qIsInf(value)) + return QLatin1String(value < 0 ? "-Infinity" : "Infinity"); + + else if (value == 0) + return QLatin1String("0"); + + QByteArray buf; + buf.reserve(80); + + int decpt; + int sign; + char *result = 0; + (void) qdtoa(value, 0, 0, &decpt, &sign, 0, &result); + + if (! result) + return QString(); + + else if (decpt <= 0 && decpt > -6) { + + buf.fill('0', -decpt + 2 + sign); + + if (sign) // fix the sign. + buf[0] = '-'; + + buf[sign + 1] = '.'; + buf += result; + } + + else { + if (sign) + buf += '-'; + + buf += result; + int length = buf.length() - sign; + + if (decpt <= 21 && decpt > 0) { + if (length <= decpt) + buf += QByteArray().fill('0', decpt - length); + else + buf.insert(decpt + sign, '.'); + } + + else if (result[0] >= '0' && result[0] <= '9') { + if (length > 1) + buf.insert(1 + sign, '.'); + + buf += 'e'; + buf += (decpt >= 0) ? '+' : '-'; + + int e = decpt - 1; + + if (e < 0) + e = -e; + + if (e >= 100) + buf += '0' + e / 100; + + if (e >= 10) + buf += '0' + (e % 100) / 10; + + buf += '0' + e % 10; + } + } + + free(result); + + return QString::fromLatin1(buf); +} + +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); +} + +qsreal numberFromString(const QString &repr) +{ + QString str = repr.trimmed(); + if ((str.length() > 2) && (str.at(0) == QLatin1Char('0')) && (str.at(1).toUpper() == QLatin1Char('X'))) + return integerFromString(str.mid(2), 16); + QByteArray latin1 = str.toLatin1(); + const char *data = latin1.constData(); + const char *eptr = 0; + qsreal result = qstrtod(data, &eptr, 0); + if (eptr == data) { + if (str == QLatin1String("Infinity")) + result = +qInf(); + else if (str == QLatin1String("+Infinity")) + result = +qInf(); + else if (str == QLatin1String("-Infinity")) + result = -qInf(); + else if (str.isEmpty()) + result = 0; + else + result = qSNaN(); + } else if (eptr != (data + latin1.length())) { + result = qSNaN(); + } + return result; +} + +NodePool::NodePool(const QString &fileName, QScriptEnginePrivate *engine) + : m_fileName(fileName), m_engine(engine) +{ +#ifndef Q_SCRIPT_NO_EVENT_NOTIFY + m_id = engine->nextScriptId(); +#endif +} + +NodePool::~NodePool() +{ + qDeleteAll(m_codeCache); + m_codeCache.clear(); + +#ifndef Q_SCRIPT_NO_EVENT_NOTIFY + m_engine->notifyScriptUnload(id()); +#endif +} + +Code *NodePool::createCompiledCode(AST::Node *node, CompilationUnit &compilation) +{ + QHash<AST::Node*, Code*>::const_iterator it = m_codeCache.constFind(node); + if (it != m_codeCache.constEnd()) + return it.value(); + + Code *code = new Code(); + code->init(compilation, this); + + m_codeCache.insert(node, code); + return code; +} + +class EvalFunction : public QScriptFunction +{ +public: + EvalFunction(QScriptEnginePrivate *) + { length = 1; } + + virtual ~EvalFunction() {} + + void evaluate(QScriptContextPrivate *context, const QString &contents, + int lineNo, const QString &fileName, bool calledFromScript) + { + QScriptEnginePrivate *eng_p = context->engine(); + + QExplicitlySharedDataPointer<NodePool> pool; + pool = new NodePool(fileName, eng_p); + eng_p->setNodePool(pool.data()); + + QString errorMessage; + int errorLineNumber; + AST::Node *program = eng_p->createAbstractSyntaxTree( + contents, lineNo, &errorMessage, &errorLineNumber); + + eng_p->setNodePool(0); + +#ifndef Q_SCRIPT_NO_EVENT_NOTIFY + eng_p->notifyScriptLoad(pool->id(), contents, fileName, lineNo); +#endif + + Code *code = 0; + if (program) { + Compiler compiler(eng_p); + compiler.setTopLevelCompiler(true); + CompilationUnit compilation = compiler.compile(program); + if (!compilation.isValid()) { + errorMessage = compilation.errorMessage(); + errorLineNumber = compilation.errorLineNumber(); + } else { + code = pool->createCompiledCode(program, compilation); + } + } + + if (!code) { + context->errorLineNumber = errorLineNumber; + context->currentLine = errorLineNumber; +#ifndef Q_SCRIPT_NO_EVENT_NOTIFY + Code *oldCode = context->m_code; + Code dummy; + dummy.astPool = pool.data(); + context->m_code = &dummy; // so agents get the script ID + bool wasEvaluating = eng_p->m_evaluating; + eng_p->m_evaluating = true; + eng_p->notifyFunctionEntry(context); +#endif + context->throwError(QScriptContext::SyntaxError, errorMessage); +#ifndef Q_SCRIPT_NO_EVENT_NOTIFY + eng_p->notifyFunctionExit(context); + eng_p->m_evaluating = wasEvaluating; + context->m_code = oldCode; +#endif + return; + } + + if (calledFromScript) { + if (QScriptContextPrivate *pc = context->parentContext()) { + context->setActivationObject(pc->activationObject()); + context->setThisObject(pc->thisObject()); + context->m_scopeChain = pc->m_scopeChain; + } + } + + const QScriptInstruction *iPtr = context->instructionPointer(); + context->execute(code); + context->setInstructionPointer(iPtr); + } + + virtual void execute(QScriptContextPrivate *context) + { + QScriptEnginePrivate *eng = context->engine(); + int lineNo = context->currentLine; + if (lineNo == -1) { + QScriptContextPrivate *pc = context->parentContext(); + if (pc) + lineNo = pc->currentLine; + else + lineNo = 1; + } + QString fileName; // don't set this for now, we don't want to change the official eval() for now. + + if (context->argumentCount() == 0) { + context->setReturnValue(eng->undefinedValue()); + } else { + QScriptValueImpl arg = context->argument(0); + if (arg.isString()) { + QString contents = arg.toString(); + evaluate(context, contents, lineNo, fileName, /*calledFromScript=*/true); + } else { + context->setReturnValue(arg); + } + } + } + + QString functionName() const + { + return QLatin1String("eval"); + } +}; + +class ArgumentsClassData: public QScriptClassData +{ + +public: + + static inline QScript::ArgumentsObjectData *get(const QScriptValueImpl &object) + { return static_cast<QScript::ArgumentsObjectData*>(object.objectData()); } + + virtual bool resolve(const QScriptValueImpl &object, QScriptNameIdImpl *nameId, + QScript::Member *member, QScriptValueImpl *base, + QScript::AccessMode access); + virtual bool get(const QScriptValueImpl &object, const QScript::Member &member, + QScriptValueImpl *out_value); + virtual bool put(QScriptValueImpl *object, const QScript::Member &member, + const QScriptValueImpl &value); + virtual void mark(const QScriptValueImpl &object, int generation); + virtual QScriptClassDataIterator *newIterator(const QScriptValueImpl &object); +}; + +class ArgumentsClassDataIterator: public QScriptClassDataIterator +{ +public: + ArgumentsClassDataIterator(ArgumentsObjectData *data); + virtual ~ArgumentsClassDataIterator(); + + virtual bool hasNext() const; + virtual void next(QScript::Member *member); + + virtual bool hasPrevious() const; + virtual void previous(QScript::Member *member); + + virtual void toFront(); + virtual void toBack(); + +private: + ArgumentsObjectData *m_data; + uint m_pos; +}; + +bool ArgumentsClassData::resolve(const QScriptValueImpl &object, QScriptNameIdImpl *nameId, + QScript::Member *member, QScriptValueImpl *base, + QScript::AccessMode /*access*/) +{ + QString propertyName = object.engine()->toString(nameId); + bool isNumber; + quint32 index = propertyName.toUInt(&isNumber); + if (isNumber) { + QScript::ArgumentsObjectData *data = ArgumentsClassData::get(object); + if (index < data->length) { + member->native(/*nameId=*/0, index, QScriptValue::SkipInEnumeration); + *base = object; + return true; + } + } + + return false; +} + +bool ArgumentsClassData::get(const QScriptValueImpl &object, const QScript::Member &member, + QScriptValueImpl *out_value) +{ + QScript::ArgumentsObjectData *data = ArgumentsClassData::get(object); + if (member.nameId() == 0) { + QScriptObject *activation_data = data->activation.objectValue(); + *out_value = activation_data->m_values[member.id()]; + return true; + } + return false; +} + +bool ArgumentsClassData::put(QScriptValueImpl *object, const QScript::Member &member, + const QScriptValueImpl &value) +{ + Q_ASSERT(member.nameId() == 0); + QScript::ArgumentsObjectData *data = ArgumentsClassData::get(*object); + QScriptObject *activation_data = data->activation.objectValue(); + activation_data->m_values[member.id()] = value; + return true; +} + +void ArgumentsClassData::mark(const QScriptValueImpl &object, int generation) +{ + QScript::ArgumentsObjectData *data = ArgumentsClassData::get(object); + data->activation.mark(generation); +} + +QScriptClassDataIterator *ArgumentsClassData::newIterator(const QScriptValueImpl &object) +{ + QScript::ArgumentsObjectData *data = ArgumentsClassData::get(object); + return new ArgumentsClassDataIterator(data); +} + +ArgumentsClassDataIterator::ArgumentsClassDataIterator(ArgumentsObjectData *data) + : m_data(data), m_pos(0) +{ +} + +ArgumentsClassDataIterator::~ArgumentsClassDataIterator() +{ +} + +bool ArgumentsClassDataIterator::hasNext() const +{ + return m_pos < m_data->length; +} + +void ArgumentsClassDataIterator::next(QScript::Member *member) +{ + if (m_pos == m_data->length) { + member->invalidate(); + } else { + member->native(/*nameId=*/0, m_pos, QScriptValue::SkipInEnumeration); + ++m_pos; + } +} + +bool ArgumentsClassDataIterator::hasPrevious() const +{ + return (m_pos != 0); +} + +void ArgumentsClassDataIterator::previous(QScript::Member *member) +{ + if (m_pos == 0) { + member->invalidate(); + } else { + --m_pos; + member->native(/*nameId=*/0, m_pos, QScriptValue::SkipInEnumeration); + } +} + +void ArgumentsClassDataIterator::toFront() +{ + m_pos = 0; +} + +void ArgumentsClassDataIterator::toBack() +{ + m_pos = m_data->length; +} + +} // namespace QScript + +const qsreal QScriptEnginePrivate::D16 = 65536.0; +const qsreal QScriptEnginePrivate::D32 = 4294967296.0; + +QScriptEnginePrivate::~QScriptEnginePrivate() +{ + while (!m_agents.isEmpty()) + delete m_agents.takeFirst(); + + // invalidate values that we have references to + { + QHash<QScriptObject*, QScriptValuePrivate*>::const_iterator it; + for (it = m_objectHandles.constBegin(); it != m_objectHandles.constEnd(); ++it) + (*it)->invalidate(); + } + { + QHash<QScriptNameIdImpl*, QScriptValuePrivate*>::const_iterator it; + for (it = m_stringHandles.constBegin(); it != m_stringHandles.constEnd(); ++it) + (*it)->invalidate(); + } + { + QVector<QScriptValuePrivate*>::const_iterator it; + for (it = m_otherHandles.constBegin(); it != m_otherHandles.constEnd(); ++it) + (*it)->invalidate(); + } + + // invalidate interned strings that are known to the outside world + { + QHash<QScriptNameIdImpl*, QScriptStringPrivate*>::const_iterator it; + for (it = m_internedStrings.constBegin(); it != m_internedStrings.constEnd(); ++it) + it.value()->nameId = 0; + } + + delete[] m_string_hash_base; + qDeleteAll(m_stringRepository); + qDeleteAll(m_tempStringRepository); + + if (tempStackBegin) + delete[] tempStackBegin; + +#ifndef QT_NO_QOBJECT + deletePendingQObjects(); + qDeleteAll(m_qobjectData); +# ifndef Q_SCRIPT_NO_QMETAOBJECT_CACHE + qDeleteAll(m_cachedMetaObjects); +# endif +#endif + + qDeleteAll(m_allocated_classes); +} + +QScript::AST::Node *QScriptEnginePrivate::changeAbstractSyntaxTree(QScript::AST::Node *prg) +{ + QScript::AST::Node *was = m_abstractSyntaxTree; + m_abstractSyntaxTree = prg; + return was; +} + +QScript::AST::Node *QScriptEnginePrivate::createAbstractSyntaxTree( + const QString &source, int lineNumber, QString *errorMessage, int *errorLineNumber) +{ + QScript::Lexer lex(this); + setLexer(&lex); + lex.setCode(source, lineNumber); + + QScriptParser parser; + + if (! parser.parse(this)) { + if (errorMessage) + *errorMessage = parser.errorMessage(); + if (errorLineNumber) + *errorLineNumber = parser.errorLineNumber(); + return 0; + } + + return abstractSyntaxTree(); +} + +void QScriptEnginePrivate::markObject(const QScriptValueImpl &object, int generation) +{ + QScriptObject *instance = object.objectValue(); + QScript::GCBlock *block = QScript::GCBlock::get(instance); + + enum { MAX_GC_DEPTH = 32 }; + + if (block->generation + 1 != generation) + return; + + if (m_gc_depth >= MAX_GC_DEPTH) { + // do the marking later + m_markStack.append(object); + return; + } + + ++block->generation; + ++m_gc_depth; + + if (QScriptClassData *data = object.classInfo()->data()) + data->mark(object, generation); + + if (instance->m_prototype.isObject()) + markObject(instance->m_prototype, generation); + + if (instance->m_scope.isObject()) + markObject(instance->m_scope, generation); + + const QScriptValueImpl &internalValue = instance->m_internalValue; + + if (internalValue.isValid()) { + if (internalValue.isObject()) + markObject(internalValue, generation); + + else if (internalValue.isString()) + markString(internalValue.m_string_value, generation); + } + + int garbage = 0; + + for (int i = 0; i < instance->memberCount(); ++i) { + QScript::Member m; + instance->member(i, &m); + + if (! m.isValid()) { + ++garbage; + continue; + } + + Q_ASSERT(m.isObjectProperty()); + + QScriptValueImpl child; + instance->get(m, &child); + + if (m.nameId()) + markString(m.nameId(), generation); + + if (! child.isValid()) + continue; + + else if (child.isObject()) + markObject(child, generation); + + else if (child.isString()) + markString(child.m_string_value, generation); + } + + --m_gc_depth; + + if (garbage < 128) // ### + return; + + int j = 0; + for (int i = 0; i < instance->memberCount(); ++i) { + QScript::Member m; + instance->member(i, &m); + + if (! m.isValid()) + continue; + + if (i != j) { + instance->m_members[j].object(m.nameId(), j, m.flags()); + instance->m_values[j] = instance->m_values[i]; + } + ++j; + } + //qDebug() << "==> old:" << instance->m_members.size() << "new:" << j; + instance->m_members.resize(j); + instance->m_values.resize(j); +} + +void QScriptEnginePrivate::markFrame(QScriptContextPrivate *context, int generation) +{ + QScriptValueImpl activation = context->activationObject(); + QScriptValueImpl thisObject = context->thisObject(); + QScriptValueImpl scopeChain = context->m_scopeChain; + QScriptValueImpl callee = context->m_callee; + QScriptValueImpl arguments = context->m_arguments; + + if (activation.isObject()) + markObject(activation, generation); + + if (scopeChain.isObject()) + markObject(scopeChain, generation); + + if (thisObject.isObject()) + markObject(thisObject, generation); + + if (callee.isObject()) + markObject(callee, generation); + + if (arguments.isObject()) + markObject(arguments, generation); + + if (context->returnValue().isValid()) { + if (context->returnValue().isObject()) + markObject(context->returnValue(), generation); + + else if (context->returnValue().isString()) + markString(context->returnValue().m_string_value, generation); + } + + if (context->baseStackPointer() != context->currentStackPointer()) { + // mark the temp stack + + for (const QScriptValueImpl *it = context->baseStackPointer(); it != (context->currentStackPointer() + 1); ++it) { + if (! it) { + qWarning() << "no temp stack!!!"; + break; + } + + else if (! it->isValid()) // ### assert? + continue; + + else if (it->isObject()) + markObject(*it, generation); + + else if (it->isString()) + markString(it->m_string_value, generation); + } + } +} + +bool QScriptEnginePrivate::isCollecting() const +{ + return (m_gc_depth != -1) || objectAllocator.sweeping(); +} + +void QScriptEnginePrivate::maybeGC_helper(bool do_string_gc) +{ + // qDebug() << "==>" << objectAllocator.newAllocatedBlocks() << "free:" << objectAllocator.freeBlocks(); + Q_ASSERT(m_gc_depth == -1); + ++m_gc_depth; + + int generation = m_objectGeneration + 1; + + markObject(m_globalObject, generation); + + objectConstructor->mark(this, generation); + numberConstructor->mark(this, generation); + booleanConstructor->mark(this, generation); + stringConstructor->mark(this, generation); + dateConstructor->mark(this, generation); + functionConstructor->mark(this, generation); + arrayConstructor->mark(this, generation); + regexpConstructor->mark(this, generation); + errorConstructor->mark(this, generation); + enumerationConstructor->mark(this, generation); + variantConstructor->mark(this, generation); +#ifndef QT_NO_QOBJECT + qobjectConstructor->mark(this, generation); + qmetaObjectConstructor->mark(this, generation); +#endif + + { + QScriptContextPrivate *current = currentContext(); + while (current != 0) { + markFrame (current, generation); + current = current->parentContext(); + } + } + + { + QHash<QScriptObject*, QScriptValuePrivate*>::const_iterator it; + for (it = m_objectHandles.constBegin(); it != m_objectHandles.constEnd(); ++it) + markObject((*it)->value, generation); + } + + { + QHash<QScriptNameIdImpl*, QScriptValuePrivate*>::const_iterator it; + for (it = m_stringHandles.constBegin(); it != m_stringHandles.constEnd(); ++it) + markString((*it)->value.stringValue(), generation); + } + + { + QHash<int, QScriptCustomTypeInfo>::const_iterator it; + for (it = m_customTypes.constBegin(); it != m_customTypes.constEnd(); ++it) + (*it).prototype.mark(generation); + } + +#ifndef QT_NO_QOBJECT +# ifndef Q_SCRIPT_NO_QMETAOBJECT_CACHE + { + QHash<const QMetaObject*, QScriptMetaObject*>::const_iterator it; + for (it = m_cachedMetaObjects.constBegin(); it != m_cachedMetaObjects.constEnd(); ++it) { + { + QList<QScriptNameIdImpl*> memberNames = (*it)->registeredMemberNames(); + QList<QScriptNameIdImpl*>::const_iterator it2; + for (it2 = memberNames.constBegin(); it2 != memberNames.constEnd(); ++it2) + markString(*it2, generation); + } + { + QList<QScriptValueImpl> propertyAccessors = (*it)->registeredPropertyAccessors(); + QList<QScriptValueImpl>::const_iterator it2; + for (it2 = propertyAccessors.constBegin(); it2 != propertyAccessors.constEnd(); ++it2) + markObject(*it2, generation); + } + } + } +# endif + processMarkStack(generation); // make sure everything is marked before marking qobject data + { + QHash<QObject*, QScriptQObjectData*>::const_iterator it; + for (it = m_qobjectData.constBegin(); it != m_qobjectData.constEnd(); ++it) { + QScriptQObjectData *qdata = it.value(); + qdata->mark(generation); + } + } +#endif + processMarkStack(generation); + + Q_ASSERT(m_gc_depth == 0); + --m_gc_depth; + + objectAllocator.sweep(generation); + + m_objectGeneration = generation; + + //qDebug() << "free blocks:" << objectAllocator.freeBlocks(); + +#ifndef QT_NO_QOBJECT + deletePendingQObjects(); +#endif + + if (! do_string_gc) + return; + + { + QHash<QScriptNameIdImpl*, QScriptStringPrivate*>::const_iterator it; + for (it = m_internedStrings.constBegin(); it != m_internedStrings.constEnd(); ++it) { + it.value()->nameId->used = true; + } + } + +#if 0 + qDebug() << "do_string_gc:" << do_string_gc + << ((m_stringRepository.size() - m_oldStringRepositorySize) > 256) + << ((m_tempStringRepository.size() - m_oldTempStringRepositorySize) > 2048); +#endif + + QVector<QScriptNameIdImpl*> compressed; + compressed.reserve(m_stringRepository.size()); + + for (int i = 0; i < m_stringRepository.size(); ++i) { + QScriptNameIdImpl *entry = m_stringRepository.at(i); + + if (entry->used || entry->persistent) { + compressed.append(entry); + entry->used = false; + } + + else { + //qDebug() << "deleted unique:" << entry->s; + delete entry; + } + } + + // qDebug() << "before:" << m_stringRepository.size() << "after:" << compressed.size() << globalObject.objectValue()->m_members.size(); + m_stringRepository = compressed; + rehashStringRepository(/*resize=*/ false); + m_oldStringRepositorySize = m_stringRepository.size(); + m_newAllocatedStringRepositoryChars = 0; + + compressed.clear(); + for (int i = 0; i < m_tempStringRepository.size(); ++i) { + QScriptNameIdImpl *entry = m_tempStringRepository.at(i); + + if (entry->used || entry->persistent) { + compressed.append(entry); + entry->used = false; + } + + else { + //qDebug() << "deleted:" << entry->s; + delete entry; + } + } + + //qDebug() << "before:" << m_tempStringRepository.size() << "after:" << compressed.size(); + + m_tempStringRepository = compressed; + m_oldTempStringRepositorySize = m_tempStringRepository.size(); + m_newAllocatedTempStringRepositoryChars = 0; +} + +void QScriptEnginePrivate::processMarkStack(int generation) +{ + // mark the objects we couldn't process due to recursion depth + while (!m_markStack.isEmpty()) + markObject(m_markStack.takeLast(), generation); +} + +void QScriptEnginePrivate::evaluate(QScriptContextPrivate *context, const QString &contents, int lineNumber, const QString &fileName) +{ + // ### try to remove cast + QScript::EvalFunction *evalFunction = static_cast<QScript::EvalFunction*>(m_evalFunction); + evalFunction->evaluate(context, contents, lineNumber, fileName, /*calledFromScript=*/ false); +} + +qsreal QScriptEnginePrivate::convertToNativeDouble_helper(const QScriptValueImpl &value) +{ + switch (value.type()) { + case QScript::InvalidType: + Q_ASSERT(value.isValid()); + break; + + case QScript::UndefinedType: + case QScript::PointerType: + break; + + case QScript::NullType: + return 0; + + case QScript::BooleanType: + return value.m_bool_value; + + case QScript::IntegerType: + case QScript::ReferenceType: + return value.m_int_value; + + case QScript::NumberType: + return value.m_number_value; + + case QScript::StringType: + return QScript::numberFromString(toString(value.m_string_value)); + + case QScript::ObjectType: { + QScriptValueImpl p = value.engine()->toPrimitive(value, QScriptValueImpl::NumberTypeHint); + if (! p.isValid() || p.isObject()) + break; + + return convertToNativeDouble(p); + } + + case QScript::LazyStringType: + return QScript::numberFromString(*value.m_lazy_string_value); + + } // switch + + return qSNaN(); +} + +bool QScriptEnginePrivate::convertToNativeBoolean_helper(const QScriptValueImpl &value) +{ + switch (value.type()) { + case QScript::InvalidType: + Q_ASSERT(value.isValid()); + return false; + + case QScript::UndefinedType: + case QScript::PointerType: + case QScript::NullType: + case QScript::ReferenceType: + return false; + + case QScript::BooleanType: + return value.m_bool_value; + + case QScript::IntegerType: + return value.m_int_value != 0; + + case QScript::NumberType: + return value.m_number_value != 0 && !qIsNaN(value.m_number_value); + + case QScript::StringType: + return toString(value.m_string_value).length() != 0; + + case QScript::ObjectType: + return true; + + case QScript::LazyStringType: + return value.m_lazy_string_value->length() != 0; + + } // switch + + return false; +} + +QString QScriptEnginePrivate::convertToNativeString_helper(const QScriptValueImpl &value) +{ + static QStringList predefined; + if (predefined.isEmpty()) { + predefined.append(QString::fromLatin1("undefined")); + predefined.append(QString::fromLatin1("null")); + predefined.append(QString::fromLatin1("true")); + predefined.append(QString::fromLatin1("false")); + predefined.append(QString::fromLatin1("pointer")); + } + + switch (value.type()) { + case QScript::InvalidType: + Q_ASSERT(value.isValid()); + return QString(); + + case QScript::UndefinedType: + return predefined.at(0); + + case QScript::NullType: + return predefined.at(1); + + case QScript::BooleanType: + return value.m_bool_value ? predefined.at(2) : predefined.at(3); + + case QScript::IntegerType: + return QString::number(value.m_int_value); + + case QScript::NumberType: + return QScript::numberToString(value.m_number_value); + + case QScript::PointerType: + return predefined.at(4); + + case QScript::StringType: + return toString(value.m_string_value); + + case QScript::ReferenceType: + return QString(); + + case QScript::ObjectType: { + QScriptValueImpl p = value.engine()->toPrimitive(value, QScriptValueImpl::StringTypeHint); + + if (!p.isValid() || strictlyEquals(p, value)) + return p.classInfo()->name(); + + return convertToNativeString(p); + } + + case QScript::LazyStringType: + return *value.m_lazy_string_value; + + } // switch + + return QString(); +} + +QScriptValueImpl QScriptEnginePrivate::toObject_helper(const QScriptValueImpl &value) +{ + QScriptValueImpl result; + switch (value.type()) { + case QScript::BooleanType: + booleanConstructor->newBoolean(&result, value.m_bool_value); + break; + + case QScript::NumberType: + numberConstructor->newNumber(&result, value.m_number_value); + break; + + case QScript::StringType: + stringConstructor->newString(&result, value.m_string_value->s); + break; + + case QScript::LazyStringType: + stringConstructor->newString(&result, *value.m_lazy_string_value); + break; + + case QScript::InvalidType: + case QScript::UndefinedType: + case QScript::NullType: + case QScript::IntegerType: + case QScript::ReferenceType: + case QScript::PointerType: + case QScript::ObjectType: + break; + } // switch + + return result; +} + +// [[defaultValue]] +QScriptValueImpl QScriptEnginePrivate::toPrimitive_helper(const QScriptValueImpl &object, + QScriptValueImpl::TypeHint hint) +{ + QScriptNameIdImpl *functionIds[2]; + + if ((hint == QScriptValueImpl::NumberTypeHint) + || (hint == QScriptValueImpl::NoTypeHint + && object.classInfo() != dateConstructor->classInfo())) { + functionIds[0] = idTable()->id_valueOf; + functionIds[1] = idTable()->id_toString; + } else { + functionIds[0] = idTable()->id_toString; + functionIds[1] = idTable()->id_valueOf; + } + + for (int i = 0; i < 2; ++i) { + QScriptValueImpl base; + QScript::Member member; + + if (! object.resolve(functionIds[i], &member, &base, QScriptValue::ResolvePrototype, QScript::Read)) + return object; + + QScriptValueImpl f_valueOf; + base.get(member, &f_valueOf); + + if (QScriptFunction *foo = convertToNativeFunction(f_valueOf)) { + QScriptContextPrivate *me = pushContext(); + QScriptValueImpl activation; + newActivation(&activation); + if (f_valueOf.scope().isValid()) + activation.setScope(f_valueOf.scope()); + else + activation.setScope(m_globalObject); + me->setActivationObject(activation); + me->setThisObject(object); + me->m_callee = f_valueOf; + foo->execute(me); + QScriptValueImpl result = me->returnValue(); + bool exception = (me->state() == QScriptContext::ExceptionState); + popContext(); + if (exception || (result.isValid() && !result.isObject())) + return result; + } + } + + return object; +} + +void QScriptEnginePrivate::rehashStringRepository(bool resize) +{ + if (resize) { + delete[] m_string_hash_base; + m_string_hash_size <<= 1; // ### use primes + + m_string_hash_base = new QScriptNameIdImpl* [m_string_hash_size]; + } + + memset(m_string_hash_base, 0, sizeof(QScriptNameIdImpl*) * m_string_hash_size); + + for (int index = 0; index < m_stringRepository.size(); ++index) { + QScriptNameIdImpl *entry = m_stringRepository.at(index); + uint h = _q_scriptHash(entry->s) % m_string_hash_size; + entry->h = h; + entry->next = m_string_hash_base[h]; + m_string_hash_base[h] = entry; + } +} + +QScriptNameIdImpl *QScriptEnginePrivate::insertStringEntry(const QString &s) +{ + QScriptNameIdImpl *entry = new QScriptNameIdImpl(s); + entry->unique = true; + m_stringRepository.append(entry); + m_newAllocatedStringRepositoryChars += s.length(); + + uint h = _q_scriptHash(s) % m_string_hash_size; + entry->h = h; + entry->next = m_string_hash_base[h]; + m_string_hash_base[h] = entry; + + if (m_stringRepository.count() == m_string_hash_size) + rehashStringRepository(); + + return entry; +} + +QScriptValueImpl QScriptEnginePrivate::call(const QScriptValueImpl &callee, + const QScriptValueImpl &thisObject, + const QScriptValueImplList &args, + bool asConstructor) +{ + QScriptFunction *function = callee.toFunction(); + Q_ASSERT(function); + + if (++m_callDepth == m_maxCallDepth) { + QScriptContextPrivate *ctx_p = currentContext(); + return ctx_p->throwError(QLatin1String("call stack overflow")); + } + + QScriptContextPrivate *nested = pushContext(); + // set up the temp stack + if (! nested->tempStack) + nested->stackPtr = nested->tempStack = tempStackBegin; + + newActivation(&nested->m_activation); + if (callee.m_object_value->m_scope.isValid()) + nested->m_activation.m_object_value->m_scope = callee.m_object_value->m_scope; + else + nested->m_activation.m_object_value->m_scope = m_globalObject; + + QScriptObject *activation_data = nested->m_activation.m_object_value; + + int formalCount = function->formals.count(); + int argc = args.count(); + int mx = qMax(formalCount, argc); + activation_data->m_members.resize(mx); + activation_data->m_values.resize(mx); + for (int i = 0; i < mx; ++i) { + QScriptNameIdImpl *nameId = 0; + if (i < formalCount) + nameId = function->formals.at(i); + + activation_data->m_members[i].object(nameId, i, QScriptValue::SkipInEnumeration); + QScriptValueImpl arg = (i < argc) ? args.at(i) : m_undefinedValue; + if (arg.isValid() && arg.engine() && (arg.engine() != this)) { + qWarning("QScriptValue::call() failed: " + "cannot call function with argument created in " + "a different engine"); + popContext(); + return QScriptValueImpl(); + } + activation_data->m_values[i] = arg.isValid() ? arg : m_undefinedValue; + } + + nested->argc = argc; + QVector<QScriptValueImpl> argsv = args.toVector(); + nested->args = const_cast<QScriptValueImpl*> (argsv.constData()); + + if (thisObject.isObject()) + nested->m_thisObject = thisObject; + else + nested->m_thisObject = m_globalObject; + nested->m_callee = callee; + nested->m_calledAsConstructor = asConstructor; + + nested->m_result = m_undefinedValue; + function->execute(nested); + --m_callDepth; + QScriptValueImpl result = nested->m_result; + nested->args = 0; + popContext(); + + return result; +} + +QScriptValueImpl QScriptEnginePrivate::call(const QScriptValueImpl &callee, + const QScriptValueImpl &thisObject, + const QScriptValueImpl &args, + bool asConstructor) +{ + QScriptValueImplList argsList; + if (QScript::Ecma::Array::Instance *arr = arrayConstructor->get(args)) { + QScript::Array actuals = arr->value; + for (quint32 i = 0; i < actuals.count(); ++i) { + QScriptValueImpl a = actuals.at(i); + if (! a.isValid()) + argsList << undefinedValue(); + else + argsList << a; + } + } else if (args.classInfo() == m_class_arguments) { + QScript::ArgumentsObjectData *arguments; + arguments = static_cast<QScript::ArgumentsObjectData*> (args.objectData()); + QScriptObject *activation = arguments->activation.objectValue(); + for (uint i = 0; i < arguments->length; ++i) + argsList << activation->m_values[i]; + } else if (!(args.isUndefined() || args.isNull())) { + return currentContext()->throwError( + QScriptContext::TypeError, + QLatin1String("QScriptValue::call(): arguments must be an array")); + } + return call(callee, thisObject, argsList, asConstructor); +} + +QScriptValueImpl QScriptEnginePrivate::arrayFromStringList(const QStringList &lst) +{ + QScriptValueImpl arr = newArray(lst.size()); + for (int i = 0; i < lst.size(); ++i) + arr.setProperty(i, QScriptValueImpl(this, lst.at(i))); + return arr; +} + +QStringList QScriptEnginePrivate::stringListFromArray(const QScriptValueImpl &arr) +{ + QStringList lst; + uint len = arr.property(QLatin1String("length")).toUInt32(); + for (uint i = 0; i < len; ++i) + lst.append(arr.property(i).toString()); + return lst; +} + +QScriptValueImpl QScriptEnginePrivate::arrayFromVariantList(const QVariantList &lst) +{ + QScriptValueImpl arr = newArray(lst.size()); + for (int i = 0; i < lst.size(); ++i) + arr.setProperty(i, valueFromVariant(lst.at(i))); + return arr; +} + +QVariantList QScriptEnginePrivate::variantListFromArray(const QScriptValueImpl &arr) +{ + QVariantList lst; + uint len = arr.property(QLatin1String("length")).toUInt32(); + for (uint i = 0; i < len; ++i) + lst.append(arr.property(i).toVariant()); + return lst; +} + +QScriptValueImpl QScriptEnginePrivate::objectFromVariantMap(const QVariantMap &vmap) +{ + QScriptValueImpl obj = newObject(); + QVariantMap::const_iterator it; + for (it = vmap.constBegin(); it != vmap.constEnd(); ++it) + obj.setProperty(it.key(), valueFromVariant(it.value())); + return obj; +} + +QVariantMap QScriptEnginePrivate::variantMapFromObject(const QScriptValueImpl &obj) +{ + QVariantMap vmap; + QScriptValueIteratorImpl it(obj); + while (it.hasNext()) { + it.next(); + vmap.insert(it.name(), it.value().toVariant()); + } + return vmap; +} + +QScriptValueImpl QScriptEnginePrivate::create(int type, const void *ptr) +{ + Q_Q(QScriptEngine); + Q_ASSERT(ptr); + QScriptValueImpl result; + QScriptCustomTypeInfo info = m_customTypes.value(type); + if (info.marshal) { + result = toImpl(info.marshal(q, ptr)); + } else { + // check if it's one of the types we know + switch (QMetaType::Type(type)) { + case QMetaType::Void: + result = m_undefinedValue; + break; + case QMetaType::Bool: + result = QScriptValueImpl(*reinterpret_cast<const bool*>(ptr)); + break; + case QMetaType::Int: + result = QScriptValueImpl(*reinterpret_cast<const int*>(ptr)); + break; + case QMetaType::UInt: + result = QScriptValueImpl(*reinterpret_cast<const uint*>(ptr)); + break; + case QMetaType::LongLong: + result = QScriptValueImpl(qsreal(*reinterpret_cast<const qlonglong*>(ptr))); + break; + 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.") + result = QScriptValueImpl(qsreal((qlonglong)*reinterpret_cast<const qulonglong*>(ptr))); +#elif defined(Q_CC_MSVC) && !defined(Q_CC_MSVC_NET) + result = QScriptValueImpl(qsreal((qlonglong)*reinterpret_cast<const qulonglong*>(ptr))); +#else + result = QScriptValueImpl(qsreal(*reinterpret_cast<const qulonglong*>(ptr))); +#endif + break; + case QMetaType::Double: + result = QScriptValueImpl(*reinterpret_cast<const double*>(ptr)); + break; + case QMetaType::QString: + result = QScriptValueImpl(this, *reinterpret_cast<const QString*>(ptr)); + break; + case QMetaType::Float: + result = QScriptValueImpl(*reinterpret_cast<const float*>(ptr)); + break; + case QMetaType::Short: + result = QScriptValueImpl(*reinterpret_cast<const short*>(ptr)); + break; + case QMetaType::UShort: + result = QScriptValueImpl(*reinterpret_cast<const unsigned short*>(ptr)); + break; + case QMetaType::Char: + result = QScriptValueImpl(*reinterpret_cast<const char*>(ptr)); + break; + case QMetaType::UChar: + result = QScriptValueImpl(*reinterpret_cast<const unsigned char*>(ptr)); + break; + case QMetaType::QChar: + result = QScriptValueImpl((*reinterpret_cast<const QChar*>(ptr)).unicode()); + break; + case QMetaType::QStringList: + result = arrayFromStringList(*reinterpret_cast<const QStringList *>(ptr)); + break; + case QMetaType::QVariantList: + result = arrayFromVariantList(*reinterpret_cast<const QVariantList *>(ptr)); + break; + case QMetaType::QVariantMap: + result = objectFromVariantMap(*reinterpret_cast<const QVariantMap *>(ptr)); + break; + case QMetaType::QDateTime: { + QDateTime dateTime = *reinterpret_cast<const QDateTime *>(ptr); + dateConstructor->newDate(&result, dateTime); + } break; + case QMetaType::QDate: { + QDate date = *reinterpret_cast<const QDate *>(ptr); + dateConstructor->newDate(&result, date); + } break; +#ifndef QT_NO_REGEXP + case QMetaType::QRegExp: { + QRegExp rx = *reinterpret_cast<const QRegExp *>(ptr); + regexpConstructor->newRegExp(&result, rx); + } break; +#endif +#ifndef QT_NO_QOBJECT + case QMetaType::QObjectStar: + case QMetaType::QWidgetStar: + newQObject(&result, *reinterpret_cast<QObject* const *>(ptr)); + break; +#endif + default: + if (type == qMetaTypeId<QScriptValue>()) { + result = toImpl(*reinterpret_cast<const QScriptValue*>(ptr)); + if (!result.isValid()) + result = m_undefinedValue; + } + +#ifndef QT_NO_QOBJECT + // lazy registration of some common list types + else if (type == qMetaTypeId<QObjectList>()) { + qScriptRegisterSequenceMetaType<QObjectList>(q); + return create(type, ptr); + } +#endif + else if (type == qMetaTypeId<QList<int> >()) { + qScriptRegisterSequenceMetaType<QList<int> >(q); + return create(type, ptr); + } + + else { + QByteArray typeName = QMetaType::typeName(type); + if (typeName == "QVariant") + result = valueFromVariant(*reinterpret_cast<const QVariant*>(ptr)); + else if (typeName.endsWith('*') && !*reinterpret_cast<void* const *>(ptr)) + result = nullValue(); + else + newVariant(&result, QVariant(type, ptr)); + } + } + } + if (result.isObject() && info.prototype.isValid() + && strictlyEquals(result.prototype(), objectConstructor->publicPrototype)) { + result.setPrototype(info.prototype); + } + return result; +} + +bool QScriptEnginePrivate::convert(const QScriptValueImpl &value, + int type, void *ptr, + QScriptEnginePrivate *eng) +{ + if (!eng) + eng = value.engine(); + if (eng) { + QScriptCustomTypeInfo info = eng->m_customTypes.value(type); + if (info.demarshal) { + info.demarshal(eng->toPublic(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) = value.toBoolean(); + return true; + case QMetaType::Int: + *reinterpret_cast<int*>(ptr) = value.toInt32(); + return true; + case QMetaType::UInt: + *reinterpret_cast<uint*>(ptr) = value.toUInt32(); + return true; + case QMetaType::LongLong: + *reinterpret_cast<qlonglong*>(ptr) = qlonglong(value.toInteger()); + return true; + case QMetaType::ULongLong: + *reinterpret_cast<qulonglong*>(ptr) = qulonglong(value.toInteger()); + return true; + case QMetaType::Double: + *reinterpret_cast<double*>(ptr) = value.toNumber(); + return true; + case QMetaType::QString: + if (value.isUndefined() || value.isNull()) + *reinterpret_cast<QString*>(ptr) = QString(); + else + *reinterpret_cast<QString*>(ptr) = value.toString(); + return true; + case QMetaType::Float: + *reinterpret_cast<float*>(ptr) = value.toNumber(); + return true; + case QMetaType::Short: + *reinterpret_cast<short*>(ptr) = short(value.toInt32()); + return true; + case QMetaType::UShort: + *reinterpret_cast<unsigned short*>(ptr) = value.toUInt16(); + return true; + case QMetaType::Char: + *reinterpret_cast<char*>(ptr) = char(value.toInt32()); + return true; + case QMetaType::UChar: + *reinterpret_cast<unsigned char*>(ptr) = (unsigned char)(value.toInt32()); + return true; + case QMetaType::QChar: + if (value.isString()) { + QString str = value.toString(); + *reinterpret_cast<QChar*>(ptr) = str.isEmpty() ? QChar() : str.at(0); + } else { + *reinterpret_cast<QChar*>(ptr) = QChar(value.toUInt16()); + } + return true; + case QMetaType::QDateTime: + if (value.isDate()) { + *reinterpret_cast<QDateTime *>(ptr) = value.toDateTime(); + return true; + } break; + case QMetaType::QDate: + if (value.isDate()) { + *reinterpret_cast<QDate *>(ptr) = value.toDateTime().date(); + return true; + } break; +#ifndef QT_NO_REGEXP + case QMetaType::QRegExp: + if (value.isRegExp()) { + *reinterpret_cast<QRegExp *>(ptr) = value.toRegExp(); + return true; + } break; +#endif +#ifndef QT_NO_QOBJECT + case QMetaType::QObjectStar: + if (value.isQObject() || value.isNull()) { + *reinterpret_cast<QObject* *>(ptr) = value.toQObject(); + return true; + } break; + case QMetaType::QWidgetStar: + if (value.isQObject() || value.isNull()) { + QObject *qo = value.toQObject(); + if (!qo || qo->isWidgetType()) { + *reinterpret_cast<QWidget* *>(ptr) = reinterpret_cast<QWidget*>(qo); + return true; + } + } break; +#endif + case QMetaType::QStringList: + if (value.isArray()) { + *reinterpret_cast<QStringList *>(ptr) = stringListFromArray(value); + return true; + } break; + case QMetaType::QVariantList: + if (value.isArray()) { + *reinterpret_cast<QVariantList *>(ptr) = variantListFromArray(value); + return true; + } break; + case QMetaType::QVariantMap: + if (value.isObject()) { + *reinterpret_cast<QVariantMap *>(ptr) = variantMapFromObject(value); + return true; + } break; + default: + ; + } + + QByteArray name = QMetaType::typeName(type); +#ifndef QT_NO_QOBJECT + if (convertToNativeQObject(value, name, reinterpret_cast<void* *>(ptr))) + return true; +#endif + if (value.isVariant() && name.endsWith('*')) { + int valueType = QMetaType::type(name.left(name.size()-1)); + QVariant &var = value.variantValue(); + if (valueType == var.userType()) { + *reinterpret_cast<void* *>(ptr) = var.data(); + return true; + } else { + // look in the prototype chain + QScriptValueImpl proto = value.prototype(); + while (proto.isObject()) { + bool canCast = false; + if (proto.isVariant()) { + canCast = (type == proto.variantValue().userType()) + || (valueType && (valueType == proto.variantValue().userType())); + } +#ifndef QT_NO_QOBJECT + else if (proto.isQObject()) { + QByteArray className = name.left(name.size()-1); + if (QObject *qobject = proto.toQObject()) + 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 = 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->toPublic(value); + return true; + } else if (name == "QVariant") { + *reinterpret_cast<QVariant*>(ptr) = value.toVariant(); + 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 convert(value, type, ptr, eng); + } +#endif + else if (type == qMetaTypeId<QList<int> >()) { + if (!eng) + return false; + qScriptRegisterSequenceMetaType<QList<int> >(eng->q_func()); + return convert(value, type, ptr, eng); + } + +#if 0 + if (!name.isEmpty()) { + qWarning("QScriptEngine::convert: unable to convert value to type `%s'", + name.constData()); + } +#endif + return false; +} + +QScriptValuePrivate *QScriptEnginePrivate::registerValue(const QScriptValueImpl &value) +{ + if (value.isString()) { + QScriptNameIdImpl *id = value.stringValue(); + QScriptValuePrivate *p = m_stringHandles.value(id); + if (p) + return p; + p = m_handleRepository.get(); + p->engine = q_func(); + p->value = value; + m_stringHandles.insert(id, p); + return p; + } else if (value.isObject()) { + QScriptObject *instance = value.objectValue(); + QScriptValuePrivate *p = m_objectHandles.value(instance); + if (p) + return p; + p = m_handleRepository.get(); + p->engine = q_func(); + p->value = value; + m_objectHandles.insert(instance, p); + return p; + } + QScriptValuePrivate *p = m_handleRepository.get(); + p->engine = q_func(); + p->value = value; + m_otherHandles.append(p); + return p; +} + +QScriptEnginePrivate::QScriptEnginePrivate() +{ + m_undefinedValue = QScriptValueImpl(QScriptValue::UndefinedValue); + m_nullValue = QScriptValueImpl(QScriptValue::NullValue); + + m_evaluating = false; + m_abort = false; + m_callDepth = 0; +#if defined(Q_OS_WIN) + m_maxCallDepth = 88; +#elif defined(Q_OS_MAC) + m_maxCallDepth = 640; +#elif defined(QT_ARCH_ARM) || defined(QT_ARCH_ARMV6) + m_maxCallDepth = 360; +#else + m_maxCallDepth = 512; +#endif + m_oldStringRepositorySize = 0; + m_oldTempStringRepositorySize = 0; + m_newAllocatedStringRepositoryChars = 0; + m_newAllocatedTempStringRepositoryChars = 0; + m_context = 0; + m_abstractSyntaxTree = 0; + m_lexer = 0; + m_scriptCounter = 0; + m_agent = 0; + m_objectGeneration = 0; + m_class_prev_id = QScriptClassInfo::CustomType; + m_next_object_id = 0; + m_gc_depth = -1; + + objectConstructor = 0; + numberConstructor = 0; + booleanConstructor = 0; + stringConstructor = 0; + dateConstructor = 0; + functionConstructor = 0; + arrayConstructor = 0; + regexpConstructor = 0; + errorConstructor = 0; + enumerationConstructor = 0; + variantConstructor = 0; + qobjectConstructor = 0; + qmetaObjectConstructor = 0; + + m_processEventsInterval = -1; + m_nextProcessEvents = 0; + m_processEventIncr = 0; + + m_stringRepository.reserve(DefaultHashSize); + m_string_hash_size = DefaultHashSize; + m_string_hash_base = new QScriptNameIdImpl* [m_string_hash_size]; + memset(m_string_hash_base, 0, sizeof(QScriptNameIdImpl*) * m_string_hash_size); + + tempStackBegin = 0; +} + +void QScriptEnginePrivate::init() +{ + qMetaTypeId<QScriptValue>(); + qMetaTypeId<QList<int> >(); +#ifndef QT_NO_QOBJECT + qMetaTypeId<QObjectList>(); +#endif + + m_class_prev_id = QScriptClassInfo::CustomType; + m_class_object = registerClass(QLatin1String("Object"), QScriptClassInfo::ObjectType); + m_class_function = registerClass(QLatin1String("Function"), QScriptClassInfo::FunctionType); + m_class_activation = registerClass(QLatin1String("activation"), QScriptClassInfo::ActivationType); + + m_class_arguments = registerClass(QLatin1String("arguments"), QScript::ObjectType); + m_class_arguments->setData(new QScript::ArgumentsClassData()); + + m_class_with = registerClass(QLatin1String("__qscript_internal_with"), QScript::ObjectType); + + // public name ids + m_id_table.id_constructor = nameId(QLatin1String("constructor"), true); + m_id_table.id_false = nameId(QLatin1String("false"), true); + m_id_table.id_null = nameId(QLatin1String("null"), true); + m_id_table.id_object = nameId(QLatin1String("object"), true); + m_id_table.id_pointer = nameId(QLatin1String("pointer"), true); + m_id_table.id_prototype = nameId(QLatin1String("prototype"), true); + m_id_table.id_arguments = nameId(QLatin1String("arguments"), true); + m_id_table.id_this = nameId(QLatin1String("this"), true); + m_id_table.id_toString = nameId(QLatin1String("toString"), true); + m_id_table.id_true = nameId(QLatin1String("true"), true); + m_id_table.id_undefined = nameId(QLatin1String("undefined"), true); + m_id_table.id_valueOf = nameId(QLatin1String("valueOf"), true); + m_id_table.id_length = nameId(QLatin1String("length"), true); + m_id_table.id_callee = nameId(QLatin1String("callee"), true); + m_id_table.id___proto__ = nameId(QLatin1String("__proto__"), true); + m_id_table.id___qt_sender__ = nameId(QLatin1String("__qt_sender__"), true); + + const int TEMP_STACK_SIZE = 10 * 1024; + tempStackBegin = new QScriptValueImpl[TEMP_STACK_SIZE]; + tempStackEnd = tempStackBegin + TEMP_STACK_SIZE; + tempStackBegin[0] = m_undefinedValue; + + objectAllocator.blockGC(true); + + QScript::Ecma::Global::construct(&m_globalObject, this); + + // create the prototypes first... + objectConstructor = new QScript::Ecma::Object(this, m_class_object); + functionConstructor = new QScript::Ecma::Function(this, m_class_function); + // ... then we can initialize + functionConstructor->initialize(); + objectConstructor->initialize(); + + numberConstructor = new QScript::Ecma::Number(this); + booleanConstructor = new QScript::Ecma::Boolean(this); + stringConstructor = new QScript::Ecma::String(this); + dateConstructor = new QScript::Ecma::Date(this); + arrayConstructor = new QScript::Ecma::Array(this); + regexpConstructor = new QScript::Ecma::RegExp(this); + errorConstructor = new QScript::Ecma::Error(this); + + QScript::Ecma::Global::initialize(&m_globalObject, this); + + const QScriptValue::PropertyFlags flags = QScriptValue::SkipInEnumeration; + + m_globalObject.setProperty(QLatin1String("Object"), + objectConstructor->ctor, flags); + m_globalObject.setProperty(QLatin1String("Function"), + functionConstructor->ctor, flags); + m_globalObject.setProperty(QLatin1String("Number"), + numberConstructor->ctor, flags); + m_globalObject.setProperty(QLatin1String("Boolean"), + booleanConstructor->ctor, flags); + m_globalObject.setProperty(QLatin1String("String"), + stringConstructor->ctor, flags); + m_globalObject.setProperty(QLatin1String("Date"), + dateConstructor->ctor, flags); + m_globalObject.setProperty(QLatin1String("Array"), + arrayConstructor->ctor, flags); + m_globalObject.setProperty(QLatin1String("RegExp"), + regexpConstructor->ctor, flags); + m_globalObject.setProperty(QLatin1String("Error"), + errorConstructor->ctor, flags); + + m_globalObject.setProperty(QLatin1String("EvalError"), + errorConstructor->evalErrorCtor, flags); + m_globalObject.setProperty(QLatin1String("RangeError"), + errorConstructor->rangeErrorCtor, flags); + m_globalObject.setProperty(QLatin1String("ReferenceError"), + errorConstructor->referenceErrorCtor, flags); + m_globalObject.setProperty(QLatin1String("SyntaxError"), + errorConstructor->syntaxErrorCtor, flags); + m_globalObject.setProperty(QLatin1String("TypeError"), + errorConstructor->typeErrorCtor, flags); + m_globalObject.setProperty(QLatin1String("URIError"), + errorConstructor->uriErrorCtor, flags); + + QScriptValueImpl tmp; // ### fixme + m_evalFunction = new QScript::EvalFunction(this); + functionConstructor->newFunction(&tmp, m_evalFunction); + m_globalObject.setProperty(QLatin1String("eval"), tmp, flags); + + QScriptValueImpl mathObject; + QScript::Ecma::Math::construct(&mathObject, this); + m_globalObject.setProperty(QLatin1String("Math"), mathObject, flags); + + enumerationConstructor = new QScript::Ext::Enumeration(this); + + variantConstructor = new QScript::Ext::Variant(this); + +#ifndef QT_NO_QOBJECT + qobjectConstructor = new QScript::ExtQObject(this); + qmetaObjectConstructor = new QScript::ExtQMetaObject(this); +#endif + + objectAllocator.blockGC(false); + + QScriptContextPrivate *context_p = pushContext(); + context_p->setActivationObject(m_globalObject); + context_p->setThisObject(m_globalObject); +} + +#if !defined(QT_NO_QOBJECT) && !defined(QT_NO_LIBRARY) +static QScriptValueImpl __setupPackage__(QScriptContextPrivate *ctx, + QScriptEnginePrivate *eng, + QScriptClassInfo *) +{ + QString path = ctx->argument(0).toString(); + QStringList components = path.split(QLatin1Char('.')); + QScriptValueImpl o = eng->globalObject(); + for (int i = 0; i < components.count(); ++i) { + QString name = components.at(i); + QScriptValueImpl oo = o.property(name); + if (!oo.isValid()) { + oo = eng->newObject(); + o.setProperty(name, oo); + } + o = oo; + } + return o; +} +#endif + +QScriptValueImpl QScriptEnginePrivate::importExtension(const QString &extension) +{ +#if defined(QT_NO_QOBJECT) || defined(QT_NO_LIBRARY) || defined(QT_NO_SETTINGS) + Q_UNUSED(extension); +#else + Q_Q(QScriptEngine); + if (m_importedExtensions.contains(extension)) + return undefinedValue(); // already imported + + QScriptContextPrivate *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 (m_importedExtensions.contains(ext)) + continue; // already imported + + if (m_extensionsBeingImported.contains(ext)) { + return context->throwError(QString::fromLatin1("recursive import of %0") + .arg(extension)); + } + m_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()) { + m_extensionsBeingImported.remove(ext); + return context->throwError( + QString::fromLatin1("Unable to import %0: no such extension") + .arg(extension)); + } + + // initialize the extension in a new context + QScriptContextPrivate *ctx_p = pushContext(); + ctx_p->setThisObject(globalObject()); + newActivation(&ctx_p->m_activation); + QScriptObject *activation_data = ctx_p->m_activation.m_object_value; + activation_data->m_scope = globalObject(); + + activation_data->m_members.resize(4); + activation_data->m_values.resize(4); + activation_data->m_members[0].object( + nameId(QLatin1String("__extension__")), 0, + QScriptValue::ReadOnly | QScriptValue::Undeletable); + activation_data->m_values[0] = QScriptValueImpl(this, ext); + activation_data->m_members[1].object( + nameId(QLatin1String("__setupPackage__")), 1, 0); + activation_data->m_values[1] = createFunction(__setupPackage__, 0, 0); + activation_data->m_members[2].object( + nameId(QLatin1String("__all__")), 2, 0); + activation_data->m_values[2] = undefinedValue(); + activation_data->m_members[3].object( + nameId(QLatin1String("__postInit__")), 3, 0); + activation_data->m_values[3] = undefinedValue(); + + // the script is evaluated first + if (!initjsContents.isEmpty()) { + evaluate(ctx_p, initjsContents, /*lineNumber=*/1, initjsFileName); + if (hasUncaughtException()) { + QScriptValueImpl r = ctx_p->returnValue(); + popContext(); + m_extensionsBeingImported.remove(ext); + return r; + } + } + + // next, the C++ plugin is called + if (iface) { + iface->initialize(ext, q); + if (hasUncaughtException()) { + QScriptValueImpl r = ctx_p->returnValue(); + popContext(); + m_extensionsBeingImported.remove(ext); + return r; + } + } + + // if the __postInit__ function has been set, we call it + QScriptValueImpl postInit = ctx_p->m_activation.property(QLatin1String("__postInit__")); + if (postInit.isFunction()) { + postInit.call(globalObject()); + if (hasUncaughtException()) { + QScriptValueImpl r = ctx_p->returnValue(); + popContext(); + m_extensionsBeingImported.remove(ext); + return r; + } + } + + popContext(); + + m_importedExtensions.insert(ext); + m_extensionsBeingImported.remove(ext); + } // for (i) +#endif // QT_NO_QOBJECT + return undefinedValue(); +} + +QStringList QScriptEnginePrivate::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 +} + +QStringList QScriptEnginePrivate::importedExtensions() const +{ + QStringList lst = m_importedExtensions.toList(); + qSort(lst); + return lst; +} + +void QScriptEnginePrivate::gc() +{ + if (!objectAllocator.blocked()) { + // do the GC now + maybeGC_helper(/*do_string_gc=*/true); + } else { + // GC will be performed the next time maybeGC() + // is called and the allocator is not blocked + objectAllocator.requestGC(); + } +} + +QStringList QScriptEnginePrivate::uncaughtExceptionBacktrace() const +{ + QScriptValueImpl value = uncaughtException(); + if (!value.isError()) + return m_exceptionBacktrace; + return QScript::Ecma::Error::backtrace(value); +} + +void QScriptEnginePrivate::clearExceptions() +{ + m_exceptionBacktrace = QStringList(); + QScriptContextPrivate *ctx_p = currentContext(); + while (ctx_p) { + ctx_p->m_state = QScriptContext::NormalState; + ctx_p = ctx_p->parentContext(); + } +} + +#ifndef QT_NO_QOBJECT +void QScriptEnginePrivate::emitSignalHandlerException() +{ + Q_Q(QScriptEngine); + emit q->signalHandlerException(toPublic(uncaughtException())); +} +#endif + +void QScriptEnginePrivate::processEvents() +{ +#ifndef QT_NO_QOBJECT + Q_ASSERT(m_processEventTracker.isValid()); + int elapsed = m_processEventTracker.elapsed(); + if (m_nextProcessEvents < elapsed) { + do { + m_nextProcessEvents = m_nextProcessEvents + m_processEventsInterval; + } while (m_nextProcessEvents < elapsed); + QCoreApplication::processEvents(); + } +#endif +} + +void QScriptEnginePrivate::setupProcessEvents() +{ + if (m_processEventsInterval > 0) { + m_nextProcessEvents = m_processEventsInterval; + m_processEventIncr = 0; + m_processEventTracker.restart(); + } +} + +void QScriptEnginePrivate::abortEvaluation(const QScriptValueImpl &result) +{ + m_abort = true; + currentContext()->setReturnValue(result); +} + +#ifndef QT_NO_QOBJECT + +void QScriptEnginePrivate::newQObject(QScriptValueImpl *out, QObject *object, + QScriptEngine::ValueOwnership ownership, + const QScriptEngine::QObjectWrapOptions &options, + bool setDefaultPrototype) +{ + if (!object) { + *out = m_nullValue; + return; + } + Q_ASSERT(qobjectConstructor != 0); + QScriptQObjectData *data = qobjectData(object); + bool preferExisting = (options & QScriptEngine::PreferExistingWrapperObject) != 0; + QScriptEngine::QObjectWrapOptions opt = options & ~QScriptEngine::PreferExistingWrapperObject; + QScriptValueImpl existingWrapper; + bool hasExisting = data->findWrapper(ownership, opt, &existingWrapper); + if (preferExisting) { + if (hasExisting) { + *out = existingWrapper; + } else { + qobjectConstructor->newQObject(out, object, ownership, opt); + data->registerWrapper(*out, ownership, opt); + } + } else { + qobjectConstructor->newQObject(out, object, ownership, opt); + if (!hasExisting) + data->registerWrapper(*out, ownership, opt); + } + + if (setDefaultPrototype) { + const QMetaObject *meta = object->metaObject(); + while (meta) { + QByteArray typeString = meta->className(); + typeString.append('*'); + int typeId = QMetaType::type(typeString); + if (typeId != 0) { + QScriptValueImpl proto = defaultPrototype(typeId); + if (proto.isValid()) { + out->setPrototype(proto); + break; + } + } + meta = meta->superClass(); + } + } +} + +QScriptQObjectData *QScriptEnginePrivate::qobjectData(QObject *object) +{ + QHash<QObject*, QScriptQObjectData*>::const_iterator it; + it = m_qobjectData.constFind(object); + if (it != m_qobjectData.constEnd()) + return it.value(); + + QScriptQObjectData *data = new QScriptQObjectData(); + 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*, QScriptQObjectData*>::iterator it; + it = m_qobjectData.find(object); + Q_ASSERT(it != m_qobjectData.end()); + QScriptQObjectData *data = it.value(); + m_qobjectData.erase(it); + delete data; +} + +void QScriptEnginePrivate::disposeQObject(QObject *object) +{ + 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::deletePendingQObjects() +{ + while (!m_qobjectsToBeDeleted.isEmpty()) + delete m_qobjectsToBeDeleted.takeFirst(); +} + +bool QScriptEnginePrivate::scriptConnect(QObject *sender, const char *signal, + const QScriptValueImpl &receiver, + const QScriptValueImpl &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 scriptConnect(sender, index, receiver, function); +} + +bool QScriptEnginePrivate::scriptDisconnect(QObject *sender, const char *signal, + const QScriptValueImpl &receiver, + const QScriptValueImpl &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, + const QScriptValueImpl &receiver, + const QScriptValueImpl &function, + const QScriptValueImpl &senderWrapper) +{ + QScriptQObjectData *data = qobjectData(sender); + return data->addSignalHandler(sender, signalIndex, receiver, function, senderWrapper); +} + +bool QScriptEnginePrivate::scriptDisconnect(QObject *sender, int signalIndex, + const QScriptValueImpl &receiver, + const QScriptValueImpl &function) +{ + QScriptQObjectData *data = qobjectData(sender); + if (!data) + return false; + return data->removeSignalHandler(sender, signalIndex, receiver, function); +} + +bool QScriptEnginePrivate::scriptConnect(const QScriptValueImpl &signal, + const QScriptValueImpl &receiver, + const QScriptValueImpl &function) +{ + QScript::QtFunction *fun = static_cast<QScript::QtFunction*>(signal.toFunction()); + int index = fun->mostGeneralMethod(); + return scriptConnect(fun->qobject(), index, receiver, function, fun->object()); +} + +bool QScriptEnginePrivate::scriptDisconnect(const QScriptValueImpl &signal, + const QScriptValueImpl &receiver, + const QScriptValueImpl &function) +{ + QScript::QtFunction *fun = static_cast<QScript::QtFunction*>(signal.toFunction()); + int index = fun->mostGeneralMethod(); + return scriptDisconnect(fun->qobject(), index, receiver, function); +} + +bool QScriptEnginePrivate::convertToNativeQObject(const QScriptValueImpl &value, + const QByteArray &targetType, + void **result) +{ + if (!targetType.endsWith('*')) + return false; + if (QObject *qobject = value.toQObject()) { + 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; +} + +#endif // QT_NO_QOBJECT + +void QScriptEnginePrivate::setAgent(QScriptEngineAgent *agent) +{ + Q_Q(QScriptEngine); + if (agent && (agent->engine() != q)) { + qWarning("QScriptEngine::setAgent(): " + "cannot set agent belonging to different engine"); + return; + } + if (agent) { + int index = m_agents.indexOf(agent); + if (index == -1) + m_agents.append(agent); + } + m_agent = agent; +} + +QScriptEngineAgent *QScriptEnginePrivate::agent() const +{ + return m_agent; +} + +void QScriptEnginePrivate::agentDeleted(QScriptEngineAgent *agent) +{ + m_agents.removeOne(agent); + if (m_agent == agent) + m_agent = 0; +} + +#ifndef Q_SCRIPT_NO_EVENT_NOTIFY +qint64 QScriptEnginePrivate::nextScriptId() +{ + // ### reuse IDs by using a pool + return m_scriptCounter++; +} + +void QScriptEnginePrivate::notifyScriptLoad_helper(qint64 id, const QString &program, + const QString &fileName, int lineNumber) +{ + m_agent->scriptLoad(id, program, fileName, lineNumber); +} + +void QScriptEnginePrivate::notifyScriptUnload_helper(qint64 id) +{ + m_agent->scriptUnload(id); +} + +void QScriptEnginePrivate::notifyPositionChange_helper(QScriptContextPrivate *ctx) +{ + m_agent->positionChange(ctx->scriptId(), ctx->currentLine, ctx->currentColumn); +} + +void QScriptEnginePrivate::notifyContextPush_helper() +{ + m_agent->contextPush(); +} + +void QScriptEnginePrivate::notifyContextPop_helper() +{ + m_agent->contextPop(); +} + +void QScriptEnginePrivate::notifyFunctionEntry_helper(QScriptContextPrivate *ctx) +{ + m_agent->functionEntry(ctx->scriptId()); +} + +void QScriptEnginePrivate::notifyFunctionExit_helper(QScriptContextPrivate *ctx) +{ + m_agent->functionExit(ctx->scriptId(), toPublic(ctx->returnValue())); +} + +void QScriptEnginePrivate::notifyException_helper(QScriptContextPrivate *ctx) +{ + bool hasHandler = (ctx->exceptionHandlerContext() != 0); + m_agent->exceptionThrow(ctx->scriptId(), toPublic(ctx->returnValue()), hasHandler); +} + +void QScriptEnginePrivate::notifyExceptionCatch_helper(QScriptContextPrivate *ctx) +{ + m_agent->exceptionCatch(ctx->scriptId(), toPublic(ctx->returnValue())); +} + +void QScriptEnginePrivate::notifyDebugger(QScriptContextPrivate *ctx) +{ + if (m_agent && m_agent->supportsExtension(QScriptEngineAgent::DebuggerInvocationRequest)) { + QVariantList args; + args.append(ctx->scriptId()); + args.append(ctx->currentLine); + args.append(ctx->currentColumn); + QVariant ret = m_agent->extension(QScriptEngineAgent::DebuggerInvocationRequest, args); + QScriptValueImpl val = valueFromVariant(ret); + if (val.isValid()) + ctx->m_result = val; + } +} + +#endif // Q_SCRIPT_NO_EVENT_NOTIFY + +QScriptString QScriptEnginePrivate::internedString(const QString &str) +{ + return internedString(nameId(str, /*persistent=*/false)); +} + +QScriptString QScriptEnginePrivate::internedString(QScriptNameIdImpl *nid) +{ + if (!nid) + return QScriptString(); + QScriptStringPrivate *d = m_internedStrings.value(nid); + if (!d) { + d = m_internedStringRepository.get(); + d->nameId = nid; + d->engine = this; + m_internedStrings.insert(d->nameId, d); + } + QScriptString result; + QScriptStringPrivate::init(result, d); + return result; +} + +void QScriptEnginePrivate::uninternString(QScriptStringPrivate *d) +{ + Q_ASSERT(d->nameId); + QHash<QScriptNameIdImpl*, QScriptStringPrivate*>::iterator it; + it = m_internedStrings.find(d->nameId); + Q_ASSERT(it != m_internedStrings.end()); + m_internedStrings.erase(it); + m_internedStringRepository.release(d); +} + +QScriptValueImpl QScriptEnginePrivate::toImpl_helper(const QScriptValue &value) +{ + QScriptValuePrivate *p = QScriptValuePrivate::get(value); + Q_ASSERT(p != 0); + Q_ASSERT(p->value.type() == QScript::LazyStringType); + QString str = *p->value.m_lazy_string_value; + if (!p->ref.deref()) + delete p; + QScriptValueImpl v; + newString(&v, str); + p = registerValue(v); + QScriptValuePrivate::init(const_cast<QScriptValue&>(value), p); + return v; +} + +QScriptValueImpl QScriptEnginePrivate::newObject(QScriptClass *scriptClass, + const QScriptValueImpl &data) +{ + if (!scriptClass) + return QScriptValueImpl(); + QScriptValueImpl v; + QScriptValueImpl proto = toImpl(scriptClass->prototype()); + if (!proto.isObject()) + proto = objectConstructor->publicPrototype; + newObject(&v, proto); + QScriptClassPrivate *cls_p = QScriptClassPrivate::get(scriptClass); + QScriptClassInfo *info = cls_p->classInfo(); + v.setClassInfo(info); + if (info->type() & QScriptClassInfo::FunctionBased) { + QScriptFunction *fun = cls_p->newFunction(); + v.setObjectData(fun); + } + v.setInternalValue(data); + return v; +} + +int QScriptEnginePrivate::registerCustomClassType() +{ + return ++m_class_prev_id; +} + +QScriptValueImpl QScriptEnginePrivate::objectById(qint64 id) const +{ + QScript::GCAlloc<QScriptObject>::const_iterator it; + for (it = objectAllocator.constBegin(); it != objectAllocator.constEnd(); ++it) { + const QScriptObject *obj = it.data(); + if (obj->m_id == id) { + QScriptValueImpl ret; + ret.m_type = QScript::ObjectType; + ret.m_object_value = const_cast<QScriptObject*>(obj); + return ret; + } + } + return QScriptValueImpl(); +} + +namespace QScript { + +static QScriptValueImpl qsTranslate(QScriptContextPrivate *ctx, QScriptEnginePrivate *eng, QScriptClassInfo *) +{ + if (ctx->argumentCount() < 2) + return ctx->throwError(QString::fromLatin1("qsTranslate() requires at least two arguments")); + if (!ctx->argument(0).isString()) + return ctx->throwError(QString::fromLatin1("qsTranslate(): first argument (context) must be a string")); + if (!ctx->argument(1).isString()) + return ctx->throwError(QString::fromLatin1("qsTranslate(): second argument (text) must be a string")); + if ((ctx->argumentCount() > 2) && !ctx->argument(2).isString()) + return ctx->throwError(QString::fromLatin1("qsTranslate(): third argument (comment) must be a string")); + if ((ctx->argumentCount() > 3) && !ctx->argument(3).isString()) + return ctx->throwError(QString::fromLatin1("qsTranslate(): fourth argument (encoding) must be a string")); + if ((ctx->argumentCount() > 4) && !ctx->argument(4).isNumber()) + return ctx->throwError(QString::fromLatin1("qsTranslate(): fifth argument (n) must be a number")); +#ifndef QT_NO_QOBJECT + QString context = ctx->argument(0).toString(); +#endif + QString text = ctx->argument(1).toString(); +#ifndef QT_NO_QOBJECT + QString comment; + if (ctx->argumentCount() > 2) + comment = ctx->argument(2).toString(); + QCoreApplication::Encoding encoding = QCoreApplication::CodecForTr; + if (ctx->argumentCount() > 3) { + QString encStr = ctx->argument(3).toString(); + if (encStr == QLatin1String("CodecForTr")) + encoding = QCoreApplication::CodecForTr; + else if (encStr == QLatin1String("UnicodeUTF8")) + encoding = QCoreApplication::UnicodeUTF8; + else + return ctx->throwError(QString::fromLatin1("qsTranslate(): invalid encoding '%s'").arg(encStr)); + } + int n = -1; + if (ctx->argumentCount() > 4) + n = ctx->argument(4).toInt32(); +#endif + QString result; +#ifndef QT_NO_QOBJECT + result = QCoreApplication::translate(context.toLatin1().constData(), + text.toLatin1().constData(), + comment.toLatin1().constData(), + encoding, n); +#else + result = text; +#endif + return QScriptValueImpl(eng, result); +} + +static QScriptValueImpl qTranslateNoOp(QScriptContextPrivate *ctx, QScriptEnginePrivate *, QScriptClassInfo *) +{ + return ctx->argument(1); +} + +static QScriptValueImpl qsTr(QScriptContextPrivate *ctx, QScriptEnginePrivate *eng, QScriptClassInfo *) +{ + if (ctx->argumentCount() < 1) + return ctx->throwError(QString::fromLatin1("qsTr() requires at least one argument")); + if (!ctx->argument(0).isString()) + return ctx->throwError(QString::fromLatin1("qsTr(): first argument (text) must be a string")); + if ((ctx->argumentCount() > 1) && !ctx->argument(1).isString()) + return ctx->throwError(QString::fromLatin1("qsTr(): second argument (comment) must be a string")); + if ((ctx->argumentCount() > 2) && !ctx->argument(2).isNumber()) + return ctx->throwError(QString::fromLatin1("qsTranslate(): third argument (n) must be a number")); +#ifndef QT_NO_QOBJECT + QString context; + if (ctx->parentContext()) + context = QFileInfo(ctx->parentContext()->fileName()).baseName(); +#endif + QString text = ctx->argument(0).toString(); +#ifndef QT_NO_QOBJECT + QString comment; + if (ctx->argumentCount() > 1) + comment = ctx->argument(1).toString(); + int n = -1; + if (ctx->argumentCount() > 2) + n = ctx->argument(2).toInt32(); +#endif + QString result; +#ifndef QT_NO_QOBJECT + result = QCoreApplication::translate(context.toLatin1().constData(), + text.toLatin1().constData(), + comment.toLatin1().constData(), + QCoreApplication::CodecForTr, n); +#else + result = text; +#endif + return QScriptValueImpl(eng, result); +} + +static QScriptValueImpl qTrNoOp(QScriptContextPrivate *ctx, QScriptEnginePrivate *, QScriptClassInfo *) +{ + return ctx->argument(0); +} + +} // namespace QScript + +void QScriptEnginePrivate::installTranslatorFunctions(QScriptValueImpl &object) +{ + Q_ASSERT(object.isObject()); + const QScriptValue::PropertyFlags flags = QScriptValue::SkipInEnumeration; + object.setProperty(QLatin1String("qsTranslate"), + createFunction(QScript::qsTranslate, /*length=*/5, /*classInfo=*/0), + flags); + object.setProperty(QLatin1String("QT_TRANSLATE_NOOP"), + createFunction(QScript::qTranslateNoOp, /*length=*/2, /*classInfo=*/0), + flags); + object.setProperty(QLatin1String("qsTr"), + createFunction(QScript::qsTr, /*length=*/3, /*classInfo=*/0), + flags); + object.setProperty(QLatin1String("QT_TR_NOOP"), + createFunction(QScript::qTrNoOp, /*length=*/1, /*classInfo=*/0), + flags); + + stringConstructor->addPrototypeFunction(QLatin1String("arg"), QScript::Ecma::String::method_ext_arg, 1); +} + +bool QScriptEnginePrivate::canEvaluate(const QString &program) +{ + QScript::SyntaxChecker checker; + QScript::SyntaxChecker::Result result = checker.checkSyntax(program); + return (result.state != QScript::SyntaxChecker::Intermediate); +} + +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); +} + +QT_END_NAMESPACE + +#endif // QT_NO_SCRIPT |