summaryrefslogtreecommitdiff
path: root/src/script/api/qscriptengine_impl_p.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/script/api/qscriptengine_impl_p.h')
-rw-r--r--src/script/api/qscriptengine_impl_p.h637
1 files changed, 637 insertions, 0 deletions
diff --git a/src/script/api/qscriptengine_impl_p.h b/src/script/api/qscriptengine_impl_p.h
new file mode 100644
index 0000000..116525c
--- /dev/null
+++ b/src/script/api/qscriptengine_impl_p.h
@@ -0,0 +1,637 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#ifndef QSCRIPTENGINE_IMPL_P_H
+#define QSCRIPTENGINE_IMPL_P_H
+
+#include "qscriptengine_p.h"
+#include "qscriptengineagent_p.h"
+#include "qscriptqobject_p.h"
+
+QT_BEGIN_NAMESPACE
+
+inline v8::Handle<v8::Value> QScriptEnginePrivate::makeJSValue(bool value)
+{
+ return value ? v8::True() : v8::False();
+}
+
+inline v8::Handle<v8::Value> QScriptEnginePrivate::makeJSValue(int value)
+{
+ return v8::Integer::New(value);
+}
+
+inline v8::Handle<v8::Value> QScriptEnginePrivate::makeJSValue(uint value)
+{
+ return v8::Integer::NewFromUnsigned(value);
+}
+
+inline v8::Handle<v8::Value> QScriptEnginePrivate::makeJSValue(qsreal value)
+{
+ return v8::Number::New(value);
+}
+
+inline v8::Handle<v8::Value> QScriptEnginePrivate::makeJSValue(QScriptValue::SpecialValue value) {
+ if (value == QScriptValue::NullValue)
+ return v8::Null();
+ return v8::Undefined();
+}
+
+inline v8::Handle<v8::Value> QScriptEnginePrivate::makeJSValue(const QString& value)
+{
+ return QScriptConverter::toString(value);
+}
+
+inline QScriptEnginePrivate::operator v8::Handle<v8::Context>()
+{
+ return m_v8Context;
+}
+
+/*!
+ \internal
+ returns all property names of an object (same as ECMA getOwnPropertyNames)
+*/
+inline v8::Local<v8::Array> QScriptEnginePrivate::getOwnPropertyNames(v8::Handle<v8::Object> object) const
+{
+ return m_originalGlobalObject.getOwnPropertyNames(object);
+}
+
+/*!
+ \internal
+ \note property can be index (v8::Integer) or a property (v8::String) name, according to ECMA script
+ property would be converted to a string.
+*/
+inline QScriptValue::PropertyFlags QScriptEnginePrivate::getPropertyFlags(v8::Handle<v8::Object> object, v8::Handle<v8::Value> property, const QScriptValue::ResolveFlags& mode)
+{
+ QScriptValue::PropertyFlags flags = m_originalGlobalObject.getPropertyFlags(object, property, mode);
+ if (!flags && hasInstance(scriptClassTemplate, object)) {
+ flags = getPropertyFlagsFromScriptClassInstance(object, property, mode);
+ }
+ return flags;
+}
+inline v8::Local<v8::Value> QScriptEnginePrivate::getOwnProperty(v8::Handle<v8::Object> object, v8::Handle<v8::Value> propertyName) const
+{
+ v8::Local<v8::Value> property = m_originalGlobalObject.getOwnProperty(object, propertyName);
+ if (property.IsEmpty()) {
+ // Check if the object is not an instance of a script class.
+ if (hasInstance(scriptClassTemplate, object)) {
+ property = getOwnPropertyFromScriptClassInstance(object, propertyName);
+ }
+ }
+ return property;
+}
+
+inline v8::Local<v8::Value> QScriptEnginePrivate::getOwnProperty(v8::Handle<v8::Object> object, uint32_t index) const
+{
+ return getOwnProperty(object, v8::Integer::New(index));
+}
+
+QScriptPassPointer<QScriptValuePrivate> QScriptEnginePrivate::evaluate(const QString& program, const QString& fileName, int lineNumber)
+{
+ v8::TryCatch tryCatch;
+ v8::ScriptOrigin scriptOrigin(QScriptConverter::toString(fileName), v8::Integer::New(lineNumber - 1));
+ v8::Handle<v8::Script> script;
+ // This is a risk management problem. CompileEval function is our extension to V8, so it introduces
+ // constant risk that it is or will be broken. It is safer to use the standard function instead.
+ // Of course it doesn't mean that we shouldn't fix problems in CompileEval, we want to reduce
+ // potential bug impact.
+ // FIXME: CompileEval introduces instability in V8 debugger which affects QSEAgent. This check
+ // may be removed for testing QSEA to test other code paths.
+ // http://bugreports.qt.nokia.com/browse/QTBUG-17878
+ if (m_baseQsContext.data() == m_currentQsContext // no pushContext
+ && m_currentQsContext->scopes.empty() // no pushScope
+ && m_originalGlobalObject.strictlyEquals(globalObject()) // no setGlobalObject
+ ) {
+ script = v8::Script::Compile(QScriptConverter::toString(program), &scriptOrigin);
+ } else {
+ script = v8::Script::CompileEval(QScriptConverter::toString(program), &scriptOrigin);
+ }
+ if (script.IsEmpty()) {
+ // TODO: Why don't we get the exception, as with Script::Compile()?
+ // Q_ASSERT(tryCatch.HasCaught());
+ v8::Handle<v8::Value> error = v8::Exception::SyntaxError(v8::String::New(""));
+ setException(error);
+ return new QScriptValuePrivate(this, error);
+ }
+ if (m_currentAgent)
+ m_currentAgent->scriptLoad(script, program, fileName, lineNumber);
+
+ return evaluate(script, tryCatch);
+}
+
+inline QScriptPassPointer<QScriptValuePrivate> QScriptEnginePrivate::evaluate(QScriptProgramPrivate* program)
+{
+ // FIXME: We cannot use v8::Script::Compile() because it compiles
+ // the script in the current (global) context, and uses _the
+ // original_ context when the script is evaluated later; the
+ // semantics of evaluate(QScriptProgram) is that it should
+ // evaluate the program in the current context, even if that is
+ // different from the one where the program was evaluated the
+ // first time.
+ //
+ // We cannot use v8::Script::New() because it compiles the script
+ // as if it's evaluated in a (not necessarily the current) global
+ // context. We need something like v8::Script::NewEval().
+ //
+ // For now, fall back to evaluating the program from source every
+ // time to enforce the right semantics.
+
+// v8::TryCatch tryCatch;
+// v8::Handle<v8::Script> script = program->compiled(this);
+// return evaluate(script, tryCatch);
+ if (program->isNull())
+ return InvalidValue();
+ return evaluate(program->m_program, program->m_fileName, program->m_line);
+}
+
+inline bool QScriptEnginePrivate::isEvaluating() const
+{
+ return m_state == Evaluating;
+}
+
+inline bool QScriptEnginePrivate::isDestroyed() const
+{
+ return m_state == Destroyed;
+}
+
+void QScriptEnginePrivate::collectGarbage()
+{
+ v8::V8::LowMemoryNotification();
+ while (!v8::V8::IdleNotification()) {}
+}
+
+void QScriptEnginePrivate::reportAdditionalMemoryCost(int cost)
+{
+ // In V8, AdjustAmountOfExternalAllocatedMemory need to be balanced
+ // by a negative number when the memory is released, else
+ // the garbage collector will think it still has lot of memory and
+ // will be run too often.
+
+ // The check is needed only for compatibility.
+ if (cost > 0) {
+ int currentCost = m_reportedAddtionalMemoryCost;
+ if (currentCost == - 1) {
+ // Fist time call, add a gc callback.
+ // AddGCEpilogueCallback works per Isolate, it means that we have to install
+ // one callback per each engine instance.
+ v8::V8::AddGCEpilogueCallback(GCEpilogueCallback);
+ }
+ v8::V8::AdjustAmountOfExternalAllocatedMemory(cost);
+ m_reportedAddtionalMemoryCost += cost;
+ }
+}
+
+inline void QScriptEnginePrivate::setException(v8::Handle<v8::Value> value, v8::Handle<v8::Message> msg)
+{
+ m_exception.set(value, msg);
+}
+
+inline v8::Handle<v8::Value> QScriptEnginePrivate::throwException(v8::Handle<v8::Value> value)
+{
+ setException(value);
+ v8::ThrowException(value);
+ return value;
+}
+
+inline void QScriptEnginePrivate::clearExceptions()
+{
+ m_exception.clear();
+}
+
+inline bool QScriptEnginePrivate::hasUncaughtException() const
+{
+ return m_exception;
+}
+
+inline int QScriptEnginePrivate::uncaughtExceptionLineNumber() const
+{
+ return m_exception.lineNumber();
+}
+
+inline QStringList QScriptEnginePrivate::uncaughtExceptionBacktrace() const
+{
+ return m_exception.backtrace();
+}
+
+/*!
+ \internal
+ Save the current exception on stack so it can be set again later.
+ \sa QScriptEnginePrivate::restoreException
+*/
+inline void QScriptEnginePrivate::saveException()
+{
+ m_exception.push();
+}
+
+/*!
+ \internal
+ Load a saved exception from stack. Current exception, if exists will be dropped
+ \sa QScriptEnginePrivate::saveException
+*/
+inline void QScriptEnginePrivate::restoreException()
+{
+ m_exception.pop();
+}
+
+
+inline void QScriptEnginePrivate::enterIsolate() const
+{
+ m_isolate->Enter();
+}
+
+inline void QScriptEnginePrivate::exitIsolate() const
+{
+ m_isolate->Exit();
+}
+
+inline bool QScriptEnginePrivate::isQtVariant(v8::Handle<v8::Value> value) const
+{
+ return hasInstance(m_variantTemplate, value);
+}
+
+inline bool QScriptEnginePrivate::isQtMetaObject(v8::Handle<v8::Value> value) const
+{
+ return hasInstance(m_metaObjectTemplate, value);
+}
+
+/* set the current QScriptContext, and return the old value */
+inline QScriptContextPrivate* QScriptEnginePrivate::setCurrentQSContext(QScriptContextPrivate *ctx)
+{
+ qSwap(ctx, m_currentQsContext);
+ return ctx;
+}
+
+inline void QScriptEnginePrivate::updateGlobalObjectCache()
+{
+ m_currentGlobalObject.Dispose();
+ //in V8, the Global object is a proxy to the prototype, which is the real global object.
+ // See http://code.google.com/p/v8/issues/detail?id=1078
+ m_currentGlobalObject = v8::Persistent<v8::Object>::New(v8::Handle<v8::Object>::Cast(m_v8Context->Global()->GetPrototype()));
+}
+
+inline v8::Handle<v8::Object> QScriptEnginePrivate::globalObject() const
+{
+ Q_ASSERT_X(!m_currentGlobalObject.IsEmpty(), Q_FUNC_INFO, "Global Object handle hasn't been initialized");
+ return m_currentGlobalObject;
+}
+
+inline QScriptEnginePrivate::Exception::Exception() {}
+
+inline QScriptEnginePrivate::Exception::~Exception()
+{
+ Q_ASSERT_X(m_stack.isEmpty(), Q_FUNC_INFO, "Some saved exceptions left. Asymetric pop/push found.");
+ clear();
+}
+
+inline void QScriptEnginePrivate::Exception::set(v8::Handle<v8::Value> value, v8::Handle<v8::Message> message)
+{
+ Q_ASSERT_X(!value.IsEmpty(), Q_FUNC_INFO, "Throwing an empty value handle is highly suspected");
+ clear();
+ m_value = v8::Persistent<v8::Value>::New(value);
+ m_message = v8::Persistent<v8::Message>::New(message);
+}
+
+inline void QScriptEnginePrivate::Exception::clear()
+{
+ m_value.Dispose();
+ m_value.Clear();
+ m_message.Dispose();
+ m_message.Clear();
+}
+
+inline QScriptEnginePrivate::Exception::operator bool() const
+{
+ return !m_value.IsEmpty();
+}
+
+inline QScriptEnginePrivate::Exception::operator v8::Handle<v8::Value>() const
+{
+ Q_ASSERT(*this);
+ return m_value;
+}
+
+inline int QScriptEnginePrivate::Exception::lineNumber() const
+{
+ if (m_message.IsEmpty())
+ return -1;
+ return m_message->GetLineNumber();
+}
+
+inline QStringList QScriptEnginePrivate::Exception::backtrace() const
+{
+ if (m_message.IsEmpty())
+ return QStringList();
+
+ QStringList backtrace;
+ v8::Handle<v8::StackTrace> trace = m_message->GetStackTrace();
+ if (trace.IsEmpty())
+ // FIXME it should not happen (SetCaptureStackTraceForUncaughtExceptions is called).
+ return QStringList();
+
+ for (int i = 0; i < trace->GetFrameCount(); ++i) {
+ v8::Local<v8::StackFrame> frame = trace->GetFrame(i);
+ backtrace.append(QScriptConverter::toString(frame->GetFunctionName()));
+ backtrace.append(QScriptConverter::toString(frame->GetFunctionName()));
+ backtrace.append(QString::fromAscii("()@"));
+ backtrace.append(QScriptConverter::toString(frame->GetScriptName()));
+ backtrace.append(QString::fromAscii(":"));
+ backtrace.append(QString::number(frame->GetLineNumber()));
+ }
+ return backtrace;
+}
+
+inline void QScriptEnginePrivate::Exception::push()
+{
+ m_stack.push(qMakePair(m_value, m_message));
+ m_value.Clear();
+ m_message.Clear();
+}
+
+inline void QScriptEnginePrivate::Exception::pop()
+{
+ Q_ASSERT_X(!m_stack.empty(), Q_FUNC_INFO, "Attempt to load unsaved exception found");
+ ValueMessagePair pair = m_stack.pop();
+ clear();
+ m_value = pair.first;
+ m_message = pair.second;
+}
+
+inline QScriptEnginePrivate::TypeInfos::~TypeInfos()
+{
+ clear();
+}
+
+inline void QScriptEnginePrivate::TypeInfos::registerCustomType(int type, QScriptEngine::MarshalFunction mf, QScriptEngine::DemarshalFunction df, v8::Handle<v8::Object> prototype)
+{
+ TypeInfo &info = m_infos[type];
+ static_cast<v8::Persistent<v8::Object> >(info.prototype).Dispose();
+
+ Q_ASSERT(prototype.IsEmpty() || prototype->IsObject());
+ info.marshal = mf;
+ info.demarshal = df;
+ info.prototype = v8::Persistent<v8::Object>::New(prototype);
+
+}
+
+inline QScriptEnginePrivate::TypeInfos::TypeInfo QScriptEnginePrivate::TypeInfos::value(int type) const
+{
+ return m_infos.value(type);
+}
+
+inline void QScriptEnginePrivate::TypeInfos::clear()
+{
+ QList<TypeInfo> values = m_infos.values();
+ QList<TypeInfo>::const_iterator i = values.constBegin();
+ for (; i != values.constEnd(); ++i)
+ static_cast<v8::Persistent<v8::Object> >(i->prototype).Dispose();
+ m_infos.clear();
+}
+
+inline bool QScriptEnginePrivate::hasInstance(v8::Handle<v8::FunctionTemplate> fun, v8::Handle<v8::Value> value) const
+{
+ Q_ASSERT(!value.IsEmpty());
+ return !fun.IsEmpty() && fun->HasInstance(value);
+}
+
+inline void QScriptEnginePrivate::registerAdditionalResources(QtDataBase *data)
+{
+ m_additionalResources.insert(data);
+}
+
+inline void QScriptEnginePrivate::unregisterAdditionalResources(QtDataBase *data)
+{
+ m_additionalResources.remove(data);
+}
+
+class QtScriptBagCleaner
+{
+public:
+ template<class T>
+ void operator () (T* value) const
+ {
+ value->reinitialize();
+ }
+
+ void operator() (QScriptEngineAgentPrivate *agent) const
+ {
+ agent->kill();
+ }
+
+ void operator () (QtDataBase *data) const
+ {
+ delete data;
+ }
+
+ void operator () (QScriptEngineAgentPrivate::UnloadData *data) const
+ {
+ delete data;
+ }
+};
+
+inline void QScriptEnginePrivate::deallocateAdditionalResources()
+{
+ QtScriptBagCleaner deleter;
+ m_additionalResources.forEach(deleter);
+ m_additionalResources.clear();
+}
+
+inline void QScriptEnginePrivate::registerValue(QScriptValuePrivate *data)
+{
+ m_values.insert(data);
+}
+
+inline void QScriptEnginePrivate::unregisterValue(QScriptValuePrivate *data)
+{
+ m_values.remove(data);
+}
+
+inline void QScriptEnginePrivate::registerString(QScriptStringPrivate *data)
+{
+ m_strings.insert(data);
+}
+
+inline void QScriptEnginePrivate::unregisterString(QScriptStringPrivate *data)
+{
+ m_strings.remove(data);
+}
+
+inline void QScriptEnginePrivate::registerScriptable(QScriptablePrivate *data)
+{
+ m_scriptable.insert(data);
+}
+
+inline void QScriptEnginePrivate::unregisterScriptable(QScriptablePrivate *data)
+{
+ m_scriptable.remove(data);
+}
+
+inline void QScriptEnginePrivate::registerAgent(QScriptEngineAgentPrivate *data)
+{
+ m_agents.insert(data);
+}
+
+inline void QScriptEnginePrivate::unregisterAgent(QScriptEngineAgentPrivate *data)
+{
+ m_agents.remove(data);
+ if (m_currentAgent == data)
+ m_currentAgent = 0;
+}
+
+inline void QScriptEnginePrivate::registerScript(QScriptEngineAgentPrivate::UnloadData *data)
+{
+ m_scripts.insert(data);
+}
+
+inline void QScriptEnginePrivate::unregisterScript(QScriptEngineAgentPrivate::UnloadData *data)
+{
+ m_scripts.remove(data);
+}
+
+inline void QScriptEnginePrivate::invalidateAllValues()
+{
+ QtScriptBagCleaner invalidator;
+ m_values.forEach(invalidator);
+ m_values.clear();
+}
+
+inline void QScriptEnginePrivate::invalidateAllString()
+{
+ QtScriptBagCleaner invalidator;
+ m_strings.forEach(invalidator);
+ m_strings.clear();
+}
+
+inline void QScriptEnginePrivate::invalidateAllScriptable()
+{
+ QtScriptBagCleaner invalidator;
+ m_scriptable.forEach(invalidator);
+ m_scriptable.clear();
+}
+
+inline void QScriptEnginePrivate::invalidateAllAgents()
+{
+ QtScriptBagCleaner killer;
+ while (!m_agents.isEmpty()) {
+ // action of deleting agents potentially can add new agents.
+ m_agents.forEach(killer);
+ }
+}
+
+inline void QScriptEnginePrivate::invalidateAllScripts()
+{
+ QtScriptBagCleaner deleter;
+ m_scripts.forEach(deleter);
+}
+
+inline bool QScriptEnginePrivate::hasDemarshalFunction(int metaTypeId) const
+{
+ return m_typeInfos.value(metaTypeId).demarshal;
+}
+
+inline QScriptPassPointer<QScriptValuePrivate> QScriptEnginePrivate::newQObject(QScriptValuePrivate *scriptObject,
+ QObject *qtobject,
+ QScriptEngine::ValueOwnership ownership,
+ const QScriptEngine::QObjectWrapOptions &options)
+{
+ if (!scriptObject->isObject())
+ return new QScriptValuePrivate(this, newQObject(qtobject, ownership, options));
+
+ if (scriptObject->isQObject()) {
+ v8::Handle<v8::Object> jsobject = *scriptObject;
+ // scriptObject is a wrapper of an qt object.
+ QScriptQObjectData::set(jsobject, new QScriptQObjectData(this, qtobject, ownership, options));
+ return scriptObject;
+ }
+
+ // FIXME it create a new instance instead of reusing this one. It doesn't replace existing references in JS.
+ // Similar problem is in QSV::setScriptClass.
+ // Q_UNIMPLEMENTED();
+ QScriptSharedDataPointer<QScriptValuePrivate> obj(new QScriptValuePrivate(this, newQObject(qtobject, ownership, options)));
+ QScriptSharedDataPointer<QScriptValuePrivate> proto(scriptObject->prototype());
+ scriptObject->reinitialize(this, *obj);
+ scriptObject->setPrototype(proto.data());
+
+ return scriptObject;
+}
+
+inline v8::Handle<v8::FunctionTemplate> QScriptEnginePrivate::metaObjectTemplate()
+{
+ if (m_metaObjectTemplate.IsEmpty())
+ m_metaObjectTemplate = v8::Persistent<v8::FunctionTemplate>::New(createMetaObjectTemplate());
+ return m_metaObjectTemplate;
+}
+
+inline v8::Handle<v8::FunctionTemplate> QScriptEnginePrivate::variantTemplate()
+{
+ if (m_variantTemplate.IsEmpty())
+ m_variantTemplate = v8::Persistent<v8::FunctionTemplate>::New(createVariantTemplate());
+ return m_variantTemplate;
+}
+
+inline QScriptPassPointer<QScriptValuePrivate> QScriptEnginePrivate::newVariant(QScriptValuePrivate* object, const QVariant &value)
+{
+
+ if (!object->isObject())
+ return new QScriptValuePrivate(this, newVariant(value));
+
+ if (object->isVariant()) {
+ // object is a wrapper of a qvariant.
+ QtVariantData::set(*object, new QtVariantData(this, value));
+ return object;
+ }
+ // FIXME it create a new instance instead of reusing this one. It doesn't replace existing references in JS.
+ // Similar problem is in QSV::setScriptClass and QSE::newQObject.
+ // Q_UNIMPLEMENTED();
+ QScriptSharedDataPointer<QScriptValuePrivate> obj(new QScriptValuePrivate(this, newVariant(value)));
+ QScriptSharedDataPointer<QScriptValuePrivate> proto(object->prototype());
+ object->reinitialize(this, *obj);
+ object->setPrototype(proto.data());
+ return object;
+}
+
+inline void QScriptEnginePrivate::setAgent(QScriptEngineAgentPrivate *agent)
+{
+ if (agent && (agent->engine() != this)) {
+ qWarning("QScriptEngine::setAgent(): cannot set agent belonging to different engine");
+ return;
+ }
+ m_currentAgent = agent;
+}
+
+inline QScriptEngineAgentPrivate *QScriptEnginePrivate::agent() const
+{
+ return m_currentAgent;
+}
+
+void QScriptEnginePrivate::abortEvaluation(v8::Handle< v8::Value > result)
+{
+ if (!isEvaluating())
+ return;
+ m_shouldAbort = true;
+ m_abortResult.Dispose();
+ m_abortResult = v8::Persistent<v8::Value>::New(result);
+ v8::V8::TerminateExecution();
+}
+
+QT_END_NAMESPACE
+
+#endif